Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

38
arch/arm/kernel/Makefile Normal file
View File

@@ -0,0 +1,38 @@
#
# Makefile for the linux kernel.
#
AFLAGS_head.o := -DTEXTADDR=$(TEXTADDR) -DDATAADDR=$(DATAADDR)
# Object file lists.
obj-y := arch.o compat.o dma.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_APM) += apm.o
obj-$(CONFIG_ARCH_ACORN) += ecard.o
obj-$(CONFIG_FOOTBRIDGE) += isa.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
obj-$(CONFIG_SMP) += smp.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.o
obj-$(CONFIG_DEBUG_LL) += debug.o
extra-y := $(head-y) init_task.o vmlinux.lds
# Spell out some dependencies that aren't automatically figured out
$(obj)/entry-armv.o: $(obj)/entry-header.S include/asm-arm/constants.h
$(obj)/entry-common.o: $(obj)/entry-header.S include/asm-arm/constants.h \
$(obj)/calls.S

610
arch/arm/kernel/apm.c Normal file
View File

@@ -0,0 +1,610 @@
/*
* bios-less APM driver for ARM Linux
* Jamey Hicks <jamey@crl.dec.com>
* adapted from the APM BIOS driver for Linux by Stephen Rothwell (sfr@linuxcare.com)
*
* APM 1.2 Reference:
* Intel Corporation, Microsoft Corporation. Advanced Power Management
* (APM) BIOS Interface Specification, Revision 1.2, February 1996.
*
* [This document is available from Microsoft at:
* http://www.microsoft.com/hwdev/busbios/amp_12.htm]
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/poll.h>
#include <linux/timer.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/apm_bios.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/device.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/init.h>
#include <linux/completion.h>
#include <asm/apm.h> /* apm_power_info */
#include <asm/system.h>
/*
* The apm_bios device is one of the misc char devices.
* This is its minor number.
*/
#define APM_MINOR_DEV 134
/*
* See Documentation/Config.help for the configuration options.
*
* Various options can be changed at boot time as follows:
* (We allow underscores for compatibility with the modules code)
* apm=on/off enable/disable APM
*/
/*
* Maximum number of events stored
*/
#define APM_MAX_EVENTS 16
struct apm_queue {
unsigned int event_head;
unsigned int event_tail;
apm_event_t events[APM_MAX_EVENTS];
};
/*
* The per-file APM data
*/
struct apm_user {
struct list_head list;
unsigned int suser: 1;
unsigned int writer: 1;
unsigned int reader: 1;
int suspend_result;
unsigned int suspend_state;
#define SUSPEND_NONE 0 /* no suspend pending */
#define SUSPEND_PENDING 1 /* suspend pending read */
#define SUSPEND_READ 2 /* suspend read, pending ack */
#define SUSPEND_ACKED 3 /* suspend acked */
#define SUSPEND_DONE 4 /* suspend completed */
struct apm_queue queue;
};
/*
* Local variables
*/
static int suspends_pending;
static int apm_disabled;
static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
/*
* This is a list of everyone who has opened /dev/apm_bios
*/
static DECLARE_RWSEM(user_list_lock);
static LIST_HEAD(apm_user_list);
/*
* kapmd info. kapmd provides us a process context to handle
* "APM" events within - specifically necessary if we're going
* to be suspending the system.
*/
static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait);
static DECLARE_COMPLETION(kapmd_exit);
static DEFINE_SPINLOCK(kapmd_queue_lock);
static struct apm_queue kapmd_queue;
static const char driver_version[] = "1.13"; /* no spaces */
/*
* Compatibility cruft until the IPAQ people move over to the new
* interface.
*/
static void __apm_get_power_status(struct apm_power_info *info)
{
}
/*
* This allows machines to provide their own "apm get power status" function.
*/
void (*apm_get_power_status)(struct apm_power_info *) = __apm_get_power_status;
EXPORT_SYMBOL(apm_get_power_status);
/*
* APM event queue management.
*/
static inline int queue_empty(struct apm_queue *q)
{
return q->event_head == q->event_tail;
}
static inline apm_event_t queue_get_event(struct apm_queue *q)
{
q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
return q->events[q->event_tail];
}
static void queue_add_event(struct apm_queue *q, apm_event_t event)
{
q->event_head = (q->event_head + 1) % APM_MAX_EVENTS;
if (q->event_head == q->event_tail) {
static int notified;
if (notified++ == 0)
printk(KERN_ERR "apm: an event queue overflowed\n");
q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
}
q->events[q->event_head] = event;
}
static void queue_event_one_user(struct apm_user *as, apm_event_t event)
{
if (as->suser && as->writer) {
switch (event) {
case APM_SYS_SUSPEND:
case APM_USER_SUSPEND:
/*
* If this user already has a suspend pending,
* don't queue another one.
*/
if (as->suspend_state != SUSPEND_NONE)
return;
as->suspend_state = SUSPEND_PENDING;
suspends_pending++;
break;
}
}
queue_add_event(&as->queue, event);
}
static void queue_event(apm_event_t event, struct apm_user *sender)
{
struct apm_user *as;
down_read(&user_list_lock);
list_for_each_entry(as, &apm_user_list, list) {
if (as != sender && as->reader)
queue_event_one_user(as, event);
}
up_read(&user_list_lock);
wake_up_interruptible(&apm_waitqueue);
}
static void apm_suspend(void)
{
struct apm_user *as;
int err = pm_suspend(PM_SUSPEND_MEM);
/*
* Anyone on the APM queues will think we're still suspended.
* Send a message so everyone knows we're now awake again.
*/
queue_event(APM_NORMAL_RESUME, NULL);
/*
* Finally, wake up anyone who is sleeping on the suspend.
*/
down_read(&user_list_lock);
list_for_each_entry(as, &apm_user_list, list) {
as->suspend_result = err;
as->suspend_state = SUSPEND_DONE;
}
up_read(&user_list_lock);
wake_up(&apm_suspend_waitqueue);
}
static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
{
struct apm_user *as = fp->private_data;
apm_event_t event;
int i = count, ret = 0;
if (count < sizeof(apm_event_t))
return -EINVAL;
if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK)
return -EAGAIN;
wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue));
while ((i >= sizeof(event)) && !queue_empty(&as->queue)) {
event = queue_get_event(&as->queue);
ret = -EFAULT;
if (copy_to_user(buf, &event, sizeof(event)))
break;
if (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND)
as->suspend_state = SUSPEND_READ;
buf += sizeof(event);
i -= sizeof(event);
}
if (i < count)
ret = count - i;
return ret;
}
static unsigned int apm_poll(struct file *fp, poll_table * wait)
{
struct apm_user *as = fp->private_data;
poll_wait(fp, &apm_waitqueue, wait);
return queue_empty(&as->queue) ? 0 : POLLIN | POLLRDNORM;
}
/*
* apm_ioctl - handle APM ioctl
*
* APM_IOC_SUSPEND
* This IOCTL is overloaded, and performs two functions. It is used to:
* - initiate a suspend
* - acknowledge a suspend read from /dev/apm_bios.
* Only when everyone who has opened /dev/apm_bios with write permission
* has acknowledge does the actual suspend happen.
*/
static int
apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg)
{
struct apm_user *as = filp->private_data;
unsigned long flags;
int err = -EINVAL;
if (!as->suser || !as->writer)
return -EPERM;
switch (cmd) {
case APM_IOC_SUSPEND:
as->suspend_result = -EINTR;
if (as->suspend_state == SUSPEND_READ) {
/*
* If we read a suspend command from /dev/apm_bios,
* then the corresponding APM_IOC_SUSPEND ioctl is
* interpreted as an acknowledge.
*/
as->suspend_state = SUSPEND_ACKED;
suspends_pending--;
} else {
/*
* Otherwise it is a request to suspend the system.
* Queue an event for all readers, and expect an
* acknowledge from all writers who haven't already
* acknowledged.
*/
queue_event(APM_USER_SUSPEND, as);
}
/*
* If there are no further acknowledges required, suspend
* the system.
*/
if (suspends_pending == 0)
apm_suspend();
/*
* Wait for the suspend/resume to complete. If there are
* pending acknowledges, we wait here for them.
*
* Note that we need to ensure that the PM subsystem does
* not kick us out of the wait when it suspends the threads.
*/
flags = current->flags;
current->flags |= PF_NOFREEZE;
/*
* Note: do not allow a thread which is acking the suspend
* to escape until the resume is complete.
*/
if (as->suspend_state == SUSPEND_ACKED)
wait_event(apm_suspend_waitqueue,
as->suspend_state == SUSPEND_DONE);
else
wait_event_interruptible(apm_suspend_waitqueue,
as->suspend_state == SUSPEND_DONE);
current->flags = flags;
err = as->suspend_result;
as->suspend_state = SUSPEND_NONE;
break;
}
return err;
}
static int apm_release(struct inode * inode, struct file * filp)
{
struct apm_user *as = filp->private_data;
filp->private_data = NULL;
down_write(&user_list_lock);
list_del(&as->list);
up_write(&user_list_lock);
/*
* We are now unhooked from the chain. As far as new
* events are concerned, we no longer exist. However, we
* need to balance suspends_pending, which means the
* possibility of sleeping.
*/
if (as->suspend_state != SUSPEND_NONE) {
suspends_pending -= 1;
if (suspends_pending == 0)
apm_suspend();
}
kfree(as);
return 0;
}
static int apm_open(struct inode * inode, struct file * filp)
{
struct apm_user *as;
as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
if (as) {
memset(as, 0, sizeof(*as));
/*
* XXX - this is a tiny bit broken, when we consider BSD
* process accounting. If the device is opened by root, we
* instantly flag that we used superuser privs. Who knows,
* we might close the device immediately without doing a
* privileged operation -- cevans
*/
as->suser = capable(CAP_SYS_ADMIN);
as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
down_write(&user_list_lock);
list_add(&as->list, &apm_user_list);
up_write(&user_list_lock);
filp->private_data = as;
}
return as ? 0 : -ENOMEM;
}
static struct file_operations apm_bios_fops = {
.owner = THIS_MODULE,
.read = apm_read,
.poll = apm_poll,
.ioctl = apm_ioctl,
.open = apm_open,
.release = apm_release,
};
static struct miscdevice apm_device = {
.minor = APM_MINOR_DEV,
.name = "apm_bios",
.fops = &apm_bios_fops
};
#ifdef CONFIG_PROC_FS
/*
* Arguments, with symbols from linux/apm_bios.h.
*
* 0) Linux driver version (this will change if format changes)
* 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
* 2) APM flags from APM Installation Check (0x00):
* bit 0: APM_16_BIT_SUPPORT
* bit 1: APM_32_BIT_SUPPORT
* bit 2: APM_IDLE_SLOWS_CLOCK
* bit 3: APM_BIOS_DISABLED
* bit 4: APM_BIOS_DISENGAGED
* 3) AC line status
* 0x00: Off-line
* 0x01: On-line
* 0x02: On backup power (BIOS >= 1.1 only)
* 0xff: Unknown
* 4) Battery status
* 0x00: High
* 0x01: Low
* 0x02: Critical
* 0x03: Charging
* 0x04: Selected battery not present (BIOS >= 1.2 only)
* 0xff: Unknown
* 5) Battery flag
* bit 0: High
* bit 1: Low
* bit 2: Critical
* bit 3: Charging
* bit 7: No system battery
* 0xff: Unknown
* 6) Remaining battery life (percentage of charge):
* 0-100: valid
* -1: Unknown
* 7) Remaining battery life (time units):
* Number of remaining minutes or seconds
* -1: Unknown
* 8) min = minutes; sec = seconds
*/
static int apm_get_info(char *buf, char **start, off_t fpos, int length)
{
struct apm_power_info info;
char *units;
int ret;
info.ac_line_status = 0xff;
info.battery_status = 0xff;
info.battery_flag = 0xff;
info.battery_life = -1;
info.time = -1;
info.units = -1;
if (apm_get_power_status)
apm_get_power_status(&info);
switch (info.units) {
default: units = "?"; break;
case 0: units = "min"; break;
case 1: units = "sec"; break;
}
ret = sprintf(buf, "%s 1.2 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
driver_version, APM_32_BIT_SUPPORT,
info.ac_line_status, info.battery_status,
info.battery_flag, info.battery_life,
info.time, units);
return ret;
}
#endif
static int kapmd(void *arg)
{
daemonize("kapmd");
current->flags |= PF_NOFREEZE;
do {
apm_event_t event;
wait_event_interruptible(kapmd_wait,
!queue_empty(&kapmd_queue) || !pm_active);
if (!pm_active)
break;
spin_lock_irq(&kapmd_queue_lock);
event = 0;
if (!queue_empty(&kapmd_queue))
event = queue_get_event(&kapmd_queue);
spin_unlock_irq(&kapmd_queue_lock);
switch (event) {
case 0:
break;
case APM_LOW_BATTERY:
case APM_POWER_STATUS_CHANGE:
queue_event(event, NULL);
break;
case APM_USER_SUSPEND:
case APM_SYS_SUSPEND:
queue_event(event, NULL);
if (suspends_pending == 0)
apm_suspend();
break;
case APM_CRITICAL_SUSPEND:
apm_suspend();
break;
}
} while (1);
complete_and_exit(&kapmd_exit, 0);
}
static int __init apm_init(void)
{
int ret;
if (apm_disabled) {
printk(KERN_NOTICE "apm: disabled on user request.\n");
return -ENODEV;
}
if (PM_IS_ACTIVE()) {
printk(KERN_NOTICE "apm: overridden by ACPI.\n");
return -EINVAL;
}
pm_active = 1;
ret = kernel_thread(kapmd, NULL, CLONE_KERNEL);
if (ret < 0) {
pm_active = 0;
return ret;
}
#ifdef CONFIG_PROC_FS
create_proc_info_entry("apm", 0, NULL, apm_get_info);
#endif
ret = misc_register(&apm_device);
if (ret != 0) {
remove_proc_entry("apm", NULL);
pm_active = 0;
wake_up(&kapmd_wait);
wait_for_completion(&kapmd_exit);
}
return ret;
}
static void __exit apm_exit(void)
{
misc_deregister(&apm_device);
remove_proc_entry("apm", NULL);
pm_active = 0;
wake_up(&kapmd_wait);
wait_for_completion(&kapmd_exit);
}
module_init(apm_init);
module_exit(apm_exit);
MODULE_AUTHOR("Stephen Rothwell");
MODULE_DESCRIPTION("Advanced Power Management");
MODULE_LICENSE("GPL");
#ifndef MODULE
static int __init apm_setup(char *str)
{
while ((str != NULL) && (*str != '\0')) {
if (strncmp(str, "off", 3) == 0)
apm_disabled = 1;
if (strncmp(str, "on", 2) == 0)
apm_disabled = 0;
str = strchr(str, ',');
if (str != NULL)
str += strspn(str, ", \t");
}
return 1;
}
__setup("apm=", apm_setup);
#endif
/**
* apm_queue_event - queue an APM event for kapmd
* @event: APM event
*
* Queue an APM event for kapmd to process and ultimately take the
* appropriate action. Only a subset of events are handled:
* %APM_LOW_BATTERY
* %APM_POWER_STATUS_CHANGE
* %APM_USER_SUSPEND
* %APM_SYS_SUSPEND
* %APM_CRITICAL_SUSPEND
*/
void apm_queue_event(apm_event_t event)
{
unsigned long flags;
spin_lock_irqsave(&kapmd_queue_lock, flags);
queue_add_event(&kapmd_queue, event);
spin_unlock_irqrestore(&kapmd_queue_lock, flags);
wake_up_interruptible(&kapmd_wait);
}
EXPORT_SYMBOL(apm_queue_event);

46
arch/arm/kernel/arch.c Normal file
View File

@@ -0,0 +1,46 @@
/*
* linux/arch/arm/kernel/arch.c
*
* Architecture specific fixups.
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/types.h>
#include <asm/elf.h>
#include <asm/page.h>
#include <asm/setup.h>
#include <asm/mach/arch.h>
unsigned int vram_size;
#ifdef CONFIG_ARCH_ACORN
unsigned int memc_ctrl_reg;
unsigned int number_mfm_drives;
static int __init parse_tag_acorn(const struct tag *tag)
{
memc_ctrl_reg = tag->u.acorn.memc_control_reg;
number_mfm_drives = tag->u.acorn.adfsdrives;
switch (tag->u.acorn.vram_pages) {
case 512:
vram_size += PAGE_SIZE * 256;
case 256:
vram_size += PAGE_SIZE * 256;
default:
break;
}
#if 0
if (vram_size) {
desc->video_start = 0x02000000;
desc->video_end = 0x02000000 + vram_size;
}
#endif
return 0;
}
__tagtable(ATAG_ACORN, parse_tag_acorn);
#endif

175
arch/arm/kernel/armksyms.c Normal file
View File

@@ -0,0 +1,175 @@
/*
* 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/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 __udivdi3(void);
extern void __umoddi3(void);
extern void __udivmoddi4(void);
extern void __udivsi3(void);
extern void __umodsi3(void);
extern void __do_div64(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_SYMBOL_ALIAS(sym,orig) \
const struct kernel_symbol __ksymtab_##sym \
__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(strcpy);
EXPORT_SYMBOL(strncpy);
EXPORT_SYMBOL(strcat);
EXPORT_SYMBOL(strncat);
EXPORT_SYMBOL(strcmp);
EXPORT_SYMBOL(strncmp);
EXPORT_SYMBOL(strchr);
EXPORT_SYMBOL(strlen);
EXPORT_SYMBOL(strnlen);
EXPORT_SYMBOL(strpbrk);
EXPORT_SYMBOL(strrchr);
EXPORT_SYMBOL(strstr);
EXPORT_SYMBOL(memset);
EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memmove);
EXPORT_SYMBOL(memcmp);
EXPORT_SYMBOL(memscan);
EXPORT_SYMBOL(memchr);
EXPORT_SYMBOL(__memzero);
/* user mem (segment) */
EXPORT_SYMBOL(__arch_copy_from_user);
EXPORT_SYMBOL(__arch_copy_to_user);
EXPORT_SYMBOL(__arch_clear_user);
EXPORT_SYMBOL(__arch_strnlen_user);
EXPORT_SYMBOL(__arch_strncpy_from_user);
EXPORT_SYMBOL(__get_user_1);
EXPORT_SYMBOL(__get_user_2);
EXPORT_SYMBOL(__get_user_4);
EXPORT_SYMBOL(__get_user_8);
EXPORT_SYMBOL(__put_user_1);
EXPORT_SYMBOL(__put_user_2);
EXPORT_SYMBOL(__put_user_4);
EXPORT_SYMBOL(__put_user_8);
/* gcc lib functions */
EXPORT_SYMBOL(__ashldi3);
EXPORT_SYMBOL(__ashrdi3);
EXPORT_SYMBOL(__divsi3);
EXPORT_SYMBOL(__lshrdi3);
EXPORT_SYMBOL(__modsi3);
EXPORT_SYMBOL(__muldi3);
EXPORT_SYMBOL(__ucmpdi2);
EXPORT_SYMBOL(__udivdi3);
EXPORT_SYMBOL(__umoddi3);
EXPORT_SYMBOL(__udivmoddi4);
EXPORT_SYMBOL(__udivsi3);
EXPORT_SYMBOL(__umodsi3);
EXPORT_SYMBOL(__do_div64);
/* 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
/* syscalls */
EXPORT_SYMBOL(sys_write);
EXPORT_SYMBOL(sys_read);
EXPORT_SYMBOL(sys_lseek);
EXPORT_SYMBOL(sys_open);
EXPORT_SYMBOL(sys_exit);
EXPORT_SYMBOL(sys_wait4);

91
arch/arm/kernel/arthur.c Normal file
View File

@@ -0,0 +1,91 @@
/*
* 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 <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);

View File

@@ -0,0 +1,83 @@
/*
* 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>
/*
* 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 2.95.1, 2.95.2: ignores register clobber list in asm().
* 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__ < 2 || \
(__GNUC__ == 2 && __GNUC_MINOR__ < 95) || \
(__GNUC__ == 2 && __GNUC_MINOR__ == 95 && __GNUC_PATCHLEVEL__ != 0 && \
__GNUC_PATCHLEVEL__ < 3) || \
(__GNUC__ == 3 && __GNUC_MINOR__ < 3)
#error Your compiler is too buggy; it is known to miscompile kernels.
#error Known good compilers: 2.95.3, 2.95.4, 2.96, 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));
DEFINE(TI_IWMMXT_STATE, (offsetof(struct thread_info, fpstate)+4)&~7);
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);
DEFINE(VIRT_OFFSET, PAGE_OFFSET);
BLANK();
DEFINE(SYS_ERROR0, 0x9f0000);
BLANK();
DEFINE(SIZEOF_MACHINE_DESC, sizeof(struct machine_desc));
return 0;
}

699
arch/arm/kernel/bios32.c Normal file
View File

@@ -0,0 +1,699 @@
/*
* linux/arch/arm/kernel/bios32.c
*
* PCI bios-type initialisation for PCI machines
*
* Bits taken from various places.
*/
#include <linux/config.h>
#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)
{
unsigned long 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) {
#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
case PCI_CLASS_BRIDGE_ISA:
case PCI_CLASS_BRIDGE_EISA:
/*
* If this device is an ISA bridge, set isa_bridge
* to point at this device. We will then go looking
* for things like keyboard, etc.
*/
isa_bridge = dev;
break;
#endif
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;
}
#ifdef CONFIG_HOTPLUG
EXPORT_SYMBOL(pcibios_fixup_bus);
EXPORT_SYMBOL(pcibios_resource_to_bus);
#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 = kmalloc(sizeof(struct pci_sys_data), GFP_KERNEL);
if (!sys)
panic("PCI: unable to allocate sys data!");
memset(sys, 0, sizeof(struct pci_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,
unsigned long size, unsigned long align)
{
unsigned long 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_flags |= VM_SHM | VM_LOCKED | VM_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;
}

335
arch/arm/kernel/calls.S Normal file
View File

@@ -0,0 +1,335 @@
/*
* 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 twice in entry-common.S
*/
#ifndef NR_syscalls
#define NR_syscalls 320
#else
__syscall_start:
/* 0 */ .long sys_restart_syscall
.long sys_exit
.long sys_fork_wrapper
.long sys_read
.long sys_write
/* 5 */ .long sys_open
.long sys_close
.long sys_ni_syscall /* was sys_waitpid */
.long sys_creat
.long sys_link
/* 10 */ .long sys_unlink
.long sys_execve_wrapper
.long sys_chdir
.long sys_time /* used by libc4 */
.long sys_mknod
/* 15 */ .long sys_chmod
.long sys_lchown16
.long sys_ni_syscall /* was sys_break */
.long sys_ni_syscall /* was sys_stat */
.long sys_lseek
/* 20 */ .long sys_getpid
.long sys_mount
.long sys_oldumount /* used by libc4 */
.long sys_setuid16
.long sys_getuid16
/* 25 */ .long sys_stime
.long sys_ptrace
.long sys_alarm /* used by libc4 */
.long sys_ni_syscall /* was sys_fstat */
.long sys_pause
/* 30 */ .long sys_utime /* used by libc4 */
.long sys_ni_syscall /* was sys_stty */
.long sys_ni_syscall /* was sys_getty */
.long sys_access
.long sys_nice
/* 35 */ .long sys_ni_syscall /* was sys_ftime */
.long sys_sync
.long sys_kill
.long sys_rename
.long sys_mkdir
/* 40 */ .long sys_rmdir
.long sys_dup
.long sys_pipe
.long sys_times
.long sys_ni_syscall /* was sys_prof */
/* 45 */ .long sys_brk
.long sys_setgid16
.long sys_getgid16
.long sys_ni_syscall /* was sys_signal */
.long sys_geteuid16
/* 50 */ .long sys_getegid16
.long sys_acct
.long sys_umount
.long sys_ni_syscall /* was sys_lock */
.long sys_ioctl
/* 55 */ .long sys_fcntl
.long sys_ni_syscall /* was sys_mpx */
.long sys_setpgid
.long sys_ni_syscall /* was sys_ulimit */
.long sys_ni_syscall /* was sys_olduname */
/* 60 */ .long sys_umask
.long sys_chroot
.long sys_ustat
.long sys_dup2
.long sys_getppid
/* 65 */ .long sys_getpgrp
.long sys_setsid
.long sys_sigaction
.long sys_ni_syscall /* was sys_sgetmask */
.long sys_ni_syscall /* was sys_ssetmask */
/* 70 */ .long sys_setreuid16
.long sys_setregid16
.long sys_sigsuspend_wrapper
.long sys_sigpending
.long sys_sethostname
/* 75 */ .long sys_setrlimit
.long sys_old_getrlimit /* used by libc4 */
.long sys_getrusage
.long sys_gettimeofday
.long sys_settimeofday
/* 80 */ .long sys_getgroups16
.long sys_setgroups16
.long old_select /* used by libc4 */
.long sys_symlink
.long sys_ni_syscall /* was sys_lstat */
/* 85 */ .long sys_readlink
.long sys_uselib
.long sys_swapon
.long sys_reboot
.long old_readdir /* used by libc4 */
/* 90 */ .long old_mmap /* used by libc4 */
.long sys_munmap
.long sys_truncate
.long sys_ftruncate
.long sys_fchmod
/* 95 */ .long sys_fchown16
.long sys_getpriority
.long sys_setpriority
.long sys_ni_syscall /* was sys_profil */
.long sys_statfs
/* 100 */ .long sys_fstatfs
.long sys_ni_syscall
.long sys_socketcall
.long sys_syslog
.long sys_setitimer
/* 105 */ .long sys_getitimer
.long sys_newstat
.long sys_newlstat
.long sys_newfstat
.long sys_ni_syscall /* was sys_uname */
/* 110 */ .long sys_ni_syscall /* was sys_iopl */
.long sys_vhangup
.long sys_ni_syscall
.long sys_syscall /* call a syscall */
.long sys_wait4
/* 115 */ .long sys_swapoff
.long sys_sysinfo
.long sys_ipc
.long sys_fsync
.long sys_sigreturn_wrapper
/* 120 */ .long sys_clone_wrapper
.long sys_setdomainname
.long sys_newuname
.long sys_ni_syscall
.long sys_adjtimex
/* 125 */ .long sys_mprotect
.long sys_sigprocmask
.long sys_ni_syscall /* was sys_create_module */
.long sys_init_module
.long sys_delete_module
/* 130 */ .long sys_ni_syscall /* was sys_get_kernel_syms */
.long sys_quotactl
.long sys_getpgid
.long sys_fchdir
.long sys_bdflush
/* 135 */ .long sys_sysfs
.long sys_personality
.long sys_ni_syscall /* .long _sys_afs_syscall */
.long sys_setfsuid16
.long sys_setfsgid16
/* 140 */ .long sys_llseek
.long sys_getdents
.long sys_select
.long sys_flock
.long sys_msync
/* 145 */ .long sys_readv
.long sys_writev
.long sys_getsid
.long sys_fdatasync
.long sys_sysctl
/* 150 */ .long sys_mlock
.long sys_munlock
.long sys_mlockall
.long sys_munlockall
.long sys_sched_setparam
/* 155 */ .long sys_sched_getparam
.long sys_sched_setscheduler
.long sys_sched_getscheduler
.long sys_sched_yield
.long sys_sched_get_priority_max
/* 160 */ .long sys_sched_get_priority_min
.long sys_sched_rr_get_interval
.long sys_nanosleep
.long sys_arm_mremap
.long sys_setresuid16
/* 165 */ .long sys_getresuid16
.long sys_ni_syscall
.long sys_ni_syscall /* was sys_query_module */
.long sys_poll
.long sys_nfsservctl
/* 170 */ .long sys_setresgid16
.long sys_getresgid16
.long sys_prctl
.long sys_rt_sigreturn_wrapper
.long sys_rt_sigaction
/* 175 */ .long sys_rt_sigprocmask
.long sys_rt_sigpending
.long sys_rt_sigtimedwait
.long sys_rt_sigqueueinfo
.long sys_rt_sigsuspend_wrapper
/* 180 */ .long sys_pread64
.long sys_pwrite64
.long sys_chown16
.long sys_getcwd
.long sys_capget
/* 185 */ .long sys_capset
.long sys_sigaltstack_wrapper
.long sys_sendfile
.long sys_ni_syscall
.long sys_ni_syscall
/* 190 */ .long sys_vfork_wrapper
.long sys_getrlimit
.long sys_mmap2
.long sys_truncate64
.long sys_ftruncate64
/* 195 */ .long sys_stat64
.long sys_lstat64
.long sys_fstat64
.long sys_lchown
.long sys_getuid
/* 200 */ .long sys_getgid
.long sys_geteuid
.long sys_getegid
.long sys_setreuid
.long sys_setregid
/* 205 */ .long sys_getgroups
.long sys_setgroups
.long sys_fchown
.long sys_setresuid
.long sys_getresuid
/* 210 */ .long sys_setresgid
.long sys_getresgid
.long sys_chown
.long sys_setuid
.long sys_setgid
/* 215 */ .long sys_setfsuid
.long sys_setfsgid
.long sys_getdents64
.long sys_pivot_root
.long sys_mincore
/* 220 */ .long sys_madvise
.long sys_fcntl64
.long sys_ni_syscall /* TUX */
.long sys_ni_syscall
.long sys_gettid
/* 225 */ .long sys_readahead
.long sys_setxattr
.long sys_lsetxattr
.long sys_fsetxattr
.long sys_getxattr
/* 230 */ .long sys_lgetxattr
.long sys_fgetxattr
.long sys_listxattr
.long sys_llistxattr
.long sys_flistxattr
/* 235 */ .long sys_removexattr
.long sys_lremovexattr
.long sys_fremovexattr
.long sys_tkill
.long sys_sendfile64
/* 240 */ .long sys_futex_wrapper
.long sys_sched_setaffinity
.long sys_sched_getaffinity
.long sys_io_setup
.long sys_io_destroy
/* 245 */ .long sys_io_getevents
.long sys_io_submit
.long sys_io_cancel
.long sys_exit_group
.long sys_lookup_dcookie
/* 250 */ .long sys_epoll_create
.long sys_epoll_ctl
.long sys_epoll_wait
.long sys_remap_file_pages
.long sys_ni_syscall /* sys_set_thread_area */
/* 255 */ .long sys_ni_syscall /* sys_get_thread_area */
.long sys_set_tid_address
.long sys_timer_create
.long sys_timer_settime
.long sys_timer_gettime
/* 260 */ .long sys_timer_getoverrun
.long sys_timer_delete
.long sys_clock_settime
.long sys_clock_gettime
.long sys_clock_getres
/* 265 */ .long sys_clock_nanosleep
.long sys_statfs64
.long sys_fstatfs64
.long sys_tgkill
.long sys_utimes
/* 270 */ .long sys_fadvise64_64
.long sys_pciconfig_iobase
.long sys_pciconfig_read
.long sys_pciconfig_write
.long sys_mq_open
/* 275 */ .long sys_mq_unlink
.long sys_mq_timedsend
.long sys_mq_timedreceive
.long sys_mq_notify
.long sys_mq_getsetattr
/* 280 */ .long sys_waitid
.long sys_socket
.long sys_bind
.long sys_connect
.long sys_listen
/* 285 */ .long sys_accept
.long sys_getsockname
.long sys_getpeername
.long sys_socketpair
.long sys_send
/* 290 */ .long sys_sendto
.long sys_recv
.long sys_recvfrom
.long sys_shutdown
.long sys_setsockopt
/* 295 */ .long sys_getsockopt
.long sys_sendmsg
.long sys_recvmsg
.long sys_semop
.long sys_semget
/* 300 */ .long sys_semctl
.long sys_msgsnd
.long sys_msgrcv
.long sys_msgget
.long sys_msgctl
/* 305 */ .long sys_shmat
.long sys_shmdt
.long sys_shmget
.long sys_shmctl
.long sys_add_key
/* 310 */ .long sys_request_key
.long sys_keyctl
.long sys_semtimedop
__syscall_end:
.rept NR_syscalls - (__syscall_end - __syscall_start) / 4
.long sys_ni_syscall
.endr
#endif

225
arch/arm/kernel/compat.c Normal file
View File

@@ -0,0 +1,225 @@
/*
* 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/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/init.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/page.h>
#include <asm/mach/arch.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, &params->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;
}

106
arch/arm/kernel/debug.S Normal file
View File

@@ -0,0 +1,106 @@
/*
* 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/config.h>
#include <linux/linkage.h>
#include <asm/hardware.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
.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
#else
#include <asm/arch/debug-macro.S>
#endif
/*
* 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

207
arch/arm/kernel/dma-isa.c Normal file
View File

@@ -0,0 +1,207 @@
/*
* 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/pci.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, direction;
mode = channel & 3;
switch (dma->dma_mode & DMA_MODE_MASK) {
case DMA_MODE_READ:
mode |= ISA_DMA_MODE_READ;
direction = PCI_DMA_FROMDEVICE;
break;
case DMA_MODE_WRITE:
mode |= ISA_DMA_MODE_WRITE;
direction = PCI_DMA_TODEVICE;
break;
case DMA_MODE_CASCADE:
mode |= ISA_DMA_MODE_CASCADE;
direction = PCI_DMA_BIDIRECTIONAL;
break;
default:
direction = PCI_DMA_NONE;
break;
}
if (!dma->using_sg) {
/*
* Cope with ISA-style drivers which expect cache
* coherence.
*/
dma->buf.dma_address = pci_map_single(NULL,
dma->buf.__address, dma->buf.length,
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[] = {
{ "dma1", 0x0000, 0x000f },
{ "dma low page", 0x0080, 0x008f },
{ "dma2", 0x00c0, 0x00df },
{ "dma high page", 0x0480, 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);
}
}

302
arch/arm/kernel/dma.c Normal file
View File

@@ -0,0 +1,302 @@
/*
* 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/slab.h>
#include <linux/mman.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);
#if MAX_DMA_CHANNELS > 0
static dma_t dma_chan[MAX_DMA_CHANNELS];
/*
* Get dma list for /proc/dma
*/
int get_dma_list(char *buf)
{
dma_t *dma;
char *p = buf;
int i;
for (i = 0, dma = dma_chan; i < MAX_DMA_CHANNELS; i++, dma++)
if (dma->lock)
p += sprintf(p, "%2d: %14s %s\n", i,
dma->d_ops->type, dma->device_id);
return p - buf;
}
/*
* Request DMA channel
*
* On certain platforms, we have to allocate an interrupt as well...
*/
int request_dma(dmach_t channel, const char *device_id)
{
dma_t *dma = dma_chan + channel;
int ret;
if (channel >= MAX_DMA_CHANNELS || !dma->d_ops)
goto bad_dma;
if (xchg(&dma->lock, 1) != 0)
goto busy;
dma->device_id = device_id;
dma->active = 0;
dma->invalid = 1;
ret = 0;
if (dma->d_ops->request)
ret = dma->d_ops->request(channel, dma);
if (ret)
xchg(&dma->lock, 0);
return ret;
bad_dma:
printk(KERN_ERR "dma: trying to allocate DMA%d\n", channel);
return -EINVAL;
busy:
return -EBUSY;
}
/*
* Free DMA channel
*
* On certain platforms, we have to free interrupt as well...
*/
void free_dma(dmach_t channel)
{
dma_t *dma = dma_chan + channel;
if (channel >= MAX_DMA_CHANNELS || !dma->d_ops)
goto bad_dma;
if (dma->active) {
printk(KERN_ERR "dma%d: freeing active DMA\n", channel);
dma->d_ops->disable(channel, dma);
dma->active = 0;
}
if (xchg(&dma->lock, 0) != 0) {
if (dma->d_ops->free)
dma->d_ops->free(channel, dma);
return;
}
printk(KERN_ERR "dma%d: trying to free free DMA\n", channel);
return;
bad_dma:
printk(KERN_ERR "dma: trying to free DMA%d\n", channel);
}
/* Set DMA Scatter-Gather list
*/
void set_dma_sg (dmach_t channel, struct scatterlist *sg, int nr_sg)
{
dma_t *dma = dma_chan + channel;
if (dma->active)
printk(KERN_ERR "dma%d: altering DMA SG while "
"DMA active\n", channel);
dma->sg = sg;
dma->sgcount = nr_sg;
dma->using_sg = 1;
dma->invalid = 1;
}
/* Set DMA address
*
* Copy address to the structure, and set the invalid bit
*/
void set_dma_addr (dmach_t channel, unsigned long physaddr)
{
dma_t *dma = dma_chan + channel;
if (dma->active)
printk(KERN_ERR "dma%d: altering DMA address while "
"DMA active\n", channel);
dma->sg = &dma->buf;
dma->sgcount = 1;
dma->buf.__address = bus_to_virt(physaddr);
dma->using_sg = 0;
dma->invalid = 1;
}
/* Set DMA byte count
*
* Copy address to the structure, and set the invalid bit
*/
void set_dma_count (dmach_t channel, unsigned long count)
{
dma_t *dma = dma_chan + channel;
if (dma->active)
printk(KERN_ERR "dma%d: altering DMA count while "
"DMA active\n", channel);
dma->sg = &dma->buf;
dma->sgcount = 1;
dma->buf.length = count;
dma->using_sg = 0;
dma->invalid = 1;
}
/* Set DMA direction mode
*/
void set_dma_mode (dmach_t channel, dmamode_t mode)
{
dma_t *dma = dma_chan + channel;
if (dma->active)
printk(KERN_ERR "dma%d: altering DMA mode while "
"DMA active\n", channel);
dma->dma_mode = mode;
dma->invalid = 1;
}
/* Enable DMA channel
*/
void enable_dma (dmach_t channel)
{
dma_t *dma = dma_chan + channel;
if (!dma->lock)
goto free_dma;
if (dma->active == 0) {
dma->active = 1;
dma->d_ops->enable(channel, dma);
}
return;
free_dma:
printk(KERN_ERR "dma%d: trying to enable free DMA\n", channel);
BUG();
}
/* Disable DMA channel
*/
void disable_dma (dmach_t channel)
{
dma_t *dma = dma_chan + channel;
if (!dma->lock)
goto free_dma;
if (dma->active == 1) {
dma->active = 0;
dma->d_ops->disable(channel, dma);
}
return;
free_dma:
printk(KERN_ERR "dma%d: trying to disable free DMA\n", channel);
BUG();
}
/*
* Is the specified DMA channel active?
*/
int dma_channel_active(dmach_t channel)
{
return dma_chan[channel].active;
}
void set_dma_page(dmach_t channel, char pagenr)
{
printk(KERN_ERR "dma%d: trying to set_dma_page\n", channel);
}
void set_dma_speed(dmach_t channel, int cycle_ns)
{
dma_t *dma = dma_chan + channel;
int ret = 0;
if (dma->d_ops->setspeed)
ret = dma->d_ops->setspeed(channel, dma, cycle_ns);
dma->speed = ret;
}
int get_dma_residue(dmach_t channel)
{
dma_t *dma = dma_chan + channel;
int ret = 0;
if (dma->d_ops->residue)
ret = dma->d_ops->residue(channel, dma);
return ret;
}
void __init init_dma(void)
{
arch_dma_init(dma_chan);
}
#else
int request_dma(dmach_t channel, const char *device_id)
{
return -EINVAL;
}
int get_dma_residue(dmach_t channel)
{
return 0;
}
#define GLOBAL_ALIAS(_a,_b) asm (".set " #_a "," #_b "; .globl " #_a)
GLOBAL_ALIAS(disable_dma, get_dma_residue);
GLOBAL_ALIAS(enable_dma, get_dma_residue);
GLOBAL_ALIAS(free_dma, get_dma_residue);
GLOBAL_ALIAS(get_dma_list, get_dma_residue);
GLOBAL_ALIAS(set_dma_mode, get_dma_residue);
GLOBAL_ALIAS(set_dma_page, get_dma_residue);
GLOBAL_ALIAS(set_dma_count, get_dma_residue);
GLOBAL_ALIAS(set_dma_addr, get_dma_residue);
GLOBAL_ALIAS(set_dma_sg, get_dma_residue);
GLOBAL_ALIAS(set_dma_speed, get_dma_residue);
GLOBAL_ALIAS(init_dma, get_dma_residue);
#endif
EXPORT_SYMBOL(request_dma);
EXPORT_SYMBOL(free_dma);
EXPORT_SYMBOL(enable_dma);
EXPORT_SYMBOL(disable_dma);
EXPORT_SYMBOL(set_dma_addr);
EXPORT_SYMBOL(set_dma_count);
EXPORT_SYMBOL(set_dma_mode);
EXPORT_SYMBOL(set_dma_page);
EXPORT_SYMBOL(get_dma_residue);
EXPORT_SYMBOL(set_dma_sg);
EXPORT_SYMBOL(set_dma_speed);
EXPORT_SYMBOL(dma_spin_lock);

1210
arch/arm/kernel/ecard.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,745 @@
/*
* linux/arch/arm/kernel/entry-armv.S
*
* Copyright (C) 1996,1997,1998 Russell King.
* ARM700 fix by Matthew Godbolt (linux-user@willothewisp.demon.co.uk)
*
* 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.
*
* Low-level vector interface routines
*
* Note: there is a StrongARM bug in the STMIA rn, {regs}^ instruction that causes
* it to save wrong values... Be aware!
*/
#include <linux/config.h>
#include <linux/init.h>
#include <asm/thread_info.h>
#include <asm/glue.h>
#include <asm/ptrace.h>
#include <asm/vfpmacros.h>
#include "entry-header.S"
/*
* Invalid mode handlers
*/
.macro inv_entry, sym, reason
sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
stmia sp, {r0 - lr} @ Save XXX r0 - lr
ldr r4, .LC\sym
mov r1, #\reason
.endm
__pabt_invalid:
inv_entry abt, BAD_PREFETCH
b 1f
__dabt_invalid:
inv_entry abt, BAD_DATA
b 1f
__irq_invalid:
inv_entry irq, BAD_IRQ
b 1f
__und_invalid:
inv_entry und, BAD_UNDEFINSTR
1: zero_fp
ldmia r4, {r5 - r7} @ Get XXX pc, cpsr, old_r0
add r4, sp, #S_PC
stmia r4, {r5 - r7} @ Save XXX pc, cpsr, old_r0
mov r0, sp
and r2, r6, #31 @ int mode
b bad_mode
/*
* SVC mode handlers
*/
.macro svc_entry, sym
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ save r0 - r12
ldr r2, .LC\sym
add r0, sp, #S_FRAME_SIZE
ldmia r2, {r2 - r4} @ get pc, cpsr
add r5, sp, #S_SP
mov r1, lr
@
@ We are now ready to fill in the remaining blanks on the stack:
@
@ r0 - sp_svc
@ r1 - lr_svc
@ r2 - lr_<exception>, already fixed up for correct return/restart
@ r3 - spsr_<exception>
@ r4 - orig_r0 (see pt_regs definition in ptrace.h)
@
stmia r5, {r0 - r4}
.endm
.align 5
__dabt_svc:
svc_entry abt
@
@ get ready to re-enable interrupts if appropriate
@
mrs r9, cpsr
tst r3, #PSR_I_BIT
biceq r9, r9, #PSR_I_BIT
@
@ Call the processor-specific abort handler:
@
@ r2 - aborted context pc
@ r3 - aborted context cpsr
@
@ The abort handler must return the aborted address in r0, and
@ the fault status register in r1. r9 must be preserved.
@
#ifdef MULTI_ABORT
ldr r4, .LCprocfns
mov lr, pc
ldr pc, [r4]
#else
bl CPU_ABORT_HANDLER
#endif
@
@ set desired IRQ state, then call main handler
@
msr cpsr_c, r9
mov r2, sp
bl do_DataAbort
@
@ IRQs off again before pulling preserved data off the stack
@
disable_irq r0
@
@ restore SPSR and restart the instruction
@
ldr r0, [sp, #S_PSR]
msr spsr_cxsf, r0
ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
.align 5
__irq_svc:
svc_entry irq
#ifdef CONFIG_PREEMPT
get_thread_info r8
ldr r9, [r8, #TI_PREEMPT] @ get preempt count
add r7, r9, #1 @ increment it
str r7, [r8, #TI_PREEMPT]
#endif
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
adrne lr, 1b
bne asm_do_IRQ
#ifdef CONFIG_PREEMPT
ldr r0, [r8, #TI_FLAGS] @ get flags
tst r0, #_TIF_NEED_RESCHED
blne svc_preempt
preempt_return:
ldr r0, [r8, #TI_PREEMPT] @ read preempt value
teq r0, r7
str r9, [r8, #TI_PREEMPT] @ restore preempt count
strne r0, [r0, -r0] @ bug()
#endif
ldr r0, [sp, #S_PSR] @ irqs are already disabled
msr spsr_cxsf, r0
ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
.ltorg
#ifdef CONFIG_PREEMPT
svc_preempt:
teq r9, #0 @ was preempt count = 0
ldreq r6, .LCirq_stat
movne pc, lr @ no
ldr r0, [r6, #4] @ local_irq_count
ldr r1, [r6, #8] @ local_bh_count
adds r0, r0, r1
movne pc, lr
mov r7, #0 @ preempt_schedule_irq
str r7, [r8, #TI_PREEMPT] @ expects preempt_count == 0
1: bl preempt_schedule_irq @ irq en/disable is done inside
ldr r0, [r8, #TI_FLAGS] @ get new tasks TI_FLAGS
tst r0, #_TIF_NEED_RESCHED
beq preempt_return @ go again
b 1b
#endif
.align 5
__und_svc:
svc_entry und
@
@ call emulation code, which returns using r9 if it has emulated
@ the instruction, or the more conventional lr if we are to treat
@ this as a real undefined instruction
@
@ r0 - instruction
@
ldr r0, [r2, #-4]
adr r9, 1f
bl call_fpe
mov r0, sp @ struct pt_regs *regs
bl do_undefinstr
@
@ IRQs off again before pulling preserved data off the stack
@
1: disable_irq r0
@
@ restore SPSR and restart the instruction
@
ldr lr, [sp, #S_PSR] @ Get SVC cpsr
msr spsr_cxsf, lr
ldmia sp, {r0 - pc}^ @ Restore SVC registers
.align 5
__pabt_svc:
svc_entry abt
@
@ re-enable interrupts if appropriate
@
mrs r9, cpsr
tst r3, #PSR_I_BIT
biceq r9, r9, #PSR_I_BIT
msr cpsr_c, r9
@
@ set args, then call main handler
@
@ r0 - address of faulting instruction
@ r1 - pointer to registers on stack
@
mov r0, r2 @ address (pc)
mov r1, sp @ regs
bl do_PrefetchAbort @ call abort handler
@
@ IRQs off again before pulling preserved data off the stack
@
disable_irq r0
@
@ restore SPSR and restart the instruction
@
ldr r0, [sp, #S_PSR]
msr spsr_cxsf, r0
ldmia sp, {r0 - pc}^ @ load r0 - pc, cpsr
.align 5
.LCirq:
.word __temp_irq
.LCund:
.word __temp_und
.LCabt:
.word __temp_abt
#ifdef MULTI_ABORT
.LCprocfns:
.word processor
#endif
.LCfp:
.word fp_enter
#ifdef CONFIG_PREEMPT
.LCirq_stat:
.word irq_stat
#endif
/*
* User mode handlers
*/
.macro usr_entry, sym
sub sp, sp, #S_FRAME_SIZE @ Allocate frame size in one go
stmia sp, {r0 - r12} @ save r0 - r12
ldr r7, .LC\sym
add r5, sp, #S_PC
ldmia r7, {r2 - r4} @ Get USR pc, cpsr
@
@ We are now ready to fill in the remaining blanks on the stack:
@
@ r2 - lr_<exception>, already fixed up for correct return/restart
@ r3 - spsr_<exception>
@ r4 - orig_r0 (see pt_regs definition in ptrace.h)
@
@ Also, separately save sp_usr and lr_usr
@
stmia r5, {r2 - r4}
stmdb r5, {sp, lr}^
@
@ Enable the alignment trap while in kernel mode
@
alignment_trap r7, r0, __temp_\sym
@
@ Clear FP to mark the first stack frame
@
zero_fp
.endm
.align 5
__dabt_usr:
usr_entry abt
@
@ Call the processor-specific abort handler:
@
@ r2 - aborted context pc
@ r3 - aborted context cpsr
@
@ The abort handler must return the aborted address in r0, and
@ the fault status register in r1.
@
#ifdef MULTI_ABORT
ldr r4, .LCprocfns
mov lr, pc
ldr pc, [r4]
#else
bl CPU_ABORT_HANDLER
#endif
@
@ IRQs on, then call the main handler
@
enable_irq r2
mov r2, sp
adr lr, ret_from_exception
b do_DataAbort
.align 5
__irq_usr:
usr_entry irq
#ifdef CONFIG_PREEMPT
get_thread_info r8
ldr r9, [r8, #TI_PREEMPT] @ get preempt count
add r7, r9, #1 @ increment it
str r7, [r8, #TI_PREEMPT]
#endif
1: get_irqnr_and_base r0, r6, r5, lr
movne r1, sp
adrne lr, 1b
@
@ routine called with r0 = irq number, r1 = struct pt_regs *
@
bne asm_do_IRQ
#ifdef CONFIG_PREEMPT
ldr r0, [r8, #TI_PREEMPT]
teq r0, r7
str r9, [r8, #TI_PREEMPT]
strne r0, [r0, -r0]
mov tsk, r8
#else
get_thread_info tsk
#endif
mov why, #0
b ret_to_user
.ltorg
.align 5
__und_usr:
usr_entry und
tst r3, #PSR_T_BIT @ Thumb mode?
bne fpundefinstr @ ignore FP
sub r4, r2, #4
@
@ fall through to the emulation code, which returns using r9 if
@ it has emulated the instruction, or the more conventional lr
@ if we are to treat this as a real undefined instruction
@
@ r0 - instruction
@
1: ldrt r0, [r4]
adr r9, ret_from_exception
adr lr, fpundefinstr
@
@ fallthrough to call_fpe
@
/*
* The out of line fixup for the ldrt above.
*/
.section .fixup, "ax"
2: mov pc, r9
.previous
.section __ex_table,"a"
.long 1b, 2b
.previous
/*
* Check whether the instruction is a co-processor instruction.
* If yes, we need to call the relevant co-processor handler.
*
* Note that we don't do a full check here for the co-processor
* instructions; all instructions with bit 27 set are well
* defined. The only instructions that should fault are the
* co-processor instructions. However, we have to watch out
* for the ARM6/ARM7 SWI bug.
*
* Emulators may wish to make use of the following registers:
* r0 = instruction opcode.
* r2 = PC+4
* r10 = this threads thread_info structure.
*/
call_fpe:
tst r0, #0x08000000 @ only CDP/CPRT/LDC/STC have bit 27
#if defined(CONFIG_CPU_ARM610) || defined(CONFIG_CPU_ARM710)
and r8, r0, #0x0f000000 @ mask out op-code bits
teqne r8, #0x0f000000 @ SWI (ARM6/7 bug)?
#endif
moveq pc, lr
get_thread_info r10 @ get current thread
and r8, r0, #0x00000f00 @ mask out CP number
mov r7, #1
add r6, r10, #TI_USED_CP
strb r7, [r6, r8, lsr #8] @ set appropriate used_cp[]
#ifdef CONFIG_IWMMXT
@ Test if we need to give access to iWMMXt coprocessors
ldr r5, [r10, #TI_FLAGS]
rsbs r7, r8, #(1 << 8) @ CP 0 or 1 only
movcss r7, r5, lsr #(TIF_USING_IWMMXT + 1)
bcs iwmmxt_task_enable
#endif
enable_irq r7
add pc, pc, r8, lsr #6
mov r0, r0
mov pc, lr @ CP#0
b do_fpe @ CP#1 (FPE)
b do_fpe @ CP#2 (FPE)
mov pc, lr @ CP#3
mov pc, lr @ CP#4
mov pc, lr @ CP#5
mov pc, lr @ CP#6
mov pc, lr @ CP#7
mov pc, lr @ CP#8
mov pc, lr @ CP#9
#ifdef CONFIG_VFP
b do_vfp @ CP#10 (VFP)
b do_vfp @ CP#11 (VFP)
#else
mov pc, lr @ CP#10 (VFP)
mov pc, lr @ CP#11 (VFP)
#endif
mov pc, lr @ CP#12
mov pc, lr @ CP#13
mov pc, lr @ CP#14 (Debug)
mov pc, lr @ CP#15 (Control)
do_fpe:
ldr r4, .LCfp
add r10, r10, #TI_FPSTATE @ r10 = workspace
ldr pc, [r4] @ Call FP module USR entry point
/*
* The FP module is called with these registers set:
* r0 = instruction
* r2 = PC+4
* r9 = normal "successful" return address
* r10 = FP workspace
* lr = unrecognised FP instruction return address
*/
.data
ENTRY(fp_enter)
.word fpundefinstr
.text
fpundefinstr:
mov r0, sp
adr lr, ret_from_exception
b do_undefinstr
.align 5
__pabt_usr:
usr_entry abt
enable_irq r0 @ Enable interrupts
mov r0, r2 @ address (pc)
mov r1, sp @ regs
bl do_PrefetchAbort @ call abort handler
/* fall through */
/*
* This is the return code to user mode for abort handlers
*/
ENTRY(ret_from_exception)
get_thread_info tsk
mov why, #0
b ret_to_user
/*
* Register switch for ARMv3 and ARMv4 processors
* r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
* previous and next are guaranteed not to be the same.
*/
ENTRY(__switch_to)
add ip, r1, #TI_CPU_SAVE
ldr r3, [r2, #TI_TP_VALUE]
stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack
ldr r6, [r2, #TI_CPU_DOMAIN]!
#if defined(CONFIG_CPU_XSCALE) && !defined(CONFIG_IWMMXT)
mra r4, r5, acc0
stmia ip, {r4, r5}
#endif
mov r4, #0xffff0fff
str r3, [r4, #-3] @ Set TLS ptr
mcr p15, 0, r6, c3, c0, 0 @ Set domain register
#ifdef CONFIG_VFP
@ Always disable VFP so we can lazily save/restore the old
@ state. This occurs in the context of the previous thread.
VFPFMRX r4, FPEXC
bic r4, r4, #FPEXC_ENABLE
VFPFMXR FPEXC, r4
#endif
#if defined(CONFIG_IWMMXT)
bl iwmmxt_task_switch
#elif defined(CONFIG_CPU_XSCALE)
add r4, r2, #40 @ cpu_context_save->extra
ldmib r4, {r4, r5}
mar acc0, r4, r5
#endif
ldmib r2, {r4 - sl, fp, sp, pc} @ Load all regs saved previously
__INIT
/*
* Vector stubs.
*
* This code is copied to 0x200 or 0xffff0200 so we can use branches in the
* vectors, rather than ldr's.
*
* Common stub entry macro:
* Enter in IRQ mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*/
.macro vector_stub, name, sym, correction=0
.align 5
vector_\name:
ldr r13, .LCs\sym
.if \correction
sub lr, lr, #\correction
.endif
str lr, [r13] @ save lr_IRQ
mrs lr, spsr
str lr, [r13, #4] @ save spsr_IRQ
@
@ now branch to the relevant MODE handling routine
@
mrs r13, cpsr
bic r13, r13, #MODE_MASK
orr r13, r13, #MODE_SVC
msr spsr_cxsf, r13 @ switch to SVC_32 mode
and lr, lr, #15
ldr lr, [pc, lr, lsl #2]
movs pc, lr @ Changes mode and branches
.endm
__stubs_start:
/*
* Interrupt dispatcher
*/
vector_stub irq, irq, 4
.long __irq_usr @ 0 (USR_26 / USR_32)
.long __irq_invalid @ 1 (FIQ_26 / FIQ_32)
.long __irq_invalid @ 2 (IRQ_26 / IRQ_32)
.long __irq_svc @ 3 (SVC_26 / SVC_32)
.long __irq_invalid @ 4
.long __irq_invalid @ 5
.long __irq_invalid @ 6
.long __irq_invalid @ 7
.long __irq_invalid @ 8
.long __irq_invalid @ 9
.long __irq_invalid @ a
.long __irq_invalid @ b
.long __irq_invalid @ c
.long __irq_invalid @ d
.long __irq_invalid @ e
.long __irq_invalid @ f
/*
* Data abort dispatcher
* Enter in ABT mode, spsr = USR CPSR, lr = USR PC
*/
vector_stub dabt, abt, 8
.long __dabt_usr @ 0 (USR_26 / USR_32)
.long __dabt_invalid @ 1 (FIQ_26 / FIQ_32)
.long __dabt_invalid @ 2 (IRQ_26 / IRQ_32)
.long __dabt_svc @ 3 (SVC_26 / SVC_32)
.long __dabt_invalid @ 4
.long __dabt_invalid @ 5
.long __dabt_invalid @ 6
.long __dabt_invalid @ 7
.long __dabt_invalid @ 8
.long __dabt_invalid @ 9
.long __dabt_invalid @ a
.long __dabt_invalid @ b
.long __dabt_invalid @ c
.long __dabt_invalid @ d
.long __dabt_invalid @ e
.long __dabt_invalid @ f
/*
* Prefetch abort dispatcher
* Enter in ABT mode, spsr = USR CPSR, lr = USR PC
*/
vector_stub pabt, abt, 4
.long __pabt_usr @ 0 (USR_26 / USR_32)
.long __pabt_invalid @ 1 (FIQ_26 / FIQ_32)
.long __pabt_invalid @ 2 (IRQ_26 / IRQ_32)
.long __pabt_svc @ 3 (SVC_26 / SVC_32)
.long __pabt_invalid @ 4
.long __pabt_invalid @ 5
.long __pabt_invalid @ 6
.long __pabt_invalid @ 7
.long __pabt_invalid @ 8
.long __pabt_invalid @ 9
.long __pabt_invalid @ a
.long __pabt_invalid @ b
.long __pabt_invalid @ c
.long __pabt_invalid @ d
.long __pabt_invalid @ e
.long __pabt_invalid @ f
/*
* Undef instr entry dispatcher
* Enter in UND mode, spsr = SVC/USR CPSR, lr = SVC/USR PC
*/
vector_stub und, und
.long __und_usr @ 0 (USR_26 / USR_32)
.long __und_invalid @ 1 (FIQ_26 / FIQ_32)
.long __und_invalid @ 2 (IRQ_26 / IRQ_32)
.long __und_svc @ 3 (SVC_26 / SVC_32)
.long __und_invalid @ 4
.long __und_invalid @ 5
.long __und_invalid @ 6
.long __und_invalid @ 7
.long __und_invalid @ 8
.long __und_invalid @ 9
.long __und_invalid @ a
.long __und_invalid @ b
.long __und_invalid @ c
.long __und_invalid @ d
.long __und_invalid @ e
.long __und_invalid @ f
.align 5
/*=============================================================================
* Undefined FIQs
*-----------------------------------------------------------------------------
* Enter in FIQ mode, spsr = ANY CPSR, lr = ANY PC
* MUST PRESERVE SVC SPSR, but need to switch to SVC mode to show our msg.
* Basically to switch modes, we *HAVE* to clobber one register... brain
* damage alert! I don't think that we can execute any code in here in any
* other mode than FIQ... Ok you can switch to another mode, but you can't
* get out of that mode without clobbering one register.
*/
vector_fiq:
disable_fiq
subs pc, lr, #4
/*=============================================================================
* Address exception handler
*-----------------------------------------------------------------------------
* These aren't too critical.
* (they're not supposed to happen, and won't happen in 32-bit data mode).
*/
vector_addrexcptn:
b vector_addrexcptn
/*
* We group all the following data together to optimise
* for CPUs with separate I & D caches.
*/
.align 5
.LCvswi:
.word vector_swi
.LCsirq:
.word __temp_irq
.LCsund:
.word __temp_und
.LCsabt:
.word __temp_abt
__stubs_end:
.equ __real_stubs_start, .LCvectors + 0x200
.LCvectors:
swi SYS_ERROR0
b __real_stubs_start + (vector_und - __stubs_start)
ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
b __real_stubs_start + (vector_pabt - __stubs_start)
b __real_stubs_start + (vector_dabt - __stubs_start)
b __real_stubs_start + (vector_addrexcptn - __stubs_start)
b __real_stubs_start + (vector_irq - __stubs_start)
b __real_stubs_start + (vector_fiq - __stubs_start)
ENTRY(__trap_init)
stmfd sp!, {r4 - r6, lr}
mov r0, #0xff000000
orr r0, r0, #0x00ff0000 @ high vectors position
adr r1, .LCvectors @ set up the vectors
ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr}
stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr}
add r2, r0, #0x200
adr r0, __stubs_start @ copy stubs to 0x200
adr r1, __stubs_end
1: ldr r3, [r0], #4
str r3, [r2], #4
cmp r0, r1
blt 1b
LOADREGS(fd, sp!, {r4 - r6, pc})
.data
/*
* Do not reorder these, and do not insert extra data between...
*/
__temp_irq:
.word 0 @ saved lr_irq
.word 0 @ saved spsr_irq
.word -1 @ old_r0
__temp_und:
.word 0 @ Saved lr_und
.word 0 @ Saved spsr_und
.word -1 @ old_r0
__temp_abt:
.word 0 @ Saved lr_abt
.word 0 @ Saved spsr_abt
.word -1 @ old_r0
.globl cr_alignment
.globl cr_no_alignment
cr_alignment:
.space 4
cr_no_alignment:
.space 4

View File

@@ -0,0 +1,260 @@
/*
* 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 <linux/config.h>
#include <asm/thread_info.h>
#include <asm/ptrace.h>
#include <asm/unistd.h>
#include "entry-header.S"
/*
* We rely on the fact that R0 is at the bottom of the stack (due to
* slow/fast restore user regs).
*/
#if S_R0 != 0
#error "Please fix"
#endif
.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 r1 @ disable interrupts
ldr r1, [tsk, #TI_FLAGS]
tst r1, #_TIF_WORK_MASK
bne fast_work_pending
fast_restore_user_regs
/*
* Ok, we need to do extra processing, enter the slow path.
*/
fast_work_pending:
str r0, [sp, #S_R0+S_OFF]! @ returned r0
work_pending:
tst r1, #_TIF_NEED_RESCHED
bne work_resched
tst r1, #_TIF_NOTIFY_RESUME | _TIF_SIGPENDING
beq no_work_pending
mov r0, sp @ 'regs'
mov r2, why @ 'syscall'
bl do_notify_resume
disable_irq r1 @ disable interrupts
b no_work_pending
work_resched:
bl schedule
/*
* "slow" syscall return path. "why" tells us if this was a real syscall.
*/
ENTRY(ret_to_user)
ret_slow_syscall:
disable_irq r1 @ disable interrupts
ldr r1, [tsk, #TI_FLAGS]
tst r1, #_TIF_WORK_MASK
bne work_pending
no_work_pending:
slow_restore_user_regs
/*
* This is how we return from a fork.
*/
ENTRY(ret_from_fork)
bl schedule_tail
get_thread_info tsk
ldr r1, [tsk, #TI_FLAGS] @ check for syscall tracing
mov why, #1
tst r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
beq ret_slow_syscall
mov r1, sp
mov r0, #1 @ trace exit [IP = 1]
bl syscall_trace
b ret_slow_syscall
#include "calls.S"
/*=============================================================================
* 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
.macro arm710_bug_check, instr, temp
and \temp, \instr, #0x0f000000 @ check for SWI
teq \temp, #0x0f000000
bne .Larm700bug
.endm
.Larm700bug:
ldr r0, [sp, #S_PSR] @ Get calling cpsr
sub lr, lr, #4
str lr, [r8]
msr spsr_cxsf, r0
ldmia sp, {r0 - lr}^ @ Get calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
movs pc, lr
#else
.macro arm710_bug_check, instr, temp
.endm
#endif
.align 5
ENTRY(vector_swi)
save_user_regs
zero_fp
get_scno
arm710_bug_check scno, ip
#ifdef CONFIG_ALIGNMENT_TRAP
ldr ip, __cr_alignment
ldr ip, [ip]
mcr p15, 0, ip, c1, c0 @ update control register
#endif
enable_irq ip
str r4, [sp, #-S_OFF]! @ push fifth arg
get_thread_info tsk
ldr ip, [tsk, #TI_FLAGS] @ check for syscall tracing
bic scno, scno, #0xff000000 @ mask off SWI op-code
eor scno, scno, #OS_NUMBER << 20 @ check OS number
adr tbl, sys_call_table @ load syscall table pointer
tst ip, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
bne __sys_trace
adr lr, ret_fast_syscall @ return address
cmp scno, #NR_syscalls @ check upper syscall limit
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
add r1, sp, #S_OFF
2: mov why, #0 @ no longer a real syscall
cmp scno, #ARMSWI_OFFSET
eor r0, scno, #OS_NUMBER << 20 @ put OS number back
bcs arm_syscall
b sys_ni_syscall @ not private func
/*
* This is the really slow path. We're going to be doing
* context switches, and waiting for our parent to respond.
*/
__sys_trace:
add r1, sp, #S_OFF
mov r0, #0 @ trace entry [IP = 0]
bl syscall_trace
adr lr, __sys_trace_return @ return address
add r1, sp, #S_R0 + S_OFF @ pointer to regs
cmp scno, #NR_syscalls @ check upper syscall limit
ldmccia r1, {r0 - r3} @ have to reload r0 - r3
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
b 2b
__sys_trace_return:
str r0, [sp, #S_R0 + S_OFF]! @ save returned r0
mov r1, sp
mov r0, #1 @ trace exit [IP = 1]
bl syscall_trace
b ret_slow_syscall
.align 5
#ifdef CONFIG_ALIGNMENT_TRAP
.type __cr_alignment, #object
__cr_alignment:
.word cr_alignment
#endif
.type sys_call_table, #object
ENTRY(sys_call_table)
#include "calls.S"
/*============================================================================
* Special system call wrappers
*/
@ r0 = syscall number
@ r5 = syscall table
.type sys_syscall, #function
sys_syscall:
eor scno, r0, #OS_NUMBER << 20
cmp scno, #__NR_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_futex_wrapper:
str r5, [sp, #4] @ push sixth arg
b sys_futex
/*
* 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
RETINSTR(mov,pc, lr)
#else
str r5, [sp, #4]
b do_mmap2
#endif

View File

@@ -0,0 +1,182 @@
#include <linux/config.h> /* for CONFIG_ARCH_xxxx */
#include <linux/linkage.h>
#include <asm/assembler.h>
#include <asm/constants.h>
#include <asm/errno.h>
#include <asm/hardware.h>
#include <asm/arch/irqs.h>
#include <asm/arch/entry-macro.S>
#ifndef MODE_SVC
#define MODE_SVC 0x13
#endif
.macro zero_fp
#ifdef CONFIG_FRAME_POINTER
mov fp, #0
#endif
.endm
.text
@ Bad Abort numbers
@ -----------------
@
#define BAD_PREFETCH 0
#define BAD_DATA 1
#define BAD_ADDREXCPTN 2
#define BAD_IRQ 3
#define BAD_UNDEFINSTR 4
#define PT_TRACESYS 0x00000002
@ OS version number used in SWIs
@ RISC OS is 0
@ RISC iX is 8
@
#define OS_NUMBER 9
#define ARMSWI_OFFSET 0x000f0000
@
@ Stack format (ensured by USER_* and SVC_*)
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define S_OFF 8
.macro set_cpsr_c, reg, mode
msr cpsr_c, \mode
.endm
#if __LINUX_ARM_ARCH__ >= 6
.macro disable_irq, temp
cpsid i
.endm
.macro enable_irq, temp
cpsie i
.endm
#else
.macro disable_irq, temp
set_cpsr_c \temp, #PSR_I_BIT | MODE_SVC
.endm
.macro enable_irq, temp
set_cpsr_c \temp, #MODE_SVC
.endm
#endif
.macro save_user_regs
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
.endm
.macro restore_user_regs
ldr r1, [sp, #S_PSR] @ Get calling cpsr
disable_irq ip @ disable IRQs
ldr lr, [sp, #S_PC]! @ Get PC
msr spsr_cxsf, r1 @ save in spsr_svc
ldmdb sp, {r0 - lr}^ @ Get calling r0 - lr
mov r0, r0
add sp, sp, #S_FRAME_SIZE - S_PC
movs pc, lr @ return & move spsr_svc into cpsr
.endm
/*
* Must be called with IRQs already disabled.
*/
.macro 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
.endm
/*
* Must be called with IRQs already disabled.
*/
.macro 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
.endm
.macro mask_pc, rd, rm
.endm
.macro get_thread_info, rd
mov \rd, sp, lsr #13
mov \rd, \rd, lsl #13
.endm
.macro alignment_trap, rbase, rtemp, sym
#ifdef CONFIG_ALIGNMENT_TRAP
#define OFF_CR_ALIGNMENT(x) cr_alignment - x
ldr \rtemp, [\rbase, #OFF_CR_ALIGNMENT(\sym)]
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
/*
* Get the system call number.
*/
.macro get_scno
#ifdef CONFIG_ARM_THUMB
tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs
addne scno, r7, #OS_NUMBER << 20 @ put OS number in
ldreq scno, [lr, #-4]
#else
mask_pc lr, lr
ldr scno, [lr, #-4] @ get SWI instruction
#endif
.endm

181
arch/arm/kernel/fiq.c Normal file
View File

@@ -0,0 +1,181 @@
/*
* 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/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\
ldmea fp, {fp, sp, pc}"
: "=&r" (tmp)
: "r" (&regs->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\
ldmea fp, {fp, sp, pc}"
: "=&r" (tmp)
: "r" (&regs->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;
}

516
arch/arm/kernel/head.S Normal file
View File

@@ -0,0 +1,516 @@
/*
* linux/arch/arm/kernel/head.S
*
* Copyright (C) 1994-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.
*
* Kernel startup code for all 32-bit CPUs
*/
#include <linux/config.h>
#include <linux/linkage.h>
#include <linux/init.h>
#include <asm/assembler.h>
#include <asm/domain.h>
#include <asm/mach-types.h>
#include <asm/procinfo.h>
#include <asm/ptrace.h>
#include <asm/constants.h>
#include <asm/system.h>
#define PROCINFO_MMUFLAGS 8
#define PROCINFO_INITFUNC 12
#define MACHINFO_TYPE 0
#define MACHINFO_PHYSRAM 4
#define MACHINFO_PHYSIO 8
#define MACHINFO_PGOFFIO 12
#define MACHINFO_NAME 16
#ifndef CONFIG_XIP_KERNEL
/*
* We place the page tables 16K below TEXTADDR. Therefore, we must make sure
* that TEXTADDR is correctly set. Currently, we expect the least significant
* 16 bits to be 0x8000, but we could probably relax this restriction to
* TEXTADDR >= PAGE_OFFSET + 0x4000
*
* Note that swapper_pg_dir is the virtual address of the page tables, and
* pgtbl gives us a position-independent reference to these tables. We can
* do this because stext == TEXTADDR
*/
#if (TEXTADDR & 0xffff) != 0x8000
#error TEXTADDR must start at 0xXXXX8000
#endif
.globl swapper_pg_dir
.equ swapper_pg_dir, TEXTADDR - 0x4000
.macro pgtbl, rd, phys
adr \rd, stext
sub \rd, \rd, #0x4000
.endm
#else
/*
* XIP Kernel:
*
* We place the page tables 16K below DATAADDR. Therefore, we must make sure
* that DATAADDR is correctly set. Currently, we expect the least significant
* 16 bits to be 0x8000, but we could probably relax this restriction to
* DATAADDR >= PAGE_OFFSET + 0x4000
*
* Note that pgtbl is meant to return the physical address of swapper_pg_dir.
* We can't make it relative to the kernel position in this case since
* the kernel can physically be anywhere.
*/
#if (DATAADDR & 0xffff) != 0x8000
#error DATAADDR must start at 0xXXXX8000
#endif
.globl swapper_pg_dir
.equ swapper_pg_dir, DATAADDR - 0x4000
.macro pgtbl, rd, phys
ldr \rd, =((DATAADDR - 0x4000) - VIRT_OFFSET)
add \rd, \rd, \phys
.endm
#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 | MODE_SVC @ ensure svc mode
@ and irqs disabled
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
.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+8192 @ sp
/*
* The following fragment of code is executed with the MMU on, 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
/*
* 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, r5, r6, r7 corrupted
* r4 = physical page table address
*/
.type __create_page_tables, %function
__create_page_tables:
ldr r5, [r8, #MACHINFO_PHYSRAM] @ physram
pgtbl r4, r5 @ 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_MMUFLAGS] @ 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. We round TEXTADDR down to the
* nearest megabyte boundary. It is assumed that
* the kernel fits within 4 contigous 1MB sections.
*/
add r0, r4, #(TEXTADDR & 0xff000000) >> 18 @ start of kernel
str r3, [r0, #(TEXTADDR & 0x00f00000) >> 18]!
add r3, r3, #1 << 20
str r3, [r0, #4]! @ KERNEL + 1MB
add r3, r3, #1 << 20
str r3, [r0, #4]! @ KERNEL + 2MB
add r3, r3, #1 << 20
str r3, [r0, #4] @ KERNEL + 3MB
/*
* Then map first 1MB of ram in case it contains our boot params.
*/
add r0, r4, #VIRT_OFFSET >> 18
orr r6, r5, r7
str r6, [r0]
#ifdef CONFIG_XIP_KERNEL
/*
* Map some ram to cover our .data and .bss areas.
* Mapping 3MB should be plenty.
*/
sub r3, r4, r5
mov r3, r3, lsr #20
add r0, r0, r3, lsl #2
add r6, r6, r3, lsl #20
str r6, [r0], #4
add r6, r6, #(1 << 20)
str r6, [r0], #4
add r6, r6, #(1 << 20)
str r6, [r0]
#endif
bic r7, r7, #0x0c @ turn off cacheable
@ and bufferable bits
#ifdef CONFIG_DEBUG_LL
/*
* 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, we need to map in
* the 16550-type serial port for the debug messages
*/
teq r1, #MACH_TYPE_NETWINDER
teqne r1, #MACH_TYPE_CATS
bne 1f
add r0, r4, #0x3fc0 @ ff000000
mov r3, #0x7c000000
orr r3, r3, r7
str r3, [r0], #4
add r3, r3, #1 << 20
str r3, [r0], #4
1:
#endif
#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, #0x80 @ 02000000
mov r3, #0x02000000
orr r3, r3, r7
str r3, [r0]
add r0, r4, #0x3600 @ d8000000
str r3, [r0]
#endif
mov pc, lr
.ltorg
/*
* 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.
*
* Returns:
* r3, r4, r6 corrupted
* r5 = proc_info pointer in physical address space
* r9 = cpuid
*/
.type __lookup_processor_type, %function
__lookup_processor_type:
adr r3, 3f
ldmda r3, {r5, r6, r9}
sub r3, r3, r9 @ get offset between virt&phys
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space
mrc p15, 0, r9, c0, c0 @ get processor id
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 - r6, r9, lr}
bl __lookup_processor_type
mov r0, r5
ldmfd sp!, {r4 - r6, 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}

View 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
View 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, 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(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(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);

1038
arch/arm/kernel/irq.c Normal file

File diff suppressed because it is too large Load Diff

53
arch/arm/kernel/isa.c Normal file
View File

@@ -0,0 +1,53 @@
/*
* linux/arch/arm/kernel/isa.c
*
* Copyright (C) 1999 Phil Blundell
*
* ISA shared memory and I/O port support
*/
/*
* 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.
*/
/*
* Nothing about this is actually ARM specific. One day we could move
* it into kernel/resource.c or some place like that.
*/
#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] = {
{BUS_ISA_MEM_BASE, "membase", &isa_membase,
sizeof(isa_membase), 0444, NULL, &proc_dointvec},
{BUS_ISA_PORT_BASE, "portbase", &isa_portbase,
sizeof(isa_portbase), 0444, NULL, &proc_dointvec},
{BUS_ISA_PORT_SHIFT, "portshift", &isa_portshift,
sizeof(isa_portshift), 0444, NULL, &proc_dointvec},
{0}
};
static struct ctl_table_header *isa_sysctl_header;
static ctl_table ctl_isa[2] = {{CTL_BUS_ISA, "isa", NULL, 0, 0555, ctl_isa_vars},
{0}};
static ctl_table ctl_bus[2] = {{CTL_BUS, "bus", NULL, 0, 0555, 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, 0);
}

320
arch/arm/kernel/iwmmxt.S Normal file
View File

@@ -0,0 +1,320 @@
/*
* 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/constants.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 = previous task_struct pointer (must be preserved)
* r1 = previous thread_info pointer
* r2 = next thread_info.cpu_domain pointer (must be preserved)
*
* Called only from __switch_to with task preemption disabled.
* No need to care about preserving r4 and above.
*/
ENTRY(iwmmxt_task_switch)
mrc p15, 0, r4, c15, c1, 0
tst r4, #0x3 @ CP0 and CP1 accessible?
bne 1f @ yes: block them for next task
ldr r5, =concan_owner
add r6, r2, #(TI_IWMMXT_STATE - TI_CPU_DOMAIN) @ get next task Concan save area
ldr r5, [r5] @ get current Concan owner
teq r5, r6 @ next task owns it?
movne pc, lr @ no: leave Concan disabled
1: eor r4, r4, #3 @ flip Concan access
mcr p15, 0, r4, c15, c1, 0
mrc p15, 0, r4, c2, c0, 0
sub pc, lr, r4, 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

152
arch/arm/kernel/module.c Normal file
View File

@@ -0,0 +1,152 @@
/*
* linux/arch/arm/kernel/module.c
*
* Copyright (C) 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.
*
* Module allocation method suggested by Andi Kleen.
*/
#include <linux/config.h>
#include <linux/module.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
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);
}
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:
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)
{
}

460
arch/arm/kernel/process.c Normal file
View File

@@ -0,0 +1,460 @@
/*
* 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/config.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/slab.h>
#include <linux/user.h>
#include <linux/a.out.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/interrupt.h>
#include <linux/kallsyms.h>
#include <linux/init.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/leds.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
extern const char *processor_modes[];
extern void setup_mm_for_reboot(char mode);
static volatile int hlt_counter;
#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);
/*
* The following aren't currently used.
*/
void (*pm_idle)(void);
EXPORT_SYMBOL(pm_idle);
void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);
/*
* This is our default idle handler. We need to disable
* interrupts here to ensure we don't miss a wakeup call.
*/
void default_idle(void)
{
local_irq_disable();
if (!need_resched() && !hlt_counter)
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;
if (!idle)
idle = default_idle;
preempt_disable();
leds_event(led_idle_start);
while (!need_resched())
idle();
leds_event(led_idle_end);
preempt_enable();
schedule();
}
}
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)
{
}
EXPORT_SYMBOL(machine_halt);
void machine_power_off(void)
{
if (pm_power_off)
pm_power_off();
}
EXPORT_SYMBOL(machine_power_off);
void machine_restart(char * __unused)
{
/*
* Clean and disable cache, and turn off interrupts
*/
cpu_proc_fin();
/*
* Tell the mm system that we are going to reboot -
* we may need it to insert some 1:1 mappings so that
* soft boot works.
*/
setup_mm_for_reboot(reboot_mode);
/*
* Now call the architecture specific reboot code.
*/
arch_reset(reboot_mode);
/*
* Whoops - the architecture was unable to reboot.
* Tell the user!
*/
mdelay(1000);
printk("Reboot failed -- System halted\n");
while (1);
}
EXPORT_SYMBOL(machine_restart);
void show_regs(struct pt_regs * regs)
{
unsigned long flags;
flags = condition_codes(regs);
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");
{
unsigned int ctrl, transbase, dac;
__asm__ (
" mrc p15, 0, %0, c1, c0\n"
" mrc p15, 0, %1, c2, c0\n"
" mrc p15, 0, %2, c3, c0\n"
: "=r" (ctrl), "=r" (transbase), "=r" (dac));
printk("Control: %04X Table: %08X DAC: %08X\n",
ctrl, transbase, dac);
}
}
void show_fpregs(struct user_fp *regs)
{
int i;
for (i = 0; i < 8; i++) {
unsigned long *p;
char type;
p = (unsigned long *)(regs->fpregs + i);
switch (regs->ftype[i]) {
case 1: type = 'f'; break;
case 2: type = 'd'; break;
case 3: type = 'e'; break;
default: type = '?'; break;
}
if (regs->init_flag)
type = '?';
printk(" f%d(%c): %08lx %08lx %08lx%c",
i, type, p[0], p[1], p[2], i & 1 ? '\n' : ' ');
}
printk("FPSR: %08lx FPCR: %08lx\n",
(unsigned long)regs->fpsr,
(unsigned long)regs->fpcr);
}
/*
* Task structure and kernel stack allocation.
*/
static unsigned long *thread_info_head;
static unsigned int nr_thread_info;
#define EXTRA_TASK_STRUCT 4
#define ll_alloc_task_struct() ((struct thread_info *) __get_free_pages(GFP_KERNEL,1))
#define ll_free_task_struct(p) free_pages((unsigned long)(p),1)
struct thread_info *alloc_thread_info(struct task_struct *task)
{
struct thread_info *thread = NULL;
if (EXTRA_TASK_STRUCT) {
unsigned long *p = thread_info_head;
if (p) {
thread_info_head = (unsigned long *)p[0];
nr_thread_info -= 1;
}
thread = (struct thread_info *)p;
}
if (!thread)
thread = ll_alloc_task_struct();
#ifdef CONFIG_MAGIC_SYSRQ
/*
* The stack must be cleared if you want SYSRQ-T to
* give sensible stack usage information
*/
if (thread) {
char *p = (char *)thread;
memzero(p+KERNEL_STACK_SIZE, KERNEL_STACK_SIZE);
}
#endif
return thread;
}
void free_thread_info(struct thread_info *thread)
{
if (EXTRA_TASK_STRUCT && nr_thread_info < EXTRA_TASK_STRUCT) {
unsigned long *p = (unsigned long *)thread;
p[0] = (unsigned long)thread_info_head;
thread_info_head = p;
nr_thread_info += 1;
} else
ll_free_task_struct(thread);
}
/*
* Free current thread data structures etc..
*/
void exit_thread(void)
{
}
static void default_fp_init(union fp_state *fp)
{
memset(fp, 0, sizeof(union fp_state));
}
void (*fp_init)(union fp_state *) = default_fp_init;
EXPORT_SYMBOL(fp_init);
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));
#if defined(CONFIG_IWMMXT)
iwmmxt_task_release(thread);
#endif
fp_init(&thread->fpstate);
#if defined(CONFIG_VFP)
vfp_flush_thread(&thread->vfpstate);
#endif
}
void release_thread(struct task_struct *dead_task)
{
#if defined(CONFIG_VFP)
vfp_release_thread(&dead_task->thread_info->vfpstate);
#endif
#if defined(CONFIG_IWMMXT)
iwmmxt_task_release(dead_task->thread_info);
#endif
}
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 = p->thread_info;
struct pt_regs *childregs;
childregs = ((struct pt_regs *)((unsigned long)thread + THREAD_SIZE - 8)) - 1;
*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(&regs, 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, &regs, 0, NULL, NULL);
}
EXPORT_SYMBOL(kernel_thread);
unsigned long get_wchan(struct task_struct *p)
{
unsigned long fp, lr;
unsigned long stack_page;
int count = 0;
if (!p || p == current || p->state == TASK_RUNNING)
return 0;
stack_page = 4096 + (unsigned long)p->thread_info;
fp = thread_saved_fp(p);
do {
if (fp < stack_page || fp > 4092+stack_page)
return 0;
lr = pc_pointer (((unsigned long *)fp)[-1]);
if (!in_sched_functions(lr))
return lr;
fp = *(unsigned long *) (fp - 12);
} while (count ++ < 16);
return 0;
}
EXPORT_SYMBOL(get_wchan);

861
arch/arm/kernel/ptrace.c Normal file
View File

@@ -0,0 +1,861 @@
/*
* 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/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/smp_lock.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/init.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
/*
* Get the address of the live pt_regs for the specified task.
* These are saved onto the top kernel stack when the process
* is not running.
*
* Note: if a user thread is execve'd from kernel space, the
* kernel stack will not be empty on entry to the kernel, so
* ptracing these tasks will fail.
*/
static inline struct pt_regs *
get_user_regs(struct task_struct *task)
{
return (struct pt_regs *)
((unsigned long)task->thread_info + THREAD_SIZE -
8 - sizeof(struct pt_regs));
}
/*
* this routine will get a word off of the processes privileged stack.
* the offset is how far from the base addr as stored in the THREAD.
* this routine assumes that all the privileged stacks are in our
* data space.
*/
static inline long get_user_reg(struct task_struct *task, int offset)
{
return get_user_regs(task)->uregs[offset];
}
/*
* this routine will put a word on the processes privileged stack.
* the offset is how far from the base addr as stored in the THREAD.
* this routine assumes that all the privileged stacks are in our
* data space.
*/
static inline int
put_user_reg(struct task_struct *task, int offset, long data)
{
struct pt_regs newregs, *regs = get_user_regs(task);
int ret = -EINVAL;
newregs = *regs;
newregs.uregs[offset] = data;
if (valid_user_regs(&newregs)) {
regs->uregs[offset] = data;
ret = 0;
}
return ret;
}
static inline int
read_u32(struct task_struct *task, unsigned long addr, u32 *res)
{
int ret;
ret = access_process_vm(task, addr, res, sizeof(*res), 0);
return ret == sizeof(*res) ? 0 : -EIO;
}
static inline int
read_instr(struct task_struct *task, unsigned long addr, u32 *res)
{
int ret;
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 & 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 = get_user_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 = get_user_regs(tsk);
return copy_to_user(uregs, regs, sizeof(struct pt_regs)) ? -EFAULT : 0;
}
/*
* Set all user integer registers.
*/
static int ptrace_setregs(struct task_struct *tsk, void __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 = get_user_regs(tsk);
ret = -EINVAL;
if (valid_user_regs(&newregs)) {
*regs = newregs;
ret = 0;
}
}
return ret;
}
/*
* Get the child FPU state.
*/
static int ptrace_getfpregs(struct task_struct *tsk, void __user *ufp)
{
return copy_to_user(ufp, &tsk->thread_info->fpstate,
sizeof(struct user_fp)) ? -EFAULT : 0;
}
/*
* Set the child FPU state.
*/
static int ptrace_setfpregs(struct task_struct *tsk, void __user *ufp)
{
struct thread_info *thread = tsk->thread_info;
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 = tsk->thread_info;
void *ptr = &thread->fpstate;
if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT))
return -ENODATA;
iwmmxt_task_disable(thread); /* force it to ram */
/* The iWMMXt state is stored doubleword-aligned. */
if (((long) ptr) & 4)
ptr += 4;
return copy_to_user(ufp, ptr, 0x98) ? -EFAULT : 0;
}
/*
* Set the child iWMMXt state.
*/
static int ptrace_setwmmxregs(struct task_struct *tsk, void __user *ufp)
{
struct thread_info *thread = tsk->thread_info;
void *ptr = &thread->fpstate;
if (!test_ti_thread_flag(thread, TIF_USING_IWMMXT))
return -EACCES;
iwmmxt_task_release(thread); /* force a reload */
/* The iWMMXt state is stored doubleword-aligned. */
if (((long) ptr) & 4)
ptr += 4;
return copy_from_user(ptr, ufp, 0x98) ? -EFAULT : 0;
}
#endif
static int do_ptrace(int request, struct task_struct *child, long addr, long data)
{
unsigned long tmp;
int ret;
switch (request) {
/*
* read word at location "addr" in the child process.
*/
case PTRACE_PEEKTEXT:
case PTRACE_PEEKDATA:
ret = access_process_vm(child, addr, &tmp,
sizeof(unsigned long), 0);
if (ret == sizeof(unsigned long))
ret = put_user(tmp, (unsigned long __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 ((unsigned long) data > _NSIG)
break;
if (request == PTRACE_SYSCALL)
set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
else
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* make sure single-step breakpoint is gone. */
child->ptrace &= ~PT_SINGLESTEP;
ptrace_cancel_bpt(child);
wake_up_process(child);
ret = 0;
break;
/*
* make the child exit. Best I can do is send it a sigkill.
* perhaps it should be put in the status that it wants to
* exit.
*/
case PTRACE_KILL:
/* make sure single-step breakpoint is gone. */
child->ptrace &= ~PT_SINGLESTEP;
ptrace_cancel_bpt(child);
if (child->exit_state != EXIT_ZOMBIE) {
child->exit_code = SIGKILL;
wake_up_process(child);
}
ret = 0;
break;
/*
* execute single instruction.
*/
case PTRACE_SINGLESTEP:
ret = -EIO;
if ((unsigned long) data > _NSIG)
break;
child->ptrace |= PT_SINGLESTEP;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* give it a chance to run. */
wake_up_process(child);
ret = 0;
break;
case PTRACE_DETACH:
ret = ptrace_detach(child, data);
break;
case PTRACE_GETREGS:
ret = ptrace_getregs(child, (void __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(child->thread_info->tp_value,
(unsigned long __user *) data);
break;
default:
ret = ptrace_request(child, request, addr, data);
break;
}
return ret;
}
asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
{
struct task_struct *child;
int ret;
lock_kernel();
ret = -EPERM;
if (request == PTRACE_TRACEME) {
/* are we already being traced? */
if (current->ptrace & PT_PTRACED)
goto out;
ret = security_ptrace(current->parent, current);
if (ret)
goto out;
/* set the ptrace bit in the process flags. */
current->ptrace |= PT_PTRACED;
ret = 0;
goto out;
}
ret = -ESRCH;
read_lock(&tasklist_lock);
child = find_task_by_pid(pid);
if (child)
get_task_struct(child);
read_unlock(&tasklist_lock);
if (!child)
goto out;
ret = -EPERM;
if (pid == 1) /* you may not mess with init */
goto out_tsk;
if (request == PTRACE_ATTACH) {
ret = ptrace_attach(child);
goto out_tsk;
}
ret = ptrace_check_attach(child, request == PTRACE_KILL);
if (ret == 0)
ret = do_ptrace(request, child, addr, data);
out_tsk:
put_task_struct(child);
out:
unlock_kernel();
return ret;
}
asmlinkage void syscall_trace(int why, struct pt_regs *regs)
{
unsigned long ip;
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
if (!(current->ptrace & PT_PTRACED))
return;
/*
* Save IP. IP is used to denote syscall entry/exit:
* IP = 0 -> entry, = 1 -> exit
*/
ip = regs->ARM_ip;
regs->ARM_ip = why;
/* the 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
? 0x80 : 0));
/*
* this isn't the same as continuing with a signal, but it will do
* for normal use. strace only continues with a signal if the
* stopping signal is not SIGTRAP. -brl
*/
if (current->exit_code) {
send_sig(current->exit_code, current, 1);
current->exit_code = 0;
}
regs->ARM_ip = ip;
}

12
arch/arm/kernel/ptrace.h Normal file
View 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 *);

220
arch/arm/kernel/semaphore.c Normal file
View File

@@ -0,0 +1,220 @@
/*
* 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..
*/
asm(" .section .sched.text,\"ax\" \n\
.align 5 \n\
.globl __down_failed \n\
__down_failed: \n\
stmfd sp!, {r0 - r3, lr} \n\
mov r0, ip \n\
bl __down \n\
ldmfd sp!, {r0 - r3, pc} \n\
\n\
.align 5 \n\
.globl __down_interruptible_failed \n\
__down_interruptible_failed: \n\
stmfd sp!, {r0 - r3, lr} \n\
mov r0, ip \n\
bl __down_interruptible \n\
mov ip, r0 \n\
ldmfd sp!, {r0 - r3, pc} \n\
\n\
.align 5 \n\
.globl __down_trylock_failed \n\
__down_trylock_failed: \n\
stmfd sp!, {r0 - r3, lr} \n\
mov r0, ip \n\
bl __down_trylock \n\
mov ip, r0 \n\
ldmfd sp!, {r0 - r3, pc} \n\
\n\
.align 5 \n\
.globl __up_wakeup \n\
__up_wakeup: \n\
stmfd sp!, {r0 - r3, lr} \n\
mov r0, ip \n\
bl __up \n\
ldmfd sp!, {r0 - r3, pc} \n\
");
EXPORT_SYMBOL(__down_failed);
EXPORT_SYMBOL(__down_interruptible_failed);
EXPORT_SYMBOL(__down_trylock_failed);
EXPORT_SYMBOL(__up_wakeup);

875
arch/arm/kernel/setup.c Normal file
View File

@@ -0,0 +1,875 @@
/*
* linux/arch/arm/kernel/setup.c
*
* Copyright (C) 1995-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.
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/stddef.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/utsname.h>
#include <linux/initrd.h>
#include <linux/console.h>
#include <linux/bootmem.h>
#include <linux/seq_file.h>
#include <linux/tty.h>
#include <linux/init.h>
#include <linux/root_dev.h>
#include <linux/cpu.h>
#include <linux/interrupt.h>
#include <asm/cpu.h>
#include <asm/elf.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/procinfo.h>
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include <asm/mach/arch.h>
#include <asm/mach/irq.h>
#include <asm/mach/time.h>
#ifndef MEM_SIZE
#define MEM_SIZE (16*1024*1024)
#endif
#if defined(CONFIG_FPE_NWFPE) || defined(CONFIG_FPE_FASTFPE)
char fpe_type[8];
static int __init fpe_setup(char *line)
{
memcpy(fpe_type, line, 8);
return 1;
}
__setup("fpe=", fpe_setup);
#endif
extern unsigned int mem_fclk_21285;
extern void paging_init(struct meminfo *, struct machine_desc *desc);
extern void convert_to_tag_list(struct tag *tags);
extern void squash_mem_tags(struct tag *tag);
extern void reboot_setup(char *str);
extern int root_mountflags;
extern void _stext, _text, _etext, __data_start, _edata, _end;
unsigned int processor_id;
unsigned int __machine_arch_type;
EXPORT_SYMBOL(__machine_arch_type);
unsigned int system_rev;
EXPORT_SYMBOL(system_rev);
unsigned int system_serial_low;
EXPORT_SYMBOL(system_serial_low);
unsigned int system_serial_high;
EXPORT_SYMBOL(system_serial_high);
unsigned int elf_hwcap;
EXPORT_SYMBOL(elf_hwcap);
#ifdef MULTI_CPU
struct processor processor;
#endif
#ifdef MULTI_TLB
struct cpu_tlb_fns cpu_tlb;
#endif
#ifdef MULTI_USER
struct cpu_user_fns cpu_user;
#endif
#ifdef MULTI_CACHE
struct cpu_cache_fns cpu_cache;
#endif
char elf_platform[ELF_PLATFORM_SIZE];
EXPORT_SYMBOL(elf_platform);
unsigned long phys_initrd_start __initdata = 0;
unsigned long phys_initrd_size __initdata = 0;
static struct meminfo meminfo __initdata = { 0, };
static const char *cpu_name;
static const char *machine_name;
static char command_line[COMMAND_LINE_SIZE];
static char default_command_line[COMMAND_LINE_SIZE] __initdata = CONFIG_CMDLINE;
static union { char c[4]; unsigned long l; } endian_test __initdata = { { 'l', '?', '?', 'b' } };
#define ENDIANNESS ((char)endian_test.l)
DEFINE_PER_CPU(struct cpuinfo_arm, cpu_data);
/*
* Standard memory resources
*/
static struct resource mem_res[] = {
{ "Video RAM", 0, 0, IORESOURCE_MEM },
{ "Kernel text", 0, 0, IORESOURCE_MEM },
{ "Kernel data", 0, 0, IORESOURCE_MEM }
};
#define video_ram mem_res[0]
#define kernel_code mem_res[1]
#define kernel_data mem_res[2]
static struct resource io_res[] = {
{ "reserved", 0x3bc, 0x3be, IORESOURCE_IO | IORESOURCE_BUSY },
{ "reserved", 0x378, 0x37f, IORESOURCE_IO | IORESOURCE_BUSY },
{ "reserved", 0x278, 0x27f, IORESOURCE_IO | IORESOURCE_BUSY }
};
#define lp0 io_res[0]
#define lp1 io_res[1]
#define lp2 io_res[2]
static const char *cache_types[16] = {
"write-through",
"write-back",
"write-back",
"undefined 3",
"undefined 4",
"undefined 5",
"write-back",
"write-back",
"undefined 8",
"undefined 9",
"undefined 10",
"undefined 11",
"undefined 12",
"undefined 13",
"write-back",
"undefined 15",
};
static const char *cache_clean[16] = {
"not required",
"read-block",
"cp15 c7 ops",
"undefined 3",
"undefined 4",
"undefined 5",
"cp15 c7 ops",
"cp15 c7 ops",
"undefined 8",
"undefined 9",
"undefined 10",
"undefined 11",
"undefined 12",
"undefined 13",
"cp15 c7 ops",
"undefined 15",
};
static const char *cache_lockdown[16] = {
"not supported",
"not supported",
"not supported",
"undefined 3",
"undefined 4",
"undefined 5",
"format A",
"format B",
"undefined 8",
"undefined 9",
"undefined 10",
"undefined 11",
"undefined 12",
"undefined 13",
"format C",
"undefined 15",
};
static const char *proc_arch[] = {
"undefined/unknown",
"3",
"4",
"4T",
"5",
"5T",
"5TE",
"5TEJ",
"6TEJ",
"?(10)",
"?(11)",
"?(12)",
"?(13)",
"?(14)",
"?(15)",
"?(16)",
"?(17)",
};
#define CACHE_TYPE(x) (((x) >> 25) & 15)
#define CACHE_S(x) ((x) & (1 << 24))
#define CACHE_DSIZE(x) (((x) >> 12) & 4095) /* only if S=1 */
#define CACHE_ISIZE(x) ((x) & 4095)
#define CACHE_SIZE(y) (((y) >> 6) & 7)
#define CACHE_ASSOC(y) (((y) >> 3) & 7)
#define CACHE_M(y) ((y) & (1 << 2))
#define CACHE_LINE(y) ((y) & 3)
static inline void dump_cache(const char *prefix, int cpu, unsigned int cache)
{
unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0);
printk("CPU%u: %s: %d bytes, associativity %d, %d byte lines, %d sets\n",
cpu, prefix,
mult << (8 + CACHE_SIZE(cache)),
(mult << CACHE_ASSOC(cache)) >> 1,
8 << CACHE_LINE(cache),
1 << (6 + CACHE_SIZE(cache) - CACHE_ASSOC(cache) -
CACHE_LINE(cache)));
}
static void __init dump_cpu_info(int cpu)
{
unsigned int info = read_cpuid(CPUID_CACHETYPE);
if (info != processor_id) {
printk("CPU%u: D %s %s cache\n", cpu, cache_is_vivt() ? "VIVT" : "VIPT",
cache_types[CACHE_TYPE(info)]);
if (CACHE_S(info)) {
dump_cache("I cache", cpu, CACHE_ISIZE(info));
dump_cache("D cache", cpu, CACHE_DSIZE(info));
} else {
dump_cache("cache", cpu, CACHE_ISIZE(info));
}
}
}
int cpu_architecture(void)
{
int cpu_arch;
if ((processor_id & 0x0000f000) == 0) {
cpu_arch = CPU_ARCH_UNKNOWN;
} else if ((processor_id & 0x0000f000) == 0x00007000) {
cpu_arch = (processor_id & (1 << 23)) ? CPU_ARCH_ARMv4T : CPU_ARCH_ARMv3;
} else {
cpu_arch = (processor_id >> 16) & 7;
if (cpu_arch)
cpu_arch += CPU_ARCH_ARMv3;
}
return cpu_arch;
}
/*
* These functions re-use the assembly code in head.S, which
* already provide the required functionality.
*/
extern struct proc_info_list *lookup_processor_type(void);
extern struct machine_desc *lookup_machine_type(unsigned int);
static void __init setup_processor(void)
{
struct proc_info_list *list;
/*
* locate processor in the list of supported processor
* types. The linker builds this table for us from the
* entries in arch/arm/mm/proc-*.S
*/
list = lookup_processor_type();
if (!list) {
printk("CPU configuration botched (ID %08x), unable "
"to continue.\n", processor_id);
while (1);
}
cpu_name = list->cpu_name;
#ifdef MULTI_CPU
processor = *list->proc;
#endif
#ifdef MULTI_TLB
cpu_tlb = *list->tlb;
#endif
#ifdef MULTI_USER
cpu_user = *list->user;
#endif
#ifdef MULTI_CACHE
cpu_cache = *list->cache;
#endif
printk("CPU: %s [%08x] revision %d (ARMv%s)\n",
cpu_name, processor_id, (int)processor_id & 15,
proc_arch[cpu_architecture()]);
dump_cpu_info(smp_processor_id());
sprintf(system_utsname.machine, "%s%c", list->arch_name, ENDIANNESS);
sprintf(elf_platform, "%s%c", list->elf_name, ENDIANNESS);
elf_hwcap = list->elf_hwcap;
cpu_proc_init();
}
static struct machine_desc * __init setup_machine(unsigned int nr)
{
struct machine_desc *list;
/*
* locate machine in the list of supported machines.
*/
list = lookup_machine_type(nr);
if (!list) {
printk("Machine configuration botched (nr %d), unable "
"to continue.\n", nr);
while (1);
}
printk("Machine: %s\n", list->name);
return list;
}
static void __init early_initrd(char **p)
{
unsigned long start, size;
start = memparse(*p, p);
if (**p == ',') {
size = memparse((*p) + 1, p);
phys_initrd_start = start;
phys_initrd_size = size;
}
}
__early_param("initrd=", early_initrd);
/*
* Pick out the memory size. We look for mem=size@start,
* where start and size are "size[KkMm]"
*/
static void __init early_mem(char **p)
{
static int usermem __initdata = 0;
unsigned long size, start;
/*
* If the user specifies memory size, we
* blow away any automatically generated
* size.
*/
if (usermem == 0) {
usermem = 1;
meminfo.nr_banks = 0;
}
start = PHYS_OFFSET;
size = memparse(*p, p);
if (**p == '@')
start = memparse(*p + 1, p);
meminfo.bank[meminfo.nr_banks].start = start;
meminfo.bank[meminfo.nr_banks].size = size;
meminfo.bank[meminfo.nr_banks].node = PHYS_TO_NID(start);
meminfo.nr_banks += 1;
}
__early_param("mem=", early_mem);
/*
* Initial parsing of the command line.
*/
static void __init parse_cmdline(char **cmdline_p, char *from)
{
char c = ' ', *to = command_line;
int len = 0;
for (;;) {
if (c == ' ') {
extern struct early_params __early_begin, __early_end;
struct early_params *p;
for (p = &__early_begin; p < &__early_end; p++) {
int len = strlen(p->arg);
if (memcmp(from, p->arg, len) == 0) {
if (to != command_line)
to -= 1;
from += len;
p->fn(&from);
while (*from != ' ' && *from != '\0')
from++;
break;
}
}
}
c = *from++;
if (!c)
break;
if (COMMAND_LINE_SIZE <= ++len)
break;
*to++ = c;
}
*to = '\0';
*cmdline_p = command_line;
}
static void __init
setup_ramdisk(int doload, int prompt, int image_start, unsigned int rd_sz)
{
#ifdef CONFIG_BLK_DEV_RAM
extern int rd_size, rd_image_start, rd_prompt, rd_doload;
rd_image_start = image_start;
rd_prompt = prompt;
rd_doload = doload;
if (rd_sz)
rd_size = rd_sz;
#endif
}
static void __init
request_standard_resources(struct meminfo *mi, struct machine_desc *mdesc)
{
struct resource *res;
int i;
kernel_code.start = virt_to_phys(&_text);
kernel_code.end = virt_to_phys(&_etext - 1);
kernel_data.start = virt_to_phys(&__data_start);
kernel_data.end = virt_to_phys(&_end - 1);
for (i = 0; i < mi->nr_banks; i++) {
unsigned long virt_start, virt_end;
if (mi->bank[i].size == 0)
continue;
virt_start = __phys_to_virt(mi->bank[i].start);
virt_end = virt_start + mi->bank[i].size - 1;
res = alloc_bootmem_low(sizeof(*res));
res->name = "System RAM";
res->start = __virt_to_phys(virt_start);
res->end = __virt_to_phys(virt_end);
res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
request_resource(&iomem_resource, res);
if (kernel_code.start >= res->start &&
kernel_code.end <= res->end)
request_resource(res, &kernel_code);
if (kernel_data.start >= res->start &&
kernel_data.end <= res->end)
request_resource(res, &kernel_data);
}
if (mdesc->video_start) {
video_ram.start = mdesc->video_start;
video_ram.end = mdesc->video_end;
request_resource(&iomem_resource, &video_ram);
}
/*
* Some machines don't have the possibility of ever
* possessing lp0, lp1 or lp2
*/
if (mdesc->reserve_lp0)
request_resource(&ioport_resource, &lp0);
if (mdesc->reserve_lp1)
request_resource(&ioport_resource, &lp1);
if (mdesc->reserve_lp2)
request_resource(&ioport_resource, &lp2);
}
/*
* Tag parsing.
*
* This is the new way of passing data to the kernel at boot time. Rather
* than passing a fixed inflexible structure to the kernel, we pass a list
* of variable-sized tags to the kernel. The first tag must be a ATAG_CORE
* tag for the list to be recognised (to distinguish the tagged list from
* a param_struct). The list is terminated with a zero-length tag (this tag
* is not parsed in any way).
*/
static int __init parse_tag_core(const struct tag *tag)
{
if (tag->hdr.size > 2) {
if ((tag->u.core.flags & 1) == 0)
root_mountflags &= ~MS_RDONLY;
ROOT_DEV = old_decode_dev(tag->u.core.rootdev);
}
return 0;
}
__tagtable(ATAG_CORE, parse_tag_core);
static int __init parse_tag_mem32(const struct tag *tag)
{
if (meminfo.nr_banks >= NR_BANKS) {
printk(KERN_WARNING
"Ignoring memory bank 0x%08x size %dKB\n",
tag->u.mem.start, tag->u.mem.size / 1024);
return -EINVAL;
}
meminfo.bank[meminfo.nr_banks].start = tag->u.mem.start;
meminfo.bank[meminfo.nr_banks].size = tag->u.mem.size;
meminfo.bank[meminfo.nr_banks].node = PHYS_TO_NID(tag->u.mem.start);
meminfo.nr_banks += 1;
return 0;
}
__tagtable(ATAG_MEM, parse_tag_mem32);
#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_DUMMY_CONSOLE)
struct screen_info screen_info = {
.orig_video_lines = 30,
.orig_video_cols = 80,
.orig_video_mode = 0,
.orig_video_ega_bx = 0,
.orig_video_isVGA = 1,
.orig_video_points = 8
};
static int __init parse_tag_videotext(const struct tag *tag)
{
screen_info.orig_x = tag->u.videotext.x;
screen_info.orig_y = tag->u.videotext.y;
screen_info.orig_video_page = tag->u.videotext.video_page;
screen_info.orig_video_mode = tag->u.videotext.video_mode;
screen_info.orig_video_cols = tag->u.videotext.video_cols;
screen_info.orig_video_ega_bx = tag->u.videotext.video_ega_bx;
screen_info.orig_video_lines = tag->u.videotext.video_lines;
screen_info.orig_video_isVGA = tag->u.videotext.video_isvga;
screen_info.orig_video_points = tag->u.videotext.video_points;
return 0;
}
__tagtable(ATAG_VIDEOTEXT, parse_tag_videotext);
#endif
static int __init parse_tag_ramdisk(const struct tag *tag)
{
setup_ramdisk((tag->u.ramdisk.flags & 1) == 0,
(tag->u.ramdisk.flags & 2) == 0,
tag->u.ramdisk.start, tag->u.ramdisk.size);
return 0;
}
__tagtable(ATAG_RAMDISK, parse_tag_ramdisk);
static int __init parse_tag_initrd(const struct tag *tag)
{
printk(KERN_WARNING "ATAG_INITRD is deprecated; "
"please update your bootloader.\n");
phys_initrd_start = __virt_to_phys(tag->u.initrd.start);
phys_initrd_size = tag->u.initrd.size;
return 0;
}
__tagtable(ATAG_INITRD, parse_tag_initrd);
static int __init parse_tag_initrd2(const struct tag *tag)
{
phys_initrd_start = tag->u.initrd.start;
phys_initrd_size = tag->u.initrd.size;
return 0;
}
__tagtable(ATAG_INITRD2, parse_tag_initrd2);
static int __init parse_tag_serialnr(const struct tag *tag)
{
system_serial_low = tag->u.serialnr.low;
system_serial_high = tag->u.serialnr.high;
return 0;
}
__tagtable(ATAG_SERIAL, parse_tag_serialnr);
static int __init parse_tag_revision(const struct tag *tag)
{
system_rev = tag->u.revision.rev;
return 0;
}
__tagtable(ATAG_REVISION, parse_tag_revision);
static int __init parse_tag_cmdline(const struct tag *tag)
{
strlcpy(default_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE);
return 0;
}
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
/*
* Scan the tag table for this tag, and call its parse function.
* The tag table is built by the linker from all the __tagtable
* declarations.
*/
static int __init parse_tag(const struct tag *tag)
{
extern struct tagtable __tagtable_begin, __tagtable_end;
struct tagtable *t;
for (t = &__tagtable_begin; t < &__tagtable_end; t++)
if (tag->hdr.tag == t->tag) {
t->parse(tag);
break;
}
return t < &__tagtable_end;
}
/*
* Parse all tags in the list, checking both the global and architecture
* specific tag tables.
*/
static void __init parse_tags(const struct tag *t)
{
for (; t->hdr.size; t = tag_next(t))
if (!parse_tag(t))
printk(KERN_WARNING
"Ignoring unrecognised tag 0x%08x\n",
t->hdr.tag);
}
/*
* This holds our defaults.
*/
static struct init_tags {
struct tag_header hdr1;
struct tag_core core;
struct tag_header hdr2;
struct tag_mem32 mem;
struct tag_header hdr3;
} init_tags __initdata = {
{ tag_size(tag_core), ATAG_CORE },
{ 1, PAGE_SIZE, 0xff },
{ tag_size(tag_mem32), ATAG_MEM },
{ MEM_SIZE, PHYS_OFFSET },
{ 0, ATAG_NONE }
};
static void (*init_machine)(void) __initdata;
static int __init customize_machine(void)
{
/* customizes platform devices, or adds new ones */
if (init_machine)
init_machine();
return 0;
}
arch_initcall(customize_machine);
void __init setup_arch(char **cmdline_p)
{
struct tag *tags = (struct tag *)&init_tags;
struct machine_desc *mdesc;
char *from = default_command_line;
setup_processor();
mdesc = setup_machine(machine_arch_type);
machine_name = mdesc->name;
if (mdesc->soft_reboot)
reboot_setup("s");
if (mdesc->param_offset)
tags = phys_to_virt(mdesc->param_offset);
/*
* If we have the old style parameters, convert them to
* a tag list.
*/
if (tags->hdr.tag != ATAG_CORE)
convert_to_tag_list(tags);
if (tags->hdr.tag != ATAG_CORE)
tags = (struct tag *)&init_tags;
if (mdesc->fixup)
mdesc->fixup(mdesc, tags, &from, &meminfo);
if (tags->hdr.tag == ATAG_CORE) {
if (meminfo.nr_banks != 0)
squash_mem_tags(tags);
parse_tags(tags);
}
init_mm.start_code = (unsigned long) &_text;
init_mm.end_code = (unsigned long) &_etext;
init_mm.end_data = (unsigned long) &_edata;
init_mm.brk = (unsigned long) &_end;
memcpy(saved_command_line, from, COMMAND_LINE_SIZE);
saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
parse_cmdline(cmdline_p, from);
paging_init(&meminfo, mdesc);
request_standard_resources(&meminfo, mdesc);
/*
* Set up various architecture-specific pointers
*/
init_arch_irq = mdesc->init_irq;
system_timer = mdesc->timer;
init_machine = mdesc->init_machine;
#ifdef CONFIG_VT
#if defined(CONFIG_VGA_CONSOLE)
conswitchp = &vga_con;
#elif defined(CONFIG_DUMMY_CONSOLE)
conswitchp = &dummy_con;
#endif
#endif
}
static int __init topology_init(void)
{
int cpu;
for_each_cpu(cpu)
register_cpu(&per_cpu(cpu_data, cpu).cpu, cpu, NULL);
return 0;
}
subsys_initcall(topology_init);
static const char *hwcap_str[] = {
"swp",
"half",
"thumb",
"26bit",
"fastmult",
"fpa",
"vfp",
"edsp",
"java",
NULL
};
static void
c_show_cache(struct seq_file *m, const char *type, unsigned int cache)
{
unsigned int mult = 2 + (CACHE_M(cache) ? 1 : 0);
seq_printf(m, "%s size\t\t: %d\n"
"%s assoc\t\t: %d\n"
"%s line length\t: %d\n"
"%s sets\t\t: %d\n",
type, mult << (8 + CACHE_SIZE(cache)),
type, (mult << CACHE_ASSOC(cache)) >> 1,
type, 8 << CACHE_LINE(cache),
type, 1 << (6 + CACHE_SIZE(cache) - CACHE_ASSOC(cache) -
CACHE_LINE(cache)));
}
static int c_show(struct seq_file *m, void *v)
{
int i;
seq_printf(m, "Processor\t: %s rev %d (%s)\n",
cpu_name, (int)processor_id & 15, elf_platform);
#if defined(CONFIG_SMP)
for_each_online_cpu(i) {
seq_printf(m, "Processor\t: %d\n", i);
seq_printf(m, "BogoMIPS\t: %lu.%02lu\n\n",
per_cpu(cpu_data, i).loops_per_jiffy / (500000UL/HZ),
(per_cpu(cpu_data, i).loops_per_jiffy / (5000UL/HZ)) % 100);
}
#else /* CONFIG_SMP */
seq_printf(m, "BogoMIPS\t: %lu.%02lu\n",
loops_per_jiffy / (500000/HZ),
(loops_per_jiffy / (5000/HZ)) % 100);
#endif
/* dump out the processor features */
seq_puts(m, "Features\t: ");
for (i = 0; hwcap_str[i]; i++)
if (elf_hwcap & (1 << i))
seq_printf(m, "%s ", hwcap_str[i]);
seq_printf(m, "\nCPU implementer\t: 0x%02x\n", processor_id >> 24);
seq_printf(m, "CPU architecture: %s\n", proc_arch[cpu_architecture()]);
if ((processor_id & 0x0000f000) == 0x00000000) {
/* pre-ARM7 */
seq_printf(m, "CPU part\t\t: %07x\n", processor_id >> 4);
} else {
if ((processor_id & 0x0000f000) == 0x00007000) {
/* ARM7 */
seq_printf(m, "CPU variant\t: 0x%02x\n",
(processor_id >> 16) & 127);
} else {
/* post-ARM7 */
seq_printf(m, "CPU variant\t: 0x%x\n",
(processor_id >> 20) & 15);
}
seq_printf(m, "CPU part\t: 0x%03x\n",
(processor_id >> 4) & 0xfff);
}
seq_printf(m, "CPU revision\t: %d\n", processor_id & 15);
{
unsigned int cache_info = read_cpuid(CPUID_CACHETYPE);
if (cache_info != processor_id) {
seq_printf(m, "Cache type\t: %s\n"
"Cache clean\t: %s\n"
"Cache lockdown\t: %s\n"
"Cache format\t: %s\n",
cache_types[CACHE_TYPE(cache_info)],
cache_clean[CACHE_TYPE(cache_info)],
cache_lockdown[CACHE_TYPE(cache_info)],
CACHE_S(cache_info) ? "Harvard" : "Unified");
if (CACHE_S(cache_info)) {
c_show_cache(m, "I", CACHE_ISIZE(cache_info));
c_show_cache(m, "D", CACHE_DSIZE(cache_info));
} else {
c_show_cache(m, "Cache", CACHE_ISIZE(cache_info));
}
}
}
seq_puts(m, "\n");
seq_printf(m, "Hardware\t: %s\n", machine_name);
seq_printf(m, "Revision\t: %04x\n", system_rev);
seq_printf(m, "Serial\t\t: %08x%08x\n",
system_serial_high, system_serial_low);
return 0;
}
static void *c_start(struct seq_file *m, loff_t *pos)
{
return *pos < 1 ? (void *)1 : NULL;
}
static void *c_next(struct seq_file *m, void *v, loff_t *pos)
{
++*pos;
return NULL;
}
static void c_stop(struct seq_file *m, void *v)
{
}
struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
.show = c_show
};

748
arch/arm/kernel/signal.c Normal file
View File

@@ -0,0 +1,748 @@
/*
* 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/config.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/ptrace.h>
#include <linux/personality.h>
#include <asm/cacheflush.h>
#include <asm/ucontext.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include "ptrace.h"
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
/*
* For ARM syscalls, we encode the syscall number into the instruction.
*/
#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn))
#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn))
/*
* 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))
static const unsigned long retcodes[4] = {
SWI_SYS_SIGRETURN, SWI_THUMB_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(&current->sighand->siglock);
saveset = current->blocked;
siginitset(&current->blocked, mask);
recalc_sigpending();
spin_unlock_irq(&current->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(&current->sighand->siglock);
saveset = current->blocked;
current->blocked = newset;
recalc_sigpending();
spin_unlock_irq(&current->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_IWMMXT
/* iwmmxt_area is 0x98 bytes long, preceeded by 8 bytes of signature */
#define IWMMXT_STORAGE_SIZE (0x98 + 8)
#define IWMMXT_MAGIC0 0x12ef842a
#define IWMMXT_MAGIC1 0x1c07ca71
struct iwmmxt_sigframe {
unsigned long magic0;
unsigned long magic1;
unsigned long storage[0x98/4];
};
static int page_present(struct mm_struct *mm, void __user *uptr, int wr)
{
unsigned long addr = (unsigned long)uptr;
pgd_t *pgd = pgd_offset(mm, addr);
if (pgd_present(*pgd)) {
pmd_t *pmd = pmd_offset(pgd, addr);
if (pmd_present(*pmd)) {
pte_t *pte = pte_offset_map(pmd, addr);
return (pte_present(*pte) && (!wr || pte_write(*pte)));
}
}
return 0;
}
static int copy_locked(void __user *uptr, void *kptr, size_t size, int write,
void (*copyfn)(void *, void __user *))
{
unsigned char v, __user *userptr = uptr;
int err = 0;
do {
struct mm_struct *mm;
if (write) {
__put_user_error(0, userptr, err);
__put_user_error(0, userptr + size - 1, err);
} else {
__get_user_error(v, userptr, err);
__get_user_error(v, userptr + size - 1, err);
}
if (err)
break;
mm = current->mm;
spin_lock(&mm->page_table_lock);
if (page_present(mm, userptr, write) &&
page_present(mm, userptr + size - 1, write)) {
copyfn(kptr, uptr);
} else
err = 1;
spin_unlock(&mm->page_table_lock);
} while (err);
return err;
}
static int preserve_iwmmxt_context(struct iwmmxt_sigframe *frame)
{
int err = 0;
/* the iWMMXt context must be 64 bit aligned */
WARN_ON((unsigned long)frame & 7);
__put_user_error(IWMMXT_MAGIC0, &frame->magic0, err);
__put_user_error(IWMMXT_MAGIC1, &frame->magic1, err);
/*
* iwmmxt_task_copy() doesn't check user permissions.
* Let's do a dummy write on the upper boundary to ensure
* access to user mem is OK all way up.
*/
err |= copy_locked(&frame->storage, current_thread_info(),
sizeof(frame->storage), 1, iwmmxt_task_copy);
return err;
}
static int restore_iwmmxt_context(struct iwmmxt_sigframe *frame)
{
unsigned long magic0, magic1;
int err = 0;
/* the iWMMXt context is 64 bit aligned */
WARN_ON((unsigned long)frame & 7);
/*
* Validate iWMMXt context signature.
* Also, iwmmxt_task_restore() doesn't check user permissions.
* Let's do a dummy write on the upper boundary to ensure
* access to user mem is OK all way up.
*/
__get_user_error(magic0, &frame->magic0, err);
__get_user_error(magic1, &frame->magic1, err);
if (!err && magic0 == IWMMXT_MAGIC0 && magic1 == IWMMXT_MAGIC1)
err = copy_locked(&frame->storage, current_thread_info(),
sizeof(frame->storage), 0, iwmmxt_task_restore);
return err;
}
#endif
/*
* Auxiliary signal frame. This saves stuff like FP state.
* The layout of this structure is not part of the user ABI.
*/
struct aux_sigframe {
#ifdef CONFIG_IWMMXT
struct iwmmxt_sigframe iwmmxt;
#endif
#ifdef CONFIG_VFP
union vfp_state vfp;
#endif
};
/*
* Do a signal return; undo the signal stack. These are aligned to 64-bit.
*/
struct sigframe {
struct sigcontext sc;
unsigned long extramask[_NSIG_WORDS-1];
unsigned long retcode;
struct aux_sigframe aux __attribute__((aligned(8)));
};
struct rt_sigframe {
struct siginfo __user *pinfo;
void __user *puc;
struct siginfo info;
struct ucontext uc;
unsigned long retcode;
struct aux_sigframe aux __attribute__((aligned(8)));
};
static int
restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
struct aux_sigframe __user *aux)
{
int err = 0;
__get_user_error(regs->ARM_r0, &sc->arm_r0, err);
__get_user_error(regs->ARM_r1, &sc->arm_r1, err);
__get_user_error(regs->ARM_r2, &sc->arm_r2, err);
__get_user_error(regs->ARM_r3, &sc->arm_r3, err);
__get_user_error(regs->ARM_r4, &sc->arm_r4, err);
__get_user_error(regs->ARM_r5, &sc->arm_r5, err);
__get_user_error(regs->ARM_r6, &sc->arm_r6, err);
__get_user_error(regs->ARM_r7, &sc->arm_r7, err);
__get_user_error(regs->ARM_r8, &sc->arm_r8, err);
__get_user_error(regs->ARM_r9, &sc->arm_r9, err);
__get_user_error(regs->ARM_r10, &sc->arm_r10, err);
__get_user_error(regs->ARM_fp, &sc->arm_fp, err);
__get_user_error(regs->ARM_ip, &sc->arm_ip, err);
__get_user_error(regs->ARM_sp, &sc->arm_sp, err);
__get_user_error(regs->ARM_lr, &sc->arm_lr, err);
__get_user_error(regs->ARM_pc, &sc->arm_pc, err);
__get_user_error(regs->ARM_cpsr, &sc->arm_cpsr, err);
err |= !valid_user_regs(regs);
#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(&aux->vfp);
#endif
return err;
}
asmlinkage int sys_sigreturn(struct pt_regs *regs)
{
struct sigframe __user *frame;
sigset_t set;
/* 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 (__get_user(set.sig[0], &frame->sc.oldmask)
|| (_NSIG_WORDS > 1
&& __copy_from_user(&set.sig[1], &frame->extramask,
sizeof(frame->extramask))))
goto badframe;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(regs, &frame->sc, &frame->aux))
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;
sigset_t set;
/* 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 (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
sigdelsetmask(&set, ~_BLOCKABLE);
spin_lock_irq(&current->sighand->siglock);
current->blocked = set;
recalc_sigpending();
spin_unlock_irq(&current->sighand->siglock);
if (restore_sigcontext(regs, &frame->uc.uc_mcontext, &frame->aux))
goto badframe;
if (do_sigaltstack(&frame->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_sigcontext(struct sigcontext __user *sc, struct aux_sigframe __user *aux,
struct pt_regs *regs, unsigned long mask)
{
int err = 0;
__put_user_error(regs->ARM_r0, &sc->arm_r0, err);
__put_user_error(regs->ARM_r1, &sc->arm_r1, err);
__put_user_error(regs->ARM_r2, &sc->arm_r2, err);
__put_user_error(regs->ARM_r3, &sc->arm_r3, err);
__put_user_error(regs->ARM_r4, &sc->arm_r4, err);
__put_user_error(regs->ARM_r5, &sc->arm_r5, err);
__put_user_error(regs->ARM_r6, &sc->arm_r6, err);
__put_user_error(regs->ARM_r7, &sc->arm_r7, err);
__put_user_error(regs->ARM_r8, &sc->arm_r8, err);
__put_user_error(regs->ARM_r9, &sc->arm_r9, err);
__put_user_error(regs->ARM_r10, &sc->arm_r10, err);
__put_user_error(regs->ARM_fp, &sc->arm_fp, err);
__put_user_error(regs->ARM_ip, &sc->arm_ip, err);
__put_user_error(regs->ARM_sp, &sc->arm_sp, err);
__put_user_error(regs->ARM_lr, &sc->arm_lr, err);
__put_user_error(regs->ARM_pc, &sc->arm_pc, err);
__put_user_error(regs->ARM_cpsr, &sc->arm_cpsr, err);
__put_user_error(current->thread.trap_no, &sc->trap_no, err);
__put_user_error(current->thread.error_code, &sc->error_code, err);
__put_user_error(current->thread.address, &sc->fault_address, err);
__put_user_error(mask, &sc->oldmask, err);
#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(&aux->vfp);
#endif
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;
if (ka->sa.sa_flags & SA_SIGINFO)
idx += 2;
if (__put_user(retcodes[idx], rc))
return 1;
/*
* Ensure that the instruction cache sees
* the return code written onto the stack.
*/
flush_icache_range((unsigned long)rc,
(unsigned long)(rc + 1));
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;
err |= setup_sigcontext(&frame->sc, &frame->aux, regs, set->sig[0]);
if (_NSIG_WORDS > 1) {
err |= __copy_to_user(frame->extramask, &set->sig[1],
sizeof(frame->extramask));
}
if (err == 0)
err = setup_return(regs, ka, &frame->retcode, frame, usig);
return err;
}
static int
setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe __user *frame = get_sigframe(ka, regs, sizeof(*frame));
stack_t stack;
int err = 0;
if (!frame)
return 1;
__put_user_error(&frame->info, &frame->pinfo, err);
__put_user_error(&frame->uc, &frame->puc, err);
err |= copy_siginfo_to_user(&frame->info, info);
__put_user_error(0, &frame->uc.uc_flags, err);
__put_user_error(NULL, &frame->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->uc.uc_stack, &stack, sizeof(stack));
err |= setup_sigcontext(&frame->uc.uc_mcontext, &frame->aux,
regs, set->sig[0]);
err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (err == 0)
err = setup_return(regs, ka, &frame->retcode, frame, usig);
if (err == 0) {
/*
* For realtime signals we must also set the second and third
* arguments for the signal handler.
* -- Peter Maydell <pmaydell@chiark.greenend.org.uk> 2000-12-06
*/
regs->ARM_r1 = (unsigned long)&frame->info;
regs->ARM_r2 = (unsigned long)&frame->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);
/*
* Block the signal if we were unsuccessful.
*/
if (ret != 0 || !(ka->sa.sa_flags & SA_NODEFER)) {
spin_lock_irq(&tsk->sighand->siglock);
sigorsets(&tsk->blocked, &tsk->blocked,
&ka->sa.sa_mask);
sigaddset(&tsk->blocked, sig);
recalc_sigpending();
spin_unlock_irq(&tsk->sighand->siglock);
}
if (ret == 0)
return;
force_sigsegv(sig, tsk);
}
/*
* Note that 'init' is a special process: it doesn't get signals it doesn't
* want to handle. Thus you cannot kill init even with a SIGKILL even by
* mistake.
*
* Note that we go through the signals twice: once to check the signals that
* the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that.
*/
static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
{
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(0))
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;
regs->ARM_pc -= 2;
} else {
u32 __user *usp;
regs->ARM_sp -= 12;
usp = (u32 __user *)regs->ARM_sp;
put_user(regs->ARM_pc, &usp[0]);
/* swi __NR_restart_syscall */
put_user(0xef000000 | __NR_restart_syscall, &usp[1]);
/* ldr pc, [sp], #12 */
put_user(0xe49df00c, &usp[2]);
flush_icache_range((unsigned long)usp,
(unsigned long)(usp + 3));
regs->ARM_pc = regs->ARM_sp + 4;
}
}
if (regs->ARM_r0 == -ERESTARTNOHAND ||
regs->ARM_r0 == -ERESTARTSYS ||
regs->ARM_r0 == -ERESTARTNOINTR) {
restart_syscall(regs);
}
}
if (current->ptrace & PT_SINGLESTEP)
ptrace_set_bpt(current);
return 0;
}
asmlinkage void
do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{
if (thread_flags & _TIF_SIGPENDING)
do_signal(&current->blocked, regs, syscall);
}

396
arch/arm/kernel/smp.c Normal file
View File

@@ -0,0 +1,396 @@
/*
* 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/config.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 <asm/atomic.h>
#include <asm/cacheflush.h>
#include <asm/cpu.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_present_mask;
cpumask_t cpu_online_map;
/*
* 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 __init __cpu_up(unsigned int cpu)
{
struct task_struct *idle;
int ret;
/*
* Spawn a new process manually. Grab a pointer to
* its task struct so we can mess with it
*/
idle = fork_idle(cpu);
if (IS_ERR(idle)) {
printk(KERN_ERR "CPU%u: fork() failed\n", cpu);
return PTR_ERR(idle);
}
/*
* Now bring the CPU into our world.
*/
ret = boot_secondary(cpu, idle);
if (ret) {
printk(KERN_CRIT "cpu_up: processor %d failed to boot\n", cpu);
/*
* FIXME: We need to clean up the new idle thread. --rmk
*/
}
return ret;
}
/*
* Called by both boot and secondaries to move global data into
* per-processor storage.
*/
void __init 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();
cpu_set(cpu, cpu_present_mask);
cpu_set(cpu, cpu_online_map);
}
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.
*/
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, callmap, 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);
}
void show_ipi_list(struct seq_file *p)
{
unsigned int cpu;
seq_puts(p, "IPI:");
for_each_online_cpu(cpu)
seq_printf(p, " %10lu", per_cpu(ipi_data, cpu).ipi_count);
seq_putc(p, '\n');
}
static void ipi_timer(struct pt_regs *regs)
{
int user = user_mode(regs);
irq_enter();
profile_tick(CPU_PROFILING, regs);
update_process_times(user);
irq_exit();
}
/*
* 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
*/
void do_IPI(struct pt_regs *regs)
{
unsigned int cpu = smp_processor_id();
struct ipi_data *ipi = &per_cpu(ipi_data, cpu);
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(regs);
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);
}
}
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;
}

332
arch/arm/kernel/sys_arm.c Normal file
View File

@@ -0,0 +1,332 @@
/*
* 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;
}
/*
* This is the lowest virtual address we can permit any user space
* mapping to be mapped at. This is particularly important for
* non-high vector CPUs.
*/
#define MIN_MAP_ADDR (PAGE_SIZE)
/* 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 < MIN_MAP_ADDR)
goto out;
error = -EBADF;
if (!(flags & MAP_ANONYMOUS)) {
file = fget(fd);
if (!file)
goto out;
}
down_write(&current->mm->mmap_sem);
error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
up_write(&current->mm->mmap_sem);
if (file)
fput(file);
out:
return error;
}
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 < MIN_MAP_ADDR)
goto out;
down_write(&current->mm->mmap_sem);
ret = do_mremap(addr, old_len, new_len, flags, new_addr);
up_write(&current->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);
}
/*
* 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;
}
}
asmlinkage long sys_shmat(int shmid, char __user *shmaddr, int shmflg,
unsigned long __user *addr)
{
unsigned long ret;
long err;
err = do_shmat(shmid, shmaddr, shmflg, &ret);
if (err == 0)
err = put_user(ret, addr);
return err;
}
/* Fork a new task - this creates a new program thread.
* This is called indirectly via a small wrapper
*/
asmlinkage int sys_fork(struct pt_regs *regs)
{
return do_fork(SIGCHLD, regs->ARM_sp, regs, 0, NULL, NULL);
}
/* Clone a task - this clones the calling program thread.
* This is called indirectly via a small wrapper
*/
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
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;
}
long execve(const char *filename, char **argv, char **envp)
{
struct pt_regs regs;
int ret;
memset(&regs, 0, sizeof(struct pt_regs));
ret = do_execve((char *)filename, (char __user * __user *)argv,
(char __user * __user *)envp, &regs);
if (ret < 0)
goto out;
/*
* Save argc to the register structure for userspace.
*/
regs.ARM_r0 = ret;
/*
* We were successful. We won't be returning to our caller, but
* instead to user space by manipulating the kernel stack.
*/
asm( "add r0, %0, %1\n\t"
"mov r1, %2\n\t"
"mov r2, %3\n\t"
"bl memmove\n\t" /* copy regs to top of stack */
"mov r8, #0\n\t" /* not a syscall */
"mov r9, %0\n\t" /* thread structure */
"mov sp, r0\n\t" /* reposition stack pointer */
"b ret_to_user"
:
: "r" (current_thread_info()),
"Ir" (THREAD_SIZE - 8 - sizeof(regs)),
"r" (&regs),
"Ir" (sizeof(regs))
: "r0", "r1", "r2", "r3", "ip", "memory");
out:
return ret;
}
EXPORT_SYMBOL(execve);

402
arch/arm/kernel/time.c Normal file
View File

@@ -0,0 +1,402 @@
/*
* 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/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/timex.h>
#include <linux/errno.h>
#include <linux/profile.h>
#include <linux/sysdev.h>
#include <linux/timer.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/leds.h>
#include <asm/thread_info.h>
#include <asm/mach/time.h>
u64 jiffies_64 = INITIAL_JIFFIES;
EXPORT_SYMBOL(jiffies_64);
/*
* Our system timer.
*/
struct sys_timer *system_timer;
extern unsigned long wall_jiffies;
/* this needs a better home */
DEFINE_SPINLOCK(rtc_lock);
#ifdef CONFIG_SA1100_RTC_MODULE
EXPORT_SYMBOL(rtc_lock);
#endif
/* 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);
static unsigned long dummy_gettimeoffset(void)
{
return 0;
}
/*
* Scheduler clock - returns current time in nanosec units.
* This is the default implementation. Sub-architecture
* implementations can override this.
*/
unsigned long long __attribute__((weak)) sched_clock(void)
{
return (unsigned long long)jiffies * (1000000000 / HZ);
}
static unsigned long next_rtc_update;
/*
* If we have an externally synchronized linux clock, then update
* CMOS clock accordingly every ~11 minutes. set_rtc() has to be
* called as close as possible to 500 ms before the new second
* starts.
*/
static inline void do_set_rtc(void)
{
if (time_status & STA_UNSYNC || set_rtc == NULL)
return;
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 = 50;
if (--count == 0) {
count = 50;
leds_event(led_timer);
}
}
#else
#define do_leds()
#endif
void do_gettimeofday(struct timeval *tv)
{
unsigned long flags;
unsigned long seq;
unsigned long usec, sec, lost;
do {
seq = read_seqbegin_irqsave(&xtime_lock, flags);
usec = system_timer->offset();
lost = jiffies - wall_jiffies;
if (lost)
usec += lost * USECS_PER_JIFFY;
sec = xtime.tv_sec;
usec += xtime.tv_nsec / 1000;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
/* usec may have gone up a lot: be safe */
while (usec >= 1000000) {
usec -= 1000000;
sec++;
}
tv->tv_sec = sec;
tv->tv_usec = usec;
}
EXPORT_SYMBOL(do_gettimeofday);
int do_settimeofday(struct timespec *tv)
{
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;
nsec -= (jiffies - wall_jiffies) * TICK_NSEC;
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
set_normalized_timespec(&xtime, sec, nsec);
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
write_sequnlock_irq(&xtime_lock);
clock_was_set();
return 0;
}
EXPORT_SYMBOL(do_settimeofday);
/**
* 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(struct pt_regs *regs)
{
profile_tick(CPU_PROFILING, regs);
do_leds();
do_set_rtc();
do_timer(regs);
#ifndef CONFIG_SMP
update_process_times(user_mode(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,
};
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);
}
return ret;
}
device_initcall(timer_init_sysfs);
void __init time_init(void)
{
if (system_timer->offset == NULL)
system_timer->offset = dummy_gettimeoffset;
system_timer->init();
}

590
arch/arm/kernel/traps.c Normal file
View File

@@ -0,0 +1,590 @@
/*
* 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/config.h>
#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/init.h>
#include <asm/atomic.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include <asm/traps.h>
#include "ptrace.h"
extern void c_backtrace (unsigned long fp, int pmode);
extern void show_pte(struct mm_struct *mm, unsigned long addr);
const char *processor_modes[]=
{ "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "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"
};
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)(tsk->thread_info + 1))
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));
c_backtrace(fp, 0x10);
barrier();
}
DEFINE_SPINLOCK(die_lock);
/*
* This function is protected against re-entrancy.
*/
NORET_TYPE void die(const char *str, struct pt_regs *regs, int err)
{
struct task_struct *tsk = current;
static int die_counter;
console_verbose();
spin_lock_irq(&die_lock);
bust_spinlocks(1);
printk("Internal error: %s: %x [#%d]\n", str, err, ++die_counter);
print_modules();
printk("CPU: %d\n", smp_processor_id());
show_regs(regs);
printk("Process %s (pid: %d, stack limit = 0x%p)\n",
tsk->comm, tsk->pid, tsk->thread_info + 1);
if (!user_mode(regs) || in_interrupt()) {
dump_mem("Stack: ", regs->ARM_sp, 8192+(unsigned long)tsk->thread_info);
dump_backtrace(regs, tsk);
dump_instr(regs);
}
bust_spinlocks(0);
spin_unlock_irq(&die_lock);
do_exit(SIGSEGV);
}
void die_if_kernel(const char *str, struct pt_regs *regs, int err)
{
if (user_mode(regs))
return;
die(str, regs, err);
}
static void notify_die(const char *str, struct pt_regs *regs, siginfo_t *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)
{
spin_lock_irq(&undef_lock);
list_add(&hook->node, &undef_hook);
spin_unlock_irq(&undef_lock);
}
void unregister_undef_hook(struct undef_hook *hook)
{
spin_lock_irq(&undef_lock);
list_del(&hook->node);
spin_unlock_irq(&undef_lock);
}
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;
/*
* 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 (thumb_mode(regs)) {
get_user(instr, (u16 __user *)pc);
} else {
get_user(instr, (u32 __user *)pc);
}
spin_lock_irq(&undef_lock);
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_irq(&undef_lock);
#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, int proc_mode)
{
console_verbose();
printk(KERN_CRIT "Bad mode in %s handler detected: mode %s\n",
handler[reason], processor_modes[proc_mode]);
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 && 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) != 0x9f)
return bad_syscall(no, regs);
switch (no & 0xffff) {
case 0: /* branch through 0 */
info.si_signo = SIGSEGV;
info.si_errno = 0;
info.si_code = SEGV_MAPERR;
info.si_addr = NULL;
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;
/*
* Our user accessible TLS ptr is located at 0xffff0ffc.
* On SMP read access to this address must raise a fault
* and be emulated from the data abort handler.
* m
*/
*((unsigned long *)0xffff0ffc) = thread->tp_value;
return 0;
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;
}
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);
}
volatile void __bug(const char *file, int line, void *data)
{
printk(KERN_CRIT"kernel BUG at %s:%d!", file, line);
if (data)
printk(" - extra data = %p", data);
printk("\n");
*(int *)0 = 0;
}
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)
{
extern void __trap_init(void);
__trap_init();
flush_icache_range(0xffff0000, 0xffff0000 + PAGE_SIZE);
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
}

View File

@@ -0,0 +1,166 @@
/* 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 <linux/config.h>
OUTPUT_ARCH(arm)
ENTRY(stext)
#ifndef __ARMEB__
jiffies = jiffies_64;
#else
jiffies = jiffies_64 + 4;
#endif
SECTIONS
{
. = TEXTADDR;
.init : { /* Init code and data */
_stext = .;
_sinittext = .;
*(.init.text)
_einittext = .;
__proc_info_begin = .;
*(.proc.info)
__proc_info_end = .;
__arch_info_begin = .;
*(.arch.info)
__arch_info_end = .;
__tagtable_begin = .;
*(.taglist)
__tagtable_end = .;
. = ALIGN(16);
__setup_start = .;
*(.init.setup)
__setup_end = .;
__early_begin = .;
*(__early_param)
__early_end = .;
__initcall_start = .;
*(.initcall1.init)
*(.initcall2.init)
*(.initcall3.init)
*(.initcall4.init)
*(.initcall5.init)
*(.initcall6.init)
*(.initcall7.init)
__initcall_end = .;
__con_initcall_start = .;
*(.con_initcall.init)
__con_initcall_end = .;
__security_initcall_start = .;
*(.security_initcall.init)
__security_initcall_end = .;
. = ALIGN(32);
__initramfs_start = .;
usr/built-in.o(.init.ramfs)
__initramfs_end = .;
. = 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)
}
.text : { /* Real text segment */
_text = .; /* Text and read-only data */
*(.text)
SCHED_TEXT
LOCK_TEXT
*(.fixup)
*(.gnu.warning)
*(.rodata)
*(.rodata.*)
*(.glue_7)
*(.glue_7t)
*(.got) /* Global offset table */
}
. = ALIGN(16);
__ex_table : { /* Exception table */
__start___ex_table = .;
*(__ex_table)
__stop___ex_table = .;
}
RODATA
_etext = .; /* End of text and rodata section */
#ifdef CONFIG_XIP_KERNEL
__data_loc = ALIGN(4); /* location in binary */
. = DATAADDR;
#else
. = ALIGN(8192);
__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)
/*
* and the usual data section
*/
*(.data)
CONSTRUCTORS
_edata = .;
}
.bss : {
__bss_start = .; /* BSS */
*(.bss)
*(COMMON)
_end = .;
}
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
}
/* those must never be empty */
ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support")
ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined")