Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
39
arch/arm/kernel/Makefile
Normal file
39
arch/arm/kernel/Makefile
Normal file
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# Makefile for the linux kernel.
|
||||
#
|
||||
|
||||
AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
|
||||
|
||||
# Object file lists.
|
||||
|
||||
obj-y := compat.o entry-armv.o entry-common.o irq.o \
|
||||
process.o ptrace.o semaphore.o setup.o signal.o sys_arm.o \
|
||||
time.o traps.o
|
||||
|
||||
obj-$(CONFIG_ISA_DMA_API) += dma.o
|
||||
obj-$(CONFIG_ARCH_ACORN) += ecard.o
|
||||
obj-$(CONFIG_FIQ) += fiq.o
|
||||
obj-$(CONFIG_MODULES) += armksyms.o module.o
|
||||
obj-$(CONFIG_ARTHUR) += arthur.o
|
||||
obj-$(CONFIG_ISA_DMA) += dma-isa.o
|
||||
obj-$(CONFIG_PCI) += bios32.o isa.o
|
||||
obj-$(CONFIG_SMP) += smp.o
|
||||
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
|
||||
obj-$(CONFIG_OABI_COMPAT) += sys_oabi-compat.o
|
||||
|
||||
obj-$(CONFIG_CRUNCH) += crunch.o crunch-bits.o
|
||||
AFLAGS_crunch-bits.o := -Wa,-mcpu=ep9312
|
||||
|
||||
obj-$(CONFIG_CPU_XSCALE) += xscale-cp0.o
|
||||
obj-$(CONFIG_CPU_XSC3) += xscale-cp0.o
|
||||
obj-$(CONFIG_IWMMXT) += iwmmxt.o
|
||||
AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt
|
||||
|
||||
ifneq ($(CONFIG_ARCH_EBSA110),y)
|
||||
obj-y += io.o
|
||||
endif
|
||||
|
||||
head-y := head$(MMUEXT).o
|
||||
obj-$(CONFIG_DEBUG_LL) += debug.o
|
||||
|
||||
extra-y := $(head-y) init_task.o vmlinux.lds
|
||||
180
arch/arm/kernel/armksyms.c
Normal file
180
arch/arm/kernel/armksyms.c
Normal file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/armksyms.c
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/cryptohash.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/syscalls.h>
|
||||
|
||||
#include <asm/checksum.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* 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 __udivsi3(void);
|
||||
extern void __umodsi3(void);
|
||||
extern void __do_div64(void);
|
||||
|
||||
extern void __aeabi_idiv(void);
|
||||
extern void __aeabi_idivmod(void);
|
||||
extern void __aeabi_lasr(void);
|
||||
extern void __aeabi_llsl(void);
|
||||
extern void __aeabi_llsr(void);
|
||||
extern void __aeabi_lmul(void);
|
||||
extern void __aeabi_uidiv(void);
|
||||
extern void __aeabi_uidivmod(void);
|
||||
extern void __aeabi_ulcmp(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.
|
||||
*/
|
||||
#define EXPORT_CRC_ALIAS(sym) __CRC_SYMBOL(sym, "")
|
||||
|
||||
#define EXPORT_SYMBOL_ALIAS(sym,orig) \
|
||||
EXPORT_CRC_ALIAS(sym) \
|
||||
static const struct kernel_symbol __ksymtab_##sym \
|
||||
__attribute_used__ __attribute__((section("__ksymtab"))) = \
|
||||
{ (unsigned long)&orig, #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(__backtrace);
|
||||
|
||||
/* platform dependent support */
|
||||
EXPORT_SYMBOL(__udelay);
|
||||
EXPORT_SYMBOL(__const_udelay);
|
||||
|
||||
/* networking */
|
||||
EXPORT_SYMBOL(csum_partial);
|
||||
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(strchr);
|
||||
EXPORT_SYMBOL(strrchr);
|
||||
EXPORT_SYMBOL(memset);
|
||||
EXPORT_SYMBOL(memcpy);
|
||||
EXPORT_SYMBOL(memmove);
|
||||
EXPORT_SYMBOL(memchr);
|
||||
EXPORT_SYMBOL(__memzero);
|
||||
|
||||
/* user mem (segment) */
|
||||
EXPORT_SYMBOL(__strnlen_user);
|
||||
EXPORT_SYMBOL(__strncpy_from_user);
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
EXPORT_SYMBOL(__copy_from_user);
|
||||
EXPORT_SYMBOL(__copy_to_user);
|
||||
EXPORT_SYMBOL(__clear_user);
|
||||
|
||||
EXPORT_SYMBOL(__get_user_1);
|
||||
EXPORT_SYMBOL(__get_user_2);
|
||||
EXPORT_SYMBOL(__get_user_4);
|
||||
|
||||
EXPORT_SYMBOL(__put_user_1);
|
||||
EXPORT_SYMBOL(__put_user_2);
|
||||
EXPORT_SYMBOL(__put_user_4);
|
||||
EXPORT_SYMBOL(__put_user_8);
|
||||
#endif
|
||||
|
||||
/* crypto hash */
|
||||
EXPORT_SYMBOL(sha_transform);
|
||||
|
||||
/* 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(__udivsi3);
|
||||
EXPORT_SYMBOL(__umodsi3);
|
||||
EXPORT_SYMBOL(__do_div64);
|
||||
|
||||
#ifdef CONFIG_AEABI
|
||||
EXPORT_SYMBOL(__aeabi_idiv);
|
||||
EXPORT_SYMBOL(__aeabi_idivmod);
|
||||
EXPORT_SYMBOL(__aeabi_lasr);
|
||||
EXPORT_SYMBOL(__aeabi_llsl);
|
||||
EXPORT_SYMBOL(__aeabi_llsr);
|
||||
EXPORT_SYMBOL(__aeabi_lmul);
|
||||
EXPORT_SYMBOL(__aeabi_uidiv);
|
||||
EXPORT_SYMBOL(__aeabi_uidivmod);
|
||||
EXPORT_SYMBOL(__aeabi_ulcmp);
|
||||
#endif
|
||||
|
||||
/* 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);
|
||||
EXPORT_SYMBOL(_find_first_bit_le);
|
||||
EXPORT_SYMBOL(_find_next_bit_le);
|
||||
|
||||
#ifdef __ARMEB__
|
||||
EXPORT_SYMBOL(_set_bit_be);
|
||||
EXPORT_SYMBOL(_test_and_set_bit_be);
|
||||
EXPORT_SYMBOL(_clear_bit_be);
|
||||
EXPORT_SYMBOL(_test_and_clear_bit_be);
|
||||
EXPORT_SYMBOL(_change_bit_be);
|
||||
EXPORT_SYMBOL(_test_and_change_bit_be);
|
||||
EXPORT_SYMBOL(_find_first_zero_bit_be);
|
||||
EXPORT_SYMBOL(_find_next_zero_bit_be);
|
||||
EXPORT_SYMBOL(_find_first_bit_be);
|
||||
EXPORT_SYMBOL(_find_next_bit_be);
|
||||
#endif
|
||||
92
arch/arm/kernel/arthur.c
Normal file
92
arch/arm/kernel/arthur.c
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/arthur.c
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2000, 2001 Philip Blundell
|
||||
*
|
||||
* Arthur personality
|
||||
*/
|
||||
|
||||
/*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
/* Arthur doesn't have many signals, and a lot of those that it does
|
||||
have don't map easily to any Linux equivalent. Never mind. */
|
||||
|
||||
#define ARTHUR_SIGABRT 1
|
||||
#define ARTHUR_SIGFPE 2
|
||||
#define ARTHUR_SIGILL 3
|
||||
#define ARTHUR_SIGINT 4
|
||||
#define ARTHUR_SIGSEGV 5
|
||||
#define ARTHUR_SIGTERM 6
|
||||
#define ARTHUR_SIGSTAK 7
|
||||
#define ARTHUR_SIGUSR1 8
|
||||
#define ARTHUR_SIGUSR2 9
|
||||
#define ARTHUR_SIGOSERROR 10
|
||||
|
||||
static unsigned long arthur_to_linux_signals[32] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7,
|
||||
8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23,
|
||||
24, 25, 26, 27, 28, 29, 30, 31
|
||||
};
|
||||
|
||||
static unsigned long linux_to_arthur_signals[32] = {
|
||||
0, -1, ARTHUR_SIGINT, -1,
|
||||
ARTHUR_SIGILL, 5, ARTHUR_SIGABRT, 7,
|
||||
ARTHUR_SIGFPE, 9, ARTHUR_SIGUSR1, ARTHUR_SIGSEGV,
|
||||
ARTHUR_SIGUSR2, 13, 14, ARTHUR_SIGTERM,
|
||||
16, 17, 18, 19,
|
||||
20, 21, 22, 23,
|
||||
24, 25, 26, 27,
|
||||
28, 29, 30, 31
|
||||
};
|
||||
|
||||
static void arthur_lcall7(int nr, struct pt_regs *regs)
|
||||
{
|
||||
struct siginfo info;
|
||||
info.si_signo = SIGSWI;
|
||||
info.si_errno = nr;
|
||||
/* Bounce it to the emulator */
|
||||
send_sig_info(SIGSWI, &info, current);
|
||||
}
|
||||
|
||||
static struct exec_domain arthur_exec_domain = {
|
||||
.name = "Arthur",
|
||||
.handler = arthur_lcall7,
|
||||
.pers_low = PER_RISCOS,
|
||||
.pers_high = PER_RISCOS,
|
||||
.signal_map = arthur_to_linux_signals,
|
||||
.signal_invmap = linux_to_arthur_signals,
|
||||
.module = THIS_MODULE,
|
||||
};
|
||||
|
||||
/*
|
||||
* We could do with some locking to stop Arthur being removed while
|
||||
* processes are using it.
|
||||
*/
|
||||
|
||||
static int __init arthur_init(void)
|
||||
{
|
||||
return register_exec_domain(&arthur_exec_domain);
|
||||
}
|
||||
|
||||
static void __exit arthur_exit(void)
|
||||
{
|
||||
unregister_exec_domain(&arthur_exec_domain);
|
||||
}
|
||||
|
||||
module_init(arthur_init);
|
||||
module_exit(arthur_exit);
|
||||
112
arch/arm/kernel/asm-offsets.c
Normal file
112
arch/arm/kernel/asm-offsets.c
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 1995-2003 Russell King
|
||||
* 2001-2002 Keith Owens
|
||||
*
|
||||
* 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/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/memory.h>
|
||||
#include <asm/procinfo.h>
|
||||
|
||||
/*
|
||||
* Make sure that the compiler and target are compatible.
|
||||
*/
|
||||
#if defined(__APCS_26__)
|
||||
#error Sorry, your compiler targets APCS-26 but this kernel requires APCS-32
|
||||
#endif
|
||||
/*
|
||||
* GCC 3.0, 3.1: general bad code generation.
|
||||
* GCC 3.2.0: incorrect function argument offset calculation.
|
||||
* GCC 3.2.x: miscompiles NEW_AUX_ENT in fs/binfmt_elf.c
|
||||
* (http://gcc.gnu.org/PR8896) and incorrect structure
|
||||
* initialisation in fs/jffs2/erase.c
|
||||
*/
|
||||
#if (__GNUC__ == 3 && __GNUC_MINOR__ < 3)
|
||||
#error Your compiler is too buggy; it is known to miscompile kernels.
|
||||
#error Known good compilers: 3.3
|
||||
#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(TI_FLAGS, offsetof(struct thread_info, flags));
|
||||
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
|
||||
DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
|
||||
DEFINE(TI_TASK, offsetof(struct thread_info, task));
|
||||
DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
|
||||
DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
|
||||
DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain));
|
||||
DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context));
|
||||
DEFINE(TI_USED_CP, offsetof(struct thread_info, used_cp));
|
||||
DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value));
|
||||
DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate));
|
||||
DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate));
|
||||
#ifdef CONFIG_IWMMXT
|
||||
DEFINE(TI_IWMMXT_STATE, offsetof(struct thread_info, fpstate.iwmmxt));
|
||||
#endif
|
||||
#ifdef CONFIG_CRUNCH
|
||||
DEFINE(TI_CRUNCH_STATE, offsetof(struct thread_info, crunchstate));
|
||||
#endif
|
||||
BLANK();
|
||||
DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
|
||||
DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));
|
||||
DEFINE(S_R2, offsetof(struct pt_regs, ARM_r2));
|
||||
DEFINE(S_R3, offsetof(struct pt_regs, ARM_r3));
|
||||
DEFINE(S_R4, offsetof(struct pt_regs, ARM_r4));
|
||||
DEFINE(S_R5, offsetof(struct pt_regs, ARM_r5));
|
||||
DEFINE(S_R6, offsetof(struct pt_regs, ARM_r6));
|
||||
DEFINE(S_R7, offsetof(struct pt_regs, ARM_r7));
|
||||
DEFINE(S_R8, offsetof(struct pt_regs, ARM_r8));
|
||||
DEFINE(S_R9, offsetof(struct pt_regs, ARM_r9));
|
||||
DEFINE(S_R10, offsetof(struct pt_regs, ARM_r10));
|
||||
DEFINE(S_FP, offsetof(struct pt_regs, ARM_fp));
|
||||
DEFINE(S_IP, offsetof(struct pt_regs, ARM_ip));
|
||||
DEFINE(S_SP, offsetof(struct pt_regs, ARM_sp));
|
||||
DEFINE(S_LR, offsetof(struct pt_regs, ARM_lr));
|
||||
DEFINE(S_PC, offsetof(struct pt_regs, ARM_pc));
|
||||
DEFINE(S_PSR, offsetof(struct pt_regs, ARM_cpsr));
|
||||
DEFINE(S_OLD_R0, offsetof(struct pt_regs, ARM_ORIG_r0));
|
||||
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
|
||||
BLANK();
|
||||
#if __LINUX_ARM_ARCH__ >= 6
|
||||
DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id));
|
||||
BLANK();
|
||||
#endif
|
||||
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();
|
||||
DEFINE(PAGE_SZ, PAGE_SIZE);
|
||||
BLANK();
|
||||
DEFINE(SYS_ERROR0, 0x9f0000);
|
||||
BLANK();
|
||||
DEFINE(SIZEOF_MACHINE_DESC, sizeof(struct machine_desc));
|
||||
DEFINE(MACHINFO_TYPE, offsetof(struct machine_desc, nr));
|
||||
DEFINE(MACHINFO_NAME, offsetof(struct machine_desc, name));
|
||||
DEFINE(MACHINFO_PHYSIO, offsetof(struct machine_desc, phys_io));
|
||||
DEFINE(MACHINFO_PGOFFIO, offsetof(struct machine_desc, io_pg_offst));
|
||||
BLANK();
|
||||
DEFINE(PROC_INFO_SZ, sizeof(struct proc_info_list));
|
||||
DEFINE(PROCINFO_INITFUNC, offsetof(struct proc_info_list, __cpu_flush));
|
||||
DEFINE(PROCINFO_MM_MMUFLAGS, offsetof(struct proc_info_list, __cpu_mm_mmu_flags));
|
||||
DEFINE(PROCINFO_IO_MMUFLAGS, offsetof(struct proc_info_list, __cpu_io_mmu_flags));
|
||||
return 0;
|
||||
}
|
||||
701
arch/arm/kernel/bios32.c
Normal file
701
arch/arm/kernel/bios32.c
Normal file
@@ -0,0 +1,701 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/bios32.c
|
||||
*
|
||||
* PCI bios-type initialisation for PCI machines
|
||||
*
|
||||
* Bits taken from various places.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/mach/pci.h>
|
||||
|
||||
static int debug_pci;
|
||||
static int use_firmware;
|
||||
|
||||
/*
|
||||
* We can't use pci_find_device() here since we are
|
||||
* called from interrupt context.
|
||||
*/
|
||||
static void pcibios_bus_report_status(struct pci_bus *bus, u_int status_mask, int warn)
|
||||
{
|
||||
struct pci_dev *dev;
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
u16 status;
|
||||
|
||||
/*
|
||||
* ignore host bridge - we handle
|
||||
* that separately
|
||||
*/
|
||||
if (dev->bus->number == 0 && dev->devfn == 0)
|
||||
continue;
|
||||
|
||||
pci_read_config_word(dev, PCI_STATUS, &status);
|
||||
if (status == 0xffff)
|
||||
continue;
|
||||
|
||||
if ((status & status_mask) == 0)
|
||||
continue;
|
||||
|
||||
/* clear the status errors */
|
||||
pci_write_config_word(dev, PCI_STATUS, status & status_mask);
|
||||
|
||||
if (warn)
|
||||
printk("(%s: %04X) ", pci_name(dev), status);
|
||||
}
|
||||
|
||||
list_for_each_entry(dev, &bus->devices, bus_list)
|
||||
if (dev->subordinate)
|
||||
pcibios_bus_report_status(dev->subordinate, status_mask, warn);
|
||||
}
|
||||
|
||||
void pcibios_report_status(u_int status_mask, int warn)
|
||||
{
|
||||
struct list_head *l;
|
||||
|
||||
list_for_each(l, &pci_root_buses) {
|
||||
struct pci_bus *bus = pci_bus_b(l);
|
||||
|
||||
pcibios_bus_report_status(bus, status_mask, warn);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We don't use this to fix the device, but initialisation of it.
|
||||
* It's not the correct use for this, but it works.
|
||||
* Note that the arbiter/ISA bridge appears to be buggy, specifically in
|
||||
* the following area:
|
||||
* 1. park on CPU
|
||||
* 2. ISA bridge ping-pong
|
||||
* 3. ISA bridge master handling of target RETRY
|
||||
*
|
||||
* Bug 3 is responsible for the sound DMA grinding to a halt. We now
|
||||
* live with bug 2.
|
||||
*/
|
||||
static void __devinit pci_fixup_83c553(struct pci_dev *dev)
|
||||
{
|
||||
/*
|
||||
* Set memory region to start at address 0, and enable IO
|
||||
*/
|
||||
pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, PCI_BASE_ADDRESS_SPACE_MEMORY);
|
||||
pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_IO);
|
||||
|
||||
dev->resource[0].end -= dev->resource[0].start;
|
||||
dev->resource[0].start = 0;
|
||||
|
||||
/*
|
||||
* All memory requests from ISA to be channelled to PCI
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x48, 0xff);
|
||||
|
||||
/*
|
||||
* Enable ping-pong on bus master to ISA bridge transactions.
|
||||
* This improves the sound DMA substantially. The fixed
|
||||
* priority arbiter also helps (see below).
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x42, 0x01);
|
||||
|
||||
/*
|
||||
* Enable PCI retry
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x40, 0x22);
|
||||
|
||||
/*
|
||||
* We used to set the arbiter to "park on last master" (bit
|
||||
* 1 set), but unfortunately the CyberPro does not park the
|
||||
* bus. We must therefore park on CPU. Unfortunately, this
|
||||
* may trigger yet another bug in the 553.
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x83, 0x02);
|
||||
|
||||
/*
|
||||
* Make the ISA DMA request lowest priority, and disable
|
||||
* rotating priorities completely.
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x80, 0x11);
|
||||
pci_write_config_byte(dev, 0x81, 0x00);
|
||||
|
||||
/*
|
||||
* Route INTA input to IRQ 11, and set IRQ11 to be level
|
||||
* sensitive.
|
||||
*/
|
||||
pci_write_config_word(dev, 0x44, 0xb000);
|
||||
outb(0x08, 0x4d1);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_83C553, pci_fixup_83c553);
|
||||
|
||||
static void __devinit pci_fixup_unassign(struct pci_dev *dev)
|
||||
{
|
||||
dev->resource[0].end -= dev->resource[0].start;
|
||||
dev->resource[0].start = 0;
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_89C940F, pci_fixup_unassign);
|
||||
|
||||
/*
|
||||
* Prevent the PCI layer from seeing the resources allocated to this device
|
||||
* if it is the host bridge by marking it as such. These resources are of
|
||||
* no consequence to the PCI layer (they are handled elsewhere).
|
||||
*/
|
||||
static void __devinit pci_fixup_dec21285(struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (dev->devfn == 0) {
|
||||
dev->class &= 0xff;
|
||||
dev->class |= PCI_CLASS_BRIDGE_HOST << 8;
|
||||
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
||||
dev->resource[i].start = 0;
|
||||
dev->resource[i].end = 0;
|
||||
dev->resource[i].flags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21285, pci_fixup_dec21285);
|
||||
|
||||
/*
|
||||
* Same as above. The PrPMC800 carrier board for the PrPMC1100
|
||||
* card maps the host-bridge @ 00:01:00 for some reason and it
|
||||
* ends up getting scanned. Note that we only want to do this
|
||||
* fixup when we find the IXP4xx on a PrPMC system, which is why
|
||||
* we check the machine type. We could be running on a board
|
||||
* with an IXP4xx target device and we don't want to kill the
|
||||
* resources in that case.
|
||||
*/
|
||||
static void __devinit pci_fixup_prpmc1100(struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (machine_is_prpmc1100()) {
|
||||
dev->class &= 0xff;
|
||||
dev->class |= PCI_CLASS_BRIDGE_HOST << 8;
|
||||
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
||||
dev->resource[i].start = 0;
|
||||
dev->resource[i].end = 0;
|
||||
dev->resource[i].flags = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IXP4XX, pci_fixup_prpmc1100);
|
||||
|
||||
/*
|
||||
* PCI IDE controllers use non-standard I/O port decoding, respect it.
|
||||
*/
|
||||
static void __devinit pci_fixup_ide_bases(struct pci_dev *dev)
|
||||
{
|
||||
struct resource *r;
|
||||
int i;
|
||||
|
||||
if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE)
|
||||
return;
|
||||
|
||||
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
||||
r = dev->resource + i;
|
||||
if ((r->start & ~0x80) == 0x374) {
|
||||
r->start |= 2;
|
||||
r->end = r->start;
|
||||
}
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases);
|
||||
|
||||
/*
|
||||
* Put the DEC21142 to sleep
|
||||
*/
|
||||
static void __devinit pci_fixup_dec21142(struct pci_dev *dev)
|
||||
{
|
||||
pci_write_config_dword(dev, 0x40, 0x80000000);
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_21142, pci_fixup_dec21142);
|
||||
|
||||
/*
|
||||
* The CY82C693 needs some rather major fixups to ensure that it does
|
||||
* the right thing. Idea from the Alpha people, with a few additions.
|
||||
*
|
||||
* We ensure that the IDE base registers are set to 1f0/3f4 for the
|
||||
* primary bus, and 170/374 for the secondary bus. Also, hide them
|
||||
* from the PCI subsystem view as well so we won't try to perform
|
||||
* our own auto-configuration on them.
|
||||
*
|
||||
* In addition, we ensure that the PCI IDE interrupts are routed to
|
||||
* IRQ 14 and IRQ 15 respectively.
|
||||
*
|
||||
* The above gets us to a point where the IDE on this device is
|
||||
* functional. However, The CY82C693U _does not work_ in bus
|
||||
* master mode without locking the PCI bus solid.
|
||||
*/
|
||||
static void __devinit pci_fixup_cy82c693(struct pci_dev *dev)
|
||||
{
|
||||
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE) {
|
||||
u32 base0, base1;
|
||||
|
||||
if (dev->class & 0x80) { /* primary */
|
||||
base0 = 0x1f0;
|
||||
base1 = 0x3f4;
|
||||
} else { /* secondary */
|
||||
base0 = 0x170;
|
||||
base1 = 0x374;
|
||||
}
|
||||
|
||||
pci_write_config_dword(dev, PCI_BASE_ADDRESS_0,
|
||||
base0 | PCI_BASE_ADDRESS_SPACE_IO);
|
||||
pci_write_config_dword(dev, PCI_BASE_ADDRESS_1,
|
||||
base1 | PCI_BASE_ADDRESS_SPACE_IO);
|
||||
|
||||
dev->resource[0].start = 0;
|
||||
dev->resource[0].end = 0;
|
||||
dev->resource[0].flags = 0;
|
||||
|
||||
dev->resource[1].start = 0;
|
||||
dev->resource[1].end = 0;
|
||||
dev->resource[1].flags = 0;
|
||||
} else if (PCI_FUNC(dev->devfn) == 0) {
|
||||
/*
|
||||
* Setup IDE IRQ routing.
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x4b, 14);
|
||||
pci_write_config_byte(dev, 0x4c, 15);
|
||||
|
||||
/*
|
||||
* Disable FREQACK handshake, enable USB.
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x4d, 0x41);
|
||||
|
||||
/*
|
||||
* Enable PCI retry, and PCI post-write buffer.
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x44, 0x17);
|
||||
|
||||
/*
|
||||
* Enable ISA master and DMA post write buffering.
|
||||
*/
|
||||
pci_write_config_byte(dev, 0x45, 0x03);
|
||||
}
|
||||
}
|
||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693, pci_fixup_cy82c693);
|
||||
|
||||
void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
|
||||
{
|
||||
if (debug_pci)
|
||||
printk("PCI: Assigning IRQ %02d to %s\n", irq, pci_name(dev));
|
||||
pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the bus contains any of these devices, then we must not turn on
|
||||
* parity checking of any kind. Currently this is CyberPro 20x0 only.
|
||||
*/
|
||||
static inline int pdev_bad_for_parity(struct pci_dev *dev)
|
||||
{
|
||||
return (dev->vendor == PCI_VENDOR_ID_INTERG &&
|
||||
(dev->device == PCI_DEVICE_ID_INTERG_2000 ||
|
||||
dev->device == PCI_DEVICE_ID_INTERG_2010));
|
||||
}
|
||||
|
||||
/*
|
||||
* Adjust the device resources from bus-centric to Linux-centric.
|
||||
*/
|
||||
static void __devinit
|
||||
pdev_fixup_device_resources(struct pci_sys_data *root, struct pci_dev *dev)
|
||||
{
|
||||
resource_size_t offset;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
|
||||
if (dev->resource[i].start == 0)
|
||||
continue;
|
||||
if (dev->resource[i].flags & IORESOURCE_MEM)
|
||||
offset = root->mem_offset;
|
||||
else
|
||||
offset = root->io_offset;
|
||||
|
||||
dev->resource[i].start += offset;
|
||||
dev->resource[i].end += offset;
|
||||
}
|
||||
}
|
||||
|
||||
static void __devinit
|
||||
pbus_assign_bus_resources(struct pci_bus *bus, struct pci_sys_data *root)
|
||||
{
|
||||
struct pci_dev *dev = bus->self;
|
||||
int i;
|
||||
|
||||
if (!dev) {
|
||||
/*
|
||||
* Assign root bus resources.
|
||||
*/
|
||||
for (i = 0; i < 3; i++)
|
||||
bus->resource[i] = root->resource[i];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* pcibios_fixup_bus - Called after each bus is probed,
|
||||
* but before its children are examined.
|
||||
*/
|
||||
void __devinit pcibios_fixup_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_sys_data *root = bus->sysdata;
|
||||
struct pci_dev *dev;
|
||||
u16 features = PCI_COMMAND_SERR | PCI_COMMAND_PARITY | PCI_COMMAND_FAST_BACK;
|
||||
|
||||
pbus_assign_bus_resources(bus, root);
|
||||
|
||||
/*
|
||||
* Walk the devices on this bus, working out what we can
|
||||
* and can't support.
|
||||
*/
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
u16 status;
|
||||
|
||||
pdev_fixup_device_resources(root, dev);
|
||||
|
||||
pci_read_config_word(dev, PCI_STATUS, &status);
|
||||
|
||||
/*
|
||||
* If any device on this bus does not support fast back
|
||||
* to back transfers, then the bus as a whole is not able
|
||||
* to support them. Having fast back to back transfers
|
||||
* on saves us one PCI cycle per transaction.
|
||||
*/
|
||||
if (!(status & PCI_STATUS_FAST_BACK))
|
||||
features &= ~PCI_COMMAND_FAST_BACK;
|
||||
|
||||
if (pdev_bad_for_parity(dev))
|
||||
features &= ~(PCI_COMMAND_SERR | PCI_COMMAND_PARITY);
|
||||
|
||||
switch (dev->class >> 8) {
|
||||
case PCI_CLASS_BRIDGE_PCI:
|
||||
pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &status);
|
||||
status |= PCI_BRIDGE_CTL_PARITY|PCI_BRIDGE_CTL_MASTER_ABORT;
|
||||
status &= ~(PCI_BRIDGE_CTL_BUS_RESET|PCI_BRIDGE_CTL_FAST_BACK);
|
||||
pci_write_config_word(dev, PCI_BRIDGE_CONTROL, status);
|
||||
break;
|
||||
|
||||
case PCI_CLASS_BRIDGE_CARDBUS:
|
||||
pci_read_config_word(dev, PCI_CB_BRIDGE_CONTROL, &status);
|
||||
status |= PCI_CB_BRIDGE_CTL_PARITY|PCI_CB_BRIDGE_CTL_MASTER_ABORT;
|
||||
pci_write_config_word(dev, PCI_CB_BRIDGE_CONTROL, status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now walk the devices again, this time setting them up.
|
||||
*/
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
u16 cmd;
|
||||
|
||||
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
||||
cmd |= features;
|
||||
pci_write_config_word(dev, PCI_COMMAND, cmd);
|
||||
|
||||
pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE,
|
||||
L1_CACHE_BYTES >> 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Propagate the flags to the PCI bridge.
|
||||
*/
|
||||
if (bus->self && bus->self->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
||||
if (features & PCI_COMMAND_FAST_BACK)
|
||||
bus->bridge_ctl |= PCI_BRIDGE_CTL_FAST_BACK;
|
||||
if (features & PCI_COMMAND_PARITY)
|
||||
bus->bridge_ctl |= PCI_BRIDGE_CTL_PARITY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Report what we did for this bus
|
||||
*/
|
||||
printk(KERN_INFO "PCI: bus%d: Fast back to back transfers %sabled\n",
|
||||
bus->number, (features & PCI_COMMAND_FAST_BACK) ? "en" : "dis");
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert from Linux-centric to bus-centric addresses for bridge devices.
|
||||
*/
|
||||
void __devinit
|
||||
pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region,
|
||||
struct resource *res)
|
||||
{
|
||||
struct pci_sys_data *root = dev->sysdata;
|
||||
unsigned long offset = 0;
|
||||
|
||||
if (res->flags & IORESOURCE_IO)
|
||||
offset = root->io_offset;
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
offset = root->mem_offset;
|
||||
|
||||
region->start = res->start - offset;
|
||||
region->end = res->end - offset;
|
||||
}
|
||||
|
||||
void __devinit
|
||||
pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res,
|
||||
struct pci_bus_region *region)
|
||||
{
|
||||
struct pci_sys_data *root = dev->sysdata;
|
||||
unsigned long offset = 0;
|
||||
|
||||
if (res->flags & IORESOURCE_IO)
|
||||
offset = root->io_offset;
|
||||
if (res->flags & IORESOURCE_MEM)
|
||||
offset = root->mem_offset;
|
||||
|
||||
res->start = region->start + offset;
|
||||
res->end = region->end + offset;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG
|
||||
EXPORT_SYMBOL(pcibios_fixup_bus);
|
||||
EXPORT_SYMBOL(pcibios_resource_to_bus);
|
||||
EXPORT_SYMBOL(pcibios_bus_to_resource);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is the standard PCI-PCI bridge swizzling algorithm:
|
||||
*
|
||||
* Dev: 0 1 2 3
|
||||
* A A B C D
|
||||
* B B C D A
|
||||
* C C D A B
|
||||
* D D A B C
|
||||
* ^^^^^^^^^^ irq pin on bridge
|
||||
*/
|
||||
u8 __devinit pci_std_swizzle(struct pci_dev *dev, u8 *pinp)
|
||||
{
|
||||
int pin = *pinp - 1;
|
||||
|
||||
while (dev->bus->self) {
|
||||
pin = (pin + PCI_SLOT(dev->devfn)) & 3;
|
||||
/*
|
||||
* move up the chain of bridges,
|
||||
* swizzling as we go.
|
||||
*/
|
||||
dev = dev->bus->self;
|
||||
}
|
||||
*pinp = pin + 1;
|
||||
|
||||
return PCI_SLOT(dev->devfn);
|
||||
}
|
||||
|
||||
/*
|
||||
* Swizzle the device pin each time we cross a bridge.
|
||||
* This might update pin and returns the slot number.
|
||||
*/
|
||||
static u8 __devinit pcibios_swizzle(struct pci_dev *dev, u8 *pin)
|
||||
{
|
||||
struct pci_sys_data *sys = dev->sysdata;
|
||||
int slot = 0, oldpin = *pin;
|
||||
|
||||
if (sys->swizzle)
|
||||
slot = sys->swizzle(dev, pin);
|
||||
|
||||
if (debug_pci)
|
||||
printk("PCI: %s swizzling pin %d => pin %d slot %d\n",
|
||||
pci_name(dev), oldpin, *pin, slot);
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
/*
|
||||
* Map a slot/pin to an IRQ.
|
||||
*/
|
||||
static int pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
|
||||
{
|
||||
struct pci_sys_data *sys = dev->sysdata;
|
||||
int irq = -1;
|
||||
|
||||
if (sys->map_irq)
|
||||
irq = sys->map_irq(dev, slot, pin);
|
||||
|
||||
if (debug_pci)
|
||||
printk("PCI: %s mapping slot %d pin %d => irq %d\n",
|
||||
pci_name(dev), slot, pin, irq);
|
||||
|
||||
return irq;
|
||||
}
|
||||
|
||||
static void __init pcibios_init_hw(struct hw_pci *hw)
|
||||
{
|
||||
struct pci_sys_data *sys = NULL;
|
||||
int ret;
|
||||
int nr, busnr;
|
||||
|
||||
for (nr = busnr = 0; nr < hw->nr_controllers; nr++) {
|
||||
sys = kzalloc(sizeof(struct pci_sys_data), GFP_KERNEL);
|
||||
if (!sys)
|
||||
panic("PCI: unable to allocate sys data!");
|
||||
|
||||
sys->hw = hw;
|
||||
sys->busnr = busnr;
|
||||
sys->swizzle = hw->swizzle;
|
||||
sys->map_irq = hw->map_irq;
|
||||
sys->resource[0] = &ioport_resource;
|
||||
sys->resource[1] = &iomem_resource;
|
||||
|
||||
ret = hw->setup(nr, sys);
|
||||
|
||||
if (ret > 0) {
|
||||
sys->bus = hw->scan(nr, sys);
|
||||
|
||||
if (!sys->bus)
|
||||
panic("PCI: unable to scan bus!");
|
||||
|
||||
busnr = sys->bus->subordinate + 1;
|
||||
|
||||
list_add(&sys->node, &hw->buses);
|
||||
} else {
|
||||
kfree(sys);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void __init pci_common_init(struct hw_pci *hw)
|
||||
{
|
||||
struct pci_sys_data *sys;
|
||||
|
||||
INIT_LIST_HEAD(&hw->buses);
|
||||
|
||||
if (hw->preinit)
|
||||
hw->preinit();
|
||||
pcibios_init_hw(hw);
|
||||
if (hw->postinit)
|
||||
hw->postinit();
|
||||
|
||||
pci_fixup_irqs(pcibios_swizzle, pcibios_map_irq);
|
||||
|
||||
list_for_each_entry(sys, &hw->buses, node) {
|
||||
struct pci_bus *bus = sys->bus;
|
||||
|
||||
if (!use_firmware) {
|
||||
/*
|
||||
* Size the bridge windows.
|
||||
*/
|
||||
pci_bus_size_bridges(bus);
|
||||
|
||||
/*
|
||||
* Assign resources.
|
||||
*/
|
||||
pci_bus_assign_resources(bus);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell drivers about devices found.
|
||||
*/
|
||||
pci_bus_add_devices(bus);
|
||||
}
|
||||
}
|
||||
|
||||
char * __init pcibios_setup(char *str)
|
||||
{
|
||||
if (!strcmp(str, "debug")) {
|
||||
debug_pci = 1;
|
||||
return NULL;
|
||||
} else if (!strcmp(str, "firmware")) {
|
||||
use_firmware = 1;
|
||||
return NULL;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* From arch/i386/kernel/pci-i386.c:
|
||||
*
|
||||
* We need to avoid collisions with `mirrored' VGA ports
|
||||
* and other strange ISA hardware, so we always want the
|
||||
* addresses to be allocated in the 0x000-0x0ff region
|
||||
* modulo 0x400.
|
||||
*
|
||||
* Why? Because some silly external IO cards only decode
|
||||
* the low 10 bits of the IO address. The 0x00-0xff region
|
||||
* is reserved for motherboard devices that decode all 16
|
||||
* bits, so it's ok to allocate at, say, 0x2800-0x28ff,
|
||||
* but we want to try to avoid allocating at 0x2900-0x2bff
|
||||
* which might be mirrored at 0x0100-0x03ff..
|
||||
*/
|
||||
void pcibios_align_resource(void *data, struct resource *res,
|
||||
resource_size_t size, resource_size_t align)
|
||||
{
|
||||
resource_size_t start = res->start;
|
||||
|
||||
if (res->flags & IORESOURCE_IO && start & 0x300)
|
||||
start = (start + 0x3ff) & ~0x3ff;
|
||||
|
||||
res->start = (start + align - 1) & ~(align - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcibios_enable_device - Enable I/O and memory.
|
||||
* @dev: PCI device to be enabled
|
||||
*/
|
||||
int pcibios_enable_device(struct pci_dev *dev, int mask)
|
||||
{
|
||||
u16 cmd, old_cmd;
|
||||
int idx;
|
||||
struct resource *r;
|
||||
|
||||
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
||||
old_cmd = cmd;
|
||||
for (idx = 0; idx < 6; idx++) {
|
||||
/* Only set up the requested stuff */
|
||||
if (!(mask & (1 << idx)))
|
||||
continue;
|
||||
|
||||
r = dev->resource + idx;
|
||||
if (!r->start && r->end) {
|
||||
printk(KERN_ERR "PCI: Device %s not available because"
|
||||
" of resource collisions\n", pci_name(dev));
|
||||
return -EINVAL;
|
||||
}
|
||||
if (r->flags & IORESOURCE_IO)
|
||||
cmd |= PCI_COMMAND_IO;
|
||||
if (r->flags & IORESOURCE_MEM)
|
||||
cmd |= PCI_COMMAND_MEMORY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Bridges (eg, cardbus bridges) need to be fully enabled
|
||||
*/
|
||||
if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE)
|
||||
cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
|
||||
|
||||
if (cmd != old_cmd) {
|
||||
printk("PCI: enabling device %s (%04x -> %04x)\n",
|
||||
pci_name(dev), old_cmd, cmd);
|
||||
pci_write_config_word(dev, PCI_COMMAND, cmd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
|
||||
enum pci_mmap_state mmap_state, int write_combine)
|
||||
{
|
||||
struct pci_sys_data *root = dev->sysdata;
|
||||
unsigned long phys;
|
||||
|
||||
if (mmap_state == pci_mmap_io) {
|
||||
return -EINVAL;
|
||||
} else {
|
||||
phys = vma->vm_pgoff + (root->mem_offset >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark this as IO
|
||||
*/
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
|
||||
if (remap_pfn_range(vma, vma->vm_start, phys,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot))
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
366
arch/arm/kernel/calls.S
Normal file
366
arch/arm/kernel/calls.S
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/calls.S
|
||||
*
|
||||
* Copyright (C) 1995-2005 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.
|
||||
*
|
||||
* This file is included thrice in entry-common.S
|
||||
*/
|
||||
/* 0 */ CALL(sys_restart_syscall)
|
||||
CALL(sys_exit)
|
||||
CALL(sys_fork_wrapper)
|
||||
CALL(sys_read)
|
||||
CALL(sys_write)
|
||||
/* 5 */ CALL(sys_open)
|
||||
CALL(sys_close)
|
||||
CALL(sys_ni_syscall) /* was sys_waitpid */
|
||||
CALL(sys_creat)
|
||||
CALL(sys_link)
|
||||
/* 10 */ CALL(sys_unlink)
|
||||
CALL(sys_execve_wrapper)
|
||||
CALL(sys_chdir)
|
||||
CALL(OBSOLETE(sys_time)) /* used by libc4 */
|
||||
CALL(sys_mknod)
|
||||
/* 15 */ CALL(sys_chmod)
|
||||
CALL(sys_lchown16)
|
||||
CALL(sys_ni_syscall) /* was sys_break */
|
||||
CALL(sys_ni_syscall) /* was sys_stat */
|
||||
CALL(sys_lseek)
|
||||
/* 20 */ CALL(sys_getpid)
|
||||
CALL(sys_mount)
|
||||
CALL(OBSOLETE(sys_oldumount)) /* used by libc4 */
|
||||
CALL(sys_setuid16)
|
||||
CALL(sys_getuid16)
|
||||
/* 25 */ CALL(OBSOLETE(sys_stime))
|
||||
CALL(sys_ptrace)
|
||||
CALL(OBSOLETE(sys_alarm)) /* used by libc4 */
|
||||
CALL(sys_ni_syscall) /* was sys_fstat */
|
||||
CALL(sys_pause)
|
||||
/* 30 */ CALL(OBSOLETE(sys_utime)) /* used by libc4 */
|
||||
CALL(sys_ni_syscall) /* was sys_stty */
|
||||
CALL(sys_ni_syscall) /* was sys_getty */
|
||||
CALL(sys_access)
|
||||
CALL(sys_nice)
|
||||
/* 35 */ CALL(sys_ni_syscall) /* was sys_ftime */
|
||||
CALL(sys_sync)
|
||||
CALL(sys_kill)
|
||||
CALL(sys_rename)
|
||||
CALL(sys_mkdir)
|
||||
/* 40 */ CALL(sys_rmdir)
|
||||
CALL(sys_dup)
|
||||
CALL(sys_pipe)
|
||||
CALL(sys_times)
|
||||
CALL(sys_ni_syscall) /* was sys_prof */
|
||||
/* 45 */ CALL(sys_brk)
|
||||
CALL(sys_setgid16)
|
||||
CALL(sys_getgid16)
|
||||
CALL(sys_ni_syscall) /* was sys_signal */
|
||||
CALL(sys_geteuid16)
|
||||
/* 50 */ CALL(sys_getegid16)
|
||||
CALL(sys_acct)
|
||||
CALL(sys_umount)
|
||||
CALL(sys_ni_syscall) /* was sys_lock */
|
||||
CALL(sys_ioctl)
|
||||
/* 55 */ CALL(sys_fcntl)
|
||||
CALL(sys_ni_syscall) /* was sys_mpx */
|
||||
CALL(sys_setpgid)
|
||||
CALL(sys_ni_syscall) /* was sys_ulimit */
|
||||
CALL(sys_ni_syscall) /* was sys_olduname */
|
||||
/* 60 */ CALL(sys_umask)
|
||||
CALL(sys_chroot)
|
||||
CALL(sys_ustat)
|
||||
CALL(sys_dup2)
|
||||
CALL(sys_getppid)
|
||||
/* 65 */ CALL(sys_getpgrp)
|
||||
CALL(sys_setsid)
|
||||
CALL(sys_sigaction)
|
||||
CALL(sys_ni_syscall) /* was sys_sgetmask */
|
||||
CALL(sys_ni_syscall) /* was sys_ssetmask */
|
||||
/* 70 */ CALL(sys_setreuid16)
|
||||
CALL(sys_setregid16)
|
||||
CALL(sys_sigsuspend_wrapper)
|
||||
CALL(sys_sigpending)
|
||||
CALL(sys_sethostname)
|
||||
/* 75 */ CALL(sys_setrlimit)
|
||||
CALL(OBSOLETE(sys_old_getrlimit)) /* used by libc4 */
|
||||
CALL(sys_getrusage)
|
||||
CALL(sys_gettimeofday)
|
||||
CALL(sys_settimeofday)
|
||||
/* 80 */ CALL(sys_getgroups16)
|
||||
CALL(sys_setgroups16)
|
||||
CALL(OBSOLETE(old_select)) /* used by libc4 */
|
||||
CALL(sys_symlink)
|
||||
CALL(sys_ni_syscall) /* was sys_lstat */
|
||||
/* 85 */ CALL(sys_readlink)
|
||||
CALL(sys_uselib)
|
||||
CALL(sys_swapon)
|
||||
CALL(sys_reboot)
|
||||
CALL(OBSOLETE(old_readdir)) /* used by libc4 */
|
||||
/* 90 */ CALL(OBSOLETE(old_mmap)) /* used by libc4 */
|
||||
CALL(sys_munmap)
|
||||
CALL(sys_truncate)
|
||||
CALL(sys_ftruncate)
|
||||
CALL(sys_fchmod)
|
||||
/* 95 */ CALL(sys_fchown16)
|
||||
CALL(sys_getpriority)
|
||||
CALL(sys_setpriority)
|
||||
CALL(sys_ni_syscall) /* was sys_profil */
|
||||
CALL(sys_statfs)
|
||||
/* 100 */ CALL(sys_fstatfs)
|
||||
CALL(sys_ni_syscall) /* sys_ioperm */
|
||||
CALL(OBSOLETE(ABI(sys_socketcall, sys_oabi_socketcall)))
|
||||
CALL(sys_syslog)
|
||||
CALL(sys_setitimer)
|
||||
/* 105 */ CALL(sys_getitimer)
|
||||
CALL(sys_newstat)
|
||||
CALL(sys_newlstat)
|
||||
CALL(sys_newfstat)
|
||||
CALL(sys_ni_syscall) /* was sys_uname */
|
||||
/* 110 */ CALL(sys_ni_syscall) /* was sys_iopl */
|
||||
CALL(sys_vhangup)
|
||||
CALL(sys_ni_syscall)
|
||||
CALL(OBSOLETE(sys_syscall)) /* call a syscall */
|
||||
CALL(sys_wait4)
|
||||
/* 115 */ CALL(sys_swapoff)
|
||||
CALL(sys_sysinfo)
|
||||
CALL(OBSOLETE(ABI(sys_ipc, sys_oabi_ipc)))
|
||||
CALL(sys_fsync)
|
||||
CALL(sys_sigreturn_wrapper)
|
||||
/* 120 */ CALL(sys_clone_wrapper)
|
||||
CALL(sys_setdomainname)
|
||||
CALL(sys_newuname)
|
||||
CALL(sys_ni_syscall) /* modify_ldt */
|
||||
CALL(sys_adjtimex)
|
||||
/* 125 */ CALL(sys_mprotect)
|
||||
CALL(sys_sigprocmask)
|
||||
CALL(sys_ni_syscall) /* was sys_create_module */
|
||||
CALL(sys_init_module)
|
||||
CALL(sys_delete_module)
|
||||
/* 130 */ CALL(sys_ni_syscall) /* was sys_get_kernel_syms */
|
||||
CALL(sys_quotactl)
|
||||
CALL(sys_getpgid)
|
||||
CALL(sys_fchdir)
|
||||
CALL(sys_bdflush)
|
||||
/* 135 */ CALL(sys_sysfs)
|
||||
CALL(sys_personality)
|
||||
CALL(sys_ni_syscall) /* reserved for afs_syscall */
|
||||
CALL(sys_setfsuid16)
|
||||
CALL(sys_setfsgid16)
|
||||
/* 140 */ CALL(sys_llseek)
|
||||
CALL(sys_getdents)
|
||||
CALL(sys_select)
|
||||
CALL(sys_flock)
|
||||
CALL(sys_msync)
|
||||
/* 145 */ CALL(sys_readv)
|
||||
CALL(sys_writev)
|
||||
CALL(sys_getsid)
|
||||
CALL(sys_fdatasync)
|
||||
CALL(sys_sysctl)
|
||||
/* 150 */ CALL(sys_mlock)
|
||||
CALL(sys_munlock)
|
||||
CALL(sys_mlockall)
|
||||
CALL(sys_munlockall)
|
||||
CALL(sys_sched_setparam)
|
||||
/* 155 */ CALL(sys_sched_getparam)
|
||||
CALL(sys_sched_setscheduler)
|
||||
CALL(sys_sched_getscheduler)
|
||||
CALL(sys_sched_yield)
|
||||
CALL(sys_sched_get_priority_max)
|
||||
/* 160 */ CALL(sys_sched_get_priority_min)
|
||||
CALL(sys_sched_rr_get_interval)
|
||||
CALL(sys_nanosleep)
|
||||
CALL(sys_arm_mremap)
|
||||
CALL(sys_setresuid16)
|
||||
/* 165 */ CALL(sys_getresuid16)
|
||||
CALL(sys_ni_syscall) /* vm86 */
|
||||
CALL(sys_ni_syscall) /* was sys_query_module */
|
||||
CALL(sys_poll)
|
||||
CALL(sys_nfsservctl)
|
||||
/* 170 */ CALL(sys_setresgid16)
|
||||
CALL(sys_getresgid16)
|
||||
CALL(sys_prctl)
|
||||
CALL(sys_rt_sigreturn_wrapper)
|
||||
CALL(sys_rt_sigaction)
|
||||
/* 175 */ CALL(sys_rt_sigprocmask)
|
||||
CALL(sys_rt_sigpending)
|
||||
CALL(sys_rt_sigtimedwait)
|
||||
CALL(sys_rt_sigqueueinfo)
|
||||
CALL(sys_rt_sigsuspend_wrapper)
|
||||
/* 180 */ CALL(ABI(sys_pread64, sys_oabi_pread64))
|
||||
CALL(ABI(sys_pwrite64, sys_oabi_pwrite64))
|
||||
CALL(sys_chown16)
|
||||
CALL(sys_getcwd)
|
||||
CALL(sys_capget)
|
||||
/* 185 */ CALL(sys_capset)
|
||||
CALL(sys_sigaltstack_wrapper)
|
||||
CALL(sys_sendfile)
|
||||
CALL(sys_ni_syscall) /* getpmsg */
|
||||
CALL(sys_ni_syscall) /* putpmsg */
|
||||
/* 190 */ CALL(sys_vfork_wrapper)
|
||||
CALL(sys_getrlimit)
|
||||
CALL(sys_mmap2)
|
||||
CALL(ABI(sys_truncate64, sys_oabi_truncate64))
|
||||
CALL(ABI(sys_ftruncate64, sys_oabi_ftruncate64))
|
||||
/* 195 */ CALL(ABI(sys_stat64, sys_oabi_stat64))
|
||||
CALL(ABI(sys_lstat64, sys_oabi_lstat64))
|
||||
CALL(ABI(sys_fstat64, sys_oabi_fstat64))
|
||||
CALL(sys_lchown)
|
||||
CALL(sys_getuid)
|
||||
/* 200 */ CALL(sys_getgid)
|
||||
CALL(sys_geteuid)
|
||||
CALL(sys_getegid)
|
||||
CALL(sys_setreuid)
|
||||
CALL(sys_setregid)
|
||||
/* 205 */ CALL(sys_getgroups)
|
||||
CALL(sys_setgroups)
|
||||
CALL(sys_fchown)
|
||||
CALL(sys_setresuid)
|
||||
CALL(sys_getresuid)
|
||||
/* 210 */ CALL(sys_setresgid)
|
||||
CALL(sys_getresgid)
|
||||
CALL(sys_chown)
|
||||
CALL(sys_setuid)
|
||||
CALL(sys_setgid)
|
||||
/* 215 */ CALL(sys_setfsuid)
|
||||
CALL(sys_setfsgid)
|
||||
CALL(sys_getdents64)
|
||||
CALL(sys_pivot_root)
|
||||
CALL(sys_mincore)
|
||||
/* 220 */ CALL(sys_madvise)
|
||||
CALL(ABI(sys_fcntl64, sys_oabi_fcntl64))
|
||||
CALL(sys_ni_syscall) /* TUX */
|
||||
CALL(sys_ni_syscall)
|
||||
CALL(sys_gettid)
|
||||
/* 225 */ CALL(ABI(sys_readahead, sys_oabi_readahead))
|
||||
CALL(sys_setxattr)
|
||||
CALL(sys_lsetxattr)
|
||||
CALL(sys_fsetxattr)
|
||||
CALL(sys_getxattr)
|
||||
/* 230 */ CALL(sys_lgetxattr)
|
||||
CALL(sys_fgetxattr)
|
||||
CALL(sys_listxattr)
|
||||
CALL(sys_llistxattr)
|
||||
CALL(sys_flistxattr)
|
||||
/* 235 */ CALL(sys_removexattr)
|
||||
CALL(sys_lremovexattr)
|
||||
CALL(sys_fremovexattr)
|
||||
CALL(sys_tkill)
|
||||
CALL(sys_sendfile64)
|
||||
/* 240 */ CALL(sys_futex)
|
||||
CALL(sys_sched_setaffinity)
|
||||
CALL(sys_sched_getaffinity)
|
||||
CALL(sys_io_setup)
|
||||
CALL(sys_io_destroy)
|
||||
/* 245 */ CALL(sys_io_getevents)
|
||||
CALL(sys_io_submit)
|
||||
CALL(sys_io_cancel)
|
||||
CALL(sys_exit_group)
|
||||
CALL(sys_lookup_dcookie)
|
||||
/* 250 */ CALL(sys_epoll_create)
|
||||
CALL(ABI(sys_epoll_ctl, sys_oabi_epoll_ctl))
|
||||
CALL(ABI(sys_epoll_wait, sys_oabi_epoll_wait))
|
||||
CALL(sys_remap_file_pages)
|
||||
CALL(sys_ni_syscall) /* sys_set_thread_area */
|
||||
/* 255 */ CALL(sys_ni_syscall) /* sys_get_thread_area */
|
||||
CALL(sys_set_tid_address)
|
||||
CALL(sys_timer_create)
|
||||
CALL(sys_timer_settime)
|
||||
CALL(sys_timer_gettime)
|
||||
/* 260 */ CALL(sys_timer_getoverrun)
|
||||
CALL(sys_timer_delete)
|
||||
CALL(sys_clock_settime)
|
||||
CALL(sys_clock_gettime)
|
||||
CALL(sys_clock_getres)
|
||||
/* 265 */ CALL(sys_clock_nanosleep)
|
||||
CALL(sys_statfs64_wrapper)
|
||||
CALL(sys_fstatfs64_wrapper)
|
||||
CALL(sys_tgkill)
|
||||
CALL(sys_utimes)
|
||||
/* 270 */ CALL(sys_arm_fadvise64_64)
|
||||
CALL(sys_pciconfig_iobase)
|
||||
CALL(sys_pciconfig_read)
|
||||
CALL(sys_pciconfig_write)
|
||||
CALL(sys_mq_open)
|
||||
/* 275 */ CALL(sys_mq_unlink)
|
||||
CALL(sys_mq_timedsend)
|
||||
CALL(sys_mq_timedreceive)
|
||||
CALL(sys_mq_notify)
|
||||
CALL(sys_mq_getsetattr)
|
||||
/* 280 */ CALL(sys_waitid)
|
||||
CALL(sys_socket)
|
||||
CALL(ABI(sys_bind, sys_oabi_bind))
|
||||
CALL(ABI(sys_connect, sys_oabi_connect))
|
||||
CALL(sys_listen)
|
||||
/* 285 */ CALL(sys_accept)
|
||||
CALL(sys_getsockname)
|
||||
CALL(sys_getpeername)
|
||||
CALL(sys_socketpair)
|
||||
CALL(sys_send)
|
||||
/* 290 */ CALL(ABI(sys_sendto, sys_oabi_sendto))
|
||||
CALL(sys_recv)
|
||||
CALL(sys_recvfrom)
|
||||
CALL(sys_shutdown)
|
||||
CALL(sys_setsockopt)
|
||||
/* 295 */ CALL(sys_getsockopt)
|
||||
CALL(ABI(sys_sendmsg, sys_oabi_sendmsg))
|
||||
CALL(sys_recvmsg)
|
||||
CALL(ABI(sys_semop, sys_oabi_semop))
|
||||
CALL(sys_semget)
|
||||
/* 300 */ CALL(sys_semctl)
|
||||
CALL(sys_msgsnd)
|
||||
CALL(sys_msgrcv)
|
||||
CALL(sys_msgget)
|
||||
CALL(sys_msgctl)
|
||||
/* 305 */ CALL(sys_shmat)
|
||||
CALL(sys_shmdt)
|
||||
CALL(sys_shmget)
|
||||
CALL(sys_shmctl)
|
||||
CALL(sys_add_key)
|
||||
/* 310 */ CALL(sys_request_key)
|
||||
CALL(sys_keyctl)
|
||||
CALL(ABI(sys_semtimedop, sys_oabi_semtimedop))
|
||||
/* vserver */ CALL(sys_ni_syscall)
|
||||
CALL(sys_ioprio_set)
|
||||
/* 315 */ CALL(sys_ioprio_get)
|
||||
CALL(sys_inotify_init)
|
||||
CALL(sys_inotify_add_watch)
|
||||
CALL(sys_inotify_rm_watch)
|
||||
CALL(sys_mbind)
|
||||
/* 320 */ CALL(sys_get_mempolicy)
|
||||
CALL(sys_set_mempolicy)
|
||||
CALL(sys_openat)
|
||||
CALL(sys_mkdirat)
|
||||
CALL(sys_mknodat)
|
||||
/* 325 */ CALL(sys_fchownat)
|
||||
CALL(sys_futimesat)
|
||||
CALL(sys_fstatat64)
|
||||
CALL(sys_unlinkat)
|
||||
CALL(sys_renameat)
|
||||
/* 330 */ CALL(sys_linkat)
|
||||
CALL(sys_symlinkat)
|
||||
CALL(sys_readlinkat)
|
||||
CALL(sys_fchmodat)
|
||||
CALL(sys_faccessat)
|
||||
/* 335 */ CALL(sys_ni_syscall) /* eventually pselect6 */
|
||||
CALL(sys_ni_syscall) /* eventually ppoll */
|
||||
CALL(sys_unshare)
|
||||
CALL(sys_set_robust_list)
|
||||
CALL(sys_get_robust_list)
|
||||
/* 340 */ CALL(sys_splice)
|
||||
CALL(sys_arm_sync_file_range)
|
||||
CALL(sys_tee)
|
||||
CALL(sys_vmsplice)
|
||||
CALL(sys_move_pages)
|
||||
/* 345 */ CALL(sys_getcpu)
|
||||
CALL(sys_ni_syscall) /* eventually epoll_pwait */
|
||||
CALL(sys_kexec_load)
|
||||
#ifndef syscalls_counted
|
||||
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
|
||||
#define syscalls_counted
|
||||
#endif
|
||||
.rept syscalls_padding
|
||||
CALL(sys_ni_syscall)
|
||||
.endr
|
||||
226
arch/arm/kernel/compat.c
Normal file
226
arch/arm/kernel/compat.c
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/compat.c
|
||||
*
|
||||
* Copyright (C) 2001 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.
|
||||
*
|
||||
* 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/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/mach/arch.h>
|
||||
|
||||
#include "compat.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");
|
||||
|
||||
#ifdef CONFIG_ARCH_NETWINDER
|
||||
if (params->u1.s.nr_pages != 0x02000 &&
|
||||
params->u1.s.nr_pages != 0x04000 &&
|
||||
params->u1.s.nr_pages != 0x08000 &&
|
||||
params->u1.s.nr_pages != 0x10000) {
|
||||
printk(KERN_WARNING "Warning: bad NeTTrom parameters "
|
||||
"detected, using defaults\n");
|
||||
|
||||
params->u1.s.nr_pages = 0x1000; /* 16MB */
|
||||
params->u1.s.ramdisk_size = 0;
|
||||
params->u1.s.flags = FLAG_READONLY;
|
||||
params->u1.s.initrd_start = 0;
|
||||
params->u1.s.initrd_size = 0;
|
||||
params->u1.s.rd_start = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
#ifdef CONFIG_ARCH_ACORN
|
||||
if (machine_is_riscpc()) {
|
||||
int i;
|
||||
for (i = 0; i < 4; i++)
|
||||
tag = memtag(tag, PHYS_OFFSET + (i << 26),
|
||||
params->u1.s.pages_in_bank[i] * PAGE_SIZE);
|
||||
} else
|
||||
#endif
|
||||
tag = memtag(tag, PHYS_OFFSET, params->u1.s.nr_pages * PAGE_SIZE);
|
||||
|
||||
#ifdef CONFIG_FOOTBRIDGE
|
||||
if (params->u1.s.mem_fclk_21285) {
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_MEMCLK;
|
||||
tag->hdr.size = tag_size(tag_memclk);
|
||||
tag->u.memclk.fmemclk = params->u1.s.mem_fclk_21285;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_EBSA285
|
||||
if (machine_is_ebsa285()) {
|
||||
tag = tag_next(tag);
|
||||
tag->hdr.tag = ATAG_VIDEOTEXT;
|
||||
tag->hdr.size = tag_size(tag_videotext);
|
||||
tag->u.videotext.x = params->u1.s.video_x;
|
||||
tag->u.videotext.y = params->u1.s.video_y;
|
||||
tag->u.videotext.video_page = 0;
|
||||
tag->u.videotext.video_mode = 0;
|
||||
tag->u.videotext.video_cols = params->u1.s.video_num_cols;
|
||||
tag->u.videotext.video_ega_bx = 0;
|
||||
tag->u.videotext.video_lines = params->u1.s.video_num_rows;
|
||||
tag->u.videotext.video_isvga = 1;
|
||||
tag->u.videotext.video_points = 8;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_ACORN
|
||||
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;
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
13
arch/arm/kernel/compat.h
Normal file
13
arch/arm/kernel/compat.h
Normal file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/compat.h
|
||||
*
|
||||
* Copyright (C) 2001 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.
|
||||
*/
|
||||
|
||||
extern void convert_to_tag_list(struct tag *tags);
|
||||
|
||||
extern void squash_mem_tags(struct tag *tag);
|
||||
305
arch/arm/kernel/crunch-bits.S
Normal file
305
arch/arm/kernel/crunch-bits.S
Normal file
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* arch/arm/kernel/crunch-bits.S
|
||||
* Cirrus MaverickCrunch context switching and handling
|
||||
*
|
||||
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
|
||||
*
|
||||
* Shamelessly stolen from the iWMMXt code by Nicolas Pitre, which is
|
||||
* Copyright (c) 2003-2004, MontaVista Software, Inc.
|
||||
*
|
||||
* 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/linkage.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/arch/ep93xx-regs.h>
|
||||
|
||||
/*
|
||||
* We can't use hex constants here due to a bug in gas.
|
||||
*/
|
||||
#define CRUNCH_MVDX0 0
|
||||
#define CRUNCH_MVDX1 8
|
||||
#define CRUNCH_MVDX2 16
|
||||
#define CRUNCH_MVDX3 24
|
||||
#define CRUNCH_MVDX4 32
|
||||
#define CRUNCH_MVDX5 40
|
||||
#define CRUNCH_MVDX6 48
|
||||
#define CRUNCH_MVDX7 56
|
||||
#define CRUNCH_MVDX8 64
|
||||
#define CRUNCH_MVDX9 72
|
||||
#define CRUNCH_MVDX10 80
|
||||
#define CRUNCH_MVDX11 88
|
||||
#define CRUNCH_MVDX12 96
|
||||
#define CRUNCH_MVDX13 104
|
||||
#define CRUNCH_MVDX14 112
|
||||
#define CRUNCH_MVDX15 120
|
||||
#define CRUNCH_MVAX0L 128
|
||||
#define CRUNCH_MVAX0M 132
|
||||
#define CRUNCH_MVAX0H 136
|
||||
#define CRUNCH_MVAX1L 140
|
||||
#define CRUNCH_MVAX1M 144
|
||||
#define CRUNCH_MVAX1H 148
|
||||
#define CRUNCH_MVAX2L 152
|
||||
#define CRUNCH_MVAX2M 156
|
||||
#define CRUNCH_MVAX2H 160
|
||||
#define CRUNCH_MVAX3L 164
|
||||
#define CRUNCH_MVAX3M 168
|
||||
#define CRUNCH_MVAX3H 172
|
||||
#define CRUNCH_DSPSC 176
|
||||
|
||||
#define CRUNCH_SIZE 184
|
||||
|
||||
.text
|
||||
|
||||
/*
|
||||
* Lazy switching of crunch coprocessor context
|
||||
*
|
||||
* r10 = struct thread_info pointer
|
||||
* r9 = ret_from_exception
|
||||
* lr = undefined instr exit
|
||||
*
|
||||
* called from prefetch exception handler with interrupts disabled
|
||||
*/
|
||||
ENTRY(crunch_task_enable)
|
||||
ldr r8, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr
|
||||
|
||||
ldr r1, [r8, #0x80]
|
||||
tst r1, #0x00800000 @ access to crunch enabled?
|
||||
movne pc, lr @ if so no business here
|
||||
mov r3, #0xaa @ unlock syscon swlock
|
||||
str r3, [r8, #0xc0]
|
||||
orr r1, r1, #0x00800000 @ enable access to crunch
|
||||
str r1, [r8, #0x80]
|
||||
|
||||
ldr r3, =crunch_owner
|
||||
add r0, r10, #TI_CRUNCH_STATE @ get task crunch save area
|
||||
ldr r2, [sp, #60] @ current task pc value
|
||||
ldr r1, [r3] @ get current crunch owner
|
||||
str r0, [r3] @ this task now owns crunch
|
||||
sub r2, r2, #4 @ adjust pc back
|
||||
str r2, [sp, #60]
|
||||
|
||||
ldr r2, [r8, #0x80]
|
||||
mov r2, r2 @ flush out enable (@@@)
|
||||
|
||||
teq r1, #0 @ test for last ownership
|
||||
mov lr, r9 @ normal exit from exception
|
||||
beq crunch_load @ no owner, skip save
|
||||
|
||||
crunch_save:
|
||||
cfstr64 mvdx0, [r1, #CRUNCH_MVDX0] @ save 64b registers
|
||||
cfstr64 mvdx1, [r1, #CRUNCH_MVDX1]
|
||||
cfstr64 mvdx2, [r1, #CRUNCH_MVDX2]
|
||||
cfstr64 mvdx3, [r1, #CRUNCH_MVDX3]
|
||||
cfstr64 mvdx4, [r1, #CRUNCH_MVDX4]
|
||||
cfstr64 mvdx5, [r1, #CRUNCH_MVDX5]
|
||||
cfstr64 mvdx6, [r1, #CRUNCH_MVDX6]
|
||||
cfstr64 mvdx7, [r1, #CRUNCH_MVDX7]
|
||||
cfstr64 mvdx8, [r1, #CRUNCH_MVDX8]
|
||||
cfstr64 mvdx9, [r1, #CRUNCH_MVDX9]
|
||||
cfstr64 mvdx10, [r1, #CRUNCH_MVDX10]
|
||||
cfstr64 mvdx11, [r1, #CRUNCH_MVDX11]
|
||||
cfstr64 mvdx12, [r1, #CRUNCH_MVDX12]
|
||||
cfstr64 mvdx13, [r1, #CRUNCH_MVDX13]
|
||||
cfstr64 mvdx14, [r1, #CRUNCH_MVDX14]
|
||||
cfstr64 mvdx15, [r1, #CRUNCH_MVDX15]
|
||||
|
||||
#ifdef __ARMEB__
|
||||
#error fix me for ARMEB
|
||||
#endif
|
||||
|
||||
cfmv32al mvfx0, mvax0 @ save 72b accumulators
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX0L]
|
||||
cfmv32am mvfx0, mvax0
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX0M]
|
||||
cfmv32ah mvfx0, mvax0
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX0H]
|
||||
cfmv32al mvfx0, mvax1
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX1L]
|
||||
cfmv32am mvfx0, mvax1
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX1M]
|
||||
cfmv32ah mvfx0, mvax1
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX1H]
|
||||
cfmv32al mvfx0, mvax2
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX2L]
|
||||
cfmv32am mvfx0, mvax2
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX2M]
|
||||
cfmv32ah mvfx0, mvax2
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX2H]
|
||||
cfmv32al mvfx0, mvax3
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX3L]
|
||||
cfmv32am mvfx0, mvax3
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX3M]
|
||||
cfmv32ah mvfx0, mvax3
|
||||
cfstr32 mvfx0, [r1, #CRUNCH_MVAX3H]
|
||||
|
||||
cfmv32sc mvdx0, dspsc @ save status word
|
||||
cfstr64 mvdx0, [r1, #CRUNCH_DSPSC]
|
||||
|
||||
teq r0, #0 @ anything to load?
|
||||
cfldr64eq mvdx0, [r1, #CRUNCH_MVDX0] @ mvdx0 was clobbered
|
||||
moveq pc, lr
|
||||
|
||||
crunch_load:
|
||||
cfldr64 mvdx0, [r0, #CRUNCH_DSPSC] @ load status word
|
||||
cfmvsc32 dspsc, mvdx0
|
||||
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX0L] @ load 72b accumulators
|
||||
cfmval32 mvax0, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX0M]
|
||||
cfmvam32 mvax0, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX0H]
|
||||
cfmvah32 mvax0, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX1L]
|
||||
cfmval32 mvax1, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX1M]
|
||||
cfmvam32 mvax1, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX1H]
|
||||
cfmvah32 mvax1, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX2L]
|
||||
cfmval32 mvax2, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX2M]
|
||||
cfmvam32 mvax2, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX2H]
|
||||
cfmvah32 mvax2, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX3L]
|
||||
cfmval32 mvax3, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX3M]
|
||||
cfmvam32 mvax3, mvfx0
|
||||
cfldr32 mvfx0, [r0, #CRUNCH_MVAX3H]
|
||||
cfmvah32 mvax3, mvfx0
|
||||
|
||||
cfldr64 mvdx0, [r0, #CRUNCH_MVDX0] @ load 64b registers
|
||||
cfldr64 mvdx1, [r0, #CRUNCH_MVDX1]
|
||||
cfldr64 mvdx2, [r0, #CRUNCH_MVDX2]
|
||||
cfldr64 mvdx3, [r0, #CRUNCH_MVDX3]
|
||||
cfldr64 mvdx4, [r0, #CRUNCH_MVDX4]
|
||||
cfldr64 mvdx5, [r0, #CRUNCH_MVDX5]
|
||||
cfldr64 mvdx6, [r0, #CRUNCH_MVDX6]
|
||||
cfldr64 mvdx7, [r0, #CRUNCH_MVDX7]
|
||||
cfldr64 mvdx8, [r0, #CRUNCH_MVDX8]
|
||||
cfldr64 mvdx9, [r0, #CRUNCH_MVDX9]
|
||||
cfldr64 mvdx10, [r0, #CRUNCH_MVDX10]
|
||||
cfldr64 mvdx11, [r0, #CRUNCH_MVDX11]
|
||||
cfldr64 mvdx12, [r0, #CRUNCH_MVDX12]
|
||||
cfldr64 mvdx13, [r0, #CRUNCH_MVDX13]
|
||||
cfldr64 mvdx14, [r0, #CRUNCH_MVDX14]
|
||||
cfldr64 mvdx15, [r0, #CRUNCH_MVDX15]
|
||||
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
* Back up crunch regs to save area and disable access to them
|
||||
* (mainly for gdb or sleep mode usage)
|
||||
*
|
||||
* r0 = struct thread_info pointer of target task or NULL for any
|
||||
*/
|
||||
ENTRY(crunch_task_disable)
|
||||
stmfd sp!, {r4, r5, lr}
|
||||
|
||||
mrs ip, cpsr
|
||||
orr r2, ip, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, r2
|
||||
|
||||
ldr r4, =(EP93XX_APB_VIRT_BASE + 0x00130000) @ syscon addr
|
||||
|
||||
ldr r3, =crunch_owner
|
||||
add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area
|
||||
ldr r1, [r3] @ get current crunch owner
|
||||
teq r1, #0 @ any current owner?
|
||||
beq 1f @ no: quit
|
||||
teq r0, #0 @ any owner?
|
||||
teqne r1, r2 @ or specified one?
|
||||
bne 1f @ no: quit
|
||||
|
||||
ldr r5, [r4, #0x80] @ enable access to crunch
|
||||
mov r2, #0xaa
|
||||
str r2, [r4, #0xc0]
|
||||
orr r5, r5, #0x00800000
|
||||
str r5, [r4, #0x80]
|
||||
|
||||
mov r0, #0 @ nothing to load
|
||||
str r0, [r3] @ no more current owner
|
||||
ldr r2, [r4, #0x80] @ flush out enable (@@@)
|
||||
mov r2, r2
|
||||
bl crunch_save
|
||||
|
||||
mov r2, #0xaa @ disable access to crunch
|
||||
str r2, [r4, #0xc0]
|
||||
bic r5, r5, #0x00800000
|
||||
str r5, [r4, #0x80]
|
||||
ldr r5, [r4, #0x80] @ flush out enable (@@@)
|
||||
mov r5, r5
|
||||
|
||||
1: msr cpsr_c, ip @ restore interrupt mode
|
||||
ldmfd sp!, {r4, r5, pc}
|
||||
|
||||
/*
|
||||
* Copy crunch state to given memory address
|
||||
*
|
||||
* r0 = struct thread_info pointer of target task
|
||||
* r1 = memory address where to store crunch state
|
||||
*
|
||||
* this is called mainly in the creation of signal stack frames
|
||||
*/
|
||||
ENTRY(crunch_task_copy)
|
||||
mrs ip, cpsr
|
||||
orr r2, ip, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, r2
|
||||
|
||||
ldr r3, =crunch_owner
|
||||
add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area
|
||||
ldr r3, [r3] @ get current crunch owner
|
||||
teq r2, r3 @ does this task own it...
|
||||
beq 1f
|
||||
|
||||
@ current crunch values are in the task save area
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov r0, r1
|
||||
mov r1, r2
|
||||
mov r2, #CRUNCH_SIZE
|
||||
b memcpy
|
||||
|
||||
1: @ this task owns crunch regs -- grab a copy from there
|
||||
mov r0, #0 @ nothing to load
|
||||
mov r3, lr @ preserve return address
|
||||
bl crunch_save
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov pc, r3
|
||||
|
||||
/*
|
||||
* Restore crunch state from given memory address
|
||||
*
|
||||
* r0 = struct thread_info pointer of target task
|
||||
* r1 = memory address where to get crunch state from
|
||||
*
|
||||
* this is used to restore crunch state when unwinding a signal stack frame
|
||||
*/
|
||||
ENTRY(crunch_task_restore)
|
||||
mrs ip, cpsr
|
||||
orr r2, ip, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, r2
|
||||
|
||||
ldr r3, =crunch_owner
|
||||
add r2, r0, #TI_CRUNCH_STATE @ get task crunch save area
|
||||
ldr r3, [r3] @ get current crunch owner
|
||||
teq r2, r3 @ does this task own it...
|
||||
beq 1f
|
||||
|
||||
@ this task doesn't own crunch regs -- use its save area
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov r0, r2
|
||||
mov r2, #CRUNCH_SIZE
|
||||
b memcpy
|
||||
|
||||
1: @ this task owns crunch regs -- load them directly
|
||||
mov r0, r1
|
||||
mov r1, #0 @ nothing to save
|
||||
mov r3, lr @ preserve return address
|
||||
bl crunch_load
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov pc, r3
|
||||
83
arch/arm/kernel/crunch.c
Normal file
83
arch/arm/kernel/crunch.c
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* arch/arm/kernel/crunch.c
|
||||
* Cirrus MaverickCrunch context switching and handling
|
||||
*
|
||||
* Copyright (C) 2006 Lennert Buytenhek <buytenh@wantstofly.org>
|
||||
*
|
||||
* 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/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/arch/ep93xx-regs.h>
|
||||
#include <asm/thread_notify.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
struct crunch_state *crunch_owner;
|
||||
|
||||
void crunch_task_release(struct thread_info *thread)
|
||||
{
|
||||
local_irq_disable();
|
||||
if (crunch_owner == &thread->crunchstate)
|
||||
crunch_owner = NULL;
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
static int crunch_enabled(u32 devcfg)
|
||||
{
|
||||
return !!(devcfg & EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE);
|
||||
}
|
||||
|
||||
static int crunch_do(struct notifier_block *self, unsigned long cmd, void *t)
|
||||
{
|
||||
struct thread_info *thread = (struct thread_info *)t;
|
||||
struct crunch_state *crunch_state;
|
||||
u32 devcfg;
|
||||
|
||||
crunch_state = &thread->crunchstate;
|
||||
|
||||
switch (cmd) {
|
||||
case THREAD_NOTIFY_FLUSH:
|
||||
memset(crunch_state, 0, sizeof(*crunch_state));
|
||||
|
||||
/*
|
||||
* FALLTHROUGH: Ensure we don't try to overwrite our newly
|
||||
* initialised state information on the first fault.
|
||||
*/
|
||||
|
||||
case THREAD_NOTIFY_RELEASE:
|
||||
crunch_task_release(thread);
|
||||
break;
|
||||
|
||||
case THREAD_NOTIFY_SWITCH:
|
||||
devcfg = __raw_readl(EP93XX_SYSCON_DEVICE_CONFIG);
|
||||
if (crunch_enabled(devcfg) || crunch_owner == crunch_state) {
|
||||
devcfg ^= EP93XX_SYSCON_DEVICE_CONFIG_CRUNCH_ENABLE;
|
||||
__raw_writel(0xaa, EP93XX_SYSCON_SWLOCK);
|
||||
__raw_writel(devcfg, EP93XX_SYSCON_DEVICE_CONFIG);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block crunch_notifier_block = {
|
||||
.notifier_call = crunch_do,
|
||||
};
|
||||
|
||||
static int __init crunch_init(void)
|
||||
{
|
||||
thread_register_notifier(&crunch_notifier_block);
|
||||
elf_hwcap |= HWCAP_CRUNCH;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(crunch_init);
|
||||
137
arch/arm/kernel/debug.S
Normal file
137
arch/arm/kernel/debug.S
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/debug.S
|
||||
*
|
||||
* Copyright (C) 1994-1999 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.
|
||||
*
|
||||
* 32-bit debugging code
|
||||
*/
|
||||
#include <linux/linkage.h>
|
||||
|
||||
.text
|
||||
|
||||
/*
|
||||
* Some debugging routines (useful if you've got MM problems and
|
||||
* printk isn't working). For DEBUGGING ONLY!!! Do not leave
|
||||
* references to these in a production kernel!
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_DEBUG_ICEDCC)
|
||||
@@ debug using ARM EmbeddedICE DCC channel
|
||||
|
||||
#if defined(CONFIG_CPU_V6)
|
||||
|
||||
.macro addruart, rx
|
||||
.endm
|
||||
|
||||
.macro senduart, rd, rx
|
||||
mcr p14, 0, \rd, c0, c5, 0
|
||||
.endm
|
||||
|
||||
.macro busyuart, rd, rx
|
||||
1001:
|
||||
mrc p14, 0, \rx, c0, c1, 0
|
||||
tst \rx, #0x20000000
|
||||
beq 1001b
|
||||
.endm
|
||||
|
||||
.macro waituart, rd, rx
|
||||
mov \rd, #0x2000000
|
||||
1001:
|
||||
subs \rd, \rd, #1
|
||||
bmi 1002f
|
||||
mrc p14, 0, \rx, c0, c1, 0
|
||||
tst \rx, #0x20000000
|
||||
bne 1001b
|
||||
1002:
|
||||
.endm
|
||||
|
||||
#else
|
||||
|
||||
.macro addruart, rx
|
||||
.endm
|
||||
|
||||
.macro senduart, rd, rx
|
||||
mcr p14, 0, \rd, c1, c0, 0
|
||||
.endm
|
||||
|
||||
.macro busyuart, rd, rx
|
||||
1001:
|
||||
mrc p14, 0, \rx, c0, c0, 0
|
||||
tst \rx, #2
|
||||
beq 1001b
|
||||
|
||||
.endm
|
||||
|
||||
.macro waituart, rd, rx
|
||||
mov \rd, #0x2000000
|
||||
1001:
|
||||
subs \rd, \rd, #1
|
||||
bmi 1002f
|
||||
mrc p14, 0, \rx, c0, c0, 0
|
||||
tst \rx, #2
|
||||
bne 1001b
|
||||
1002:
|
||||
.endm
|
||||
|
||||
#endif /* CONFIG_CPU_V6 */
|
||||
|
||||
#else
|
||||
#include <asm/arch/debug-macro.S>
|
||||
#endif /* CONFIG_DEBUG_ICEDCC */
|
||||
|
||||
/*
|
||||
* Useful debugging routines
|
||||
*/
|
||||
ENTRY(printhex8)
|
||||
mov r1, #8
|
||||
b printhex
|
||||
|
||||
ENTRY(printhex4)
|
||||
mov r1, #4
|
||||
b printhex
|
||||
|
||||
ENTRY(printhex2)
|
||||
mov r1, #2
|
||||
printhex: adr r2, hexbuf
|
||||
add r3, r2, r1
|
||||
mov r1, #0
|
||||
strb r1, [r3]
|
||||
1: and r1, r0, #15
|
||||
mov r0, r0, lsr #4
|
||||
cmp r1, #10
|
||||
addlt r1, r1, #'0'
|
||||
addge r1, r1, #'a' - 10
|
||||
strb r1, [r3, #-1]!
|
||||
teq r3, r2
|
||||
bne 1b
|
||||
mov r0, r2
|
||||
b printascii
|
||||
|
||||
.ltorg
|
||||
|
||||
ENTRY(printascii)
|
||||
addruart r3
|
||||
b 2f
|
||||
1: waituart r2, r3
|
||||
senduart r1, r3
|
||||
busyuart r2, r3
|
||||
teq r1, #'\n'
|
||||
moveq r1, #'\r'
|
||||
beq 1b
|
||||
2: teq r0, #0
|
||||
ldrneb r1, [r0], #1
|
||||
teqne r1, #0
|
||||
bne 1b
|
||||
mov pc, lr
|
||||
|
||||
ENTRY(printch)
|
||||
addruart r3
|
||||
mov r1, r0
|
||||
mov r0, #0
|
||||
b 1b
|
||||
|
||||
hexbuf: .space 16
|
||||
222
arch/arm/kernel/dma-isa.c
Normal file
222
arch/arm/kernel/dma-isa.c
Normal file
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/dma-isa.c
|
||||
*
|
||||
* Copyright (C) 1999-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.
|
||||
*
|
||||
* ISA DMA primitives
|
||||
* Taken from various sources, including:
|
||||
* linux/include/asm/dma.h: Defines for using and allocating dma channels.
|
||||
* Written by Hennus Bergman, 1992.
|
||||
* High DMA channel support & info by Hannu Savolainen and John Boyd,
|
||||
* Nov. 1992.
|
||||
* arch/arm/kernel/dma-ebsa285.c
|
||||
* Copyright (C) 1998 Phil Blundell
|
||||
*/
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include <asm/mach/dma.h>
|
||||
|
||||
#define ISA_DMA_MODE_READ 0x44
|
||||
#define ISA_DMA_MODE_WRITE 0x48
|
||||
#define ISA_DMA_MODE_CASCADE 0xc0
|
||||
#define ISA_DMA_AUTOINIT 0x10
|
||||
|
||||
#define ISA_DMA_MASK 0
|
||||
#define ISA_DMA_MODE 1
|
||||
#define ISA_DMA_CLRFF 2
|
||||
#define ISA_DMA_PGHI 3
|
||||
#define ISA_DMA_PGLO 4
|
||||
#define ISA_DMA_ADDR 5
|
||||
#define ISA_DMA_COUNT 6
|
||||
|
||||
static unsigned int isa_dma_port[8][7] = {
|
||||
/* MASK MODE CLRFF PAGE_HI PAGE_LO ADDR COUNT */
|
||||
{ 0x0a, 0x0b, 0x0c, 0x487, 0x087, 0x00, 0x01 },
|
||||
{ 0x0a, 0x0b, 0x0c, 0x483, 0x083, 0x02, 0x03 },
|
||||
{ 0x0a, 0x0b, 0x0c, 0x481, 0x081, 0x04, 0x05 },
|
||||
{ 0x0a, 0x0b, 0x0c, 0x482, 0x082, 0x06, 0x07 },
|
||||
{ 0xd4, 0xd6, 0xd8, 0x000, 0x000, 0xc0, 0xc2 },
|
||||
{ 0xd4, 0xd6, 0xd8, 0x48b, 0x08b, 0xc4, 0xc6 },
|
||||
{ 0xd4, 0xd6, 0xd8, 0x489, 0x089, 0xc8, 0xca },
|
||||
{ 0xd4, 0xd6, 0xd8, 0x48a, 0x08a, 0xcc, 0xce }
|
||||
};
|
||||
|
||||
static int isa_get_dma_residue(dmach_t channel, dma_t *dma)
|
||||
{
|
||||
unsigned int io_port = isa_dma_port[channel][ISA_DMA_COUNT];
|
||||
int count;
|
||||
|
||||
count = 1 + inb(io_port);
|
||||
count |= inb(io_port) << 8;
|
||||
|
||||
return channel < 4 ? count : (count << 1);
|
||||
}
|
||||
|
||||
static void isa_enable_dma(dmach_t channel, dma_t *dma)
|
||||
{
|
||||
if (dma->invalid) {
|
||||
unsigned long address, length;
|
||||
unsigned int mode;
|
||||
enum dma_data_direction direction;
|
||||
|
||||
mode = channel & 3;
|
||||
switch (dma->dma_mode & DMA_MODE_MASK) {
|
||||
case DMA_MODE_READ:
|
||||
mode |= ISA_DMA_MODE_READ;
|
||||
direction = DMA_FROM_DEVICE;
|
||||
break;
|
||||
|
||||
case DMA_MODE_WRITE:
|
||||
mode |= ISA_DMA_MODE_WRITE;
|
||||
direction = DMA_TO_DEVICE;
|
||||
break;
|
||||
|
||||
case DMA_MODE_CASCADE:
|
||||
mode |= ISA_DMA_MODE_CASCADE;
|
||||
direction = DMA_BIDIRECTIONAL;
|
||||
break;
|
||||
|
||||
default:
|
||||
direction = DMA_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!dma->sg) {
|
||||
/*
|
||||
* Cope with ISA-style drivers which expect cache
|
||||
* coherence.
|
||||
*/
|
||||
dma->sg = &dma->buf;
|
||||
dma->sgcount = 1;
|
||||
dma->buf.length = dma->count;
|
||||
dma->buf.dma_address = dma_map_single(NULL,
|
||||
dma->addr, dma->count,
|
||||
direction);
|
||||
}
|
||||
|
||||
address = dma->buf.dma_address;
|
||||
length = dma->buf.length - 1;
|
||||
|
||||
outb(address >> 16, isa_dma_port[channel][ISA_DMA_PGLO]);
|
||||
outb(address >> 24, isa_dma_port[channel][ISA_DMA_PGHI]);
|
||||
|
||||
if (channel >= 4) {
|
||||
address >>= 1;
|
||||
length >>= 1;
|
||||
}
|
||||
|
||||
outb(0, isa_dma_port[channel][ISA_DMA_CLRFF]);
|
||||
|
||||
outb(address, isa_dma_port[channel][ISA_DMA_ADDR]);
|
||||
outb(address >> 8, isa_dma_port[channel][ISA_DMA_ADDR]);
|
||||
|
||||
outb(length, isa_dma_port[channel][ISA_DMA_COUNT]);
|
||||
outb(length >> 8, isa_dma_port[channel][ISA_DMA_COUNT]);
|
||||
|
||||
if (dma->dma_mode & DMA_AUTOINIT)
|
||||
mode |= ISA_DMA_AUTOINIT;
|
||||
|
||||
outb(mode, isa_dma_port[channel][ISA_DMA_MODE]);
|
||||
dma->invalid = 0;
|
||||
}
|
||||
outb(channel & 3, isa_dma_port[channel][ISA_DMA_MASK]);
|
||||
}
|
||||
|
||||
static void isa_disable_dma(dmach_t channel, dma_t *dma)
|
||||
{
|
||||
outb(channel | 4, isa_dma_port[channel][ISA_DMA_MASK]);
|
||||
}
|
||||
|
||||
static struct dma_ops isa_dma_ops = {
|
||||
.type = "ISA",
|
||||
.enable = isa_enable_dma,
|
||||
.disable = isa_disable_dma,
|
||||
.residue = isa_get_dma_residue,
|
||||
};
|
||||
|
||||
static struct resource dma_resources[] = { {
|
||||
.name = "dma1",
|
||||
.start = 0x0000,
|
||||
.end = 0x000f
|
||||
}, {
|
||||
.name = "dma low page",
|
||||
.start = 0x0080,
|
||||
.end = 0x008f
|
||||
}, {
|
||||
.name = "dma2",
|
||||
.start = 0x00c0,
|
||||
.end = 0x00df
|
||||
}, {
|
||||
.name = "dma high page",
|
||||
.start = 0x0480,
|
||||
.end = 0x048f
|
||||
} };
|
||||
|
||||
void __init isa_init_dma(dma_t *dma)
|
||||
{
|
||||
/*
|
||||
* Try to autodetect presence of an ISA DMA controller.
|
||||
* We do some minimal initialisation, and check that
|
||||
* channel 0's DMA address registers are writeable.
|
||||
*/
|
||||
outb(0xff, 0x0d);
|
||||
outb(0xff, 0xda);
|
||||
|
||||
/*
|
||||
* Write high and low address, and then read them back
|
||||
* in the same order.
|
||||
*/
|
||||
outb(0x55, 0x00);
|
||||
outb(0xaa, 0x00);
|
||||
|
||||
if (inb(0) == 0x55 && inb(0) == 0xaa) {
|
||||
int channel, i;
|
||||
|
||||
for (channel = 0; channel < 8; channel++) {
|
||||
dma[channel].d_ops = &isa_dma_ops;
|
||||
isa_disable_dma(channel, NULL);
|
||||
}
|
||||
|
||||
outb(0x40, 0x0b);
|
||||
outb(0x41, 0x0b);
|
||||
outb(0x42, 0x0b);
|
||||
outb(0x43, 0x0b);
|
||||
|
||||
outb(0xc0, 0xd6);
|
||||
outb(0x41, 0xd6);
|
||||
outb(0x42, 0xd6);
|
||||
outb(0x43, 0xd6);
|
||||
|
||||
outb(0, 0xd4);
|
||||
|
||||
outb(0x10, 0x08);
|
||||
outb(0x10, 0xd0);
|
||||
|
||||
/*
|
||||
* Is this correct? According to my documentation, it
|
||||
* doesn't appear to be. It should be:
|
||||
* outb(0x3f, 0x40b); outb(0x3f, 0x4d6);
|
||||
*/
|
||||
outb(0x30, 0x40b);
|
||||
outb(0x31, 0x40b);
|
||||
outb(0x32, 0x40b);
|
||||
outb(0x33, 0x40b);
|
||||
outb(0x31, 0x4d6);
|
||||
outb(0x32, 0x4d6);
|
||||
outb(0x33, 0x4d6);
|
||||
|
||||
request_dma(DMA_ISA_CASCADE, "cascade");
|
||||
|
||||
for (i = 0; i < sizeof(dma_resources) / sizeof(dma_resources[0]); i++)
|
||||
request_resource(&ioport_resource, dma_resources + i);
|
||||
}
|
||||
}
|
||||
268
arch/arm/kernel/dma.c
Normal file
268
arch/arm/kernel/dma.c
Normal file
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/dma.c
|
||||
*
|
||||
* Copyright (C) 1995-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.
|
||||
*
|
||||
* 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/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/errno.h>
|
||||
|
||||
#include <asm/dma.h>
|
||||
|
||||
#include <asm/mach/dma.h>
|
||||
|
||||
DEFINE_SPINLOCK(dma_spin_lock);
|
||||
EXPORT_SYMBOL(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;
|
||||
}
|
||||
EXPORT_SYMBOL(request_dma);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
EXPORT_SYMBOL(free_dma);
|
||||
|
||||
/* 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->invalid = 1;
|
||||
}
|
||||
EXPORT_SYMBOL(set_dma_sg);
|
||||
|
||||
/* Set DMA address
|
||||
*
|
||||
* Copy address to the structure, and set the invalid bit
|
||||
*/
|
||||
void __set_dma_addr (dmach_t channel, void *addr)
|
||||
{
|
||||
dma_t *dma = dma_chan + channel;
|
||||
|
||||
if (dma->active)
|
||||
printk(KERN_ERR "dma%d: altering DMA address while "
|
||||
"DMA active\n", channel);
|
||||
|
||||
dma->sg = NULL;
|
||||
dma->addr = addr;
|
||||
dma->invalid = 1;
|
||||
}
|
||||
EXPORT_SYMBOL(__set_dma_addr);
|
||||
|
||||
/* 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 = NULL;
|
||||
dma->count = count;
|
||||
dma->invalid = 1;
|
||||
}
|
||||
EXPORT_SYMBOL(set_dma_count);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
EXPORT_SYMBOL(set_dma_mode);
|
||||
|
||||
/* 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();
|
||||
}
|
||||
EXPORT_SYMBOL(enable_dma);
|
||||
|
||||
/* 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();
|
||||
}
|
||||
EXPORT_SYMBOL(disable_dma);
|
||||
|
||||
/*
|
||||
* Is the specified DMA channel active?
|
||||
*/
|
||||
int dma_channel_active(dmach_t channel)
|
||||
{
|
||||
return dma_chan[channel].active;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_channel_active);
|
||||
|
||||
void set_dma_page(dmach_t channel, char pagenr)
|
||||
{
|
||||
printk(KERN_ERR "dma%d: trying to set_dma_page\n", channel);
|
||||
}
|
||||
EXPORT_SYMBOL(set_dma_page);
|
||||
|
||||
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;
|
||||
}
|
||||
EXPORT_SYMBOL(set_dma_speed);
|
||||
|
||||
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;
|
||||
}
|
||||
EXPORT_SYMBOL(get_dma_residue);
|
||||
|
||||
static int __init init_dma(void)
|
||||
{
|
||||
arch_dma_init(dma_chan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
core_initcall(init_dma);
|
||||
1211
arch/arm/kernel/ecard.c
Normal file
1211
arch/arm/kernel/ecard.c
Normal file
File diff suppressed because it is too large
Load Diff
1082
arch/arm/kernel/entry-armv.S
Normal file
1082
arch/arm/kernel/entry-armv.S
Normal file
File diff suppressed because it is too large
Load Diff
399
arch/arm/kernel/entry-common.S
Normal file
399
arch/arm/kernel/entry-common.S
Normal file
@@ -0,0 +1,399 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/entry-common.S
|
||||
*
|
||||
* 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 <asm/unistd.h>
|
||||
#include <asm/arch/entry-macro.S>
|
||||
|
||||
#include "entry-header.S"
|
||||
|
||||
|
||||
.align 5
|
||||
/*
|
||||
* 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_irq @ disable interrupts
|
||||
ldr r1, [tsk, #TI_FLAGS]
|
||||
tst r1, #_TIF_WORK_MASK
|
||||
bne fast_work_pending
|
||||
|
||||
/* perform architecture specific actions before user return */
|
||||
arch_ret_to_user r1, lr
|
||||
|
||||
@ fast_restore_user_regs
|
||||
ldr r1, [sp, #S_OFF + S_PSR] @ get calling cpsr
|
||||
ldr lr, [sp, #S_OFF + S_PC]! @ get pc
|
||||
msr spsr_cxsf, r1 @ save in spsr_svc
|
||||
ldmdb sp, {r1 - lr}^ @ get calling r1 - lr
|
||||
mov r0, r0
|
||||
add sp, sp, #S_FRAME_SIZE - S_PC
|
||||
movs pc, lr @ return & move spsr_svc into cpsr
|
||||
|
||||
/*
|
||||
* 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
|
||||
b ret_slow_syscall @ Check work again
|
||||
|
||||
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_irq @ disable interrupts
|
||||
ldr r1, [tsk, #TI_FLAGS]
|
||||
tst r1, #_TIF_WORK_MASK
|
||||
bne work_pending
|
||||
no_work_pending:
|
||||
/* perform architecture specific actions before user return */
|
||||
arch_ret_to_user r1, lr
|
||||
|
||||
@ slow_restore_user_regs
|
||||
ldr r1, [sp, #S_PSR] @ get calling cpsr
|
||||
ldr lr, [sp, #S_PC]! @ get pc
|
||||
msr spsr_cxsf, r1 @ save in spsr_svc
|
||||
ldmdb sp, {r0 - lr}^ @ get calling r1 - lr
|
||||
mov r0, r0
|
||||
add sp, sp, #S_FRAME_SIZE - S_PC
|
||||
movs pc, lr @ return & move spsr_svc into cpsr
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
|
||||
.equ NR_syscalls,0
|
||||
#define CALL(x) .equ NR_syscalls,NR_syscalls+1
|
||||
#include "calls.S"
|
||||
#undef CALL
|
||||
#define CALL(x) .long x
|
||||
|
||||
/*=============================================================================
|
||||
* SWI handler
|
||||
*-----------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* If we're optimising for StrongARM the resulting code won't
|
||||
run on an ARM7 and we can save a couple of instructions.
|
||||
--pb */
|
||||
#ifdef CONFIG_CPU_ARM710
|
||||
#define A710(code...) code
|
||||
.Larm710bug:
|
||||
ldmia sp, {r0 - lr}^ @ Get calling r0 - lr
|
||||
mov r0, r0
|
||||
add sp, sp, #S_FRAME_SIZE
|
||||
subs pc, lr, #4
|
||||
#else
|
||||
#define A710(code...)
|
||||
#endif
|
||||
|
||||
.align 5
|
||||
ENTRY(vector_swi)
|
||||
sub sp, sp, #S_FRAME_SIZE
|
||||
stmia sp, {r0 - r12} @ Calling r0 - r12
|
||||
add r8, sp, #S_PC
|
||||
stmdb r8, {sp, lr}^ @ Calling sp, lr
|
||||
mrs r8, spsr @ called from non-FIQ mode, so ok.
|
||||
str lr, [sp, #S_PC] @ Save calling PC
|
||||
str r8, [sp, #S_PSR] @ Save CPSR
|
||||
str r0, [sp, #S_OLD_R0] @ Save OLD_R0
|
||||
zero_fp
|
||||
|
||||
/*
|
||||
* Get the system call number.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_OABI_COMPAT)
|
||||
|
||||
/*
|
||||
* If we have CONFIG_OABI_COMPAT then we need to look at the swi
|
||||
* value to determine if it is an EABI or an old ABI call.
|
||||
*/
|
||||
#ifdef CONFIG_ARM_THUMB
|
||||
tst r8, #PSR_T_BIT
|
||||
movne r10, #0 @ no thumb OABI emulation
|
||||
ldreq r10, [lr, #-4] @ get SWI instruction
|
||||
#else
|
||||
ldr r10, [lr, #-4] @ get SWI instruction
|
||||
A710( and ip, r10, #0x0f000000 @ check for SWI )
|
||||
A710( teq ip, #0x0f000000 )
|
||||
A710( bne .Larm710bug )
|
||||
#endif
|
||||
|
||||
#elif defined(CONFIG_AEABI)
|
||||
|
||||
/*
|
||||
* Pure EABI user space always put syscall number into scno (r7).
|
||||
*/
|
||||
A710( ldr ip, [lr, #-4] @ get SWI instruction )
|
||||
A710( and ip, ip, #0x0f000000 @ check for SWI )
|
||||
A710( teq ip, #0x0f000000 )
|
||||
A710( bne .Larm710bug )
|
||||
|
||||
#elif defined(CONFIG_ARM_THUMB)
|
||||
|
||||
/* Legacy ABI only, possibly thumb mode. */
|
||||
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
|
||||
addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in
|
||||
ldreq scno, [lr, #-4]
|
||||
|
||||
#else
|
||||
|
||||
/* Legacy ABI only. */
|
||||
ldr scno, [lr, #-4] @ get SWI instruction
|
||||
A710( and ip, scno, #0x0f000000 @ check for SWI )
|
||||
A710( teq ip, #0x0f000000 )
|
||||
A710( bne .Larm710bug )
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ALIGNMENT_TRAP
|
||||
ldr ip, __cr_alignment
|
||||
ldr ip, [ip]
|
||||
mcr p15, 0, ip, c1, c0 @ update control register
|
||||
#endif
|
||||
enable_irq
|
||||
|
||||
get_thread_info tsk
|
||||
adr tbl, sys_call_table @ load syscall table pointer
|
||||
ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
|
||||
|
||||
#if defined(CONFIG_OABI_COMPAT)
|
||||
/*
|
||||
* If the swi argument is zero, this is an EABI call and we do nothing.
|
||||
*
|
||||
* If this is an old ABI call, get the syscall number into scno and
|
||||
* get the old ABI syscall table address.
|
||||
*/
|
||||
bics r10, r10, #0xff000000
|
||||
eorne scno, r10, #__NR_OABI_SYSCALL_BASE
|
||||
ldrne tbl, =sys_oabi_call_table
|
||||
#elif !defined(CONFIG_AEABI)
|
||||
bic scno, scno, #0xff000000 @ mask off SWI op-code
|
||||
eor scno, scno, #__NR_SYSCALL_BASE @ check OS number
|
||||
#endif
|
||||
|
||||
stmdb sp!, {r4, r5} @ push fifth and sixth args
|
||||
tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
|
||||
bne __sys_trace
|
||||
|
||||
cmp scno, #NR_syscalls @ check upper syscall limit
|
||||
adr lr, ret_fast_syscall @ return address
|
||||
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, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)
|
||||
eor r0, scno, #__NR_SYSCALL_BASE @ 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:
|
||||
mov r2, scno
|
||||
add r1, sp, #S_OFF
|
||||
mov r0, #0 @ trace entry [IP = 0]
|
||||
bl syscall_trace
|
||||
|
||||
adr lr, __sys_trace_return @ return address
|
||||
mov scno, r0 @ syscall number (possibly new)
|
||||
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 r2, scno
|
||||
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
|
||||
.ltorg
|
||||
|
||||
/*
|
||||
* This is the syscall table declaration for native ABI syscalls.
|
||||
* With EABI a couple syscalls are obsolete and defined as sys_ni_syscall.
|
||||
*/
|
||||
#define ABI(native, compat) native
|
||||
#ifdef CONFIG_AEABI
|
||||
#define OBSOLETE(syscall) sys_ni_syscall
|
||||
#else
|
||||
#define OBSOLETE(syscall) syscall
|
||||
#endif
|
||||
|
||||
.type sys_call_table, #object
|
||||
ENTRY(sys_call_table)
|
||||
#include "calls.S"
|
||||
#undef ABI
|
||||
#undef OBSOLETE
|
||||
|
||||
/*============================================================================
|
||||
* Special system call wrappers
|
||||
*/
|
||||
@ r0 = syscall number
|
||||
@ r8 = syscall table
|
||||
.type sys_syscall, #function
|
||||
sys_syscall:
|
||||
bic scno, r0, #__NR_OABI_SYSCALL_BASE
|
||||
cmp scno, #__NR_syscall - __NR_SYSCALL_BASE
|
||||
cmpne scno, #NR_syscalls @ check range
|
||||
stmloia sp, {r5, r6} @ shuffle args
|
||||
movlo r0, r1
|
||||
movlo r1, r2
|
||||
movlo r2, r3
|
||||
movlo r3, r4
|
||||
ldrlo 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_wrapper:
|
||||
add ip, sp, #S_OFF
|
||||
str ip, [sp, #4]
|
||||
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
|
||||
|
||||
sys_statfs64_wrapper:
|
||||
teq r1, #88
|
||||
moveq r1, #84
|
||||
b sys_statfs64
|
||||
|
||||
sys_fstatfs64_wrapper:
|
||||
teq r1, #88
|
||||
moveq r1, #84
|
||||
b sys_fstatfs64
|
||||
|
||||
/*
|
||||
* Note: off_4k (r5) is always units of 4K. If we can't do the requested
|
||||
* offset, we return EINVAL.
|
||||
*/
|
||||
sys_mmap2:
|
||||
#if PAGE_SHIFT > 12
|
||||
tst r5, #PGOFF_MASK
|
||||
moveq r5, r5, lsr #PAGE_SHIFT - 12
|
||||
streq r5, [sp, #4]
|
||||
beq do_mmap2
|
||||
mov r0, #-EINVAL
|
||||
mov pc, lr
|
||||
#else
|
||||
str r5, [sp, #4]
|
||||
b do_mmap2
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OABI_COMPAT
|
||||
|
||||
/*
|
||||
* These are syscalls with argument register differences
|
||||
*/
|
||||
|
||||
sys_oabi_pread64:
|
||||
stmia sp, {r3, r4}
|
||||
b sys_pread64
|
||||
|
||||
sys_oabi_pwrite64:
|
||||
stmia sp, {r3, r4}
|
||||
b sys_pwrite64
|
||||
|
||||
sys_oabi_truncate64:
|
||||
mov r3, r2
|
||||
mov r2, r1
|
||||
b sys_truncate64
|
||||
|
||||
sys_oabi_ftruncate64:
|
||||
mov r3, r2
|
||||
mov r2, r1
|
||||
b sys_ftruncate64
|
||||
|
||||
sys_oabi_readahead:
|
||||
str r3, [sp]
|
||||
mov r3, r2
|
||||
mov r2, r1
|
||||
b sys_readahead
|
||||
|
||||
/*
|
||||
* Let's declare a second syscall table for old ABI binaries
|
||||
* using the compatibility syscall entries.
|
||||
*/
|
||||
#define ABI(native, compat) compat
|
||||
#define OBSOLETE(syscall) syscall
|
||||
|
||||
.type sys_oabi_call_table, #object
|
||||
ENTRY(sys_oabi_call_table)
|
||||
#include "calls.S"
|
||||
#undef ABI
|
||||
#undef OBSOLETE
|
||||
|
||||
#endif
|
||||
|
||||
66
arch/arm/kernel/entry-header.S
Normal file
66
arch/arm/kernel/entry-header.S
Normal file
@@ -0,0 +1,66 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/linkage.h>
|
||||
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/thread_info.h>
|
||||
|
||||
@ Bad Abort numbers
|
||||
@ -----------------
|
||||
@
|
||||
#define BAD_PREFETCH 0
|
||||
#define BAD_DATA 1
|
||||
#define BAD_ADDREXCPTN 2
|
||||
#define BAD_IRQ 3
|
||||
#define BAD_UNDEFINSTR 4
|
||||
|
||||
@
|
||||
@ Most of the stack format comes from struct pt_regs, but with
|
||||
@ the addition of 8 bytes for storing syscall args 5 and 6.
|
||||
@ This _must_ remain a multiple of 8 for EABI.
|
||||
@
|
||||
#define S_OFF 8
|
||||
|
||||
/*
|
||||
* The SWI code relies 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
|
||||
|
||||
.macro zero_fp
|
||||
#ifdef CONFIG_FRAME_POINTER
|
||||
mov fp, #0
|
||||
#endif
|
||||
.endm
|
||||
|
||||
.macro get_thread_info, rd
|
||||
mov \rd, sp, lsr #13
|
||||
mov \rd, \rd, lsl #13
|
||||
.endm
|
||||
|
||||
.macro alignment_trap, rtemp
|
||||
#ifdef CONFIG_ALIGNMENT_TRAP
|
||||
ldr \rtemp, .LCcralign
|
||||
ldr \rtemp, [\rtemp]
|
||||
mcr p15, 0, \rtemp, c1, c0
|
||||
#endif
|
||||
.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.
|
||||
*
|
||||
* r7 is reserved for the system call number for thumb mode.
|
||||
*
|
||||
* 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
|
||||
182
arch/arm/kernel/fiq.c
Normal file
182
arch/arm/kernel/fiq.c
Normal file
@@ -0,0 +1,182 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/fiq.c
|
||||
*
|
||||
* Copyright (C) 1998 Russell King
|
||||
* Copyright (C) 1998, 1999 Phil Blundell
|
||||
*
|
||||
* 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/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/fiq.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
static unsigned long no_fiq_insn;
|
||||
|
||||
/* Default reacquire function
|
||||
* - we always relinquish FIQ control
|
||||
* - we always reacquire FIQ control
|
||||
*/
|
||||
static int fiq_def_op(void *ref, int relinquish)
|
||||
{
|
||||
if (!relinquish)
|
||||
set_fiq_handler(&no_fiq_insn, sizeof(no_fiq_insn));
|
||||
|
||||
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)
|
||||
{
|
||||
memcpy((void *)0xffff001c, start, length);
|
||||
flush_icache_range(0xffff001c, 0xffff001c + length);
|
||||
if (!vectors_high())
|
||||
flush_icache_range(0x1c, 0x1c + length);
|
||||
}
|
||||
|
||||
/*
|
||||
* Taking an interrupt in FIQ mode is death, so both these functions
|
||||
* disable irqs for the duration. Note - these functions are almost
|
||||
* entirely coded in assembly.
|
||||
*/
|
||||
void __attribute__((naked)) set_fiq_regs(struct pt_regs *regs)
|
||||
{
|
||||
register unsigned long tmp;
|
||||
asm volatile (
|
||||
"mov ip, sp\n\
|
||||
stmfd sp!, {fp, ip, lr, pc}\n\
|
||||
sub fp, ip, #4\n\
|
||||
mrs %0, cpsr\n\
|
||||
msr cpsr_c, %2 @ select FIQ mode\n\
|
||||
mov r0, r0\n\
|
||||
ldmia %1, {r8 - r14}\n\
|
||||
msr cpsr_c, %0 @ return to SVC mode\n\
|
||||
mov r0, r0\n\
|
||||
ldmfd sp, {fp, sp, pc}"
|
||||
: "=&r" (tmp)
|
||||
: "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
|
||||
}
|
||||
|
||||
void __attribute__((naked)) get_fiq_regs(struct pt_regs *regs)
|
||||
{
|
||||
register unsigned long tmp;
|
||||
asm volatile (
|
||||
"mov ip, sp\n\
|
||||
stmfd sp!, {fp, ip, lr, pc}\n\
|
||||
sub fp, ip, #4\n\
|
||||
mrs %0, cpsr\n\
|
||||
msr cpsr_c, %2 @ select FIQ mode\n\
|
||||
mov r0, r0\n\
|
||||
stmia %1, {r8 - r14}\n\
|
||||
msr cpsr_c, %0 @ return to SVC mode\n\
|
||||
mov r0, r0\n\
|
||||
ldmfd sp, {fp, sp, pc}"
|
||||
: "=&r" (tmp)
|
||||
: "r" (®s->ARM_r8), "I" (PSR_I_BIT | PSR_F_BIT | FIQ_MODE));
|
||||
}
|
||||
|
||||
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);
|
||||
dump_stack();
|
||||
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 *)0xffff001c;
|
||||
}
|
||||
217
arch/arm/kernel/head-common.S
Normal file
217
arch/arm/kernel/head-common.S
Normal file
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/head-common.S
|
||||
*
|
||||
* Copyright (C) 1994-2002 Russell King
|
||||
* Copyright (c) 2003 ARM Limited
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
.type __switch_data, %object
|
||||
__switch_data:
|
||||
.long __mmap_switched
|
||||
.long __data_loc @ r4
|
||||
.long __data_start @ r5
|
||||
.long __bss_start @ r6
|
||||
.long _end @ r7
|
||||
.long processor_id @ r4
|
||||
.long __machine_arch_type @ r5
|
||||
.long cr_alignment @ r6
|
||||
.long init_thread_union + THREAD_START_SP @ sp
|
||||
|
||||
/*
|
||||
* The following fragment of code is executed with the MMU on in MMU mode,
|
||||
* and uses absolute addresses; this is not position independent.
|
||||
*
|
||||
* r0 = cp#15 control register
|
||||
* r1 = machine ID
|
||||
* r9 = processor ID
|
||||
*/
|
||||
.type __mmap_switched, %function
|
||||
__mmap_switched:
|
||||
adr r3, __switch_data + 4
|
||||
|
||||
ldmia r3!, {r4, r5, r6, r7}
|
||||
cmp r4, r5 @ Copy data segment if needed
|
||||
1: cmpne r5, r6
|
||||
ldrne fp, [r4], #4
|
||||
strne fp, [r5], #4
|
||||
bne 1b
|
||||
|
||||
mov fp, #0 @ Clear BSS (and zero fp)
|
||||
1: cmp r6, r7
|
||||
strcc fp, [r6],#4
|
||||
bcc 1b
|
||||
|
||||
ldmia r3, {r4, r5, r6, sp}
|
||||
str r9, [r4] @ Save processor ID
|
||||
str r1, [r5] @ Save machine type
|
||||
bic r4, r0, #CR_A @ Clear 'A' bit
|
||||
stmia r6, {r0, r4} @ Save control register values
|
||||
b start_kernel
|
||||
|
||||
/*
|
||||
* Exception handling. Something went wrong and we can't proceed. We
|
||||
* ought to tell the user, but since we don't have any guarantee that
|
||||
* we're even running on the right architecture, we do virtually nothing.
|
||||
*
|
||||
* If CONFIG_DEBUG_LL is set we try to print out something about the error
|
||||
* and hope for the best (useful if bootloader fails to pass a proper
|
||||
* machine ID for example).
|
||||
*/
|
||||
|
||||
.type __error_p, %function
|
||||
__error_p:
|
||||
#ifdef CONFIG_DEBUG_LL
|
||||
adr r0, str_p1
|
||||
bl printascii
|
||||
b __error
|
||||
str_p1: .asciz "\nError: unrecognized/unsupported processor variant.\n"
|
||||
.align
|
||||
#endif
|
||||
|
||||
.type __error_a, %function
|
||||
__error_a:
|
||||
#ifdef CONFIG_DEBUG_LL
|
||||
mov r4, r1 @ preserve machine ID
|
||||
adr r0, str_a1
|
||||
bl printascii
|
||||
mov r0, r4
|
||||
bl printhex8
|
||||
adr r0, str_a2
|
||||
bl printascii
|
||||
adr r3, 3f
|
||||
ldmia r3, {r4, r5, r6} @ get machine desc list
|
||||
sub r4, r3, r4 @ get offset between virt&phys
|
||||
add r5, r5, r4 @ convert virt addresses to
|
||||
add r6, r6, r4 @ physical address space
|
||||
1: ldr r0, [r5, #MACHINFO_TYPE] @ get machine type
|
||||
bl printhex8
|
||||
mov r0, #'\t'
|
||||
bl printch
|
||||
ldr r0, [r5, #MACHINFO_NAME] @ get machine name
|
||||
add r0, r0, r4
|
||||
bl printascii
|
||||
mov r0, #'\n'
|
||||
bl printch
|
||||
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
|
||||
cmp r5, r6
|
||||
blo 1b
|
||||
adr r0, str_a3
|
||||
bl printascii
|
||||
b __error
|
||||
str_a1: .asciz "\nError: unrecognized/unsupported machine ID (r1 = 0x"
|
||||
str_a2: .asciz ").\n\nAvailable machine support:\n\nID (hex)\tNAME\n"
|
||||
str_a3: .asciz "\nPlease check your kernel config and/or bootloader.\n"
|
||||
.align
|
||||
#endif
|
||||
|
||||
.type __error, %function
|
||||
__error:
|
||||
#ifdef CONFIG_ARCH_RPC
|
||||
/*
|
||||
* Turn the screen red on a error - RiscPC only.
|
||||
*/
|
||||
mov r0, #0x02000000
|
||||
mov r3, #0x11
|
||||
orr r3, r3, r3, lsl #8
|
||||
orr r3, r3, r3, lsl #16
|
||||
str r3, [r0], #4
|
||||
str r3, [r0], #4
|
||||
str r3, [r0], #4
|
||||
str r3, [r0], #4
|
||||
#endif
|
||||
1: mov r0, r0
|
||||
b 1b
|
||||
|
||||
|
||||
/*
|
||||
* Read processor ID register (CP#15, CR0), and look up in the linker-built
|
||||
* supported processor list. Note that we can't use the absolute addresses
|
||||
* for the __proc_info lists since we aren't running with the MMU on
|
||||
* (and therefore, we are not in the correct address space). We have to
|
||||
* calculate the offset.
|
||||
*
|
||||
* r9 = cpuid
|
||||
* Returns:
|
||||
* r3, r4, r6 corrupted
|
||||
* r5 = proc_info pointer in physical address space
|
||||
* r9 = cpuid (preserved)
|
||||
*/
|
||||
.type __lookup_processor_type, %function
|
||||
__lookup_processor_type:
|
||||
adr r3, 3f
|
||||
ldmda r3, {r5 - r7}
|
||||
sub r3, r3, r7 @ get offset between virt&phys
|
||||
add r5, r5, r3 @ convert virt addresses to
|
||||
add r6, r6, r3 @ physical address space
|
||||
1: ldmia r5, {r3, r4} @ value, mask
|
||||
and r4, r4, r9 @ mask wanted bits
|
||||
teq r3, r4
|
||||
beq 2f
|
||||
add r5, r5, #PROC_INFO_SZ @ sizeof(proc_info_list)
|
||||
cmp r5, r6
|
||||
blo 1b
|
||||
mov r5, #0 @ unknown processor
|
||||
2: mov pc, lr
|
||||
|
||||
/*
|
||||
* This provides a C-API version of the above function.
|
||||
*/
|
||||
ENTRY(lookup_processor_type)
|
||||
stmfd sp!, {r4 - r7, r9, lr}
|
||||
mov r9, r0
|
||||
bl __lookup_processor_type
|
||||
mov r0, r5
|
||||
ldmfd sp!, {r4 - r7, r9, pc}
|
||||
|
||||
/*
|
||||
* Look in include/asm-arm/procinfo.h and arch/arm/kernel/arch.[ch] for
|
||||
* more information about the __proc_info and __arch_info structures.
|
||||
*/
|
||||
.long __proc_info_begin
|
||||
.long __proc_info_end
|
||||
3: .long .
|
||||
.long __arch_info_begin
|
||||
.long __arch_info_end
|
||||
|
||||
/*
|
||||
* Lookup machine architecture in the linker-build list of architectures.
|
||||
* Note that we can't use the absolute addresses for the __arch_info
|
||||
* lists since we aren't running with the MMU on (and therefore, we are
|
||||
* not in the correct address space). We have to calculate the offset.
|
||||
*
|
||||
* r1 = machine architecture number
|
||||
* Returns:
|
||||
* r3, r4, r6 corrupted
|
||||
* r5 = mach_info pointer in physical address space
|
||||
*/
|
||||
.type __lookup_machine_type, %function
|
||||
__lookup_machine_type:
|
||||
adr r3, 3b
|
||||
ldmia r3, {r4, r5, r6}
|
||||
sub r3, r3, r4 @ get offset between virt&phys
|
||||
add r5, r5, r3 @ convert virt addresses to
|
||||
add r6, r6, r3 @ physical address space
|
||||
1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
|
||||
teq r3, r1 @ matches loader number?
|
||||
beq 2f @ found
|
||||
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
|
||||
cmp r5, r6
|
||||
blo 1b
|
||||
mov r5, #0 @ unknown machine
|
||||
2: mov pc, lr
|
||||
|
||||
/*
|
||||
* This provides a C-API version of the above function.
|
||||
*/
|
||||
ENTRY(lookup_machine_type)
|
||||
stmfd sp!, {r4 - r6, lr}
|
||||
mov r1, r0
|
||||
bl __lookup_machine_type
|
||||
mov r0, r5
|
||||
ldmfd sp!, {r4 - r6, pc}
|
||||
91
arch/arm/kernel/head-nommu.S
Normal file
91
arch/arm/kernel/head-nommu.S
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/head-nommu.S
|
||||
*
|
||||
* Copyright (C) 1994-2002 Russell King
|
||||
* Copyright (C) 2003-2006 Hyok S. Choi
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Common kernel startup code (non-paged MM)
|
||||
*
|
||||
*/
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
/*
|
||||
* Kernel startup entry point.
|
||||
* ---------------------------
|
||||
*
|
||||
* This is normally called from the decompressor code. The requirements
|
||||
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
|
||||
* r1 = machine nr.
|
||||
*
|
||||
* See linux/arch/arm/tools/mach-types for the complete list of machine
|
||||
* numbers for r1.
|
||||
*
|
||||
*/
|
||||
__INIT
|
||||
.type stext, %function
|
||||
ENTRY(stext)
|
||||
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
|
||||
@ and irqs disabled
|
||||
#ifndef CONFIG_CPU_CP15
|
||||
ldr r9, =CONFIG_PROCESSOR_ID
|
||||
#else
|
||||
mrc p15, 0, r9, c0, c0 @ get processor id
|
||||
#endif
|
||||
bl __lookup_processor_type @ r5=procinfo r9=cpuid
|
||||
movs r10, r5 @ invalid processor (r5=0)?
|
||||
beq __error_p @ yes, error 'p'
|
||||
bl __lookup_machine_type @ r5=machinfo
|
||||
movs r8, r5 @ invalid machine (r5=0)?
|
||||
beq __error_a @ yes, error 'a'
|
||||
|
||||
ldr r13, __switch_data @ address to jump to after
|
||||
@ the initialization is done
|
||||
adr lr, __after_proc_init @ return (PIC) address
|
||||
add pc, r10, #PROCINFO_INITFUNC
|
||||
|
||||
/*
|
||||
* Set the Control Register and Read the process ID.
|
||||
*/
|
||||
.type __after_proc_init, %function
|
||||
__after_proc_init:
|
||||
#ifdef CONFIG_CPU_CP15
|
||||
mrc p15, 0, r0, c1, c0, 0 @ read control reg
|
||||
#ifdef CONFIG_ALIGNMENT_TRAP
|
||||
orr r0, r0, #CR_A
|
||||
#else
|
||||
bic r0, r0, #CR_A
|
||||
#endif
|
||||
#ifdef CONFIG_CPU_DCACHE_DISABLE
|
||||
bic r0, r0, #CR_C
|
||||
#endif
|
||||
#ifdef CONFIG_CPU_BPREDICT_DISABLE
|
||||
bic r0, r0, #CR_Z
|
||||
#endif
|
||||
#ifdef CONFIG_CPU_ICACHE_DISABLE
|
||||
bic r0, r0, #CR_I
|
||||
#endif
|
||||
#ifdef CONFIG_CPU_HIGH_VECTOR
|
||||
orr r0, r0, #CR_V
|
||||
#else
|
||||
bic r0, r0, #CR_V
|
||||
#endif
|
||||
mcr p15, 0, r0, c1, c0, 0 @ write control reg
|
||||
#endif /* CONFIG_CPU_CP15 */
|
||||
|
||||
mov pc, r13 @ clear the BSS and jump
|
||||
@ to start_kernel
|
||||
.ltorg
|
||||
|
||||
#include "head-common.S"
|
||||
324
arch/arm/kernel/head.S
Normal file
324
arch/arm/kernel/head.S
Normal file
@@ -0,0 +1,324 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/head.S
|
||||
*
|
||||
* Copyright (C) 1994-2002 Russell King
|
||||
* Copyright (c) 2003 ARM Limited
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* Kernel startup code for all 32-bit CPUs
|
||||
*/
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/domain.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/memory.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
#if (PHYS_OFFSET & 0x001fffff)
|
||||
#error "PHYS_OFFSET must be at an even 2MiB boundary!"
|
||||
#endif
|
||||
|
||||
#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
|
||||
#define KERNEL_RAM_PADDR (PHYS_OFFSET + TEXT_OFFSET)
|
||||
|
||||
/*
|
||||
* swapper_pg_dir is the virtual address of the initial page table.
|
||||
* We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must
|
||||
* make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect
|
||||
* the least significant 16 bits to be 0x8000, but we could probably
|
||||
* relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000.
|
||||
*/
|
||||
#if (KERNEL_RAM_VADDR & 0xffff) != 0x8000
|
||||
#error KERNEL_RAM_VADDR must start at 0xXXXX8000
|
||||
#endif
|
||||
|
||||
.globl swapper_pg_dir
|
||||
.equ swapper_pg_dir, KERNEL_RAM_VADDR - 0x4000
|
||||
|
||||
.macro pgtbl, rd
|
||||
ldr \rd, =(KERNEL_RAM_PADDR - 0x4000)
|
||||
.endm
|
||||
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
#define KERNEL_START XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR)
|
||||
#define KERNEL_END _edata_loc
|
||||
#else
|
||||
#define KERNEL_START KERNEL_RAM_VADDR
|
||||
#define KERNEL_END _end
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Kernel startup entry point.
|
||||
* ---------------------------
|
||||
*
|
||||
* This is normally called from the decompressor code. The requirements
|
||||
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
|
||||
* r1 = machine nr.
|
||||
*
|
||||
* This code is mostly position independent, so if you link the kernel at
|
||||
* 0xc0008000, you call this at __pa(0xc0008000).
|
||||
*
|
||||
* See linux/arch/arm/tools/mach-types for the complete list of machine
|
||||
* numbers for r1.
|
||||
*
|
||||
* We're trying to keep crap to a minimum; DO NOT add any machine specific
|
||||
* crap here - that's what the boot loader (or in extreme, well justified
|
||||
* circumstances, zImage) is for.
|
||||
*/
|
||||
__INIT
|
||||
.type stext, %function
|
||||
ENTRY(stext)
|
||||
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE @ ensure svc mode
|
||||
@ and irqs disabled
|
||||
mrc p15, 0, r9, c0, c0 @ get processor id
|
||||
bl __lookup_processor_type @ r5=procinfo r9=cpuid
|
||||
movs r10, r5 @ invalid processor (r5=0)?
|
||||
beq __error_p @ yes, error 'p'
|
||||
bl __lookup_machine_type @ r5=machinfo
|
||||
movs r8, r5 @ invalid machine (r5=0)?
|
||||
beq __error_a @ yes, error 'a'
|
||||
bl __create_page_tables
|
||||
|
||||
/*
|
||||
* The following calls CPU specific code in a position independent
|
||||
* manner. See arch/arm/mm/proc-*.S for details. r10 = base of
|
||||
* xxx_proc_info structure selected by __lookup_machine_type
|
||||
* above. On return, the CPU will be ready for the MMU to be
|
||||
* turned on, and r0 will hold the CPU control register value.
|
||||
*/
|
||||
ldr r13, __switch_data @ address to jump to after
|
||||
@ mmu has been enabled
|
||||
adr lr, __enable_mmu @ return (PIC) address
|
||||
add pc, r10, #PROCINFO_INITFUNC
|
||||
|
||||
#if defined(CONFIG_SMP)
|
||||
.type secondary_startup, #function
|
||||
ENTRY(secondary_startup)
|
||||
/*
|
||||
* Common entry point for secondary CPUs.
|
||||
*
|
||||
* Ensure that we're in SVC mode, and IRQs are disabled. Lookup
|
||||
* the processor type - there is no need to check the machine type
|
||||
* as it has already been validated by the primary processor.
|
||||
*/
|
||||
msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | SVC_MODE
|
||||
mrc p15, 0, r9, c0, c0 @ get processor id
|
||||
bl __lookup_processor_type
|
||||
movs r10, r5 @ invalid processor?
|
||||
moveq r0, #'p' @ yes, error 'p'
|
||||
beq __error
|
||||
|
||||
/*
|
||||
* Use the page tables supplied from __cpu_up.
|
||||
*/
|
||||
adr r4, __secondary_data
|
||||
ldmia r4, {r5, r7, r13} @ address to jump to after
|
||||
sub r4, r4, r5 @ mmu has been enabled
|
||||
ldr r4, [r7, r4] @ get secondary_data.pgdir
|
||||
adr lr, __enable_mmu @ return address
|
||||
add pc, r10, #PROCINFO_INITFUNC @ initialise processor
|
||||
@ (return control reg)
|
||||
|
||||
/*
|
||||
* r6 = &secondary_data
|
||||
*/
|
||||
ENTRY(__secondary_switched)
|
||||
ldr sp, [r7, #4] @ get secondary_data.stack
|
||||
mov fp, #0
|
||||
b secondary_start_kernel
|
||||
|
||||
.type __secondary_data, %object
|
||||
__secondary_data:
|
||||
.long .
|
||||
.long secondary_data
|
||||
.long __secondary_switched
|
||||
#endif /* defined(CONFIG_SMP) */
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Setup common bits before finally enabling the MMU. Essentially
|
||||
* this is just loading the page table pointer and domain access
|
||||
* registers.
|
||||
*/
|
||||
.type __enable_mmu, %function
|
||||
__enable_mmu:
|
||||
#ifdef CONFIG_ALIGNMENT_TRAP
|
||||
orr r0, r0, #CR_A
|
||||
#else
|
||||
bic r0, r0, #CR_A
|
||||
#endif
|
||||
#ifdef CONFIG_CPU_DCACHE_DISABLE
|
||||
bic r0, r0, #CR_C
|
||||
#endif
|
||||
#ifdef CONFIG_CPU_BPREDICT_DISABLE
|
||||
bic r0, r0, #CR_Z
|
||||
#endif
|
||||
#ifdef CONFIG_CPU_ICACHE_DISABLE
|
||||
bic r0, r0, #CR_I
|
||||
#endif
|
||||
mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
|
||||
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
|
||||
domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
|
||||
domain_val(DOMAIN_IO, DOMAIN_CLIENT))
|
||||
mcr p15, 0, r5, c3, c0, 0 @ load domain access register
|
||||
mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
|
||||
b __turn_mmu_on
|
||||
|
||||
/*
|
||||
* Enable the MMU. This completely changes the structure of the visible
|
||||
* memory space. You will not be able to trace execution through this.
|
||||
* If you have an enquiry about this, *please* check the linux-arm-kernel
|
||||
* mailing list archives BEFORE sending another post to the list.
|
||||
*
|
||||
* r0 = cp#15 control register
|
||||
* r13 = *virtual* address to jump to upon completion
|
||||
*
|
||||
* other registers depend on the function called upon completion
|
||||
*/
|
||||
.align 5
|
||||
.type __turn_mmu_on, %function
|
||||
__turn_mmu_on:
|
||||
mov r0, r0
|
||||
mcr p15, 0, r0, c1, c0, 0 @ write control reg
|
||||
mrc p15, 0, r3, c0, c0, 0 @ read id reg
|
||||
mov r3, r3
|
||||
mov r3, r3
|
||||
mov pc, r13
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Setup the initial page tables. We only setup the barest
|
||||
* amount which are required to get the kernel running, which
|
||||
* generally means mapping in the kernel code.
|
||||
*
|
||||
* r8 = machinfo
|
||||
* r9 = cpuid
|
||||
* r10 = procinfo
|
||||
*
|
||||
* Returns:
|
||||
* r0, r3, r6, r7 corrupted
|
||||
* r4 = physical page table address
|
||||
*/
|
||||
.type __create_page_tables, %function
|
||||
__create_page_tables:
|
||||
pgtbl r4 @ page table address
|
||||
|
||||
/*
|
||||
* Clear the 16K level 1 swapper page table
|
||||
*/
|
||||
mov r0, r4
|
||||
mov r3, #0
|
||||
add r6, r0, #0x4000
|
||||
1: str r3, [r0], #4
|
||||
str r3, [r0], #4
|
||||
str r3, [r0], #4
|
||||
str r3, [r0], #4
|
||||
teq r0, r6
|
||||
bne 1b
|
||||
|
||||
ldr r7, [r10, #PROCINFO_MM_MMUFLAGS] @ mm_mmuflags
|
||||
|
||||
/*
|
||||
* Create identity mapping for first MB of kernel to
|
||||
* cater for the MMU enable. This identity mapping
|
||||
* will be removed by paging_init(). We use our current program
|
||||
* counter to determine corresponding section base address.
|
||||
*/
|
||||
mov r6, pc, lsr #20 @ start of kernel section
|
||||
orr r3, r7, r6, lsl #20 @ flags + kernel base
|
||||
str r3, [r4, r6, lsl #2] @ identity mapping
|
||||
|
||||
/*
|
||||
* Now setup the pagetables for our kernel direct
|
||||
* mapped region.
|
||||
*/
|
||||
add r0, r4, #(KERNEL_START & 0xff000000) >> 18
|
||||
str r3, [r0, #(KERNEL_START & 0x00f00000) >> 18]!
|
||||
ldr r6, =(KERNEL_END - 1)
|
||||
add r0, r0, #4
|
||||
add r6, r4, r6, lsr #18
|
||||
1: cmp r0, r6
|
||||
add r3, r3, #1 << 20
|
||||
strls r3, [r0], #4
|
||||
bls 1b
|
||||
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
/*
|
||||
* Map some ram to cover our .data and .bss areas.
|
||||
*/
|
||||
orr r3, r7, #(KERNEL_RAM_PADDR & 0xff000000)
|
||||
orr r3, r3, #(KERNEL_RAM_PADDR & 0x00f00000)
|
||||
add r0, r4, #(KERNEL_RAM_VADDR & 0xff000000) >> 18
|
||||
str r3, [r0, #(KERNEL_RAM_VADDR & 0x00f00000) >> 18]!
|
||||
ldr r6, =(_end - 1)
|
||||
add r0, r0, #4
|
||||
add r6, r4, r6, lsr #18
|
||||
1: cmp r0, r6
|
||||
add r3, r3, #1 << 20
|
||||
strls r3, [r0], #4
|
||||
bls 1b
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Then map first 1MB of ram in case it contains our boot params.
|
||||
*/
|
||||
add r0, r4, #PAGE_OFFSET >> 18
|
||||
orr r6, r7, #(PHYS_OFFSET & 0xff000000)
|
||||
orr r6, r6, #(PHYS_OFFSET & 0x00e00000)
|
||||
str r6, [r0]
|
||||
|
||||
#ifdef CONFIG_DEBUG_LL
|
||||
ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
|
||||
/*
|
||||
* Map in IO space for serial debugging.
|
||||
* This allows debug messages to be output
|
||||
* via a serial console before paging_init.
|
||||
*/
|
||||
ldr r3, [r8, #MACHINFO_PGOFFIO]
|
||||
add r0, r4, r3
|
||||
rsb r3, r3, #0x4000 @ PTRS_PER_PGD*sizeof(long)
|
||||
cmp r3, #0x0800 @ limit to 512MB
|
||||
movhi r3, #0x0800
|
||||
add r6, r0, r3
|
||||
ldr r3, [r8, #MACHINFO_PHYSIO]
|
||||
orr r3, r3, r7
|
||||
1: str r3, [r0], #4
|
||||
add r3, r3, #1 << 20
|
||||
teq r0, r6
|
||||
bne 1b
|
||||
#if defined(CONFIG_ARCH_NETWINDER) || defined(CONFIG_ARCH_CATS)
|
||||
/*
|
||||
* If we're using the NetWinder or CATS, we also need to map
|
||||
* in the 16550-type serial port for the debug messages
|
||||
*/
|
||||
add r0, r4, #0xff000000 >> 18
|
||||
orr r3, r7, #0x7c000000
|
||||
str r3, [r0]
|
||||
#endif
|
||||
#ifdef CONFIG_ARCH_RPC
|
||||
/*
|
||||
* Map in screen at 0x02000000 & SCREEN2_BASE
|
||||
* Similar reasons here - for debug. This is
|
||||
* only for Acorn RiscPC architectures.
|
||||
*/
|
||||
add r0, r4, #0x02000000 >> 18
|
||||
orr r3, r7, #0x02000000
|
||||
str r3, [r0]
|
||||
add r0, r4, #0xd8000000 >> 18
|
||||
str r3, [r0]
|
||||
#endif
|
||||
#endif
|
||||
mov pc, lr
|
||||
.ltorg
|
||||
|
||||
#include "head-common.S"
|
||||
44
arch/arm/kernel/init_task.c
Normal file
44
arch/arm/kernel/init_task.c
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/init_task.c
|
||||
*/
|
||||
#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 head.S ensure the proper alignment.
|
||||
*
|
||||
* 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);
|
||||
51
arch/arm/kernel/io.c
Normal file
51
arch/arm/kernel/io.c
Normal file
@@ -0,0 +1,51 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/*
|
||||
* Copy data from IO memory space to "real" memory space.
|
||||
* This needs to be optimized.
|
||||
*/
|
||||
void _memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
|
||||
{
|
||||
unsigned char *t = to;
|
||||
while (count) {
|
||||
count--;
|
||||
*t = readb(from);
|
||||
t++;
|
||||
from++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy data from "real" memory space to IO memory space.
|
||||
* This needs to be optimized.
|
||||
*/
|
||||
void _memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
|
||||
{
|
||||
const unsigned char *f = from;
|
||||
while (count) {
|
||||
count--;
|
||||
writeb(*f, to);
|
||||
f++;
|
||||
to++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* "memset" on IO memory space.
|
||||
* This needs to be optimized.
|
||||
*/
|
||||
void _memset_io(volatile void __iomem *dst, int c, size_t count)
|
||||
{
|
||||
while (count) {
|
||||
count--;
|
||||
writeb(c, dst);
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(_memcpy_fromio);
|
||||
EXPORT_SYMBOL(_memcpy_toio);
|
||||
EXPORT_SYMBOL(_memset_io);
|
||||
210
arch/arm/kernel/irq.c
Normal file
210
arch/arm/kernel/irq.c
Normal file
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/irq.c
|
||||
*
|
||||
* Copyright (C) 1992 Linus Torvalds
|
||||
* Modifications for ARM processor Copyright (C) 1995-2000 Russell King.
|
||||
*
|
||||
* Support for Dynamic Tick Timer Copyright (C) 2004-2005 Nokia Corporation.
|
||||
* Dynamic Tick Timer written by Tony Lindgren <tony@atomide.com> and
|
||||
* Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>.
|
||||
*
|
||||
* 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/kernel_stat.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/ptrace.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 <linux/list.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#include <asm/system.h>
|
||||
#include <asm/mach/time.h>
|
||||
|
||||
/*
|
||||
* No architecture-specific irq_finish function defined in arm/arch/irqs.h.
|
||||
*/
|
||||
#ifndef irq_finish
|
||||
#define irq_finish(irq) do { } while (0)
|
||||
#endif
|
||||
|
||||
void (*init_arch_irq)(void) __initdata = NULL;
|
||||
unsigned long irq_err_count;
|
||||
|
||||
int show_interrupts(struct seq_file *p, void *v)
|
||||
{
|
||||
int i = *(loff_t *) v, cpu;
|
||||
struct irqaction * action;
|
||||
unsigned long flags;
|
||||
|
||||
if (i == 0) {
|
||||
char cpuname[12];
|
||||
|
||||
seq_printf(p, " ");
|
||||
for_each_present_cpu(cpu) {
|
||||
sprintf(cpuname, "CPU%d", cpu);
|
||||
seq_printf(p, " %10s", cpuname);
|
||||
}
|
||||
seq_putc(p, '\n');
|
||||
}
|
||||
|
||||
if (i < NR_IRQS) {
|
||||
spin_lock_irqsave(&irq_desc[i].lock, flags);
|
||||
action = irq_desc[i].action;
|
||||
if (!action)
|
||||
goto unlock;
|
||||
|
||||
seq_printf(p, "%3d: ", i);
|
||||
for_each_present_cpu(cpu)
|
||||
seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]);
|
||||
seq_printf(p, " %10s", irq_desc[i].chip->name ? : "-");
|
||||
seq_printf(p, " %s", action->name);
|
||||
for (action = action->next; action; action = action->next)
|
||||
seq_printf(p, ", %s", action->name);
|
||||
|
||||
seq_putc(p, '\n');
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&irq_desc[i].lock, flags);
|
||||
} else if (i == NR_IRQS) {
|
||||
#ifdef CONFIG_ARCH_ACORN
|
||||
show_fiq_list(p, v);
|
||||
#endif
|
||||
#ifdef CONFIG_SMP
|
||||
show_ipi_list(p);
|
||||
show_local_irqs(p);
|
||||
#endif
|
||||
seq_printf(p, "Err: %10lu\n", irq_err_count);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle bad interrupts */
|
||||
static struct irq_desc bad_irq_desc = {
|
||||
.handle_irq = handle_bad_irq,
|
||||
.lock = SPIN_LOCK_UNLOCKED
|
||||
};
|
||||
|
||||
/*
|
||||
* 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(unsigned int irq, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *old_regs = set_irq_regs(regs);
|
||||
struct irq_desc *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();
|
||||
|
||||
desc_handle_irq(irq, desc);
|
||||
|
||||
/* AT91 specific workaround */
|
||||
irq_finish(irq);
|
||||
|
||||
irq_exit();
|
||||
set_irq_regs(old_regs);
|
||||
}
|
||||
|
||||
void set_irq_flags(unsigned int irq, unsigned int iflags)
|
||||
{
|
||||
struct irq_desc *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(&desc->lock, flags);
|
||||
desc->status |= IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN;
|
||||
if (iflags & IRQF_VALID)
|
||||
desc->status &= ~IRQ_NOREQUEST;
|
||||
if (iflags & IRQF_PROBE)
|
||||
desc->status &= ~IRQ_NOPROBE;
|
||||
if (!(iflags & IRQF_NOAUTOEN))
|
||||
desc->status &= ~IRQ_NOAUTOEN;
|
||||
spin_unlock_irqrestore(&desc->lock, flags);
|
||||
}
|
||||
|
||||
void __init init_IRQ(void)
|
||||
{
|
||||
int irq;
|
||||
|
||||
for (irq = 0; irq < NR_IRQS; irq++)
|
||||
irq_desc[irq].status |= IRQ_NOREQUEST | IRQ_NOPROBE;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
bad_irq_desc.affinity = CPU_MASK_ALL;
|
||||
bad_irq_desc.cpu = smp_processor_id();
|
||||
#endif
|
||||
init_arch_irq();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
||||
static void route_irq(struct irq_desc *desc, unsigned int irq, unsigned int cpu)
|
||||
{
|
||||
pr_debug("IRQ%u: moving from cpu%u to cpu%u\n", irq, desc->cpu, cpu);
|
||||
|
||||
spin_lock_irq(&desc->lock);
|
||||
desc->chip->set_affinity(irq, cpumask_of_cpu(cpu));
|
||||
spin_unlock_irq(&desc->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* The CPU has been marked offline. Migrate IRQs off this CPU. If
|
||||
* the affinity settings do not allow other CPUs, force them onto any
|
||||
* available CPU.
|
||||
*/
|
||||
void migrate_irqs(void)
|
||||
{
|
||||
unsigned int i, cpu = smp_processor_id();
|
||||
|
||||
for (i = 0; i < NR_IRQS; i++) {
|
||||
struct irq_desc *desc = irq_desc + i;
|
||||
|
||||
if (desc->cpu == cpu) {
|
||||
unsigned int newcpu = any_online_cpu(desc->affinity);
|
||||
|
||||
if (newcpu == NR_CPUS) {
|
||||
if (printk_ratelimit())
|
||||
printk(KERN_INFO "IRQ%u no longer affine to CPU%u\n",
|
||||
i, cpu);
|
||||
|
||||
cpus_setall(desc->affinity);
|
||||
newcpu = any_online_cpu(desc->affinity);
|
||||
}
|
||||
|
||||
route_irq(desc, i, newcpu);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
74
arch/arm/kernel/isa.c
Normal file
74
arch/arm/kernel/isa.c
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/isa.c
|
||||
*
|
||||
* Copyright (C) 1999 Phil Blundell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* ISA shared memory and I/O port support, and is required to support
|
||||
* iopl, inb, outb and friends in userspace via glibc emulation.
|
||||
*/
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
static unsigned int isa_membase, isa_portbase, isa_portshift;
|
||||
|
||||
static ctl_table ctl_isa_vars[4] = {
|
||||
{
|
||||
.ctl_name = BUS_ISA_MEM_BASE,
|
||||
.procname = "membase",
|
||||
.data = &isa_membase,
|
||||
.maxlen = sizeof(isa_membase),
|
||||
.mode = 0444,
|
||||
.proc_handler = &proc_dointvec,
|
||||
}, {
|
||||
.ctl_name = BUS_ISA_PORT_BASE,
|
||||
.procname = "portbase",
|
||||
.data = &isa_portbase,
|
||||
.maxlen = sizeof(isa_portbase),
|
||||
.mode = 0444,
|
||||
.proc_handler = &proc_dointvec,
|
||||
}, {
|
||||
.ctl_name = BUS_ISA_PORT_SHIFT,
|
||||
.procname = "portshift",
|
||||
.data = &isa_portshift,
|
||||
.maxlen = sizeof(isa_portshift),
|
||||
.mode = 0444,
|
||||
.proc_handler = &proc_dointvec,
|
||||
}, {0}
|
||||
};
|
||||
|
||||
static struct ctl_table_header *isa_sysctl_header;
|
||||
|
||||
static ctl_table ctl_isa[2] = {
|
||||
{
|
||||
.ctl_name = CTL_BUS_ISA,
|
||||
.procname = "isa",
|
||||
.mode = 0555,
|
||||
.child = ctl_isa_vars,
|
||||
}, {0}
|
||||
};
|
||||
|
||||
static ctl_table ctl_bus[2] = {
|
||||
{
|
||||
.ctl_name = CTL_BUS,
|
||||
.procname = "bus",
|
||||
.mode = 0555,
|
||||
.child = ctl_isa,
|
||||
}, {0}
|
||||
};
|
||||
|
||||
void __init
|
||||
register_isa_ports(unsigned int membase, unsigned int portbase, unsigned int portshift)
|
||||
{
|
||||
isa_membase = membase;
|
||||
isa_portbase = portbase;
|
||||
isa_portshift = portshift;
|
||||
isa_sysctl_header = register_sysctl_table(ctl_bus);
|
||||
}
|
||||
317
arch/arm/kernel/iwmmxt.S
Normal file
317
arch/arm/kernel/iwmmxt.S
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/iwmmxt.S
|
||||
*
|
||||
* XScale iWMMXt (Concan) context switching and handling
|
||||
*
|
||||
* Initial code:
|
||||
* Copyright (c) 2003, Intel Corporation
|
||||
*
|
||||
* Full lazy switching support, optimizations and more, by Nicolas Pitre
|
||||
* Copyright (c) 2003-2004, MontaVista Software, Inc.
|
||||
*
|
||||
* 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/linkage.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
|
||||
#define MMX_WR0 (0x00)
|
||||
#define MMX_WR1 (0x08)
|
||||
#define MMX_WR2 (0x10)
|
||||
#define MMX_WR3 (0x18)
|
||||
#define MMX_WR4 (0x20)
|
||||
#define MMX_WR5 (0x28)
|
||||
#define MMX_WR6 (0x30)
|
||||
#define MMX_WR7 (0x38)
|
||||
#define MMX_WR8 (0x40)
|
||||
#define MMX_WR9 (0x48)
|
||||
#define MMX_WR10 (0x50)
|
||||
#define MMX_WR11 (0x58)
|
||||
#define MMX_WR12 (0x60)
|
||||
#define MMX_WR13 (0x68)
|
||||
#define MMX_WR14 (0x70)
|
||||
#define MMX_WR15 (0x78)
|
||||
#define MMX_WCSSF (0x80)
|
||||
#define MMX_WCASF (0x84)
|
||||
#define MMX_WCGR0 (0x88)
|
||||
#define MMX_WCGR1 (0x8C)
|
||||
#define MMX_WCGR2 (0x90)
|
||||
#define MMX_WCGR3 (0x94)
|
||||
|
||||
#define MMX_SIZE (0x98)
|
||||
|
||||
.text
|
||||
|
||||
/*
|
||||
* Lazy switching of Concan coprocessor context
|
||||
*
|
||||
* r10 = struct thread_info pointer
|
||||
* r9 = ret_from_exception
|
||||
* lr = undefined instr exit
|
||||
*
|
||||
* called from prefetch exception handler with interrupts disabled
|
||||
*/
|
||||
|
||||
ENTRY(iwmmxt_task_enable)
|
||||
|
||||
mrc p15, 0, r2, c15, c1, 0
|
||||
tst r2, #0x3 @ CP0 and CP1 accessible?
|
||||
movne pc, lr @ if so no business here
|
||||
orr r2, r2, #0x3 @ enable access to CP0 and CP1
|
||||
mcr p15, 0, r2, c15, c1, 0
|
||||
|
||||
ldr r3, =concan_owner
|
||||
add r0, r10, #TI_IWMMXT_STATE @ get task Concan save area
|
||||
ldr r2, [sp, #60] @ current task pc value
|
||||
ldr r1, [r3] @ get current Concan owner
|
||||
str r0, [r3] @ this task now owns Concan regs
|
||||
sub r2, r2, #4 @ adjust pc back
|
||||
str r2, [sp, #60]
|
||||
|
||||
mrc p15, 0, r2, c2, c0, 0
|
||||
mov r2, r2 @ cpwait
|
||||
|
||||
teq r1, #0 @ test for last ownership
|
||||
mov lr, r9 @ normal exit from exception
|
||||
beq concan_load @ no owner, skip save
|
||||
|
||||
concan_save:
|
||||
|
||||
tmrc r2, wCon
|
||||
|
||||
@ CUP? wCx
|
||||
tst r2, #0x1
|
||||
beq 1f
|
||||
|
||||
concan_dump:
|
||||
|
||||
wstrw wCSSF, [r1, #MMX_WCSSF]
|
||||
wstrw wCASF, [r1, #MMX_WCASF]
|
||||
wstrw wCGR0, [r1, #MMX_WCGR0]
|
||||
wstrw wCGR1, [r1, #MMX_WCGR1]
|
||||
wstrw wCGR2, [r1, #MMX_WCGR2]
|
||||
wstrw wCGR3, [r1, #MMX_WCGR3]
|
||||
|
||||
1: @ MUP? wRn
|
||||
tst r2, #0x2
|
||||
beq 2f
|
||||
|
||||
wstrd wR0, [r1, #MMX_WR0]
|
||||
wstrd wR1, [r1, #MMX_WR1]
|
||||
wstrd wR2, [r1, #MMX_WR2]
|
||||
wstrd wR3, [r1, #MMX_WR3]
|
||||
wstrd wR4, [r1, #MMX_WR4]
|
||||
wstrd wR5, [r1, #MMX_WR5]
|
||||
wstrd wR6, [r1, #MMX_WR6]
|
||||
wstrd wR7, [r1, #MMX_WR7]
|
||||
wstrd wR8, [r1, #MMX_WR8]
|
||||
wstrd wR9, [r1, #MMX_WR9]
|
||||
wstrd wR10, [r1, #MMX_WR10]
|
||||
wstrd wR11, [r1, #MMX_WR11]
|
||||
wstrd wR12, [r1, #MMX_WR12]
|
||||
wstrd wR13, [r1, #MMX_WR13]
|
||||
wstrd wR14, [r1, #MMX_WR14]
|
||||
wstrd wR15, [r1, #MMX_WR15]
|
||||
|
||||
2: teq r0, #0 @ anything to load?
|
||||
moveq pc, lr
|
||||
|
||||
concan_load:
|
||||
|
||||
@ Load wRn
|
||||
wldrd wR0, [r0, #MMX_WR0]
|
||||
wldrd wR1, [r0, #MMX_WR1]
|
||||
wldrd wR2, [r0, #MMX_WR2]
|
||||
wldrd wR3, [r0, #MMX_WR3]
|
||||
wldrd wR4, [r0, #MMX_WR4]
|
||||
wldrd wR5, [r0, #MMX_WR5]
|
||||
wldrd wR6, [r0, #MMX_WR6]
|
||||
wldrd wR7, [r0, #MMX_WR7]
|
||||
wldrd wR8, [r0, #MMX_WR8]
|
||||
wldrd wR9, [r0, #MMX_WR9]
|
||||
wldrd wR10, [r0, #MMX_WR10]
|
||||
wldrd wR11, [r0, #MMX_WR11]
|
||||
wldrd wR12, [r0, #MMX_WR12]
|
||||
wldrd wR13, [r0, #MMX_WR13]
|
||||
wldrd wR14, [r0, #MMX_WR14]
|
||||
wldrd wR15, [r0, #MMX_WR15]
|
||||
|
||||
@ Load wCx
|
||||
wldrw wCSSF, [r0, #MMX_WCSSF]
|
||||
wldrw wCASF, [r0, #MMX_WCASF]
|
||||
wldrw wCGR0, [r0, #MMX_WCGR0]
|
||||
wldrw wCGR1, [r0, #MMX_WCGR1]
|
||||
wldrw wCGR2, [r0, #MMX_WCGR2]
|
||||
wldrw wCGR3, [r0, #MMX_WCGR3]
|
||||
|
||||
@ clear CUP/MUP (only if r1 != 0)
|
||||
teq r1, #0
|
||||
mov r2, #0
|
||||
moveq pc, lr
|
||||
tmcr wCon, r2
|
||||
mov pc, lr
|
||||
|
||||
/*
|
||||
* Back up Concan regs to save area and disable access to them
|
||||
* (mainly for gdb or sleep mode usage)
|
||||
*
|
||||
* r0 = struct thread_info pointer of target task or NULL for any
|
||||
*/
|
||||
|
||||
ENTRY(iwmmxt_task_disable)
|
||||
|
||||
stmfd sp!, {r4, lr}
|
||||
|
||||
mrs ip, cpsr
|
||||
orr r2, ip, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, r2
|
||||
|
||||
ldr r3, =concan_owner
|
||||
add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area
|
||||
ldr r1, [r3] @ get current Concan owner
|
||||
teq r1, #0 @ any current owner?
|
||||
beq 1f @ no: quit
|
||||
teq r0, #0 @ any owner?
|
||||
teqne r1, r2 @ or specified one?
|
||||
bne 1f @ no: quit
|
||||
|
||||
mrc p15, 0, r4, c15, c1, 0
|
||||
orr r4, r4, #0x3 @ enable access to CP0 and CP1
|
||||
mcr p15, 0, r4, c15, c1, 0
|
||||
mov r0, #0 @ nothing to load
|
||||
str r0, [r3] @ no more current owner
|
||||
mrc p15, 0, r2, c2, c0, 0
|
||||
mov r2, r2 @ cpwait
|
||||
bl concan_save
|
||||
|
||||
bic r4, r4, #0x3 @ disable access to CP0 and CP1
|
||||
mcr p15, 0, r4, c15, c1, 0
|
||||
mrc p15, 0, r2, c2, c0, 0
|
||||
mov r2, r2 @ cpwait
|
||||
|
||||
1: msr cpsr_c, ip @ restore interrupt mode
|
||||
ldmfd sp!, {r4, pc}
|
||||
|
||||
/*
|
||||
* Copy Concan state to given memory address
|
||||
*
|
||||
* r0 = struct thread_info pointer of target task
|
||||
* r1 = memory address where to store Concan state
|
||||
*
|
||||
* this is called mainly in the creation of signal stack frames
|
||||
*/
|
||||
|
||||
ENTRY(iwmmxt_task_copy)
|
||||
|
||||
mrs ip, cpsr
|
||||
orr r2, ip, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, r2
|
||||
|
||||
ldr r3, =concan_owner
|
||||
add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area
|
||||
ldr r3, [r3] @ get current Concan owner
|
||||
teq r2, r3 @ does this task own it...
|
||||
beq 1f
|
||||
|
||||
@ current Concan values are in the task save area
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov r0, r1
|
||||
mov r1, r2
|
||||
mov r2, #MMX_SIZE
|
||||
b memcpy
|
||||
|
||||
1: @ this task owns Concan regs -- grab a copy from there
|
||||
mov r0, #0 @ nothing to load
|
||||
mov r2, #3 @ save all regs
|
||||
mov r3, lr @ preserve return address
|
||||
bl concan_dump
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov pc, r3
|
||||
|
||||
/*
|
||||
* Restore Concan state from given memory address
|
||||
*
|
||||
* r0 = struct thread_info pointer of target task
|
||||
* r1 = memory address where to get Concan state from
|
||||
*
|
||||
* this is used to restore Concan state when unwinding a signal stack frame
|
||||
*/
|
||||
|
||||
ENTRY(iwmmxt_task_restore)
|
||||
|
||||
mrs ip, cpsr
|
||||
orr r2, ip, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, r2
|
||||
|
||||
ldr r3, =concan_owner
|
||||
add r2, r0, #TI_IWMMXT_STATE @ get task Concan save area
|
||||
ldr r3, [r3] @ get current Concan owner
|
||||
bic r2, r2, #0x7 @ 64-bit alignment
|
||||
teq r2, r3 @ does this task own it...
|
||||
beq 1f
|
||||
|
||||
@ this task doesn't own Concan regs -- use its save area
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov r0, r2
|
||||
mov r2, #MMX_SIZE
|
||||
b memcpy
|
||||
|
||||
1: @ this task owns Concan regs -- load them directly
|
||||
mov r0, r1
|
||||
mov r1, #0 @ don't clear CUP/MUP
|
||||
mov r3, lr @ preserve return address
|
||||
bl concan_load
|
||||
msr cpsr_c, ip @ restore interrupt mode
|
||||
mov pc, r3
|
||||
|
||||
/*
|
||||
* Concan handling on task switch
|
||||
*
|
||||
* r0 = next thread_info pointer
|
||||
*
|
||||
* Called only from the iwmmxt notifier with task preemption disabled.
|
||||
*/
|
||||
ENTRY(iwmmxt_task_switch)
|
||||
|
||||
mrc p15, 0, r1, c15, c1, 0
|
||||
tst r1, #0x3 @ CP0 and CP1 accessible?
|
||||
bne 1f @ yes: block them for next task
|
||||
|
||||
ldr r2, =concan_owner
|
||||
add r3, r0, #TI_IWMMXT_STATE @ get next task Concan save area
|
||||
ldr r2, [r2] @ get current Concan owner
|
||||
teq r2, r3 @ next task owns it?
|
||||
movne pc, lr @ no: leave Concan disabled
|
||||
|
||||
1: eor r1, r1, #3 @ flip Concan access
|
||||
mcr p15, 0, r1, c15, c1, 0
|
||||
|
||||
mrc p15, 0, r1, c2, c0, 0
|
||||
sub pc, lr, r1, lsr #32 @ cpwait and return
|
||||
|
||||
/*
|
||||
* Remove Concan ownership of given task
|
||||
*
|
||||
* r0 = struct thread_info pointer
|
||||
*/
|
||||
ENTRY(iwmmxt_task_release)
|
||||
|
||||
mrs r2, cpsr
|
||||
orr ip, r2, #PSR_I_BIT @ disable interrupts
|
||||
msr cpsr_c, ip
|
||||
ldr r3, =concan_owner
|
||||
add r0, r0, #TI_IWMMXT_STATE @ get task Concan save area
|
||||
ldr r1, [r3] @ get current Concan owner
|
||||
eors r0, r0, r1 @ if equal...
|
||||
streq r0, [r3] @ then clear ownership
|
||||
msr cpsr_c, r2 @ restore interrupts
|
||||
mov pc, lr
|
||||
|
||||
.data
|
||||
concan_owner:
|
||||
.word 0
|
||||
|
||||
78
arch/arm/kernel/machine_kexec.c
Normal file
78
arch/arm/kernel/machine_kexec.c
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* machine_kexec.c - handle transition of Linux booting another kernel
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/mach-types.h>
|
||||
|
||||
const extern unsigned char relocate_new_kernel[];
|
||||
const extern unsigned int relocate_new_kernel_size;
|
||||
|
||||
extern void setup_mm_for_reboot(char mode);
|
||||
|
||||
extern unsigned long kexec_start_address;
|
||||
extern unsigned long kexec_indirection_page;
|
||||
extern unsigned long kexec_mach_type;
|
||||
|
||||
/*
|
||||
* Provide a dummy crash_notes definition while crash dump arrives to arm.
|
||||
* This prevents breakage of crash_notes attribute in kernel/ksysfs.c.
|
||||
*/
|
||||
|
||||
int machine_kexec_prepare(struct kimage *image)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void machine_kexec_cleanup(struct kimage *image)
|
||||
{
|
||||
}
|
||||
|
||||
void machine_shutdown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void machine_crash_shutdown(struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
void machine_kexec(struct kimage *image)
|
||||
{
|
||||
unsigned long page_list;
|
||||
unsigned long reboot_code_buffer_phys;
|
||||
void *reboot_code_buffer;
|
||||
|
||||
|
||||
page_list = image->head & PAGE_MASK;
|
||||
|
||||
/* we need both effective and real address here */
|
||||
reboot_code_buffer_phys =
|
||||
page_to_pfn(image->control_code_page) << PAGE_SHIFT;
|
||||
reboot_code_buffer = page_address(image->control_code_page);
|
||||
|
||||
/* Prepare parameters for reboot_code_buffer*/
|
||||
kexec_start_address = image->start;
|
||||
kexec_indirection_page = page_list;
|
||||
kexec_mach_type = machine_arch_type;
|
||||
|
||||
/* copy our kernel relocation code to the control code page */
|
||||
memcpy(reboot_code_buffer,
|
||||
relocate_new_kernel, relocate_new_kernel_size);
|
||||
|
||||
|
||||
flush_icache_range((unsigned long) reboot_code_buffer,
|
||||
(unsigned long) reboot_code_buffer + KEXEC_CONTROL_CODE_SIZE);
|
||||
printk(KERN_INFO "Bye!\n");
|
||||
|
||||
cpu_proc_fin();
|
||||
setup_mm_for_reboot(0); /* mode is not used, so just pass 0*/
|
||||
cpu_reset(reboot_code_buffer_phys);
|
||||
}
|
||||
162
arch/arm/kernel/module.c
Normal file
162
arch/arm/kernel/module.c
Normal file
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/module.c
|
||||
*
|
||||
* Copyright (C) 2002 Russell King.
|
||||
* Modified for nommu by Hyok S. Choi
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Module allocation method suggested by Andi Kleen.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include <asm/pgtable.h>
|
||||
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
/*
|
||||
* The XIP kernel text is mapped in the module area for modules and
|
||||
* some other stuff to work without any indirect relocations.
|
||||
* MODULE_START is redefined here and not in asm/memory.h to avoid
|
||||
* recompiling the whole kernel when CONFIG_XIP_KERNEL is turned on/off.
|
||||
*/
|
||||
extern void _etext;
|
||||
#undef MODULE_START
|
||||
#define MODULE_START (((unsigned long)&_etext + ~PGDIR_MASK) & PGDIR_MASK)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_MMU
|
||||
void *module_alloc(unsigned long size)
|
||||
{
|
||||
struct vm_struct *area;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
if (!size)
|
||||
return NULL;
|
||||
|
||||
area = __get_vm_area(size, VM_ALLOC, MODULE_START, MODULE_END);
|
||||
if (!area)
|
||||
return NULL;
|
||||
|
||||
return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL);
|
||||
}
|
||||
#else /* CONFIG_MMU */
|
||||
void *module_alloc(unsigned long size)
|
||||
{
|
||||
return size == 0 ? NULL : vmalloc(size);
|
||||
}
|
||||
#endif /* !CONFIG_MMU */
|
||||
|
||||
void module_free(struct module *module, void *region)
|
||||
{
|
||||
vfree(region);
|
||||
}
|
||||
|
||||
int module_frob_arch_sections(Elf_Ehdr *hdr,
|
||||
Elf_Shdr *sechdrs,
|
||||
char *secstrings,
|
||||
struct module *mod)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
apply_relocate(Elf32_Shdr *sechdrs, const char *strtab, unsigned int symindex,
|
||||
unsigned int relindex, struct module *module)
|
||||
{
|
||||
Elf32_Shdr *symsec = sechdrs + symindex;
|
||||
Elf32_Shdr *relsec = sechdrs + relindex;
|
||||
Elf32_Shdr *dstsec = sechdrs + relsec->sh_info;
|
||||
Elf32_Rel *rel = (void *)relsec->sh_addr;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < relsec->sh_size / sizeof(Elf32_Rel); i++, rel++) {
|
||||
unsigned long loc;
|
||||
Elf32_Sym *sym;
|
||||
s32 offset;
|
||||
|
||||
offset = ELF32_R_SYM(rel->r_info);
|
||||
if (offset < 0 || offset > (symsec->sh_size / sizeof(Elf32_Sym))) {
|
||||
printk(KERN_ERR "%s: bad relocation, section %d reloc %d\n",
|
||||
module->name, relindex, i);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
sym = ((Elf32_Sym *)symsec->sh_addr) + offset;
|
||||
|
||||
if (rel->r_offset < 0 || rel->r_offset > dstsec->sh_size - sizeof(u32)) {
|
||||
printk(KERN_ERR "%s: out of bounds relocation, "
|
||||
"section %d reloc %d offset %d size %d\n",
|
||||
module->name, relindex, i, rel->r_offset,
|
||||
dstsec->sh_size);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
loc = dstsec->sh_addr + rel->r_offset;
|
||||
|
||||
switch (ELF32_R_TYPE(rel->r_info)) {
|
||||
case R_ARM_ABS32:
|
||||
*(u32 *)loc += sym->st_value;
|
||||
break;
|
||||
|
||||
case R_ARM_PC24:
|
||||
case R_ARM_CALL:
|
||||
case R_ARM_JUMP24:
|
||||
offset = (*(u32 *)loc & 0x00ffffff) << 2;
|
||||
if (offset & 0x02000000)
|
||||
offset -= 0x04000000;
|
||||
|
||||
offset += sym->st_value - loc;
|
||||
if (offset & 3 ||
|
||||
offset <= (s32)0xfc000000 ||
|
||||
offset >= (s32)0x04000000) {
|
||||
printk(KERN_ERR
|
||||
"%s: relocation out of range, section "
|
||||
"%d reloc %d sym '%s'\n", module->name,
|
||||
relindex, i, strtab + sym->st_name);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
offset >>= 2;
|
||||
|
||||
*(u32 *)loc &= 0xff000000;
|
||||
*(u32 *)loc |= offset & 0x00ffffff;
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_ERR "%s: unknown relocation: %u\n",
|
||||
module->name, ELF32_R_TYPE(rel->r_info));
|
||||
return -ENOEXEC;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
apply_relocate_add(Elf32_Shdr *sechdrs, const char *strtab,
|
||||
unsigned int symindex, unsigned int relsec, struct module *module)
|
||||
{
|
||||
printk(KERN_ERR "module %s: ADD RELOCATION unsupported\n",
|
||||
module->name);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
int
|
||||
module_finalize(const Elf32_Ehdr *hdr, const Elf_Shdr *sechdrs,
|
||||
struct module *module)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
module_arch_cleanup(struct module *mod)
|
||||
{
|
||||
}
|
||||
442
arch/arm/kernel/process.c
Normal file
442
arch/arm/kernel/process.c
Normal file
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/process.c
|
||||
*
|
||||
* Copyright (C) 1996-2000 Russell King - Converted to ARM.
|
||||
* Original 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/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/kallsyms.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/elfcore.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include <asm/leds.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/thread_notify.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/mach/time.h>
|
||||
|
||||
static const char *processor_modes[] = {
|
||||
"USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
|
||||
"UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
|
||||
"USER_32", "FIQ_32" , "IRQ_32" , "SVC_32" , "UK4_32" , "UK5_32" , "UK6_32" , "ABT_32" ,
|
||||
"UK8_32" , "UK9_32" , "UK10_32", "UND_32" , "UK12_32", "UK13_32", "UK14_32", "SYS_32"
|
||||
};
|
||||
|
||||
extern void setup_mm_for_reboot(char mode);
|
||||
|
||||
static volatile int hlt_counter;
|
||||
|
||||
#include <asm/arch/system.h>
|
||||
|
||||
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);
|
||||
|
||||
void arm_machine_restart(char mode)
|
||||
{
|
||||
/*
|
||||
* 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(mode);
|
||||
|
||||
/*
|
||||
* Now call the architecture specific reboot code.
|
||||
*/
|
||||
arch_reset(mode);
|
||||
|
||||
/*
|
||||
* Whoops - the architecture was unable to reboot.
|
||||
* Tell the user!
|
||||
*/
|
||||
mdelay(1000);
|
||||
printk("Reboot failed -- System halted\n");
|
||||
while (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function pointers to optional machine specific functions
|
||||
*/
|
||||
void (*pm_idle)(void);
|
||||
EXPORT_SYMBOL(pm_idle);
|
||||
|
||||
void (*pm_power_off)(void);
|
||||
EXPORT_SYMBOL(pm_power_off);
|
||||
|
||||
void (*arm_pm_restart)(char str) = arm_machine_restart;
|
||||
EXPORT_SYMBOL_GPL(arm_pm_restart);
|
||||
|
||||
|
||||
/*
|
||||
* This is our default idle handler. We need to disable
|
||||
* interrupts here to ensure we don't miss a wakeup call.
|
||||
*/
|
||||
static void default_idle(void)
|
||||
{
|
||||
if (hlt_counter)
|
||||
cpu_relax();
|
||||
else {
|
||||
local_irq_disable();
|
||||
if (!need_resched()) {
|
||||
timer_dyn_reprogram();
|
||||
arch_idle();
|
||||
}
|
||||
local_irq_enable();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The idle thread. We try to conserve power, while trying to keep
|
||||
* overall latency low. The architecture specific idle is passed
|
||||
* a value to indicate the level of "idleness" of the system.
|
||||
*/
|
||||
void cpu_idle(void)
|
||||
{
|
||||
local_fiq_enable();
|
||||
|
||||
/* endless idle loop with no priority at all */
|
||||
while (1) {
|
||||
void (*idle)(void) = pm_idle;
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
if (cpu_is_offline(smp_processor_id())) {
|
||||
leds_event(led_idle_start);
|
||||
cpu_die();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!idle)
|
||||
idle = default_idle;
|
||||
leds_event(led_idle_start);
|
||||
while (!need_resched())
|
||||
idle();
|
||||
leds_event(led_idle_end);
|
||||
preempt_enable_no_resched();
|
||||
schedule();
|
||||
preempt_disable();
|
||||
}
|
||||
}
|
||||
|
||||
static char reboot_mode = 'h';
|
||||
|
||||
int __init reboot_setup(char *str)
|
||||
{
|
||||
reboot_mode = str[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("reboot=", reboot_setup);
|
||||
|
||||
void machine_halt(void)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void machine_power_off(void)
|
||||
{
|
||||
if (pm_power_off)
|
||||
pm_power_off();
|
||||
}
|
||||
|
||||
void machine_restart(char * __unused)
|
||||
{
|
||||
arm_pm_restart(reboot_mode);
|
||||
}
|
||||
|
||||
void __show_regs(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long flags = condition_codes(regs);
|
||||
|
||||
printk("CPU: %d\n", smp_processor_id());
|
||||
print_symbol("PC is at %s\n", instruction_pointer(regs));
|
||||
print_symbol("LR is at %s\n", regs->ARM_lr);
|
||||
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%s Segment %s\n",
|
||||
interrupts_enabled(regs) ? "n" : "ff",
|
||||
fast_interrupts_enabled(regs) ? "n" : "ff",
|
||||
processor_modes[processor_mode(regs)],
|
||||
thumb_mode(regs) ? " (T)" : "",
|
||||
get_fs() == get_ds() ? "kernel" : "user");
|
||||
#if CONFIG_CPU_CP15
|
||||
{
|
||||
unsigned int ctrl;
|
||||
__asm__ (
|
||||
" mrc p15, 0, %0, c1, c0\n"
|
||||
: "=r" (ctrl));
|
||||
printk("Control: %04X\n", ctrl);
|
||||
}
|
||||
#ifdef CONFIG_CPU_CP15_MMU
|
||||
{
|
||||
unsigned int transbase, dac;
|
||||
__asm__ (
|
||||
" mrc p15, 0, %0, c2, c0\n"
|
||||
" mrc p15, 0, %1, c3, c0\n"
|
||||
: "=r" (transbase), "=r" (dac));
|
||||
printk("Table: %08X DAC: %08X\n",
|
||||
transbase, dac);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void show_regs(struct pt_regs * regs)
|
||||
{
|
||||
printk("\n");
|
||||
printk("Pid: %d, comm: %20s\n", current->pid, current->comm);
|
||||
__show_regs(regs);
|
||||
__backtrace();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Free current thread data structures etc..
|
||||
*/
|
||||
void exit_thread(void)
|
||||
{
|
||||
}
|
||||
|
||||
ATOMIC_NOTIFIER_HEAD(thread_notify_head);
|
||||
|
||||
EXPORT_SYMBOL_GPL(thread_notify_head);
|
||||
|
||||
void flush_thread(void)
|
||||
{
|
||||
struct thread_info *thread = current_thread_info();
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
memset(thread->used_cp, 0, sizeof(thread->used_cp));
|
||||
memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
|
||||
memset(&thread->fpstate, 0, sizeof(union fp_state));
|
||||
|
||||
thread_notify(THREAD_NOTIFY_FLUSH, thread);
|
||||
}
|
||||
|
||||
void release_thread(struct task_struct *dead_task)
|
||||
{
|
||||
struct thread_info *thread = task_thread_info(dead_task);
|
||||
|
||||
thread_notify(THREAD_NOTIFY_RELEASE, thread);
|
||||
}
|
||||
|
||||
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 stk_sz, struct task_struct *p, struct pt_regs *regs)
|
||||
{
|
||||
struct thread_info *thread = task_thread_info(p);
|
||||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
|
||||
*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;
|
||||
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
thread->tp_value = regs->ARM_r3;
|
||||
|
||||
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 = thread->used_cp[1] | thread->used_cp[2];
|
||||
|
||||
if (used_math)
|
||||
memcpy(fp, &thread->fpstate.soft, sizeof (*fp));
|
||||
|
||||
return used_math != 0;
|
||||
}
|
||||
EXPORT_SYMBOL(dump_fpu);
|
||||
|
||||
/*
|
||||
* 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.arm;
|
||||
dump->u_debugreg[3] = tsk->thread.debug.bp[1].insn.arm;
|
||||
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);
|
||||
}
|
||||
EXPORT_SYMBOL(dump_thread);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
regs.ARM_cpsr = SVC_MODE;
|
||||
|
||||
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_start, stack_end;
|
||||
int count = 0;
|
||||
if (!p || p == current || p->state == TASK_RUNNING)
|
||||
return 0;
|
||||
|
||||
stack_start = (unsigned long)end_of_stack(p);
|
||||
stack_end = (unsigned long)task_stack_page(p) + THREAD_SIZE;
|
||||
|
||||
fp = thread_saved_fp(p);
|
||||
do {
|
||||
if (fp < stack_start || fp > stack_end)
|
||||
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;
|
||||
}
|
||||
845
arch/arm/kernel/ptrace.c
Normal file
845
arch/arm/kernel/ptrace.c
Normal file
@@ -0,0 +1,845 @@
|
||||
/*
|
||||
* linux/arch/arm/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/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 <linux/init.h>
|
||||
#include <linux/signal.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/traps.h>
|
||||
|
||||
#include "ptrace.h"
|
||||
|
||||
#define REG_PC 15
|
||||
#define REG_PSR 16
|
||||
/*
|
||||
* does not yet catch signals sent when the child dies.
|
||||
* in exit.c or in signal.c.
|
||||
*/
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* Breakpoint SWI instruction: SWI &9F0001
|
||||
*/
|
||||
#define BREAKINST_ARM 0xef9f0001
|
||||
#define BREAKINST_THUMB 0xdf00 /* fill this in later */
|
||||
#else
|
||||
/*
|
||||
* New breakpoints - use an undefined instruction. The ARM architecture
|
||||
* reference manual guarantees that the following instruction space
|
||||
* will produce an undefined instruction exception on all CPUs:
|
||||
*
|
||||
* ARM: xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx
|
||||
* Thumb: 1101 1110 xxxx xxxx
|
||||
*/
|
||||
#define BREAKINST_ARM 0xe7f001f0
|
||||
#define BREAKINST_THUMB 0xde01
|
||||
#endif
|
||||
|
||||
/*
|
||||
* 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 task_pt_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 = task_pt_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;
|
||||
|
||||
if (addr & 1) {
|
||||
u16 val;
|
||||
ret = access_process_vm(task, addr & ~1, &val, sizeof(val), 0);
|
||||
ret = ret == sizeof(val) ? 0 : -EIO;
|
||||
*res = val;
|
||||
} else {
|
||||
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);
|
||||
|
||||
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 & 0x0fffffd0) == 0x012fff10) {
|
||||
/*
|
||||
* bx or blx
|
||||
*/
|
||||
alt = get_user_reg(child, insn & 15);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
union debug_insn old_insn;
|
||||
int ret;
|
||||
|
||||
if (addr & 1) {
|
||||
ret = swap_insn(task, addr & ~1, &old_insn.thumb,
|
||||
&bp->insn.thumb, 2);
|
||||
|
||||
if (ret != 2 || old_insn.thumb != BREAKINST_THUMB)
|
||||
printk(KERN_ERR "%s:%d: corrupted Thumb breakpoint at "
|
||||
"0x%08lx (0x%04x)\n", task->comm, task->pid,
|
||||
addr, old_insn.thumb);
|
||||
} else {
|
||||
ret = swap_insn(task, addr & ~3, &old_insn.arm,
|
||||
&bp->insn.arm, 4);
|
||||
|
||||
if (ret != 4 || old_insn.arm != BREAKINST_ARM)
|
||||
printk(KERN_ERR "%s:%d: corrupted ARM breakpoint at "
|
||||
"0x%08lx (0x%08x)\n", task->comm, task->pid,
|
||||
addr, old_insn.arm);
|
||||
}
|
||||
}
|
||||
|
||||
void ptrace_set_bpt(struct task_struct *child)
|
||||
{
|
||||
struct pt_regs *regs;
|
||||
unsigned long pc;
|
||||
u32 insn;
|
||||
int res;
|
||||
|
||||
regs = task_pt_regs(child);
|
||||
pc = instruction_pointer(regs);
|
||||
|
||||
if (thumb_mode(regs)) {
|
||||
printk(KERN_WARNING "ptrace: can't handle thumb mode\n");
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
ptrace_cancel_bpt(tsk);
|
||||
|
||||
info.si_signo = SIGTRAP;
|
||||
info.si_errno = 0;
|
||||
info.si_code = TRAP_BRKPT;
|
||||
info.si_addr = (void __user *)instruction_pointer(regs);
|
||||
|
||||
force_sig_info(SIGTRAP, &info, tsk);
|
||||
}
|
||||
|
||||
static int break_trap(struct pt_regs *regs, unsigned int instr)
|
||||
{
|
||||
ptrace_break(current, regs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct undef_hook arm_break_hook = {
|
||||
.instr_mask = 0x0fffffff,
|
||||
.instr_val = 0x07f001f0,
|
||||
.cpsr_mask = PSR_T_BIT,
|
||||
.cpsr_val = 0,
|
||||
.fn = break_trap,
|
||||
};
|
||||
|
||||
static struct undef_hook thumb_break_hook = {
|
||||
.instr_mask = 0xffff,
|
||||
.instr_val = 0xde01,
|
||||
.cpsr_mask = PSR_T_BIT,
|
||||
.cpsr_val = PSR_T_BIT,
|
||||
.fn = break_trap,
|
||||
};
|
||||
|
||||
static int __init ptrace_break_init(void)
|
||||
{
|
||||
register_undef_hook(&arm_break_hook);
|
||||
register_undef_hook(&thumb_break_hook);
|
||||
return 0;
|
||||
}
|
||||
|
||||
core_initcall(ptrace_break_init);
|
||||
|
||||
/*
|
||||
* 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 __user *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 __user *uregs)
|
||||
{
|
||||
struct pt_regs *regs = task_pt_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 __user *uregs)
|
||||
{
|
||||
struct pt_regs newregs;
|
||||
int ret;
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_from_user(&newregs, uregs, sizeof(struct pt_regs)) == 0) {
|
||||
struct pt_regs *regs = task_pt_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 __user *ufp)
|
||||
{
|
||||
return copy_to_user(ufp, &task_thread_info(tsk)->fpstate,
|
||||
sizeof(struct user_fp)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the child FPU state.
|
||||
*/
|
||||
static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp)
|
||||
{
|
||||
struct thread_info *thread = task_thread_info(tsk);
|
||||
thread->used_cp[1] = thread->used_cp[2] = 1;
|
||||
return copy_from_user(&thread->fpstate, ufp,
|
||||
sizeof(struct user_fp)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWMMXT
|
||||
|
||||
/*
|
||||
* Get the child iWMMXt state.
|
||||
*/
|
||||
static int ptrace_getwmmxregs(struct task_struct *tsk, void __user *ufp)
|
||||
{
|
||||
struct thread_info *thread = task_thread_info(tsk);
|
||||
|
||||
if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT))
|
||||
return -ENODATA;
|
||||
iwmmxt_task_disable(thread); /* force it to ram */
|
||||
return copy_to_user(ufp, &thread->fpstate.iwmmxt, IWMMXT_SIZE)
|
||||
? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the child iWMMXt state.
|
||||
*/
|
||||
static int ptrace_setwmmxregs(struct task_struct *tsk, void __user *ufp)
|
||||
{
|
||||
struct thread_info *thread = task_thread_info(tsk);
|
||||
|
||||
if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT))
|
||||
return -EACCES;
|
||||
iwmmxt_task_release(thread); /* force a reload */
|
||||
return copy_from_user(&thread->fpstate.iwmmxt, ufp, IWMMXT_SIZE)
|
||||
? -EFAULT : 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CRUNCH
|
||||
/*
|
||||
* Get the child Crunch state.
|
||||
*/
|
||||
static int ptrace_getcrunchregs(struct task_struct *tsk, void __user *ufp)
|
||||
{
|
||||
struct thread_info *thread = task_thread_info(tsk);
|
||||
|
||||
crunch_task_disable(thread); /* force it to ram */
|
||||
return copy_to_user(ufp, &thread->crunchstate, CRUNCH_SIZE)
|
||||
? -EFAULT : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the child Crunch state.
|
||||
*/
|
||||
static int ptrace_setcrunchregs(struct task_struct *tsk, void __user *ufp)
|
||||
{
|
||||
struct thread_info *thread = task_thread_info(tsk);
|
||||
|
||||
crunch_task_release(thread); /* force a reload */
|
||||
return copy_from_user(&thread->crunchstate, ufp, CRUNCH_SIZE)
|
||||
? -EFAULT : 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
long arch_ptrace(struct task_struct *child, long request, 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 __user *) data);
|
||||
else
|
||||
ret = -EIO;
|
||||
break;
|
||||
|
||||
case PTRACE_PEEKUSR:
|
||||
ret = ptrace_read_user(child, addr, (unsigned long __user *)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 (!valid_signal(data))
|
||||
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 (!valid_signal(data))
|
||||
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 __user *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_SETREGS:
|
||||
ret = ptrace_setregs(child, (void __user *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_GETFPREGS:
|
||||
ret = ptrace_getfpregs(child, (void __user *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_SETFPREGS:
|
||||
ret = ptrace_setfpregs(child, (void __user *)data);
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_IWMMXT
|
||||
case PTRACE_GETWMMXREGS:
|
||||
ret = ptrace_getwmmxregs(child, (void __user *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_SETWMMXREGS:
|
||||
ret = ptrace_setwmmxregs(child, (void __user *)data);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case PTRACE_GET_THREAD_AREA:
|
||||
ret = put_user(task_thread_info(child)->tp_value,
|
||||
(unsigned long __user *) data);
|
||||
break;
|
||||
|
||||
case PTRACE_SET_SYSCALL:
|
||||
ret = 0;
|
||||
child->ptrace_message = data;
|
||||
break;
|
||||
|
||||
#ifdef CONFIG_CRUNCH
|
||||
case PTRACE_GETCRUNCHREGS:
|
||||
ret = ptrace_getcrunchregs(child, (void __user *)data);
|
||||
break;
|
||||
|
||||
case PTRACE_SETCRUNCHREGS:
|
||||
ret = ptrace_setcrunchregs(child, (void __user *)data);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
ret = ptrace_request(child, request, addr, data);
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
|
||||
{
|
||||
unsigned long ip;
|
||||
|
||||
if (!test_thread_flag(TIF_SYSCALL_TRACE))
|
||||
return scno;
|
||||
if (!(current->ptrace & PT_PTRACED))
|
||||
return scno;
|
||||
|
||||
/*
|
||||
* Save IP. IP is used to denote syscall entry/exit:
|
||||
* IP = 0 -> entry, = 1 -> exit
|
||||
*/
|
||||
ip = regs->ARM_ip;
|
||||
regs->ARM_ip = why;
|
||||
|
||||
current->ptrace_message = scno;
|
||||
|
||||
/* 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;
|
||||
|
||||
return current->ptrace_message;
|
||||
}
|
||||
12
arch/arm/kernel/ptrace.h
Normal file
12
arch/arm/kernel/ptrace.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/ptrace.h
|
||||
*
|
||||
* Copyright (C) 2000-2003 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.
|
||||
*/
|
||||
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 *);
|
||||
74
arch/arm/kernel/relocate_kernel.S
Normal file
74
arch/arm/kernel/relocate_kernel.S
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* relocate_kernel.S - put the kernel image in place to boot
|
||||
*/
|
||||
|
||||
#include <asm/kexec.h>
|
||||
|
||||
.globl relocate_new_kernel
|
||||
relocate_new_kernel:
|
||||
|
||||
ldr r0,kexec_indirection_page
|
||||
ldr r1,kexec_start_address
|
||||
|
||||
|
||||
0: /* top, read another word for the indirection page */
|
||||
ldr r3, [r0],#4
|
||||
|
||||
/* Is it a destination page. Put destination address to r4 */
|
||||
tst r3,#1,0
|
||||
beq 1f
|
||||
bic r4,r3,#1
|
||||
b 0b
|
||||
1:
|
||||
/* Is it an indirection page */
|
||||
tst r3,#2,0
|
||||
beq 1f
|
||||
bic r0,r3,#2
|
||||
b 0b
|
||||
1:
|
||||
|
||||
/* are we done ? */
|
||||
tst r3,#4,0
|
||||
beq 1f
|
||||
b 2f
|
||||
|
||||
1:
|
||||
/* is it source ? */
|
||||
tst r3,#8,0
|
||||
beq 0b
|
||||
bic r3,r3,#8
|
||||
mov r6,#1024
|
||||
9:
|
||||
ldr r5,[r3],#4
|
||||
str r5,[r4],#4
|
||||
subs r6,r6,#1
|
||||
bne 9b
|
||||
b 0b
|
||||
|
||||
2:
|
||||
/* Jump to relocated kernel */
|
||||
mov lr,r1
|
||||
mov r0,#0
|
||||
ldr r1,kexec_mach_type
|
||||
mov r2,#0
|
||||
mov pc,lr
|
||||
|
||||
.globl kexec_start_address
|
||||
kexec_start_address:
|
||||
.long 0x0
|
||||
|
||||
.globl kexec_indirection_page
|
||||
kexec_indirection_page:
|
||||
.long 0x0
|
||||
|
||||
.globl kexec_mach_type
|
||||
kexec_mach_type:
|
||||
.long 0x0
|
||||
|
||||
relocate_new_kernel_end:
|
||||
|
||||
.globl relocate_new_kernel_size
|
||||
relocate_new_kernel_size:
|
||||
.long relocate_new_kernel_end - relocate_new_kernel
|
||||
|
||||
|
||||
221
arch/arm/kernel/semaphore.c
Normal file
221
arch/arm/kernel/semaphore.c
Normal file
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* ARM semaphore implementation, taken from
|
||||
*
|
||||
* i386 semaphore implementation.
|
||||
*
|
||||
* (C) Copyright 1999 Linus Torvalds
|
||||
*
|
||||
* 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/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..
|
||||
* To remain AAPCS compliant (64-bit stack align) we save r4 as well.
|
||||
*/
|
||||
asm(" .section .sched.text,\"ax\",%progbits \n\
|
||||
.align 5 \n\
|
||||
.globl __down_failed \n\
|
||||
__down_failed: \n\
|
||||
stmfd sp!, {r0 - r4, lr} \n\
|
||||
mov r0, ip \n\
|
||||
bl __down \n\
|
||||
ldmfd sp!, {r0 - r4, pc} \n\
|
||||
\n\
|
||||
.align 5 \n\
|
||||
.globl __down_interruptible_failed \n\
|
||||
__down_interruptible_failed: \n\
|
||||
stmfd sp!, {r0 - r4, lr} \n\
|
||||
mov r0, ip \n\
|
||||
bl __down_interruptible \n\
|
||||
mov ip, r0 \n\
|
||||
ldmfd sp!, {r0 - r4, pc} \n\
|
||||
\n\
|
||||
.align 5 \n\
|
||||
.globl __down_trylock_failed \n\
|
||||
__down_trylock_failed: \n\
|
||||
stmfd sp!, {r0 - r4, lr} \n\
|
||||
mov r0, ip \n\
|
||||
bl __down_trylock \n\
|
||||
mov ip, r0 \n\
|
||||
ldmfd sp!, {r0 - r4, pc} \n\
|
||||
\n\
|
||||
.align 5 \n\
|
||||
.globl __up_wakeup \n\
|
||||
__up_wakeup: \n\
|
||||
stmfd sp!, {r0 - r4, lr} \n\
|
||||
mov r0, ip \n\
|
||||
bl __up \n\
|
||||
ldmfd sp!, {r0 - r4, pc} \n\
|
||||
");
|
||||
|
||||
EXPORT_SYMBOL(__down_failed);
|
||||
EXPORT_SYMBOL(__down_interruptible_failed);
|
||||
EXPORT_SYMBOL(__down_trylock_failed);
|
||||
EXPORT_SYMBOL(__up_wakeup);
|
||||
1001
arch/arm/kernel/setup.c
Normal file
1001
arch/arm/kernel/setup.c
Normal file
File diff suppressed because it is too large
Load Diff
718
arch/arm/kernel/signal.c
Normal file
718
arch/arm/kernel/signal.c
Normal file
@@ -0,0 +1,718 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/signal.c
|
||||
*
|
||||
* Copyright (C) 1995-2002 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/errno.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/freezer.h>
|
||||
|
||||
#include <asm/elf.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/ucontext.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#include "ptrace.h"
|
||||
#include "signal.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))
|
||||
|
||||
/*
|
||||
* With EABI, the syscall number has to be loaded into r7.
|
||||
*/
|
||||
#define MOV_R7_NR_SIGRETURN (0xe3a07000 | (__NR_sigreturn - __NR_SYSCALL_BASE))
|
||||
#define MOV_R7_NR_RT_SIGRETURN (0xe3a07000 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE))
|
||||
|
||||
/*
|
||||
* For Thumb syscalls, we pass the syscall number via r7. We therefore
|
||||
* need two 16-bit instructions.
|
||||
*/
|
||||
#define SWI_THUMB_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_sigreturn - __NR_SYSCALL_BASE))
|
||||
#define SWI_THUMB_RT_SIGRETURN (0xdf00 << 16 | 0x2700 | (__NR_rt_sigreturn - __NR_SYSCALL_BASE))
|
||||
|
||||
const unsigned long sigreturn_codes[7] = {
|
||||
MOV_R7_NR_SIGRETURN, SWI_SYS_SIGRETURN, SWI_THUMB_SIGRETURN,
|
||||
MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_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 __user *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 __user *act,
|
||||
struct old_sigaction __user *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;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CRUNCH
|
||||
static int preserve_crunch_context(struct crunch_sigframe *frame)
|
||||
{
|
||||
char kbuf[sizeof(*frame) + 8];
|
||||
struct crunch_sigframe *kframe;
|
||||
|
||||
/* the crunch context must be 64 bit aligned */
|
||||
kframe = (struct crunch_sigframe *)((unsigned long)(kbuf + 8) & ~7);
|
||||
kframe->magic = CRUNCH_MAGIC;
|
||||
kframe->size = CRUNCH_STORAGE_SIZE;
|
||||
crunch_task_copy(current_thread_info(), &kframe->storage);
|
||||
return __copy_to_user(frame, kframe, sizeof(*frame));
|
||||
}
|
||||
|
||||
static int restore_crunch_context(struct crunch_sigframe *frame)
|
||||
{
|
||||
char kbuf[sizeof(*frame) + 8];
|
||||
struct crunch_sigframe *kframe;
|
||||
|
||||
/* the crunch context must be 64 bit aligned */
|
||||
kframe = (struct crunch_sigframe *)((unsigned long)(kbuf + 8) & ~7);
|
||||
if (__copy_from_user(kframe, frame, sizeof(*frame)))
|
||||
return -1;
|
||||
if (kframe->magic != CRUNCH_MAGIC ||
|
||||
kframe->size != CRUNCH_STORAGE_SIZE)
|
||||
return -1;
|
||||
crunch_task_restore(current_thread_info(), &kframe->storage);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IWMMXT
|
||||
|
||||
static int preserve_iwmmxt_context(struct iwmmxt_sigframe *frame)
|
||||
{
|
||||
char kbuf[sizeof(*frame) + 8];
|
||||
struct iwmmxt_sigframe *kframe;
|
||||
|
||||
/* the iWMMXt context must be 64 bit aligned */
|
||||
kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7);
|
||||
kframe->magic = IWMMXT_MAGIC;
|
||||
kframe->size = IWMMXT_STORAGE_SIZE;
|
||||
iwmmxt_task_copy(current_thread_info(), &kframe->storage);
|
||||
return __copy_to_user(frame, kframe, sizeof(*frame));
|
||||
}
|
||||
|
||||
static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame)
|
||||
{
|
||||
char kbuf[sizeof(*frame) + 8];
|
||||
struct iwmmxt_sigframe *kframe;
|
||||
|
||||
/* the iWMMXt context must be 64 bit aligned */
|
||||
kframe = (struct iwmmxt_sigframe *)((unsigned long)(kbuf + 8) & ~7);
|
||||
if (__copy_from_user(kframe, frame, sizeof(*frame)))
|
||||
return -1;
|
||||
if (kframe->magic != IWMMXT_MAGIC ||
|
||||
kframe->size != IWMMXT_STORAGE_SIZE)
|
||||
return -1;
|
||||
iwmmxt_task_restore(current_thread_info(), &kframe->storage);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Do a signal return; undo the signal stack. These are aligned to 64-bit.
|
||||
*/
|
||||
struct sigframe {
|
||||
struct ucontext uc;
|
||||
unsigned long retcode[2];
|
||||
};
|
||||
|
||||
struct rt_sigframe {
|
||||
struct siginfo info;
|
||||
struct sigframe sig;
|
||||
};
|
||||
|
||||
static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
|
||||
{
|
||||
struct aux_sigframe __user *aux;
|
||||
sigset_t set;
|
||||
int err;
|
||||
|
||||
err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
|
||||
if (err == 0) {
|
||||
sigdelsetmask(&set, ~_BLOCKABLE);
|
||||
spin_lock_irq(¤t->sighand->siglock);
|
||||
current->blocked = set;
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(¤t->sighand->siglock);
|
||||
}
|
||||
|
||||
__get_user_error(regs->ARM_r0, &sf->uc.uc_mcontext.arm_r0, err);
|
||||
__get_user_error(regs->ARM_r1, &sf->uc.uc_mcontext.arm_r1, err);
|
||||
__get_user_error(regs->ARM_r2, &sf->uc.uc_mcontext.arm_r2, err);
|
||||
__get_user_error(regs->ARM_r3, &sf->uc.uc_mcontext.arm_r3, err);
|
||||
__get_user_error(regs->ARM_r4, &sf->uc.uc_mcontext.arm_r4, err);
|
||||
__get_user_error(regs->ARM_r5, &sf->uc.uc_mcontext.arm_r5, err);
|
||||
__get_user_error(regs->ARM_r6, &sf->uc.uc_mcontext.arm_r6, err);
|
||||
__get_user_error(regs->ARM_r7, &sf->uc.uc_mcontext.arm_r7, err);
|
||||
__get_user_error(regs->ARM_r8, &sf->uc.uc_mcontext.arm_r8, err);
|
||||
__get_user_error(regs->ARM_r9, &sf->uc.uc_mcontext.arm_r9, err);
|
||||
__get_user_error(regs->ARM_r10, &sf->uc.uc_mcontext.arm_r10, err);
|
||||
__get_user_error(regs->ARM_fp, &sf->uc.uc_mcontext.arm_fp, err);
|
||||
__get_user_error(regs->ARM_ip, &sf->uc.uc_mcontext.arm_ip, err);
|
||||
__get_user_error(regs->ARM_sp, &sf->uc.uc_mcontext.arm_sp, err);
|
||||
__get_user_error(regs->ARM_lr, &sf->uc.uc_mcontext.arm_lr, err);
|
||||
__get_user_error(regs->ARM_pc, &sf->uc.uc_mcontext.arm_pc, err);
|
||||
__get_user_error(regs->ARM_cpsr, &sf->uc.uc_mcontext.arm_cpsr, err);
|
||||
|
||||
err |= !valid_user_regs(regs);
|
||||
|
||||
aux = (struct aux_sigframe __user *) sf->uc.uc_regspace;
|
||||
#ifdef CONFIG_CRUNCH
|
||||
if (err == 0)
|
||||
err |= restore_crunch_context(&aux->crunch);
|
||||
#endif
|
||||
#ifdef CONFIG_IWMMXT
|
||||
if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
|
||||
err |= restore_iwmmxt_context(&aux->iwmmxt);
|
||||
#endif
|
||||
#ifdef CONFIG_VFP
|
||||
// if (err == 0)
|
||||
// err |= vfp_restore_state(&sf->aux.vfp);
|
||||
#endif
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
asmlinkage int sys_sigreturn(struct pt_regs *regs)
|
||||
{
|
||||
struct sigframe __user *frame;
|
||||
|
||||
/* Always make any pending restarted system calls return -EINTR */
|
||||
current_thread_info()->restart_block.fn = do_no_restart_syscall;
|
||||
|
||||
/*
|
||||
* 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 __user *)regs->ARM_sp;
|
||||
|
||||
if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
|
||||
goto badframe;
|
||||
|
||||
if (restore_sigframe(regs, frame))
|
||||
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 __user *frame;
|
||||
|
||||
/* Always make any pending restarted system calls return -EINTR */
|
||||
current_thread_info()->restart_block.fn = do_no_restart_syscall;
|
||||
|
||||
/*
|
||||
* 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 __user *)regs->ARM_sp;
|
||||
|
||||
if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
|
||||
goto badframe;
|
||||
|
||||
if (restore_sigframe(regs, &frame->sig))
|
||||
goto badframe;
|
||||
|
||||
if (do_sigaltstack(&frame->sig.uc.uc_stack, NULL, regs->ARM_sp) == -EFAULT)
|
||||
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_sigframe(struct sigframe __user *sf, struct pt_regs *regs, sigset_t *set)
|
||||
{
|
||||
struct aux_sigframe __user *aux;
|
||||
int err = 0;
|
||||
|
||||
__put_user_error(regs->ARM_r0, &sf->uc.uc_mcontext.arm_r0, err);
|
||||
__put_user_error(regs->ARM_r1, &sf->uc.uc_mcontext.arm_r1, err);
|
||||
__put_user_error(regs->ARM_r2, &sf->uc.uc_mcontext.arm_r2, err);
|
||||
__put_user_error(regs->ARM_r3, &sf->uc.uc_mcontext.arm_r3, err);
|
||||
__put_user_error(regs->ARM_r4, &sf->uc.uc_mcontext.arm_r4, err);
|
||||
__put_user_error(regs->ARM_r5, &sf->uc.uc_mcontext.arm_r5, err);
|
||||
__put_user_error(regs->ARM_r6, &sf->uc.uc_mcontext.arm_r6, err);
|
||||
__put_user_error(regs->ARM_r7, &sf->uc.uc_mcontext.arm_r7, err);
|
||||
__put_user_error(regs->ARM_r8, &sf->uc.uc_mcontext.arm_r8, err);
|
||||
__put_user_error(regs->ARM_r9, &sf->uc.uc_mcontext.arm_r9, err);
|
||||
__put_user_error(regs->ARM_r10, &sf->uc.uc_mcontext.arm_r10, err);
|
||||
__put_user_error(regs->ARM_fp, &sf->uc.uc_mcontext.arm_fp, err);
|
||||
__put_user_error(regs->ARM_ip, &sf->uc.uc_mcontext.arm_ip, err);
|
||||
__put_user_error(regs->ARM_sp, &sf->uc.uc_mcontext.arm_sp, err);
|
||||
__put_user_error(regs->ARM_lr, &sf->uc.uc_mcontext.arm_lr, err);
|
||||
__put_user_error(regs->ARM_pc, &sf->uc.uc_mcontext.arm_pc, err);
|
||||
__put_user_error(regs->ARM_cpsr, &sf->uc.uc_mcontext.arm_cpsr, err);
|
||||
|
||||
__put_user_error(current->thread.trap_no, &sf->uc.uc_mcontext.trap_no, err);
|
||||
__put_user_error(current->thread.error_code, &sf->uc.uc_mcontext.error_code, err);
|
||||
__put_user_error(current->thread.address, &sf->uc.uc_mcontext.fault_address, err);
|
||||
__put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err);
|
||||
|
||||
err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
|
||||
|
||||
aux = (struct aux_sigframe __user *) sf->uc.uc_regspace;
|
||||
#ifdef CONFIG_CRUNCH
|
||||
if (err == 0)
|
||||
err |= preserve_crunch_context(&aux->crunch);
|
||||
#endif
|
||||
#ifdef CONFIG_IWMMXT
|
||||
if (err == 0 && test_thread_flag(TIF_USING_IWMMXT))
|
||||
err |= preserve_iwmmxt_context(&aux->iwmmxt);
|
||||
#endif
|
||||
#ifdef CONFIG_VFP
|
||||
// if (err == 0)
|
||||
// err |= vfp_save_state(&sf->aux.vfp);
|
||||
#endif
|
||||
__put_user_error(0, &aux->end_magic, err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void __user *
|
||||
get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, int framesize)
|
||||
{
|
||||
unsigned long sp = regs->ARM_sp;
|
||||
void __user *frame;
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
frame = (void __user *)((sp - framesize) & ~7);
|
||||
|
||||
/*
|
||||
* Check that we can actually write to the signal frame.
|
||||
*/
|
||||
if (!access_ok(VERIFY_WRITE, frame, framesize))
|
||||
frame = NULL;
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_return(struct pt_regs *regs, struct k_sigaction *ka,
|
||||
unsigned long __user *rc, void __user *frame, int usig)
|
||||
{
|
||||
unsigned long handler = (unsigned long)ka->sa.sa_handler;
|
||||
unsigned long retcode;
|
||||
int thumb = 0;
|
||||
unsigned long cpsr = regs->ARM_cpsr & ~PSR_f;
|
||||
|
||||
/*
|
||||
* Maybe we need to deliver a 32-bit signal to a 26-bit task.
|
||||
*/
|
||||
if (ka->sa.sa_flags & SA_THIRTYTWO)
|
||||
cpsr = (cpsr & ~MODE_MASK) | USR_MODE;
|
||||
|
||||
#ifdef CONFIG_ARM_THUMB
|
||||
if (elf_hwcap & HWCAP_THUMB) {
|
||||
/*
|
||||
* The LSB of the handler determines if we're going to
|
||||
* be using THUMB or ARM mode for this signal handler.
|
||||
*/
|
||||
thumb = handler & 1;
|
||||
|
||||
if (thumb)
|
||||
cpsr |= PSR_T_BIT;
|
||||
else
|
||||
cpsr &= ~PSR_T_BIT;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ka->sa.sa_flags & SA_RESTORER) {
|
||||
retcode = (unsigned long)ka->sa.sa_restorer;
|
||||
} else {
|
||||
unsigned int idx = thumb << 1;
|
||||
|
||||
if (ka->sa.sa_flags & SA_SIGINFO)
|
||||
idx += 3;
|
||||
|
||||
if (__put_user(sigreturn_codes[idx], rc) ||
|
||||
__put_user(sigreturn_codes[idx+1], rc+1))
|
||||
return 1;
|
||||
|
||||
if (cpsr & MODE32_BIT) {
|
||||
/*
|
||||
* 32-bit code can use the new high-page
|
||||
* signal return code support.
|
||||
*/
|
||||
retcode = KERN_SIGRETURN_CODE + (idx << 2) + thumb;
|
||||
} else {
|
||||
/*
|
||||
* Ensure that the instruction cache sees
|
||||
* the return code written onto the stack.
|
||||
*/
|
||||
flush_icache_range((unsigned long)rc,
|
||||
(unsigned long)(rc + 2));
|
||||
|
||||
retcode = ((unsigned long)rc) + thumb;
|
||||
}
|
||||
}
|
||||
|
||||
regs->ARM_r0 = usig;
|
||||
regs->ARM_sp = (unsigned long)frame;
|
||||
regs->ARM_lr = retcode;
|
||||
regs->ARM_pc = handler;
|
||||
regs->ARM_cpsr = cpsr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup_frame(int usig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs)
|
||||
{
|
||||
struct sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame));
|
||||
int err = 0;
|
||||
|
||||
if (!frame)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* Set uc.uc_flags to a value which sc.trap_no would never have.
|
||||
*/
|
||||
__put_user_error(0x5ac3c35a, &frame->uc.uc_flags, err);
|
||||
|
||||
err |= setup_sigframe(frame, regs, set);
|
||||
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 __user *frame = get_sigframe(ka, regs, sizeof(*frame));
|
||||
stack_t stack;
|
||||
int err = 0;
|
||||
|
||||
if (!frame)
|
||||
return 1;
|
||||
|
||||
err |= copy_siginfo_to_user(&frame->info, info);
|
||||
|
||||
__put_user_error(0, &frame->sig.uc.uc_flags, err);
|
||||
__put_user_error(NULL, &frame->sig.uc.uc_link, err);
|
||||
|
||||
memset(&stack, 0, sizeof(stack));
|
||||
stack.ss_sp = (void __user *)current->sas_ss_sp;
|
||||
stack.ss_flags = sas_ss_flags(regs->ARM_sp);
|
||||
stack.ss_size = current->sas_ss_size;
|
||||
err |= __copy_to_user(&frame->sig.uc.uc_stack, &stack, sizeof(stack));
|
||||
|
||||
err |= setup_sigframe(&frame->sig, regs, set);
|
||||
if (err == 0)
|
||||
err = setup_return(regs, ka, frame->sig.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->info;
|
||||
regs->ARM_r2 = (unsigned long)&frame->sig.uc;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline void restart_syscall(struct pt_regs *regs)
|
||||
{
|
||||
regs->ARM_r0 = regs->ARM_ORIG_r0;
|
||||
regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
|
||||
}
|
||||
|
||||
/*
|
||||
* OK, we're invoking a handler
|
||||
*/
|
||||
static void
|
||||
handle_signal(unsigned long sig, struct k_sigaction *ka,
|
||||
siginfo_t *info, sigset_t *oldset,
|
||||
struct pt_regs * regs, int syscall)
|
||||
{
|
||||
struct thread_info *thread = current_thread_info();
|
||||
struct task_struct *tsk = current;
|
||||
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:
|
||||
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) {
|
||||
force_sigsegv(sig, tsk);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Block the signal if we were successful.
|
||||
*/
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
sigorsets(&tsk->blocked, &tsk->blocked,
|
||||
&ka->sa.sa_mask);
|
||||
if (!(ka->sa.sa_flags & SA_NODEFER))
|
||||
sigaddset(&tsk->blocked, sig);
|
||||
recalc_sigpending();
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
struct k_sigaction ka;
|
||||
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 (try_to_freeze())
|
||||
goto no_signal;
|
||||
|
||||
if (current->ptrace & PT_SINGLESTEP)
|
||||
ptrace_cancel_bpt(current);
|
||||
|
||||
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
|
||||
if (signr > 0) {
|
||||
handle_signal(signr, &ka, &info, oldset, regs, syscall);
|
||||
if (current->ptrace & PT_SINGLESTEP)
|
||||
ptrace_set_bpt(current);
|
||||
return 1;
|
||||
}
|
||||
|
||||
no_signal:
|
||||
/*
|
||||
* No signal to deliver to the process - restart the syscall.
|
||||
*/
|
||||
if (syscall) {
|
||||
if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) {
|
||||
if (thumb_mode(regs)) {
|
||||
regs->ARM_r7 = __NR_restart_syscall - __NR_SYSCALL_BASE;
|
||||
regs->ARM_pc -= 2;
|
||||
} else {
|
||||
#if defined(CONFIG_AEABI) && !defined(CONFIG_OABI_COMPAT)
|
||||
regs->ARM_r7 = __NR_restart_syscall;
|
||||
regs->ARM_pc -= 4;
|
||||
#else
|
||||
u32 __user *usp;
|
||||
u32 swival = __NR_restart_syscall;
|
||||
|
||||
regs->ARM_sp -= 12;
|
||||
usp = (u32 __user *)regs->ARM_sp;
|
||||
|
||||
/*
|
||||
* Either we supports OABI only, or we have
|
||||
* EABI with the OABI compat layer enabled.
|
||||
* In the later case we don't know if user
|
||||
* space is EABI or not, and if not we must
|
||||
* not clobber r7. Always using the OABI
|
||||
* syscall solves that issue and works for
|
||||
* all those cases.
|
||||
*/
|
||||
swival = swival - __NR_SYSCALL_BASE + __NR_OABI_SYSCALL_BASE;
|
||||
|
||||
put_user(regs->ARM_pc, &usp[0]);
|
||||
/* swi __NR_restart_syscall */
|
||||
put_user(0xef000000 | swival, &usp[1]);
|
||||
/* ldr pc, [sp], #12 */
|
||||
put_user(0xe49df00c, &usp[2]);
|
||||
|
||||
flush_icache_range((unsigned long)usp,
|
||||
(unsigned long)(usp + 3));
|
||||
|
||||
regs->ARM_pc = regs->ARM_sp + 4;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
12
arch/arm/kernel/signal.h
Normal file
12
arch/arm/kernel/signal.h
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/signal.h
|
||||
*
|
||||
* Copyright (C) 2005 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.
|
||||
*/
|
||||
#define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500)
|
||||
|
||||
extern const unsigned long sigreturn_codes[7];
|
||||
759
arch/arm/kernel/smp.c
Normal file
759
arch/arm/kernel/smp.c
Normal file
@@ -0,0 +1,759 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/smp.c
|
||||
*
|
||||
* Copyright (C) 2002 ARM Limited, All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/cache.h>
|
||||
#include <linux/profile.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/ptrace.h>
|
||||
|
||||
/*
|
||||
* bitmask of present and online CPUs.
|
||||
* The present bitmask indicates that the CPU is physically present.
|
||||
* The online bitmask indicates that the CPU is up and running.
|
||||
*/
|
||||
cpumask_t cpu_possible_map;
|
||||
EXPORT_SYMBOL(cpu_possible_map);
|
||||
cpumask_t cpu_online_map;
|
||||
EXPORT_SYMBOL(cpu_online_map);
|
||||
|
||||
/*
|
||||
* as from 2.5, kernels no longer have an init_tasks structure
|
||||
* so we need some other way of telling a new secondary core
|
||||
* where to place its SVC stack
|
||||
*/
|
||||
struct secondary_data secondary_data;
|
||||
|
||||
/*
|
||||
* structures for inter-processor calls
|
||||
* - A collection of single bit ipi messages.
|
||||
*/
|
||||
struct ipi_data {
|
||||
spinlock_t lock;
|
||||
unsigned long ipi_count;
|
||||
unsigned long bits;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct ipi_data, ipi_data) = {
|
||||
.lock = SPIN_LOCK_UNLOCKED,
|
||||
};
|
||||
|
||||
enum ipi_msg_type {
|
||||
IPI_TIMER,
|
||||
IPI_RESCHEDULE,
|
||||
IPI_CALL_FUNC,
|
||||
IPI_CPU_STOP,
|
||||
};
|
||||
|
||||
struct smp_call_struct {
|
||||
void (*func)(void *info);
|
||||
void *info;
|
||||
int wait;
|
||||
cpumask_t pending;
|
||||
cpumask_t unfinished;
|
||||
};
|
||||
|
||||
static struct smp_call_struct * volatile smp_call_function_data;
|
||||
static DEFINE_SPINLOCK(smp_call_function_lock);
|
||||
|
||||
int __cpuinit __cpu_up(unsigned int cpu)
|
||||
{
|
||||
struct cpuinfo_arm *ci = &per_cpu(cpu_data, cpu);
|
||||
struct task_struct *idle = ci->idle;
|
||||
pgd_t *pgd;
|
||||
pmd_t *pmd;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Spawn a new process manually, if not already done.
|
||||
* Grab a pointer to its task struct so we can mess with it
|
||||
*/
|
||||
if (!idle) {
|
||||
idle = fork_idle(cpu);
|
||||
if (IS_ERR(idle)) {
|
||||
printk(KERN_ERR "CPU%u: fork() failed\n", cpu);
|
||||
return PTR_ERR(idle);
|
||||
}
|
||||
ci->idle = idle;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate initial page tables to allow the new CPU to
|
||||
* enable the MMU safely. This essentially means a set
|
||||
* of our "standard" page tables, with the addition of
|
||||
* a 1:1 mapping for the physical address of the kernel.
|
||||
*/
|
||||
pgd = pgd_alloc(&init_mm);
|
||||
pmd = pmd_offset(pgd, PHYS_OFFSET);
|
||||
*pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) |
|
||||
PMD_TYPE_SECT | PMD_SECT_AP_WRITE);
|
||||
|
||||
/*
|
||||
* We need to tell the secondary core where to find
|
||||
* its stack and the page tables.
|
||||
*/
|
||||
secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;
|
||||
secondary_data.pgdir = virt_to_phys(pgd);
|
||||
wmb();
|
||||
|
||||
/*
|
||||
* Now bring the CPU into our world.
|
||||
*/
|
||||
ret = boot_secondary(cpu, idle);
|
||||
if (ret == 0) {
|
||||
unsigned long timeout;
|
||||
|
||||
/*
|
||||
* CPU was successfully started, wait for it
|
||||
* to come online or time out.
|
||||
*/
|
||||
timeout = jiffies + HZ;
|
||||
while (time_before(jiffies, timeout)) {
|
||||
if (cpu_online(cpu))
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
barrier();
|
||||
}
|
||||
|
||||
if (!cpu_online(cpu))
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
secondary_data.stack = NULL;
|
||||
secondary_data.pgdir = 0;
|
||||
|
||||
*pmd_offset(pgd, PHYS_OFFSET) = __pmd(0);
|
||||
pgd_free(pgd);
|
||||
|
||||
if (ret) {
|
||||
printk(KERN_CRIT "CPU%u: processor failed to boot\n", cpu);
|
||||
|
||||
/*
|
||||
* FIXME: We need to clean up the new idle thread. --rmk
|
||||
*/
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
/*
|
||||
* __cpu_disable runs on the processor to be shutdown.
|
||||
*/
|
||||
int __cpuexit __cpu_disable(void)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct task_struct *p;
|
||||
int ret;
|
||||
|
||||
ret = mach_cpu_disable(cpu);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Take this CPU offline. Once we clear this, we can't return,
|
||||
* and we must not schedule until we're ready to give up the cpu.
|
||||
*/
|
||||
cpu_clear(cpu, cpu_online_map);
|
||||
|
||||
/*
|
||||
* OK - migrate IRQs away from this CPU
|
||||
*/
|
||||
migrate_irqs();
|
||||
|
||||
/*
|
||||
* Stop the local timer for this CPU.
|
||||
*/
|
||||
local_timer_stop(cpu);
|
||||
|
||||
/*
|
||||
* Flush user cache and TLB mappings, and then remove this CPU
|
||||
* from the vm mask set of all processes.
|
||||
*/
|
||||
flush_cache_all();
|
||||
local_flush_tlb_all();
|
||||
|
||||
read_lock(&tasklist_lock);
|
||||
for_each_process(p) {
|
||||
if (p->mm)
|
||||
cpu_clear(cpu, p->mm->cpu_vm_mask);
|
||||
}
|
||||
read_unlock(&tasklist_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* called on the thread which is asking for a CPU to be shutdown -
|
||||
* waits until shutdown has completed, or it is timed out.
|
||||
*/
|
||||
void __cpuexit __cpu_die(unsigned int cpu)
|
||||
{
|
||||
if (!platform_cpu_kill(cpu))
|
||||
printk("CPU%u: unable to kill\n", cpu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from the idle thread for the CPU which has been shutdown.
|
||||
*
|
||||
* Note that we disable IRQs here, but do not re-enable them
|
||||
* before returning to the caller. This is also the behaviour
|
||||
* of the other hotplug-cpu capable cores, so presumably coming
|
||||
* out of idle fixes this.
|
||||
*/
|
||||
void __cpuexit cpu_die(void)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
|
||||
local_irq_disable();
|
||||
idle_task_exit();
|
||||
|
||||
/*
|
||||
* actual CPU shutdown procedure is at least platform (if not
|
||||
* CPU) specific
|
||||
*/
|
||||
platform_cpu_die(cpu);
|
||||
|
||||
/*
|
||||
* Do not return to the idle loop - jump back to the secondary
|
||||
* cpu initialisation. There's some initialisation which needs
|
||||
* to be repeated to undo the effects of taking the CPU offline.
|
||||
*/
|
||||
__asm__("mov sp, %0\n"
|
||||
" b secondary_start_kernel"
|
||||
:
|
||||
: "r" (task_stack_page(current) + THREAD_SIZE - 8));
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
/*
|
||||
* This is the secondary CPU boot entry. We're using this CPUs
|
||||
* idle thread stack, but a set of temporary page tables.
|
||||
*/
|
||||
asmlinkage void __cpuinit secondary_start_kernel(void)
|
||||
{
|
||||
struct mm_struct *mm = &init_mm;
|
||||
unsigned int cpu = smp_processor_id();
|
||||
|
||||
printk("CPU%u: Booted secondary processor\n", cpu);
|
||||
|
||||
/*
|
||||
* All kernel threads share the same mm context; grab a
|
||||
* reference and switch to it.
|
||||
*/
|
||||
atomic_inc(&mm->mm_users);
|
||||
atomic_inc(&mm->mm_count);
|
||||
current->active_mm = mm;
|
||||
cpu_set(cpu, mm->cpu_vm_mask);
|
||||
cpu_switch_mm(mm->pgd, mm);
|
||||
enter_lazy_tlb(mm, current);
|
||||
local_flush_tlb_all();
|
||||
|
||||
cpu_init();
|
||||
preempt_disable();
|
||||
|
||||
/*
|
||||
* Give the platform a chance to do its own initialisation.
|
||||
*/
|
||||
platform_secondary_init(cpu);
|
||||
|
||||
/*
|
||||
* Enable local interrupts.
|
||||
*/
|
||||
local_irq_enable();
|
||||
local_fiq_enable();
|
||||
|
||||
calibrate_delay();
|
||||
|
||||
smp_store_cpu_info(cpu);
|
||||
|
||||
/*
|
||||
* OK, now it's safe to let the boot CPU continue
|
||||
*/
|
||||
cpu_set(cpu, cpu_online_map);
|
||||
|
||||
/*
|
||||
* Setup local timer for this CPU.
|
||||
*/
|
||||
local_timer_setup(cpu);
|
||||
|
||||
/*
|
||||
* OK, it's off to the idle thread for us
|
||||
*/
|
||||
cpu_idle();
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by both boot and secondaries to move global data into
|
||||
* per-processor storage.
|
||||
*/
|
||||
void __cpuinit smp_store_cpu_info(unsigned int cpuid)
|
||||
{
|
||||
struct cpuinfo_arm *cpu_info = &per_cpu(cpu_data, cpuid);
|
||||
|
||||
cpu_info->loops_per_jiffy = loops_per_jiffy;
|
||||
}
|
||||
|
||||
void __init smp_cpus_done(unsigned int max_cpus)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long bogosum = 0;
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
bogosum += per_cpu(cpu_data, cpu).loops_per_jiffy;
|
||||
|
||||
printk(KERN_INFO "SMP: Total of %d processors activated "
|
||||
"(%lu.%02lu BogoMIPS).\n",
|
||||
num_online_cpus(),
|
||||
bogosum / (500000/HZ),
|
||||
(bogosum / (5000/HZ)) % 100);
|
||||
}
|
||||
|
||||
void __init smp_prepare_boot_cpu(void)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
|
||||
per_cpu(cpu_data, cpu).idle = current;
|
||||
}
|
||||
|
||||
static void send_ipi_message(cpumask_t callmap, enum ipi_msg_type msg)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned int cpu;
|
||||
|
||||
local_irq_save(flags);
|
||||
|
||||
for_each_cpu_mask(cpu, callmap) {
|
||||
struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
|
||||
|
||||
spin_lock(&ipi->lock);
|
||||
ipi->bits |= 1 << msg;
|
||||
spin_unlock(&ipi->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Call the platform specific cross-CPU call function.
|
||||
*/
|
||||
smp_cross_call(callmap);
|
||||
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* You must not call this function with disabled interrupts, from a
|
||||
* hardware interrupt handler, nor from a bottom half handler.
|
||||
*/
|
||||
static int smp_call_function_on_cpu(void (*func)(void *info), void *info,
|
||||
int retry, int wait, cpumask_t callmap)
|
||||
{
|
||||
struct smp_call_struct data;
|
||||
unsigned long timeout;
|
||||
int ret = 0;
|
||||
|
||||
data.func = func;
|
||||
data.info = info;
|
||||
data.wait = wait;
|
||||
|
||||
cpu_clear(smp_processor_id(), callmap);
|
||||
if (cpus_empty(callmap))
|
||||
goto out;
|
||||
|
||||
data.pending = callmap;
|
||||
if (wait)
|
||||
data.unfinished = callmap;
|
||||
|
||||
/*
|
||||
* try to get the mutex on smp_call_function_data
|
||||
*/
|
||||
spin_lock(&smp_call_function_lock);
|
||||
smp_call_function_data = &data;
|
||||
|
||||
send_ipi_message(callmap, IPI_CALL_FUNC);
|
||||
|
||||
timeout = jiffies + HZ;
|
||||
while (!cpus_empty(data.pending) && time_before(jiffies, timeout))
|
||||
barrier();
|
||||
|
||||
/*
|
||||
* did we time out?
|
||||
*/
|
||||
if (!cpus_empty(data.pending)) {
|
||||
/*
|
||||
* this may be causing our panic - report it
|
||||
*/
|
||||
printk(KERN_CRIT
|
||||
"CPU%u: smp_call_function timeout for %p(%p)\n"
|
||||
" callmap %lx pending %lx, %swait\n",
|
||||
smp_processor_id(), func, info, *cpus_addr(callmap),
|
||||
*cpus_addr(data.pending), wait ? "" : "no ");
|
||||
|
||||
/*
|
||||
* TRACE
|
||||
*/
|
||||
timeout = jiffies + (5 * HZ);
|
||||
while (!cpus_empty(data.pending) && time_before(jiffies, timeout))
|
||||
barrier();
|
||||
|
||||
if (cpus_empty(data.pending))
|
||||
printk(KERN_CRIT " RESOLVED\n");
|
||||
else
|
||||
printk(KERN_CRIT " STILL STUCK\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* whatever happened, we're done with the data, so release it
|
||||
*/
|
||||
smp_call_function_data = NULL;
|
||||
spin_unlock(&smp_call_function_lock);
|
||||
|
||||
if (!cpus_empty(data.pending)) {
|
||||
ret = -ETIMEDOUT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (wait)
|
||||
while (!cpus_empty(data.unfinished))
|
||||
barrier();
|
||||
out:
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smp_call_function(void (*func)(void *info), void *info, int retry,
|
||||
int wait)
|
||||
{
|
||||
return smp_call_function_on_cpu(func, info, retry, wait,
|
||||
cpu_online_map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(smp_call_function);
|
||||
|
||||
void show_ipi_list(struct seq_file *p)
|
||||
{
|
||||
unsigned int cpu;
|
||||
|
||||
seq_puts(p, "IPI:");
|
||||
|
||||
for_each_present_cpu(cpu)
|
||||
seq_printf(p, " %10lu", per_cpu(ipi_data, cpu).ipi_count);
|
||||
|
||||
seq_putc(p, '\n');
|
||||
}
|
||||
|
||||
void show_local_irqs(struct seq_file *p)
|
||||
{
|
||||
unsigned int cpu;
|
||||
|
||||
seq_printf(p, "LOC: ");
|
||||
|
||||
for_each_present_cpu(cpu)
|
||||
seq_printf(p, "%10u ", irq_stat[cpu].local_timer_irqs);
|
||||
|
||||
seq_putc(p, '\n');
|
||||
}
|
||||
|
||||
static void ipi_timer(void)
|
||||
{
|
||||
irq_enter();
|
||||
profile_tick(CPU_PROFILING);
|
||||
update_process_times(user_mode(get_irq_regs()));
|
||||
irq_exit();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LOCAL_TIMERS
|
||||
asmlinkage void do_local_timer(struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *old_regs = set_irq_regs(regs);
|
||||
int cpu = smp_processor_id();
|
||||
|
||||
if (local_timer_ack()) {
|
||||
irq_stat[cpu].local_timer_irqs++;
|
||||
ipi_timer();
|
||||
}
|
||||
|
||||
set_irq_regs(old_regs);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* ipi_call_function - handle IPI from smp_call_function()
|
||||
*
|
||||
* Note that we copy data out of the cross-call structure and then
|
||||
* let the caller know that we're here and have done with their data
|
||||
*/
|
||||
static void ipi_call_function(unsigned int cpu)
|
||||
{
|
||||
struct smp_call_struct *data = smp_call_function_data;
|
||||
void (*func)(void *info) = data->func;
|
||||
void *info = data->info;
|
||||
int wait = data->wait;
|
||||
|
||||
cpu_clear(cpu, data->pending);
|
||||
|
||||
func(info);
|
||||
|
||||
if (wait)
|
||||
cpu_clear(cpu, data->unfinished);
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(stop_lock);
|
||||
|
||||
/*
|
||||
* ipi_cpu_stop - handle IPI from smp_send_stop()
|
||||
*/
|
||||
static void ipi_cpu_stop(unsigned int cpu)
|
||||
{
|
||||
spin_lock(&stop_lock);
|
||||
printk(KERN_CRIT "CPU%u: stopping\n", cpu);
|
||||
dump_stack();
|
||||
spin_unlock(&stop_lock);
|
||||
|
||||
cpu_clear(cpu, cpu_online_map);
|
||||
|
||||
local_fiq_disable();
|
||||
local_irq_disable();
|
||||
|
||||
while (1)
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
/*
|
||||
* Main handler for inter-processor interrupts
|
||||
*
|
||||
* For ARM, the ipimask now only identifies a single
|
||||
* category of IPI (Bit 1 IPIs have been replaced by a
|
||||
* different mechanism):
|
||||
*
|
||||
* Bit 0 - Inter-processor function call
|
||||
*/
|
||||
asmlinkage void do_IPI(struct pt_regs *regs)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
|
||||
struct pt_regs *old_regs = set_irq_regs(regs);
|
||||
|
||||
ipi->ipi_count++;
|
||||
|
||||
for (;;) {
|
||||
unsigned long msgs;
|
||||
|
||||
spin_lock(&ipi->lock);
|
||||
msgs = ipi->bits;
|
||||
ipi->bits = 0;
|
||||
spin_unlock(&ipi->lock);
|
||||
|
||||
if (!msgs)
|
||||
break;
|
||||
|
||||
do {
|
||||
unsigned nextmsg;
|
||||
|
||||
nextmsg = msgs & -msgs;
|
||||
msgs &= ~nextmsg;
|
||||
nextmsg = ffz(~nextmsg);
|
||||
|
||||
switch (nextmsg) {
|
||||
case IPI_TIMER:
|
||||
ipi_timer();
|
||||
break;
|
||||
|
||||
case IPI_RESCHEDULE:
|
||||
/*
|
||||
* nothing more to do - eveything is
|
||||
* done on the interrupt return path
|
||||
*/
|
||||
break;
|
||||
|
||||
case IPI_CALL_FUNC:
|
||||
ipi_call_function(cpu);
|
||||
break;
|
||||
|
||||
case IPI_CPU_STOP:
|
||||
ipi_cpu_stop(cpu);
|
||||
break;
|
||||
|
||||
default:
|
||||
printk(KERN_CRIT "CPU%u: Unknown IPI message 0x%x\n",
|
||||
cpu, nextmsg);
|
||||
break;
|
||||
}
|
||||
} while (msgs);
|
||||
}
|
||||
|
||||
set_irq_regs(old_regs);
|
||||
}
|
||||
|
||||
void smp_send_reschedule(int cpu)
|
||||
{
|
||||
send_ipi_message(cpumask_of_cpu(cpu), IPI_RESCHEDULE);
|
||||
}
|
||||
|
||||
void smp_send_timer(void)
|
||||
{
|
||||
cpumask_t mask = cpu_online_map;
|
||||
cpu_clear(smp_processor_id(), mask);
|
||||
send_ipi_message(mask, IPI_TIMER);
|
||||
}
|
||||
|
||||
void smp_send_stop(void)
|
||||
{
|
||||
cpumask_t mask = cpu_online_map;
|
||||
cpu_clear(smp_processor_id(), mask);
|
||||
send_ipi_message(mask, IPI_CPU_STOP);
|
||||
}
|
||||
|
||||
/*
|
||||
* not supported here
|
||||
*/
|
||||
int __init setup_profiling_timer(unsigned int multiplier)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
on_each_cpu_mask(void (*func)(void *), void *info, int retry, int wait,
|
||||
cpumask_t mask)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
preempt_disable();
|
||||
|
||||
ret = smp_call_function_on_cpu(func, info, retry, wait, mask);
|
||||
if (cpu_isset(smp_processor_id(), mask))
|
||||
func(info);
|
||||
|
||||
preempt_enable();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**********************************************************************/
|
||||
|
||||
/*
|
||||
* TLB operations
|
||||
*/
|
||||
struct tlb_args {
|
||||
struct vm_area_struct *ta_vma;
|
||||
unsigned long ta_start;
|
||||
unsigned long ta_end;
|
||||
};
|
||||
|
||||
static inline void ipi_flush_tlb_all(void *ignored)
|
||||
{
|
||||
local_flush_tlb_all();
|
||||
}
|
||||
|
||||
static inline void ipi_flush_tlb_mm(void *arg)
|
||||
{
|
||||
struct mm_struct *mm = (struct mm_struct *)arg;
|
||||
|
||||
local_flush_tlb_mm(mm);
|
||||
}
|
||||
|
||||
static inline void ipi_flush_tlb_page(void *arg)
|
||||
{
|
||||
struct tlb_args *ta = (struct tlb_args *)arg;
|
||||
|
||||
local_flush_tlb_page(ta->ta_vma, ta->ta_start);
|
||||
}
|
||||
|
||||
static inline void ipi_flush_tlb_kernel_page(void *arg)
|
||||
{
|
||||
struct tlb_args *ta = (struct tlb_args *)arg;
|
||||
|
||||
local_flush_tlb_kernel_page(ta->ta_start);
|
||||
}
|
||||
|
||||
static inline void ipi_flush_tlb_range(void *arg)
|
||||
{
|
||||
struct tlb_args *ta = (struct tlb_args *)arg;
|
||||
|
||||
local_flush_tlb_range(ta->ta_vma, ta->ta_start, ta->ta_end);
|
||||
}
|
||||
|
||||
static inline void ipi_flush_tlb_kernel_range(void *arg)
|
||||
{
|
||||
struct tlb_args *ta = (struct tlb_args *)arg;
|
||||
|
||||
local_flush_tlb_kernel_range(ta->ta_start, ta->ta_end);
|
||||
}
|
||||
|
||||
void flush_tlb_all(void)
|
||||
{
|
||||
on_each_cpu(ipi_flush_tlb_all, NULL, 1, 1);
|
||||
}
|
||||
|
||||
void flush_tlb_mm(struct mm_struct *mm)
|
||||
{
|
||||
cpumask_t mask = mm->cpu_vm_mask;
|
||||
|
||||
on_each_cpu_mask(ipi_flush_tlb_mm, mm, 1, 1, mask);
|
||||
}
|
||||
|
||||
void flush_tlb_page(struct vm_area_struct *vma, unsigned long uaddr)
|
||||
{
|
||||
cpumask_t mask = vma->vm_mm->cpu_vm_mask;
|
||||
struct tlb_args ta;
|
||||
|
||||
ta.ta_vma = vma;
|
||||
ta.ta_start = uaddr;
|
||||
|
||||
on_each_cpu_mask(ipi_flush_tlb_page, &ta, 1, 1, mask);
|
||||
}
|
||||
|
||||
void flush_tlb_kernel_page(unsigned long kaddr)
|
||||
{
|
||||
struct tlb_args ta;
|
||||
|
||||
ta.ta_start = kaddr;
|
||||
|
||||
on_each_cpu(ipi_flush_tlb_kernel_page, &ta, 1, 1);
|
||||
}
|
||||
|
||||
void flush_tlb_range(struct vm_area_struct *vma,
|
||||
unsigned long start, unsigned long end)
|
||||
{
|
||||
cpumask_t mask = vma->vm_mm->cpu_vm_mask;
|
||||
struct tlb_args ta;
|
||||
|
||||
ta.ta_vma = vma;
|
||||
ta.ta_start = start;
|
||||
ta.ta_end = end;
|
||||
|
||||
on_each_cpu_mask(ipi_flush_tlb_range, &ta, 1, 1, mask);
|
||||
}
|
||||
|
||||
void flush_tlb_kernel_range(unsigned long start, unsigned long end)
|
||||
{
|
||||
struct tlb_args ta;
|
||||
|
||||
ta.ta_start = start;
|
||||
ta.ta_end = end;
|
||||
|
||||
on_each_cpu(ipi_flush_tlb_kernel_range, &ta, 1, 1);
|
||||
}
|
||||
343
arch/arm/kernel/sys_arm.c
Normal file
343
arch/arm/kernel/sys_arm.c
Normal file
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/sys_arm.c
|
||||
*
|
||||
* Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c
|
||||
* Copyright (C) 1995, 1996 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.
|
||||
*
|
||||
* 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 __user *fildes)
|
||||
{
|
||||
int fd[2];
|
||||
int error;
|
||||
|
||||
error = do_pipe(fd);
|
||||
if (!error) {
|
||||
if (copy_to_user(fildes, fd, 2*sizeof(int)))
|
||||
error = -EFAULT;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* 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 (flags & MAP_FIXED && addr < FIRST_USER_ADDRESS)
|
||||
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 __user *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 (flags & MREMAP_FIXED && new_addr < FIRST_USER_ADDRESS)
|
||||
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 __user *inp, *outp, *exp;
|
||||
struct timeval __user *tvp;
|
||||
};
|
||||
|
||||
asmlinkage int old_select(struct sel_arg_struct __user *arg)
|
||||
{
|
||||
struct sel_arg_struct a;
|
||||
|
||||
if (copy_from_user(&a, arg, sizeof(a)))
|
||||
return -EFAULT;
|
||||
/* sys_select() does the appropriate kernel locking */
|
||||
return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp);
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_AEABI) || defined(CONFIG_OABI_COMPAT)
|
||||
/*
|
||||
* 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 __user *ptr, long fifth)
|
||||
{
|
||||
int version, ret;
|
||||
|
||||
version = call >> 16; /* hack for backward compatibility */
|
||||
call &= 0xffff;
|
||||
|
||||
switch (call) {
|
||||
case SEMOP:
|
||||
return sys_semtimedop (first, (struct sembuf __user *)ptr, second, NULL);
|
||||
case SEMTIMEDOP:
|
||||
return sys_semtimedop(first, (struct sembuf __user *)ptr, second,
|
||||
(const struct timespec __user *)fifth);
|
||||
|
||||
case SEMGET:
|
||||
return sys_semget (first, second, third);
|
||||
case SEMCTL: {
|
||||
union semun fourth;
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
if (get_user(fourth.__pad, (void __user * __user *) ptr))
|
||||
return -EFAULT;
|
||||
return sys_semctl (first, second, third, fourth);
|
||||
}
|
||||
|
||||
case MSGSND:
|
||||
return sys_msgsnd(first, (struct msgbuf __user *) ptr,
|
||||
second, third);
|
||||
case MSGRCV:
|
||||
switch (version) {
|
||||
case 0: {
|
||||
struct ipc_kludge tmp;
|
||||
if (!ptr)
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&tmp,(struct ipc_kludge __user *)ptr,
|
||||
sizeof (tmp)))
|
||||
return -EFAULT;
|
||||
return sys_msgrcv (first, tmp.msgp, second,
|
||||
tmp.msgtyp, third);
|
||||
}
|
||||
default:
|
||||
return sys_msgrcv (first,
|
||||
(struct msgbuf __user *) ptr,
|
||||
second, fifth, third);
|
||||
}
|
||||
case MSGGET:
|
||||
return sys_msgget ((key_t) first, second);
|
||||
case MSGCTL:
|
||||
return sys_msgctl(first, second, (struct msqid_ds __user *)ptr);
|
||||
|
||||
case SHMAT:
|
||||
switch (version) {
|
||||
default: {
|
||||
ulong raddr;
|
||||
ret = do_shmat(first, (char __user *)ptr, second, &raddr);
|
||||
if (ret)
|
||||
return ret;
|
||||
return put_user(raddr, (ulong __user *)third);
|
||||
}
|
||||
case 1: /* Of course, we don't support iBCS2! */
|
||||
return -EINVAL;
|
||||
}
|
||||
case SHMDT:
|
||||
return sys_shmdt ((char __user *)ptr);
|
||||
case SHMGET:
|
||||
return sys_shmget (first, second, third);
|
||||
case SHMCTL:
|
||||
return sys_shmctl (first, second,
|
||||
(struct shmid_ds __user *) ptr);
|
||||
default:
|
||||
return -ENOSYS;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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)
|
||||
{
|
||||
#ifdef CONFIG_MMU
|
||||
return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
|
||||
#else
|
||||
/* can not support in nommu mode */
|
||||
return(-EINVAL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* 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,
|
||||
int __user *parent_tidptr, int tls_val,
|
||||
int __user *child_tidptr, struct pt_regs *regs)
|
||||
{
|
||||
if (!newsp)
|
||||
newsp = regs->ARM_sp;
|
||||
|
||||
return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
|
||||
}
|
||||
|
||||
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 __user *filenamei, char __user * __user *argv,
|
||||
char __user * __user *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;
|
||||
}
|
||||
|
||||
int kernel_execve(const char *filename, char *const argv[], char *const 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_START_SP - sizeof(regs)),
|
||||
"r" (®s),
|
||||
"Ir" (sizeof(regs))
|
||||
: "r0", "r1", "r2", "r3", "ip", "lr", "memory");
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_execve);
|
||||
|
||||
/*
|
||||
* Since loff_t is a 64 bit type we avoid a lot of ABI hastle
|
||||
* with a different argument ordering.
|
||||
*/
|
||||
asmlinkage long sys_arm_fadvise64_64(int fd, int advice,
|
||||
loff_t offset, loff_t len)
|
||||
{
|
||||
return sys_fadvise64_64(fd, offset, len, advice);
|
||||
}
|
||||
|
||||
/*
|
||||
* Yet more syscall fsckage - we can't fit sys_sync_file_range's
|
||||
* arguments into the available registers with EABI. So, let's
|
||||
* create an ARM specific syscall for this which has _sane_
|
||||
* arguments. (This incidentally also has an ABI-independent
|
||||
* argument layout.)
|
||||
*/
|
||||
asmlinkage long sys_arm_sync_file_range(int fd, unsigned int flags,
|
||||
loff_t offset, loff_t nbytes)
|
||||
{
|
||||
return sys_sync_file_range(fd, offset, nbytes, flags);
|
||||
}
|
||||
440
arch/arm/kernel/sys_oabi-compat.c
Normal file
440
arch/arm/kernel/sys_oabi-compat.c
Normal file
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* arch/arm/kernel/sys_oabi-compat.c
|
||||
*
|
||||
* Compatibility wrappers for syscalls that are used from
|
||||
* old ABI user space binaries with an EABI kernel.
|
||||
*
|
||||
* Author: Nicolas Pitre
|
||||
* Created: Oct 7, 2005
|
||||
* Copyright: MontaVista Software, Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The legacy ABI and the new ARM EABI have different rules making some
|
||||
* syscalls incompatible especially with structure arguments.
|
||||
* Most notably, Eabi says 64-bit members should be 64-bit aligned instead of
|
||||
* simply word aligned. EABI also pads structures to the size of the largest
|
||||
* member it contains instead of the invariant 32-bit.
|
||||
*
|
||||
* The following syscalls are affected:
|
||||
*
|
||||
* sys_stat64:
|
||||
* sys_lstat64:
|
||||
* sys_fstat64:
|
||||
*
|
||||
* struct stat64 has different sizes and some members are shifted
|
||||
* Compatibility wrappers are needed for them and provided below.
|
||||
*
|
||||
* sys_fcntl64:
|
||||
*
|
||||
* struct flock64 has different sizes and some members are shifted
|
||||
* A compatibility wrapper is needed and provided below.
|
||||
*
|
||||
* sys_statfs64:
|
||||
* sys_fstatfs64:
|
||||
*
|
||||
* struct statfs64 has extra padding with EABI growing its size from
|
||||
* 84 to 88. This struct is now __attribute__((packed,aligned(4)))
|
||||
* with a small assembly wrapper to force the sz argument to 84 if it is 88
|
||||
* to avoid copying the extra padding over user space unexpecting it.
|
||||
*
|
||||
* sys_newuname:
|
||||
*
|
||||
* struct new_utsname has no padding with EABI. No problem there.
|
||||
*
|
||||
* sys_epoll_ctl:
|
||||
* sys_epoll_wait:
|
||||
*
|
||||
* struct epoll_event has its second member shifted also affecting the
|
||||
* structure size. Compatibility wrappers are needed and provided below.
|
||||
*
|
||||
* sys_ipc:
|
||||
* sys_semop:
|
||||
* sys_semtimedop:
|
||||
*
|
||||
* struct sembuf loses its padding with EABI. Since arrays of them are
|
||||
* used they have to be copyed to remove the padding. Compatibility wrappers
|
||||
* provided below.
|
||||
*
|
||||
* sys_bind:
|
||||
* sys_connect:
|
||||
* sys_sendmsg:
|
||||
* sys_sendto:
|
||||
* sys_socketcall:
|
||||
*
|
||||
* struct sockaddr_un loses its padding with EABI. Since the size of the
|
||||
* structure is used as a validation test in unix_mkname(), we need to
|
||||
* change the length argument to 110 whenever it is 112. Compatibility
|
||||
* wrappers provided below.
|
||||
*/
|
||||
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fcntl.h>
|
||||
#include <linux/eventpoll.h>
|
||||
#include <linux/sem.h>
|
||||
#include <linux/socket.h>
|
||||
#include <linux/net.h>
|
||||
#include <asm/ipc.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
struct oldabi_stat64 {
|
||||
unsigned long long st_dev;
|
||||
unsigned int __pad1;
|
||||
unsigned long __st_ino;
|
||||
unsigned int st_mode;
|
||||
unsigned int st_nlink;
|
||||
|
||||
unsigned long st_uid;
|
||||
unsigned long st_gid;
|
||||
|
||||
unsigned long long st_rdev;
|
||||
unsigned int __pad2;
|
||||
|
||||
long long st_size;
|
||||
unsigned long st_blksize;
|
||||
unsigned long long st_blocks;
|
||||
|
||||
unsigned long st_atime;
|
||||
unsigned long st_atime_nsec;
|
||||
|
||||
unsigned long st_mtime;
|
||||
unsigned long st_mtime_nsec;
|
||||
|
||||
unsigned long st_ctime;
|
||||
unsigned long st_ctime_nsec;
|
||||
|
||||
unsigned long long st_ino;
|
||||
} __attribute__ ((packed,aligned(4)));
|
||||
|
||||
static long cp_oldabi_stat64(struct kstat *stat,
|
||||
struct oldabi_stat64 __user *statbuf)
|
||||
{
|
||||
struct oldabi_stat64 tmp;
|
||||
|
||||
tmp.st_dev = huge_encode_dev(stat->dev);
|
||||
tmp.__pad1 = 0;
|
||||
tmp.__st_ino = stat->ino;
|
||||
tmp.st_mode = stat->mode;
|
||||
tmp.st_nlink = stat->nlink;
|
||||
tmp.st_uid = stat->uid;
|
||||
tmp.st_gid = stat->gid;
|
||||
tmp.st_rdev = huge_encode_dev(stat->rdev);
|
||||
tmp.st_size = stat->size;
|
||||
tmp.st_blocks = stat->blocks;
|
||||
tmp.__pad2 = 0;
|
||||
tmp.st_blksize = stat->blksize;
|
||||
tmp.st_atime = stat->atime.tv_sec;
|
||||
tmp.st_atime_nsec = stat->atime.tv_nsec;
|
||||
tmp.st_mtime = stat->mtime.tv_sec;
|
||||
tmp.st_mtime_nsec = stat->mtime.tv_nsec;
|
||||
tmp.st_ctime = stat->ctime.tv_sec;
|
||||
tmp.st_ctime_nsec = stat->ctime.tv_nsec;
|
||||
tmp.st_ino = stat->ino;
|
||||
return copy_to_user(statbuf,&tmp,sizeof(tmp)) ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_stat64(char __user * filename,
|
||||
struct oldabi_stat64 __user * statbuf)
|
||||
{
|
||||
struct kstat stat;
|
||||
int error = vfs_stat(filename, &stat);
|
||||
if (!error)
|
||||
error = cp_oldabi_stat64(&stat, statbuf);
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_lstat64(char __user * filename,
|
||||
struct oldabi_stat64 __user * statbuf)
|
||||
{
|
||||
struct kstat stat;
|
||||
int error = vfs_lstat(filename, &stat);
|
||||
if (!error)
|
||||
error = cp_oldabi_stat64(&stat, statbuf);
|
||||
return error;
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_fstat64(unsigned long fd,
|
||||
struct oldabi_stat64 __user * statbuf)
|
||||
{
|
||||
struct kstat stat;
|
||||
int error = vfs_fstat(fd, &stat);
|
||||
if (!error)
|
||||
error = cp_oldabi_stat64(&stat, statbuf);
|
||||
return error;
|
||||
}
|
||||
|
||||
struct oabi_flock64 {
|
||||
short l_type;
|
||||
short l_whence;
|
||||
loff_t l_start;
|
||||
loff_t l_len;
|
||||
pid_t l_pid;
|
||||
} __attribute__ ((packed,aligned(4)));
|
||||
|
||||
asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct oabi_flock64 user;
|
||||
struct flock64 kernel;
|
||||
mm_segment_t fs = USER_DS; /* initialized to kill a warning */
|
||||
unsigned long local_arg = arg;
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case F_GETLK64:
|
||||
case F_SETLK64:
|
||||
case F_SETLKW64:
|
||||
if (copy_from_user(&user, (struct oabi_flock64 __user *)arg,
|
||||
sizeof(user)))
|
||||
return -EFAULT;
|
||||
kernel.l_type = user.l_type;
|
||||
kernel.l_whence = user.l_whence;
|
||||
kernel.l_start = user.l_start;
|
||||
kernel.l_len = user.l_len;
|
||||
kernel.l_pid = user.l_pid;
|
||||
local_arg = (unsigned long)&kernel;
|
||||
fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
}
|
||||
|
||||
ret = sys_fcntl64(fd, cmd, local_arg);
|
||||
|
||||
switch (cmd) {
|
||||
case F_GETLK64:
|
||||
if (!ret) {
|
||||
user.l_type = kernel.l_type;
|
||||
user.l_whence = kernel.l_whence;
|
||||
user.l_start = kernel.l_start;
|
||||
user.l_len = kernel.l_len;
|
||||
user.l_pid = kernel.l_pid;
|
||||
if (copy_to_user((struct oabi_flock64 __user *)arg,
|
||||
&user, sizeof(user)))
|
||||
ret = -EFAULT;
|
||||
}
|
||||
case F_SETLK64:
|
||||
case F_SETLKW64:
|
||||
set_fs(fs);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct oabi_epoll_event {
|
||||
__u32 events;
|
||||
__u64 data;
|
||||
} __attribute__ ((packed,aligned(4)));
|
||||
|
||||
asmlinkage long sys_oabi_epoll_ctl(int epfd, int op, int fd,
|
||||
struct oabi_epoll_event __user *event)
|
||||
{
|
||||
struct oabi_epoll_event user;
|
||||
struct epoll_event kernel;
|
||||
mm_segment_t fs;
|
||||
long ret;
|
||||
|
||||
if (op == EPOLL_CTL_DEL)
|
||||
return sys_epoll_ctl(epfd, op, fd, NULL);
|
||||
if (copy_from_user(&user, event, sizeof(user)))
|
||||
return -EFAULT;
|
||||
kernel.events = user.events;
|
||||
kernel.data = user.data;
|
||||
fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = sys_epoll_ctl(epfd, op, fd, &kernel);
|
||||
set_fs(fs);
|
||||
return ret;
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_epoll_wait(int epfd,
|
||||
struct oabi_epoll_event __user *events,
|
||||
int maxevents, int timeout)
|
||||
{
|
||||
struct epoll_event *kbuf;
|
||||
mm_segment_t fs;
|
||||
long ret, err, i;
|
||||
|
||||
if (maxevents <= 0 || maxevents > (INT_MAX/sizeof(struct epoll_event)))
|
||||
return -EINVAL;
|
||||
kbuf = kmalloc(sizeof(*kbuf) * maxevents, GFP_KERNEL);
|
||||
if (!kbuf)
|
||||
return -ENOMEM;
|
||||
fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
ret = sys_epoll_wait(epfd, kbuf, maxevents, timeout);
|
||||
set_fs(fs);
|
||||
err = 0;
|
||||
for (i = 0; i < ret; i++) {
|
||||
__put_user_error(kbuf[i].events, &events->events, err);
|
||||
__put_user_error(kbuf[i].data, &events->data, err);
|
||||
events++;
|
||||
}
|
||||
kfree(kbuf);
|
||||
return err ? -EFAULT : ret;
|
||||
}
|
||||
|
||||
struct oabi_sembuf {
|
||||
unsigned short sem_num;
|
||||
short sem_op;
|
||||
short sem_flg;
|
||||
unsigned short __pad;
|
||||
};
|
||||
|
||||
asmlinkage long sys_oabi_semtimedop(int semid,
|
||||
struct oabi_sembuf __user *tsops,
|
||||
unsigned nsops,
|
||||
const struct timespec __user *timeout)
|
||||
{
|
||||
struct sembuf *sops;
|
||||
struct timespec local_timeout;
|
||||
long err;
|
||||
int i;
|
||||
|
||||
if (nsops < 1)
|
||||
return -EINVAL;
|
||||
sops = kmalloc(sizeof(*sops) * nsops, GFP_KERNEL);
|
||||
if (!sops)
|
||||
return -ENOMEM;
|
||||
err = 0;
|
||||
for (i = 0; i < nsops; i++) {
|
||||
__get_user_error(sops[i].sem_num, &tsops->sem_num, err);
|
||||
__get_user_error(sops[i].sem_op, &tsops->sem_op, err);
|
||||
__get_user_error(sops[i].sem_flg, &tsops->sem_flg, err);
|
||||
tsops++;
|
||||
}
|
||||
if (timeout) {
|
||||
/* copy this as well before changing domain protection */
|
||||
err |= copy_from_user(&local_timeout, timeout, sizeof(*timeout));
|
||||
timeout = &local_timeout;
|
||||
}
|
||||
if (err) {
|
||||
err = -EFAULT;
|
||||
} else {
|
||||
mm_segment_t fs = get_fs();
|
||||
set_fs(KERNEL_DS);
|
||||
err = sys_semtimedop(semid, sops, nsops, timeout);
|
||||
set_fs(fs);
|
||||
}
|
||||
kfree(sops);
|
||||
return err;
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_semop(int semid, struct oabi_sembuf __user *tsops,
|
||||
unsigned nsops)
|
||||
{
|
||||
return sys_oabi_semtimedop(semid, tsops, nsops, NULL);
|
||||
}
|
||||
|
||||
extern asmlinkage int sys_ipc(uint call, int first, int second, int third,
|
||||
void __user *ptr, long fifth);
|
||||
|
||||
asmlinkage int sys_oabi_ipc(uint call, int first, int second, int third,
|
||||
void __user *ptr, long fifth)
|
||||
{
|
||||
switch (call & 0xffff) {
|
||||
case SEMOP:
|
||||
return sys_oabi_semtimedop(first,
|
||||
(struct oabi_sembuf __user *)ptr,
|
||||
second, NULL);
|
||||
case SEMTIMEDOP:
|
||||
return sys_oabi_semtimedop(first,
|
||||
(struct oabi_sembuf __user *)ptr,
|
||||
second,
|
||||
(const struct timespec __user *)fifth);
|
||||
default:
|
||||
return sys_ipc(call, first, second, third, ptr, fifth);
|
||||
}
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_bind(int fd, struct sockaddr __user *addr, int addrlen)
|
||||
{
|
||||
sa_family_t sa_family;
|
||||
if (addrlen == 112 &&
|
||||
get_user(sa_family, &addr->sa_family) == 0 &&
|
||||
sa_family == AF_UNIX)
|
||||
addrlen = 110;
|
||||
return sys_bind(fd, addr, addrlen);
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_connect(int fd, struct sockaddr __user *addr, int addrlen)
|
||||
{
|
||||
sa_family_t sa_family;
|
||||
if (addrlen == 112 &&
|
||||
get_user(sa_family, &addr->sa_family) == 0 &&
|
||||
sa_family == AF_UNIX)
|
||||
addrlen = 110;
|
||||
return sys_connect(fd, addr, addrlen);
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_sendto(int fd, void __user *buff,
|
||||
size_t len, unsigned flags,
|
||||
struct sockaddr __user *addr,
|
||||
int addrlen)
|
||||
{
|
||||
sa_family_t sa_family;
|
||||
if (addrlen == 112 &&
|
||||
get_user(sa_family, &addr->sa_family) == 0 &&
|
||||
sa_family == AF_UNIX)
|
||||
addrlen = 110;
|
||||
return sys_sendto(fd, buff, len, flags, addr, addrlen);
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_sendmsg(int fd, struct msghdr __user *msg, unsigned flags)
|
||||
{
|
||||
struct sockaddr __user *addr;
|
||||
int msg_namelen;
|
||||
sa_family_t sa_family;
|
||||
if (msg &&
|
||||
get_user(msg_namelen, &msg->msg_namelen) == 0 &&
|
||||
msg_namelen == 112 &&
|
||||
get_user(addr, &msg->msg_name) == 0 &&
|
||||
get_user(sa_family, &addr->sa_family) == 0 &&
|
||||
sa_family == AF_UNIX)
|
||||
{
|
||||
/*
|
||||
* HACK ALERT: there is a limit to how much backward bending
|
||||
* we should do for what is actually a transitional
|
||||
* compatibility layer. This already has known flaws with
|
||||
* a few ioctls that we don't intend to fix. Therefore
|
||||
* consider this blatent hack as another one... and take care
|
||||
* to run for cover. In most cases it will "just work fine".
|
||||
* If it doesn't, well, tough.
|
||||
*/
|
||||
put_user(110, &msg->msg_namelen);
|
||||
}
|
||||
return sys_sendmsg(fd, msg, flags);
|
||||
}
|
||||
|
||||
asmlinkage long sys_oabi_socketcall(int call, unsigned long __user *args)
|
||||
{
|
||||
unsigned long r = -EFAULT, a[6];
|
||||
|
||||
switch (call) {
|
||||
case SYS_BIND:
|
||||
if (copy_from_user(a, args, 3 * sizeof(long)) == 0)
|
||||
r = sys_oabi_bind(a[0], (struct sockaddr __user *)a[1], a[2]);
|
||||
break;
|
||||
case SYS_CONNECT:
|
||||
if (copy_from_user(a, args, 3 * sizeof(long)) == 0)
|
||||
r = sys_oabi_connect(a[0], (struct sockaddr __user *)a[1], a[2]);
|
||||
break;
|
||||
case SYS_SENDTO:
|
||||
if (copy_from_user(a, args, 6 * sizeof(long)) == 0)
|
||||
r = sys_oabi_sendto(a[0], (void __user *)a[1], a[2], a[3],
|
||||
(struct sockaddr __user *)a[4], a[5]);
|
||||
break;
|
||||
case SYS_SENDMSG:
|
||||
if (copy_from_user(a, args, 3 * sizeof(long)) == 0)
|
||||
r = sys_oabi_sendmsg(a[0], (struct msghdr __user *)a[1], a[2]);
|
||||
break;
|
||||
default:
|
||||
r = sys_socketcall(call, args);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
516
arch/arm/kernel/time.c
Normal file
516
arch/arm/kernel/time.c
Normal file
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/time.c
|
||||
*
|
||||
* Copyright (C) 1991, 1992, 1995 Linus Torvalds
|
||||
* Modifications for ARM (C) 1994-2001 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.
|
||||
*
|
||||
* 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/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 <linux/sysdev.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/irq.h>
|
||||
|
||||
#include <linux/mc146818rtc.h>
|
||||
|
||||
#include <asm/leds.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/mach/time.h>
|
||||
|
||||
/*
|
||||
* Our system timer.
|
||||
*/
|
||||
struct sys_timer *system_timer;
|
||||
|
||||
#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE)
|
||||
/* this needs a better home */
|
||||
DEFINE_SPINLOCK(rtc_lock);
|
||||
|
||||
#ifdef CONFIG_RTC_DRV_CMOS_MODULE
|
||||
EXPORT_SYMBOL(rtc_lock);
|
||||
#endif
|
||||
#endif /* pc-style 'CMOS' RTC support */
|
||||
|
||||
/* change this if you have some constant time drift */
|
||||
#define USECS_PER_JIFFY (1000000/HZ)
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
unsigned long profile_pc(struct pt_regs *regs)
|
||||
{
|
||||
unsigned long fp, pc = instruction_pointer(regs);
|
||||
|
||||
if (in_lock_functions(pc)) {
|
||||
fp = regs->ARM_fp;
|
||||
pc = pc_pointer(((unsigned long *)fp)[-1]);
|
||||
}
|
||||
|
||||
return pc;
|
||||
}
|
||||
EXPORT_SYMBOL(profile_pc);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* hook for setting the RTC's idea of the current time.
|
||||
*/
|
||||
int (*set_rtc)(void);
|
||||
|
||||
#ifndef CONFIG_GENERIC_TIME
|
||||
static unsigned long dummy_gettimeoffset(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* An implementation of printk_clock() independent from
|
||||
* sched_clock(). This avoids non-bootable kernels when
|
||||
* printk_clock is enabled.
|
||||
*/
|
||||
unsigned long long printk_clock(void)
|
||||
{
|
||||
return (unsigned long long)(jiffies - INITIAL_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 (!ntp_synced() || set_rtc == NULL)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LEDS
|
||||
|
||||
static void dummy_leds_event(led_event_t evt)
|
||||
{
|
||||
}
|
||||
|
||||
void (*leds_event)(led_event_t) = dummy_leds_event;
|
||||
|
||||
struct leds_evt_name {
|
||||
const char name[8];
|
||||
int on;
|
||||
int off;
|
||||
};
|
||||
|
||||
static const struct leds_evt_name evt_names[] = {
|
||||
{ "amber", led_amber_on, led_amber_off },
|
||||
{ "blue", led_blue_on, led_blue_off },
|
||||
{ "green", led_green_on, led_green_off },
|
||||
{ "red", led_red_on, led_red_off },
|
||||
};
|
||||
|
||||
static ssize_t leds_store(struct sys_device *dev, const char *buf, size_t size)
|
||||
{
|
||||
int ret = -EINVAL, len = strcspn(buf, " ");
|
||||
|
||||
if (len > 0 && buf[len] == '\0')
|
||||
len--;
|
||||
|
||||
if (strncmp(buf, "claim", len) == 0) {
|
||||
leds_event(led_claim);
|
||||
ret = size;
|
||||
} else if (strncmp(buf, "release", len) == 0) {
|
||||
leds_event(led_release);
|
||||
ret = size;
|
||||
} else {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(evt_names); i++) {
|
||||
if (strlen(evt_names[i].name) != len ||
|
||||
strncmp(buf, evt_names[i].name, len) != 0)
|
||||
continue;
|
||||
if (strncmp(buf+len, " on", 3) == 0) {
|
||||
leds_event(evt_names[i].on);
|
||||
ret = size;
|
||||
} else if (strncmp(buf+len, " off", 4) == 0) {
|
||||
leds_event(evt_names[i].off);
|
||||
ret = size;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SYSDEV_ATTR(event, 0200, NULL, leds_store);
|
||||
|
||||
static int leds_suspend(struct sys_device *dev, pm_message_t state)
|
||||
{
|
||||
leds_event(led_stop);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int leds_resume(struct sys_device *dev)
|
||||
{
|
||||
leds_event(led_start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int leds_shutdown(struct sys_device *dev)
|
||||
{
|
||||
leds_event(led_halted);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sysdev_class leds_sysclass = {
|
||||
set_kset_name("leds"),
|
||||
.shutdown = leds_shutdown,
|
||||
.suspend = leds_suspend,
|
||||
.resume = leds_resume,
|
||||
};
|
||||
|
||||
static struct sys_device leds_device = {
|
||||
.id = 0,
|
||||
.cls = &leds_sysclass,
|
||||
};
|
||||
|
||||
static int __init leds_init(void)
|
||||
{
|
||||
int ret;
|
||||
ret = sysdev_class_register(&leds_sysclass);
|
||||
if (ret == 0)
|
||||
ret = sysdev_register(&leds_device);
|
||||
if (ret == 0)
|
||||
ret = sysdev_create_file(&leds_device, &attr_event);
|
||||
return ret;
|
||||
}
|
||||
|
||||
device_initcall(leds_init);
|
||||
|
||||
EXPORT_SYMBOL(leds_event);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LEDS_TIMER
|
||||
static inline void do_leds(void)
|
||||
{
|
||||
static unsigned int count = HZ/2;
|
||||
|
||||
if (--count == 0) {
|
||||
count = HZ/2;
|
||||
leds_event(led_timer);
|
||||
}
|
||||
}
|
||||
#else
|
||||
#define do_leds()
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_GENERIC_TIME
|
||||
void do_gettimeofday(struct timeval *tv)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned long seq;
|
||||
unsigned long usec, sec;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin_irqsave(&xtime_lock, flags);
|
||||
usec = system_timer->offset();
|
||||
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)
|
||||
{
|
||||
time_t wtm_sec, sec = tv->tv_sec;
|
||||
long wtm_nsec, nsec = tv->tv_nsec;
|
||||
|
||||
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
|
||||
return -EINVAL;
|
||||
|
||||
write_seqlock_irq(&xtime_lock);
|
||||
/*
|
||||
* This is revolting. We need to set "xtime" correctly. However, the
|
||||
* value in this location is the value at the most recent update of
|
||||
* wall time. Discover what correction gettimeofday() would have
|
||||
* done, and then undo it!
|
||||
*/
|
||||
nsec -= system_timer->offset() * NSEC_PER_USEC;
|
||||
|
||||
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
|
||||
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
|
||||
|
||||
set_normalized_timespec(&xtime, sec, nsec);
|
||||
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
|
||||
|
||||
ntp_clear();
|
||||
write_sequnlock_irq(&xtime_lock);
|
||||
clock_was_set();
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(do_settimeofday);
|
||||
#endif /* !CONFIG_GENERIC_TIME */
|
||||
|
||||
/**
|
||||
* save_time_delta - Save the offset between system time and RTC time
|
||||
* @delta: pointer to timespec to store delta
|
||||
* @rtc: pointer to timespec for current RTC time
|
||||
*
|
||||
* Return a delta between the system time and the RTC time, such
|
||||
* that system time can be restored later with restore_time_delta()
|
||||
*/
|
||||
void save_time_delta(struct timespec *delta, struct timespec *rtc)
|
||||
{
|
||||
set_normalized_timespec(delta,
|
||||
xtime.tv_sec - rtc->tv_sec,
|
||||
xtime.tv_nsec - rtc->tv_nsec);
|
||||
}
|
||||
EXPORT_SYMBOL(save_time_delta);
|
||||
|
||||
/**
|
||||
* restore_time_delta - Restore the current system time
|
||||
* @delta: delta returned by save_time_delta()
|
||||
* @rtc: pointer to timespec for current RTC time
|
||||
*/
|
||||
void restore_time_delta(struct timespec *delta, struct timespec *rtc)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
set_normalized_timespec(&ts,
|
||||
delta->tv_sec + rtc->tv_sec,
|
||||
delta->tv_nsec + rtc->tv_nsec);
|
||||
|
||||
do_settimeofday(&ts);
|
||||
}
|
||||
EXPORT_SYMBOL(restore_time_delta);
|
||||
|
||||
/*
|
||||
* Kernel system timer support.
|
||||
*/
|
||||
void timer_tick(void)
|
||||
{
|
||||
profile_tick(CPU_PROFILING);
|
||||
do_leds();
|
||||
do_set_rtc();
|
||||
do_timer(1);
|
||||
#ifndef CONFIG_SMP
|
||||
update_process_times(user_mode(get_irq_regs()));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int timer_suspend(struct sys_device *dev, pm_message_t state)
|
||||
{
|
||||
struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
|
||||
|
||||
if (timer->suspend != NULL)
|
||||
timer->suspend();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int timer_resume(struct sys_device *dev)
|
||||
{
|
||||
struct sys_timer *timer = container_of(dev, struct sys_timer, dev);
|
||||
|
||||
if (timer->resume != NULL)
|
||||
timer->resume();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define timer_suspend NULL
|
||||
#define timer_resume NULL
|
||||
#endif
|
||||
|
||||
static struct sysdev_class timer_sysclass = {
|
||||
set_kset_name("timer"),
|
||||
.suspend = timer_suspend,
|
||||
.resume = timer_resume,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_NO_IDLE_HZ
|
||||
static int timer_dyn_tick_enable(void)
|
||||
{
|
||||
struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
|
||||
unsigned long flags;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (dyn_tick) {
|
||||
spin_lock_irqsave(&dyn_tick->lock, flags);
|
||||
ret = 0;
|
||||
if (!(dyn_tick->state & DYN_TICK_ENABLED)) {
|
||||
ret = dyn_tick->enable();
|
||||
|
||||
if (ret == 0)
|
||||
dyn_tick->state |= DYN_TICK_ENABLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&dyn_tick->lock, flags);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int timer_dyn_tick_disable(void)
|
||||
{
|
||||
struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
|
||||
unsigned long flags;
|
||||
int ret = -ENODEV;
|
||||
|
||||
if (dyn_tick) {
|
||||
spin_lock_irqsave(&dyn_tick->lock, flags);
|
||||
ret = 0;
|
||||
if (dyn_tick->state & DYN_TICK_ENABLED) {
|
||||
ret = dyn_tick->disable();
|
||||
|
||||
if (ret == 0)
|
||||
dyn_tick->state &= ~DYN_TICK_ENABLED;
|
||||
}
|
||||
spin_unlock_irqrestore(&dyn_tick->lock, flags);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reprogram the system timer for at least the calculated time interval.
|
||||
* This function should be called from the idle thread with IRQs disabled,
|
||||
* immediately before sleeping.
|
||||
*/
|
||||
void timer_dyn_reprogram(void)
|
||||
{
|
||||
struct dyn_tick_timer *dyn_tick = system_timer->dyn_tick;
|
||||
unsigned long next, seq, flags;
|
||||
|
||||
if (!dyn_tick)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&dyn_tick->lock, flags);
|
||||
if (dyn_tick->state & DYN_TICK_ENABLED) {
|
||||
next = next_timer_interrupt();
|
||||
do {
|
||||
seq = read_seqbegin(&xtime_lock);
|
||||
dyn_tick->reprogram(next - jiffies);
|
||||
} while (read_seqretry(&xtime_lock, seq));
|
||||
}
|
||||
spin_unlock_irqrestore(&dyn_tick->lock, flags);
|
||||
}
|
||||
|
||||
static ssize_t timer_show_dyn_tick(struct sys_device *dev, char *buf)
|
||||
{
|
||||
return sprintf(buf, "%i\n",
|
||||
(system_timer->dyn_tick->state & DYN_TICK_ENABLED) >> 1);
|
||||
}
|
||||
|
||||
static ssize_t timer_set_dyn_tick(struct sys_device *dev, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
unsigned int enable = simple_strtoul(buf, NULL, 2);
|
||||
|
||||
if (enable)
|
||||
timer_dyn_tick_enable();
|
||||
else
|
||||
timer_dyn_tick_disable();
|
||||
|
||||
return count;
|
||||
}
|
||||
static SYSDEV_ATTR(dyn_tick, 0644, timer_show_dyn_tick, timer_set_dyn_tick);
|
||||
|
||||
/*
|
||||
* dyntick=enable|disable
|
||||
*/
|
||||
static char dyntick_str[4] __initdata = "";
|
||||
|
||||
static int __init dyntick_setup(char *str)
|
||||
{
|
||||
if (str)
|
||||
strlcpy(dyntick_str, str, sizeof(dyntick_str));
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("dyntick=", dyntick_setup);
|
||||
#endif
|
||||
|
||||
static int __init timer_init_sysfs(void)
|
||||
{
|
||||
int ret = sysdev_class_register(&timer_sysclass);
|
||||
if (ret == 0) {
|
||||
system_timer->dev.cls = &timer_sysclass;
|
||||
ret = sysdev_register(&system_timer->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NO_IDLE_HZ
|
||||
if (ret == 0 && system_timer->dyn_tick) {
|
||||
ret = sysdev_create_file(&system_timer->dev, &attr_dyn_tick);
|
||||
|
||||
/*
|
||||
* Turn on dynamic tick after calibrate delay
|
||||
* for correct bogomips
|
||||
*/
|
||||
if (ret == 0 && dyntick_str[0] == 'e')
|
||||
ret = timer_dyn_tick_enable();
|
||||
}
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
device_initcall(timer_init_sysfs);
|
||||
|
||||
void __init time_init(void)
|
||||
{
|
||||
#ifndef CONFIG_GENERIC_TIME
|
||||
if (system_timer->offset == NULL)
|
||||
system_timer->offset = dummy_gettimeoffset;
|
||||
#endif
|
||||
system_timer->init();
|
||||
|
||||
#ifdef CONFIG_NO_IDLE_HZ
|
||||
if (system_timer->dyn_tick)
|
||||
system_timer->dyn_tick->lock = SPIN_LOCK_UNLOCKED;
|
||||
#endif
|
||||
}
|
||||
|
||||
705
arch/arm/kernel/traps.c
Normal file
705
arch/arm/kernel/traps.c
Normal file
@@ -0,0 +1,705 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/traps.c
|
||||
*
|
||||
* Copyright (C) 1995-2002 Russell King
|
||||
* Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) 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.
|
||||
*
|
||||
* 'traps.c' handles hardware exceptions after we have saved some state in
|
||||
* 'linux/arch/arm/lib/traps.S'. Mostly a debugging aid, but will probably
|
||||
* kill the offending process.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/atomic.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/system.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <asm/traps.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#include "ptrace.h"
|
||||
#include "signal.h"
|
||||
|
||||
static const char *handler[]= { "prefetch abort", "data abort", "address exception", "interrupt" };
|
||||
|
||||
#ifdef CONFIG_DEBUG_USER
|
||||
unsigned int user_debug;
|
||||
|
||||
static int __init user_debug_setup(char *str)
|
||||
{
|
||||
get_option(&str, &user_debug);
|
||||
return 1;
|
||||
}
|
||||
__setup("user_debug=", user_debug_setup);
|
||||
#endif
|
||||
|
||||
void dump_backtrace_entry(unsigned long where, unsigned long from)
|
||||
{
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
printk("[<%08lx>] ", where);
|
||||
print_symbol("(%s) ", where);
|
||||
printk("from [<%08lx>] ", from);
|
||||
print_symbol("(%s)\n", from);
|
||||
#else
|
||||
printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* 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(0x%08lx to 0x%08lx)\n", str, 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 thumb = thumb_mode(regs);
|
||||
const int width = thumb ? 4 : 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;
|
||||
|
||||
if (thumb)
|
||||
bad = __get_user(val, &((u16 *)addr)[i]);
|
||||
else
|
||||
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_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)end_of_stack(tsk))
|
||||
printk("frame pointer underflow");
|
||||
printk("\n");
|
||||
|
||||
if (ok)
|
||||
c_backtrace(fp, processor_mode(regs));
|
||||
}
|
||||
|
||||
void dump_stack(void)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_ERRORS
|
||||
__backtrace();
|
||||
#endif
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(dump_stack);
|
||||
|
||||
void show_stack(struct task_struct *tsk, unsigned long *sp)
|
||||
{
|
||||
unsigned long fp;
|
||||
|
||||
if (!tsk)
|
||||
tsk = current;
|
||||
|
||||
if (tsk != current)
|
||||
fp = thread_saved_fp(tsk);
|
||||
else
|
||||
asm("mov %0, fp" : "=r" (fp) : : "cc");
|
||||
|
||||
c_backtrace(fp, 0x10);
|
||||
barrier();
|
||||
}
|
||||
|
||||
static void __die(const char *str, int err, struct thread_info *thread, struct pt_regs *regs)
|
||||
{
|
||||
struct task_struct *tsk = thread->task;
|
||||
static int die_counter;
|
||||
|
||||
printk("Internal error: %s: %x [#%d]\n", str, err, ++die_counter);
|
||||
print_modules();
|
||||
__show_regs(regs);
|
||||
printk("Process %s (pid: %d, stack limit = 0x%p)\n",
|
||||
tsk->comm, tsk->pid, thread + 1);
|
||||
|
||||
if (!user_mode(regs) || in_interrupt()) {
|
||||
dump_mem("Stack: ", regs->ARM_sp,
|
||||
THREAD_SIZE + (unsigned long)task_stack_page(tsk));
|
||||
dump_backtrace(regs, tsk);
|
||||
dump_instr(regs);
|
||||
}
|
||||
}
|
||||
|
||||
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 thread_info *thread = current_thread_info();
|
||||
|
||||
console_verbose();
|
||||
spin_lock_irq(&die_lock);
|
||||
bust_spinlocks(1);
|
||||
__die(str, err, thread, regs);
|
||||
bust_spinlocks(0);
|
||||
spin_unlock_irq(&die_lock);
|
||||
|
||||
if (panic_on_oops)
|
||||
panic("Fatal exception");
|
||||
|
||||
do_exit(SIGSEGV);
|
||||
}
|
||||
|
||||
void notify_die(const char *str, struct pt_regs *regs, struct siginfo *info,
|
||||
unsigned long err, unsigned long trap)
|
||||
{
|
||||
if (user_mode(regs)) {
|
||||
current->thread.error_code = err;
|
||||
current->thread.trap_no = trap;
|
||||
|
||||
force_sig_info(info->si_signo, info, current);
|
||||
} else {
|
||||
die(str, regs, err);
|
||||
}
|
||||
}
|
||||
|
||||
static LIST_HEAD(undef_hook);
|
||||
static DEFINE_SPINLOCK(undef_lock);
|
||||
|
||||
void register_undef_hook(struct undef_hook *hook)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&undef_lock, flags);
|
||||
list_add(&hook->node, &undef_hook);
|
||||
spin_unlock_irqrestore(&undef_lock, flags);
|
||||
}
|
||||
|
||||
void unregister_undef_hook(struct undef_hook *hook)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&undef_lock, flags);
|
||||
list_del(&hook->node);
|
||||
spin_unlock_irqrestore(&undef_lock, flags);
|
||||
}
|
||||
|
||||
asmlinkage void do_undefinstr(struct pt_regs *regs)
|
||||
{
|
||||
unsigned int correction = thumb_mode(regs) ? 2 : 4;
|
||||
unsigned int instr;
|
||||
struct undef_hook *hook;
|
||||
siginfo_t info;
|
||||
void __user *pc;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* According to the ARM ARM, PC is 2 or 4 bytes ahead,
|
||||
* depending whether we're in Thumb mode or not.
|
||||
* Correct this offset.
|
||||
*/
|
||||
regs->ARM_pc -= correction;
|
||||
|
||||
pc = (void __user *)instruction_pointer(regs);
|
||||
|
||||
if (processor_mode(regs) == SVC_MODE) {
|
||||
instr = *(u32 *) pc;
|
||||
} else if (thumb_mode(regs)) {
|
||||
get_user(instr, (u16 __user *)pc);
|
||||
} else {
|
||||
get_user(instr, (u32 __user *)pc);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&undef_lock, flags);
|
||||
list_for_each_entry(hook, &undef_hook, node) {
|
||||
if ((instr & hook->instr_mask) == hook->instr_val &&
|
||||
(regs->ARM_cpsr & hook->cpsr_mask) == hook->cpsr_val) {
|
||||
if (hook->fn(regs, instr) == 0) {
|
||||
spin_unlock_irq(&undef_lock);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&undef_lock, flags);
|
||||
|
||||
#ifdef CONFIG_DEBUG_USER
|
||||
if (user_debug & UDBG_UNDEFINED) {
|
||||
printk(KERN_INFO "%s (%d): undefined instruction: pc=%p\n",
|
||||
current->comm, current->pid, pc);
|
||||
dump_instr(regs);
|
||||
}
|
||||
#endif
|
||||
|
||||
info.si_signo = SIGILL;
|
||||
info.si_errno = 0;
|
||||
info.si_code = ILL_ILLOPC;
|
||||
info.si_addr = pc;
|
||||
|
||||
notify_die("Oops - undefined instruction", regs, &info, 0, 6);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
console_verbose();
|
||||
|
||||
printk(KERN_CRIT "Bad mode in %s handler detected\n", handler[reason]);
|
||||
|
||||
die("Oops - bad mode", 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 &&
|
||||
current->personality != PER_LINUX_32BIT &&
|
||||
thread->exec_domain->handler) {
|
||||
thread->exec_domain->handler(n, regs);
|
||||
return regs->ARM_r0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_USER
|
||||
if (user_debug & UDBG_SYSCALL) {
|
||||
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 __user *)instruction_pointer(regs) -
|
||||
(thumb_mode(regs) ? 2 : 4);
|
||||
|
||||
notify_die("Oops - bad syscall", regs, &info, n, 0);
|
||||
|
||||
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 || flags)
|
||||
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;
|
||||
|
||||
flush_cache_user_range(vma, start, 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)
|
||||
{
|
||||
struct thread_info *thread = current_thread_info();
|
||||
siginfo_t info;
|
||||
|
||||
if ((no >> 16) != (__ARM_NR_BASE>> 16))
|
||||
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;
|
||||
|
||||
notify_die("branch through zero", regs, &info, 0, 0);
|
||||
return 0;
|
||||
|
||||
case NR(breakpoint): /* SWI BREAK_POINT */
|
||||
regs->ARM_pc -= thumb_mode(regs) ? 2 : 4;
|
||||
ptrace_break(current, regs);
|
||||
return regs->ARM_r0;
|
||||
|
||||
/*
|
||||
* Flush a region from virtual address 'r0' to virtual address 'r1'
|
||||
* _exclusive_. There is no alignment requirement on either address;
|
||||
* user space does not need to know the hardware cache layout.
|
||||
*
|
||||
* r2 contains flags. It should ALWAYS be passed as ZERO until it
|
||||
* is defined to be something else. For now we ignore it, but may
|
||||
* the fires of hell burn in your belly if you break this rule. ;)
|
||||
*
|
||||
* (at a later date, we may want to allow this call to not flush
|
||||
* various aspects of the cache. Passing '0' will guarantee that
|
||||
* everything necessary gets flushed to maintain consistency in
|
||||
* the specified region).
|
||||
*/
|
||||
case NR(cacheflush):
|
||||
do_cache_op(regs->ARM_r0, regs->ARM_r1, regs->ARM_r2);
|
||||
return 0;
|
||||
|
||||
case NR(usr26):
|
||||
if (!(elf_hwcap & HWCAP_26BIT))
|
||||
break;
|
||||
regs->ARM_cpsr &= ~MODE32_BIT;
|
||||
return regs->ARM_r0;
|
||||
|
||||
case NR(usr32):
|
||||
if (!(elf_hwcap & HWCAP_26BIT))
|
||||
break;
|
||||
regs->ARM_cpsr |= MODE32_BIT;
|
||||
return regs->ARM_r0;
|
||||
|
||||
case NR(set_tls):
|
||||
thread->tp_value = regs->ARM_r0;
|
||||
#if defined(CONFIG_HAS_TLS_REG)
|
||||
asm ("mcr p15, 0, %0, c13, c0, 3" : : "r" (regs->ARM_r0) );
|
||||
#elif !defined(CONFIG_TLS_REG_EMUL)
|
||||
/*
|
||||
* User space must never try to access this directly.
|
||||
* Expect your app to break eventually if you do so.
|
||||
* The user helper at 0xffff0fe0 must be used instead.
|
||||
* (see entry-armv.S for details)
|
||||
*/
|
||||
*((unsigned int *)0xffff0ff0) = regs->ARM_r0;
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_NEEDS_SYSCALL_FOR_CMPXCHG
|
||||
/*
|
||||
* Atomically store r1 in *r2 if *r2 is equal to r0 for user space.
|
||||
* Return zero in r0 if *MEM was changed or non-zero if no exchange
|
||||
* happened. Also set the user C flag accordingly.
|
||||
* If access permissions have to be fixed up then non-zero is
|
||||
* returned and the operation has to be re-attempted.
|
||||
*
|
||||
* *NOTE*: This is a ghost syscall private to the kernel. Only the
|
||||
* __kuser_cmpxchg code in entry-armv.S should be aware of its
|
||||
* existence. Don't ever use this from user code.
|
||||
*/
|
||||
case 0xfff0:
|
||||
{
|
||||
extern void do_DataAbort(unsigned long addr, unsigned int fsr,
|
||||
struct pt_regs *regs);
|
||||
unsigned long val;
|
||||
unsigned long addr = regs->ARM_r2;
|
||||
struct mm_struct *mm = current->mm;
|
||||
pgd_t *pgd; pmd_t *pmd; pte_t *pte;
|
||||
spinlock_t *ptl;
|
||||
|
||||
regs->ARM_cpsr &= ~PSR_C_BIT;
|
||||
down_read(&mm->mmap_sem);
|
||||
pgd = pgd_offset(mm, addr);
|
||||
if (!pgd_present(*pgd))
|
||||
goto bad_access;
|
||||
pmd = pmd_offset(pgd, addr);
|
||||
if (!pmd_present(*pmd))
|
||||
goto bad_access;
|
||||
pte = pte_offset_map_lock(mm, pmd, addr, &ptl);
|
||||
if (!pte_present(*pte) || !pte_dirty(*pte)) {
|
||||
pte_unmap_unlock(pte, ptl);
|
||||
goto bad_access;
|
||||
}
|
||||
val = *(unsigned long *)addr;
|
||||
val -= regs->ARM_r0;
|
||||
if (val == 0) {
|
||||
*(unsigned long *)addr = regs->ARM_r1;
|
||||
regs->ARM_cpsr |= PSR_C_BIT;
|
||||
}
|
||||
pte_unmap_unlock(pte, ptl);
|
||||
up_read(&mm->mmap_sem);
|
||||
return val;
|
||||
|
||||
bad_access:
|
||||
up_read(&mm->mmap_sem);
|
||||
/* simulate a write access fault */
|
||||
do_DataAbort(addr, 15 + (1 << 11), regs);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
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
|
||||
*/
|
||||
if (user_debug & UDBG_SYSCALL) {
|
||||
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 __user *)instruction_pointer(regs) -
|
||||
(thumb_mode(regs) ? 2 : 4);
|
||||
|
||||
notify_die("Oops - bad syscall(2)", regs, &info, no, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TLS_REG_EMUL
|
||||
|
||||
/*
|
||||
* We might be running on an ARMv6+ processor which should have the TLS
|
||||
* register but for some reason we can't use it, or maybe an SMP system
|
||||
* using a pre-ARMv6 processor (there are apparently a few prototypes like
|
||||
* that in existence) and therefore access to that register must be
|
||||
* emulated.
|
||||
*/
|
||||
|
||||
static int get_tp_trap(struct pt_regs *regs, unsigned int instr)
|
||||
{
|
||||
int reg = (instr >> 12) & 15;
|
||||
if (reg == 15)
|
||||
return 1;
|
||||
regs->uregs[reg] = current_thread_info()->tp_value;
|
||||
regs->ARM_pc += 4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct undef_hook arm_mrc_hook = {
|
||||
.instr_mask = 0x0fff0fff,
|
||||
.instr_val = 0x0e1d0f70,
|
||||
.cpsr_mask = PSR_T_BIT,
|
||||
.cpsr_val = 0,
|
||||
.fn = get_tp_trap,
|
||||
};
|
||||
|
||||
static int __init arm_mrc_hook_init(void)
|
||||
{
|
||||
register_undef_hook(&arm_mrc_hook);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(arm_mrc_hook_init);
|
||||
|
||||
#endif
|
||||
|
||||
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();
|
||||
}
|
||||
EXPORT_SYMBOL(__bad_xchg);
|
||||
|
||||
/*
|
||||
* 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
|
||||
if (user_debug & UDBG_BADABORT) {
|
||||
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 __user *)addr;
|
||||
|
||||
notify_die("unknown data abort code", regs, &info, instr, 0);
|
||||
}
|
||||
|
||||
void __attribute__((noreturn)) __bug(const char *file, int line)
|
||||
{
|
||||
printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line);
|
||||
*(int *)0 = 0;
|
||||
|
||||
/* Avoid "noreturn function does return" */
|
||||
for (;;);
|
||||
}
|
||||
EXPORT_SYMBOL(__bug);
|
||||
|
||||
void __readwrite_bug(const char *fn)
|
||||
{
|
||||
printk("%s called, but not implemented\n", fn);
|
||||
BUG();
|
||||
}
|
||||
EXPORT_SYMBOL(__readwrite_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();
|
||||
}
|
||||
EXPORT_SYMBOL(__div0);
|
||||
|
||||
void abort(void)
|
||||
{
|
||||
BUG();
|
||||
|
||||
/* if that doesn't kill us, halt */
|
||||
panic("Oops failed to kill thread");
|
||||
}
|
||||
EXPORT_SYMBOL(abort);
|
||||
|
||||
void __init trap_init(void)
|
||||
{
|
||||
unsigned long vectors = CONFIG_VECTORS_BASE;
|
||||
extern char __stubs_start[], __stubs_end[];
|
||||
extern char __vectors_start[], __vectors_end[];
|
||||
extern char __kuser_helper_start[], __kuser_helper_end[];
|
||||
int kuser_sz = __kuser_helper_end - __kuser_helper_start;
|
||||
|
||||
/*
|
||||
* Copy the vectors, stubs and kuser helpers (in entry-armv.S)
|
||||
* into the vector page, mapped at 0xffff0000, and ensure these
|
||||
* are visible to the instruction stream.
|
||||
*/
|
||||
memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start);
|
||||
memcpy((void *)vectors + 0x200, __stubs_start, __stubs_end - __stubs_start);
|
||||
memcpy((void *)vectors + 0x1000 - kuser_sz, __kuser_helper_start, kuser_sz);
|
||||
|
||||
/*
|
||||
* Copy signal return handlers into the vector page, and
|
||||
* set sigreturn to be a pointer to these.
|
||||
*/
|
||||
memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
|
||||
sizeof(sigreturn_codes));
|
||||
|
||||
flush_icache_range(vectors, vectors + PAGE_SIZE);
|
||||
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
|
||||
}
|
||||
183
arch/arm/kernel/vmlinux.lds.S
Normal file
183
arch/arm/kernel/vmlinux.lds.S
Normal file
@@ -0,0 +1,183 @@
|
||||
/* 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>
|
||||
*/
|
||||
|
||||
#include <asm-generic/vmlinux.lds.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/memory.h>
|
||||
|
||||
OUTPUT_ARCH(arm)
|
||||
ENTRY(stext)
|
||||
|
||||
#ifndef __ARMEB__
|
||||
jiffies = jiffies_64;
|
||||
#else
|
||||
jiffies = jiffies_64 + 4;
|
||||
#endif
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
. = XIP_VIRT_ADDR(CONFIG_XIP_PHYS_ADDR);
|
||||
#else
|
||||
. = PAGE_OFFSET + TEXT_OFFSET;
|
||||
#endif
|
||||
.init : { /* Init code and data */
|
||||
_stext = .;
|
||||
_sinittext = .;
|
||||
*(.init.text)
|
||||
_einittext = .;
|
||||
__proc_info_begin = .;
|
||||
*(.proc.info.init)
|
||||
__proc_info_end = .;
|
||||
__arch_info_begin = .;
|
||||
*(.arch.info.init)
|
||||
__arch_info_end = .;
|
||||
__tagtable_begin = .;
|
||||
*(.taglist.init)
|
||||
__tagtable_end = .;
|
||||
. = ALIGN(16);
|
||||
__setup_start = .;
|
||||
*(.init.setup)
|
||||
__setup_end = .;
|
||||
__early_begin = .;
|
||||
*(.early_param.init)
|
||||
__early_end = .;
|
||||
__initcall_start = .;
|
||||
INITCALLS
|
||||
__initcall_end = .;
|
||||
__con_initcall_start = .;
|
||||
*(.con_initcall.init)
|
||||
__con_initcall_end = .;
|
||||
__security_initcall_start = .;
|
||||
*(.security_initcall.init)
|
||||
__security_initcall_end = .;
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
. = ALIGN(32);
|
||||
__initramfs_start = .;
|
||||
usr/built-in.o(.init.ramfs)
|
||||
__initramfs_end = .;
|
||||
#endif
|
||||
. = ALIGN(64);
|
||||
__per_cpu_start = .;
|
||||
*(.data.percpu)
|
||||
__per_cpu_end = .;
|
||||
#ifndef CONFIG_XIP_KERNEL
|
||||
__init_begin = _stext;
|
||||
*(.init.data)
|
||||
. = ALIGN(4096);
|
||||
__init_end = .;
|
||||
#endif
|
||||
}
|
||||
|
||||
/DISCARD/ : { /* Exit code and data */
|
||||
*(.exit.text)
|
||||
*(.exit.data)
|
||||
*(.exitcall.exit)
|
||||
#ifndef CONFIG_MMU
|
||||
*(.fixup)
|
||||
*(__ex_table)
|
||||
#endif
|
||||
}
|
||||
|
||||
.text : { /* Real text segment */
|
||||
_text = .; /* Text and read-only data */
|
||||
*(.text)
|
||||
SCHED_TEXT
|
||||
LOCK_TEXT
|
||||
#ifdef CONFIG_MMU
|
||||
*(.fixup)
|
||||
#endif
|
||||
*(.gnu.warning)
|
||||
*(.rodata)
|
||||
*(.rodata.*)
|
||||
*(.glue_7)
|
||||
*(.glue_7t)
|
||||
*(.got) /* Global offset table */
|
||||
}
|
||||
|
||||
RODATA
|
||||
|
||||
_etext = .; /* End of text and rodata section */
|
||||
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
__data_loc = ALIGN(4); /* location in binary */
|
||||
. = PAGE_OFFSET + TEXT_OFFSET;
|
||||
#else
|
||||
. = ALIGN(THREAD_SIZE);
|
||||
__data_loc = .;
|
||||
#endif
|
||||
|
||||
.data : AT(__data_loc) {
|
||||
__data_start = .; /* address in memory */
|
||||
|
||||
/*
|
||||
* first, the init task union, aligned
|
||||
* to an 8192 byte boundary.
|
||||
*/
|
||||
*(.init.task)
|
||||
|
||||
#ifdef CONFIG_XIP_KERNEL
|
||||
. = ALIGN(4096);
|
||||
__init_begin = .;
|
||||
*(.init.data)
|
||||
. = ALIGN(4096);
|
||||
__init_end = .;
|
||||
#endif
|
||||
|
||||
. = ALIGN(4096);
|
||||
__nosave_begin = .;
|
||||
*(.data.nosave)
|
||||
. = ALIGN(4096);
|
||||
__nosave_end = .;
|
||||
|
||||
/*
|
||||
* then the cacheline aligned data
|
||||
*/
|
||||
. = ALIGN(32);
|
||||
*(.data.cacheline_aligned)
|
||||
|
||||
/*
|
||||
* The exception fixup table (might need resorting at runtime)
|
||||
*/
|
||||
. = ALIGN(32);
|
||||
__start___ex_table = .;
|
||||
#ifdef CONFIG_MMU
|
||||
*(__ex_table)
|
||||
#endif
|
||||
__stop___ex_table = .;
|
||||
|
||||
/*
|
||||
* and the usual data section
|
||||
*/
|
||||
*(.data)
|
||||
CONSTRUCTORS
|
||||
|
||||
_edata = .;
|
||||
}
|
||||
_edata_loc = __data_loc + SIZEOF(.data);
|
||||
|
||||
.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) }
|
||||
}
|
||||
|
||||
/*
|
||||
* These must never be empty
|
||||
* If you have to comment these two assert statements out, your
|
||||
* binutils is too old (for other reasons as well)
|
||||
*/
|
||||
ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")
|
||||
ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")
|
||||
179
arch/arm/kernel/xscale-cp0.c
Normal file
179
arch/arm/kernel/xscale-cp0.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* linux/arch/arm/kernel/xscale-cp0.c
|
||||
*
|
||||
* XScale DSP and iWMMXt coprocessor context switching and handling
|
||||
*
|
||||
* 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/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/init.h>
|
||||
#include <asm/thread_notify.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
static inline void dsp_save_state(u32 *state)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
"mrrc p0, 0, %0, %1, c0\n"
|
||||
: "=r" (state[0]), "=r" (state[1]));
|
||||
}
|
||||
|
||||
static inline void dsp_load_state(u32 *state)
|
||||
{
|
||||
__asm__ __volatile__ (
|
||||
"mcrr p0, 0, %0, %1, c0\n"
|
||||
: : "r" (state[0]), "r" (state[1]));
|
||||
}
|
||||
|
||||
static int dsp_do(struct notifier_block *self, unsigned long cmd, void *t)
|
||||
{
|
||||
struct thread_info *thread = t;
|
||||
|
||||
switch (cmd) {
|
||||
case THREAD_NOTIFY_FLUSH:
|
||||
thread->cpu_context.extra[0] = 0;
|
||||
thread->cpu_context.extra[1] = 0;
|
||||
break;
|
||||
|
||||
case THREAD_NOTIFY_SWITCH:
|
||||
dsp_save_state(current_thread_info()->cpu_context.extra);
|
||||
dsp_load_state(thread->cpu_context.extra);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block dsp_notifier_block = {
|
||||
.notifier_call = dsp_do,
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_IWMMXT
|
||||
static int iwmmxt_do(struct notifier_block *self, unsigned long cmd, void *t)
|
||||
{
|
||||
struct thread_info *thread = t;
|
||||
|
||||
switch (cmd) {
|
||||
case THREAD_NOTIFY_FLUSH:
|
||||
/*
|
||||
* flush_thread() zeroes thread->fpstate, so no need
|
||||
* to do anything here.
|
||||
*
|
||||
* FALLTHROUGH: Ensure we don't try to overwrite our newly
|
||||
* initialised state information on the first fault.
|
||||
*/
|
||||
|
||||
case THREAD_NOTIFY_RELEASE:
|
||||
iwmmxt_task_release(thread);
|
||||
break;
|
||||
|
||||
case THREAD_NOTIFY_SWITCH:
|
||||
iwmmxt_task_switch(thread);
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static struct notifier_block iwmmxt_notifier_block = {
|
||||
.notifier_call = iwmmxt_do,
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
static u32 __init xscale_cp_access_read(void)
|
||||
{
|
||||
u32 value;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"mrc p15, 0, %0, c15, c1, 0\n\t"
|
||||
: "=r" (value));
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void __init xscale_cp_access_write(u32 value)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
__asm__ __volatile__ (
|
||||
"mcr p15, 0, %1, c15, c1, 0\n\t"
|
||||
"mrc p15, 0, %0, c15, c1, 0\n\t"
|
||||
"mov %0, %0\n\t"
|
||||
"sub pc, pc, #4\n\t"
|
||||
: "=r" (temp) : "r" (value));
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect whether we have a MAC coprocessor (40 bit register) or an
|
||||
* iWMMXt coprocessor (64 bit registers) by loading 00000100:00000000
|
||||
* into a coprocessor register and reading it back, and checking
|
||||
* whether the upper word survived intact.
|
||||
*/
|
||||
static int __init cpu_has_iwmmxt(void)
|
||||
{
|
||||
u32 lo;
|
||||
u32 hi;
|
||||
|
||||
/*
|
||||
* This sequence is interpreted by the DSP coprocessor as:
|
||||
* mar acc0, %2, %3
|
||||
* mra %0, %1, acc0
|
||||
*
|
||||
* And by the iWMMXt coprocessor as:
|
||||
* tmcrr wR0, %2, %3
|
||||
* tmrrc %0, %1, wR0
|
||||
*/
|
||||
__asm__ __volatile__ (
|
||||
"mcrr p0, 0, %2, %3, c0\n"
|
||||
"mrrc p0, 0, %0, %1, c0\n"
|
||||
: "=r" (lo), "=r" (hi)
|
||||
: "r" (0), "r" (0x100));
|
||||
|
||||
return !!hi;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If we detect that the CPU has iWMMXt (and CONFIG_IWMMXT=y), we
|
||||
* disable CP0/CP1 on boot, and let call_fpe() and the iWMMXt lazy
|
||||
* switch code handle iWMMXt context switching. If on the other
|
||||
* hand the CPU has a DSP coprocessor, we keep access to CP0 enabled
|
||||
* all the time, and save/restore acc0 on context switch in non-lazy
|
||||
* fashion.
|
||||
*/
|
||||
static int __init xscale_cp0_init(void)
|
||||
{
|
||||
u32 cp_access;
|
||||
|
||||
cp_access = xscale_cp_access_read() & ~3;
|
||||
xscale_cp_access_write(cp_access | 1);
|
||||
|
||||
if (cpu_has_iwmmxt()) {
|
||||
#ifndef CONFIG_IWMMXT
|
||||
printk(KERN_WARNING "CAUTION: XScale iWMMXt coprocessor "
|
||||
"detected, but kernel support is missing.\n");
|
||||
#else
|
||||
printk(KERN_INFO "XScale iWMMXt coprocessor detected.\n");
|
||||
elf_hwcap |= HWCAP_IWMMXT;
|
||||
thread_register_notifier(&iwmmxt_notifier_block);
|
||||
#endif
|
||||
} else {
|
||||
printk(KERN_INFO "XScale DSP coprocessor detected.\n");
|
||||
thread_register_notifier(&dsp_notifier_block);
|
||||
cp_access |= 1;
|
||||
}
|
||||
|
||||
xscale_cp_access_write(cp_access);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(xscale_cp0_init);
|
||||
Reference in New Issue
Block a user