diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/m68knommu/kernel |
Linux-2.6.12-rc2v2.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!
Diffstat (limited to 'arch/m68knommu/kernel')
-rw-r--r-- | arch/m68knommu/kernel/Makefile | 11 | ||||
-rw-r--r-- | arch/m68knommu/kernel/asm-offsets.c | 101 | ||||
-rw-r--r-- | arch/m68knommu/kernel/comempci.c | 989 | ||||
-rw-r--r-- | arch/m68knommu/kernel/dma.c | 36 | ||||
-rw-r--r-- | arch/m68knommu/kernel/entry.S | 143 | ||||
-rw-r--r-- | arch/m68knommu/kernel/init_task.c | 43 | ||||
-rw-r--r-- | arch/m68knommu/kernel/m68k_ksyms.c | 104 | ||||
-rw-r--r-- | arch/m68knommu/kernel/module.c | 128 | ||||
-rw-r--r-- | arch/m68knommu/kernel/process.c | 442 | ||||
-rw-r--r-- | arch/m68knommu/kernel/ptrace.c | 383 | ||||
-rw-r--r-- | arch/m68knommu/kernel/semaphore.c | 134 | ||||
-rw-r--r-- | arch/m68knommu/kernel/setup.c | 347 | ||||
-rw-r--r-- | arch/m68knommu/kernel/signal.c | 788 | ||||
-rw-r--r-- | arch/m68knommu/kernel/sys_m68k.c | 208 | ||||
-rw-r--r-- | arch/m68knommu/kernel/syscalltable.S | 308 | ||||
-rw-r--r-- | arch/m68knommu/kernel/time.c | 198 | ||||
-rw-r--r-- | arch/m68knommu/kernel/traps.c | 320 | ||||
-rw-r--r-- | arch/m68knommu/kernel/vmlinux.lds.S | 348 |
18 files changed, 5031 insertions, 0 deletions
diff --git a/arch/m68knommu/kernel/Makefile b/arch/m68knommu/kernel/Makefile new file mode 100644 index 00000000000..1c6cd1ab571 --- /dev/null +++ b/arch/m68knommu/kernel/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for arch/m68knommu/kernel. +# + +extra-y := vmlinux.lds + +obj-y += dma.o entry.o init_task.o m68k_ksyms.o process.o ptrace.o semaphore.o \ + setup.o signal.o syscalltable.o sys_m68k.o time.o traps.o + +obj-$(CONFIG_MODULES) += module.o +obj-$(CONFIG_COMEMPCI) += comempci.o diff --git a/arch/m68knommu/kernel/asm-offsets.c b/arch/m68knommu/kernel/asm-offsets.c new file mode 100644 index 00000000000..cd3ffe12653 --- /dev/null +++ b/arch/m68knommu/kernel/asm-offsets.c @@ -0,0 +1,101 @@ +/* + * This program is used to generate definitions needed by + * assembly language modules. + * + * We use the technique used in the OSF Mach kernel code: + * generate asm statements containing #defines, + * compile this file to assembler, and then extract the + * #defines from the assembly-language output. + */ + +#include <linux/stddef.h> +#include <linux/sched.h> +#include <linux/kernel_stat.h> +#include <linux/ptrace.h> +#include <linux/hardirq.h> +#include <asm/bootinfo.h> +#include <asm/irq.h> +#include <asm/thread_info.h> + +#define DEFINE(sym, val) \ + asm volatile("\n->" #sym " %0 " #val : : "i" (val)) + +#define BLANK() asm volatile("\n->" : : ) + +int main(void) +{ + /* offsets into the task struct */ + DEFINE(TASK_STATE, offsetof(struct task_struct, state)); + DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags)); + DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace)); + DEFINE(TASK_BLOCKED, offsetof(struct task_struct, blocked)); + DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); + DEFINE(TASK_THREAD_INFO, offsetof(struct task_struct, thread_info)); + DEFINE(TASK_MM, offsetof(struct task_struct, mm)); + DEFINE(TASK_ACTIVE_MM, offsetof(struct task_struct, active_mm)); + + /* offsets into the kernel_stat struct */ + DEFINE(STAT_IRQ, offsetof(struct kernel_stat, irqs)); + + /* offsets into the irq_cpustat_t struct */ + DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); + + /* offsets into the thread struct */ + DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); + DEFINE(THREAD_USP, offsetof(struct thread_struct, usp)); + DEFINE(THREAD_SR, offsetof(struct thread_struct, sr)); + DEFINE(THREAD_FS, offsetof(struct thread_struct, fs)); + DEFINE(THREAD_CRP, offsetof(struct thread_struct, crp)); + DEFINE(THREAD_ESP0, offsetof(struct thread_struct, esp0)); + DEFINE(THREAD_FPREG, offsetof(struct thread_struct, fp)); + DEFINE(THREAD_FPCNTL, offsetof(struct thread_struct, fpcntl)); + DEFINE(THREAD_FPSTATE, offsetof(struct thread_struct, fpstate)); + + /* offsets into the pt_regs */ + DEFINE(PT_D0, offsetof(struct pt_regs, d0)); + DEFINE(PT_ORIG_D0, offsetof(struct pt_regs, orig_d0)); + DEFINE(PT_D1, offsetof(struct pt_regs, d1)); + DEFINE(PT_D2, offsetof(struct pt_regs, d2)); + DEFINE(PT_D3, offsetof(struct pt_regs, d3)); + DEFINE(PT_D4, offsetof(struct pt_regs, d4)); + DEFINE(PT_D5, offsetof(struct pt_regs, d5)); + DEFINE(PT_A0, offsetof(struct pt_regs, a0)); + DEFINE(PT_A1, offsetof(struct pt_regs, a1)); + DEFINE(PT_A2, offsetof(struct pt_regs, a2)); + DEFINE(PT_PC, offsetof(struct pt_regs, pc)); + DEFINE(PT_SR, offsetof(struct pt_regs, sr)); + +#ifdef CONFIG_COLDFIRE + /* bitfields are a bit difficult */ + DEFINE(PT_FORMATVEC, offsetof(struct pt_regs, sr) - 2); +#else + /* bitfields are a bit difficult */ + DEFINE(PT_VECTOR, offsetof(struct pt_regs, pc) + 4); + /* offsets into the irq_handler struct */ + DEFINE(IRQ_HANDLER, offsetof(struct irq_node, handler)); + DEFINE(IRQ_DEVID, offsetof(struct irq_node, dev_id)); + DEFINE(IRQ_NEXT, offsetof(struct irq_node, next)); +#endif + + /* offsets into the kernel_stat struct */ + DEFINE(STAT_IRQ, offsetof(struct kernel_stat, irqs)); + + /* signal defines */ + DEFINE(SIGSEGV, SIGSEGV); + DEFINE(SEGV_MAPERR, SEGV_MAPERR); + DEFINE(SIGTRAP, SIGTRAP); + DEFINE(TRAP_TRACE, TRAP_TRACE); + + DEFINE(PT_PTRACED, PT_PTRACED); + DEFINE(PT_DTRACE, PT_DTRACE); + + DEFINE(THREAD_SIZE, THREAD_SIZE); + + /* Offsets in thread_info structure */ + DEFINE(TI_TASK, offsetof(struct thread_info, task)); + DEFINE(TI_EXECDOMAIN, offsetof(struct thread_info, exec_domain)); + DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); + DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); + + return 0; +} diff --git a/arch/m68knommu/kernel/comempci.c b/arch/m68knommu/kernel/comempci.c new file mode 100644 index 00000000000..8670938f110 --- /dev/null +++ b/arch/m68knommu/kernel/comempci.c @@ -0,0 +1,989 @@ +/*****************************************************************************/ + +/* + * comemlite.c -- PCI access code for embedded CO-MEM Lite PCI controller. + * + * (C) Copyright 1999-2003, Greg Ungerer (gerg@snapgear.com). + * (C) Copyright 2000, Lineo (www.lineo.com) + */ + +/*****************************************************************************/ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/ptrace.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <asm/coldfire.h> +#include <asm/mcfsim.h> +#include <asm/irq.h> +#include <asm/anchor.h> + +#ifdef CONFIG_eLIA +#include <asm/elia.h> +#endif + +/*****************************************************************************/ + +/* + * Debug configuration defines. DEBUGRES sets debugging output for + * the resource allocation phase. DEBUGPCI traces on pcibios_ function + * calls, and DEBUGIO traces all accesses to devices on the PCI bus. + */ +/*#define DEBUGRES 1*/ +/*#define DEBUGPCI 1*/ +/*#define DEBUGIO 1*/ + +/*****************************************************************************/ + +/* + * PCI markers for bus present and active slots. + */ +int pci_bus_is_present = 0; +unsigned long pci_slotmask = 0; + +/* + * We may or may not need to swap the bytes of PCI bus tranfers. + * The endianess is re-roder automatically by the CO-MEM, but it + * will get the wrong byte order for a pure data stream. + */ +#define pci_byteswap 0 + + +/* + * Resource tracking. The CO-MEM part creates a virtual address + * space that all the PCI devices live in - it is not in any way + * directly mapped into the ColdFire address space. So we can + * really assign any resources we like to devices, as long as + * they do not clash with other PCI devices. + */ +unsigned int pci_iobase = PCIBIOS_MIN_IO; /* Arbitrary start address */ +unsigned int pci_membase = PCIBIOS_MIN_MEM; /* Arbitrary start address */ + +#define PCI_MINIO 0x100 /* 256 byte minimum I/O */ +#define PCI_MINMEM 0x00010000 /* 64k minimum chunk */ + +/* + * The CO-MEM's shared memory segment is visible inside the PCI + * memory address space. We need to keep track of the address that + * this is mapped at, to setup the bus masters pointers. + */ +unsigned int pci_shmemaddr; + +/*****************************************************************************/ + +void pci_interrupt(int irq, void *id, struct pt_regs *fp); + +/*****************************************************************************/ + +/* + * Some platforms have custom ways of reseting the PCI bus. + */ + +void pci_resetbus(void) +{ +#ifdef CONFIG_eLIA + int i; + +#ifdef DEBUGPCI + printk(KERN_DEBUG "pci_resetbus()\n"); +#endif + + *((volatile unsigned short *) (MCF_MBAR+MCFSIM_PADDR)) |= eLIA_PCIRESET; + for (i = 0; (i < 1000); i++) { + *((volatile unsigned short *) (MCF_MBAR + MCFSIM_PADAT)) = + (ppdata | eLIA_PCIRESET); + } + + + *((volatile unsigned short *) (MCF_MBAR + MCFSIM_PADAT)) = ppdata; +#endif +} + +/*****************************************************************************/ + +int pcibios_assign_resource_slot(int slot) +{ + volatile unsigned long *rp; + volatile unsigned char *ip; + unsigned int idsel, addr, val, align, i; + int bar; + +#ifdef DEBUGPCI + printk(KERN_INFO "pcibios_assign_resource_slot(slot=%x)\n", slot); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + idsel = COMEM_DA_ADDR(0x1 << (slot + 16)); + + /* Try to assign resource to each BAR */ + for (bar = 0; (bar < 6); bar++) { + addr = COMEM_PCIBUS + PCI_BASE_ADDRESS_0 + (bar * 4); + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGRD | idsel; + val = rp[LREG(addr)]; +#ifdef DEBUGRES + printk(KERN_DEBUG "-----------------------------------" + "-------------------------------------\n"); + printk(KERN_DEBUG "BAR[%d]: read=%08x ", bar, val); +#endif + + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGWR | idsel; + rp[LREG(addr)] = 0xffffffff; + + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGRD | idsel; + val = rp[LREG(addr)]; +#ifdef DEBUGRES + printk(KERN_DEBUG "write=%08x ", val); +#endif + if (val == 0) { +#ifdef DEBUGRES + printk(KERN_DEBUG "\n"); +#endif + continue; + } + + /* Determine space required by BAR */ + /* FIXME: this should go backwords from 0x80000000... */ + for (i = 0; (i < 32); i++) { + if ((0x1 << i) & (val & 0xfffffffc)) + break; + } + +#ifdef DEBUGRES + printk(KERN_DEBUG "size=%08x(%d)\n", (0x1 << i), i); +#endif + i = 0x1 << i; + + /* Assign a resource */ + if (val & PCI_BASE_ADDRESS_SPACE_IO) { + if (i < PCI_MINIO) + i = PCI_MINIO; +#ifdef DEBUGRES + printk(KERN_DEBUG "BAR[%d]: IO size=%08x iobase=%08x\n", + bar, i, pci_iobase); +#endif + if (i > 0xffff) { + /* Invalid size?? */ + val = 0 | PCI_BASE_ADDRESS_SPACE_IO; +#ifdef DEBUGRES + printk(KERN_DEBUG "BAR[%d]: too big for IO??\n", bar); +#endif + } else { + /* Check for un-alignment */ + if ((align = pci_iobase % i)) + pci_iobase += (i - align); + val = pci_iobase | PCI_BASE_ADDRESS_SPACE_IO; + pci_iobase += i; + } + } else { + if (i < PCI_MINMEM) + i = PCI_MINMEM; +#ifdef DEBUGRES + printk(KERN_DEBUG "BAR[%d]: MEMORY size=%08x membase=%08x\n", + bar, i, pci_membase); +#endif + /* Check for un-alignment */ + if ((align = pci_membase % i)) + pci_membase += (i - align); + val = pci_membase | PCI_BASE_ADDRESS_SPACE_MEMORY; + pci_membase += i; + } + + /* Write resource back into BAR register */ + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGWR | idsel; + rp[LREG(addr)] = val; +#ifdef DEBUGRES + printk(KERN_DEBUG "BAR[%d]: assigned bar=%08x\n", bar, val); +#endif + } + +#ifdef DEBUGRES + printk(KERN_DEBUG "-----------------------------------" + "-------------------------------------\n"); +#endif + + /* Assign IRQ if one is wanted... */ + ip = (volatile unsigned char *) (COMEM_BASE + COMEM_PCIBUS); + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGRD | idsel; + + addr = (PCI_INTERRUPT_PIN & 0xfc) + (~PCI_INTERRUPT_PIN & 0x03); + if (ip[addr]) { + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGWR | idsel; + addr = (PCI_INTERRUPT_LINE & 0xfc)+(~PCI_INTERRUPT_LINE & 0x03); + ip[addr] = 25; +#ifdef DEBUGRES + printk(KERN_DEBUG "IRQ LINE=25\n"); +#endif + } + + return(0); +} + +/*****************************************************************************/ + +int pcibios_enable_slot(int slot) +{ + volatile unsigned long *rp; + volatile unsigned short *wp; + unsigned int idsel, addr; + unsigned short cmd; + +#ifdef DEBUGPCI + printk(KERN_DEBUG "pcibios_enbale_slot(slot=%x)\n", slot); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + wp = (volatile unsigned short *) COMEM_BASE; + idsel = COMEM_DA_ADDR(0x1 << (slot + 16)); + + /* Get current command settings */ + addr = COMEM_PCIBUS + PCI_COMMAND; + addr = (addr & ~0x3) + (~addr & 0x02); + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGRD | idsel; + cmd = wp[WREG(addr)]; + /*val = ((val & 0xff) << 8) | ((val >> 8) & 0xff);*/ + + /* Enable I/O and memory accesses to this device */ + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_CFGWR | idsel; + cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER; + wp[WREG(addr)] = cmd; + + return(0); +} + +/*****************************************************************************/ + +void pcibios_assign_resources(void) +{ + volatile unsigned long *rp; + unsigned long sel, id; + int slot; + + rp = (volatile unsigned long *) COMEM_BASE; + + /* + * Do a quick scan of the PCI bus and see what is here. + */ + for (slot = COMEM_MINDEV; (slot <= COMEM_MAXDEV); slot++) { + sel = COMEM_DA_CFGRD | COMEM_DA_ADDR(0x1 << (slot + 16)); + rp[LREG(COMEM_DAHBASE)] = sel; + rp[LREG(COMEM_PCIBUS)] = 0; /* Clear bus */ + id = rp[LREG(COMEM_PCIBUS)]; + if ((id != 0) && ((id & 0xffff0000) != (sel & 0xffff0000))) { + printk(KERN_INFO "PCI: slot=%d id=%08x\n", slot, (int) id); + pci_slotmask |= 0x1 << slot; + pcibios_assign_resource_slot(slot); + pcibios_enable_slot(slot); + } + } +} + +/*****************************************************************************/ + +int pcibios_init(void) +{ + volatile unsigned long *rp; + unsigned long sel, id; + int slot; + +#ifdef DEBUGPCI + printk(KERN_DEBUG "pcibios_init()\n"); +#endif + + pci_resetbus(); + + /* + * Do some sort of basic check to see if the CO-MEM part + * is present... This works ok, but I think we really need + * something better... + */ + rp = (volatile unsigned long *) COMEM_BASE; + if ((rp[LREG(COMEM_LBUSCFG)] & 0xff) != 0x50) { + printk(KERN_INFO "PCI: no PCI bus present\n"); + return(0); + } + +#ifdef COMEM_BRIDGEDEV + /* + * Setup the PCI bridge device first. It needs resources too, + * so that bus masters can get to its shared memory. + */ + slot = COMEM_BRIDGEDEV; + sel = COMEM_DA_CFGRD | COMEM_DA_ADDR(0x1 << (slot + 16)); + rp[LREG(COMEM_DAHBASE)] = sel; + rp[LREG(COMEM_PCIBUS)] = 0; /* Clear bus */ + id = rp[LREG(COMEM_PCIBUS)]; + if ((id == 0) || ((id & 0xffff0000) == (sel & 0xffff0000))) { + printk(KERN_INFO "PCI: no PCI bus bridge present\n"); + return(0); + } + + printk(KERN_INFO "PCI: bridge device at slot=%d id=%08x\n", slot, (int) id); + pci_slotmask |= 0x1 << slot; + pci_shmemaddr = pci_membase; + pcibios_assign_resource_slot(slot); + pcibios_enable_slot(slot); +#endif + + pci_bus_is_present = 1; + + /* Get PCI irq for local vectoring */ + if (request_irq(COMEM_IRQ, pci_interrupt, 0, "PCI bridge", NULL)) { + printk(KERN_WARNING "PCI: failed to acquire interrupt %d\n", COMEM_IRQ); + } else { + mcf_autovector(COMEM_IRQ); + } + + pcibios_assign_resources(); + + return(0); +} + +/*****************************************************************************/ + +char *pcibios_setup(char *option) +{ + /* Nothing for us to handle. */ + return(option); +} +/*****************************************************************************/ + +void pcibios_fixup_bus(struct pci_bus *b) +{ +} + +/*****************************************************************************/ + +void pcibios_align_resource(void *data, struct resource *res, unsigned long size, unsigned long align) +{ +} + +/*****************************************************************************/ + +int pcibios_enable_device(struct pci_dev *dev, int mask) +{ + int slot; + + slot = PCI_SLOT(dev->devfn); + if ((dev->bus == 0) && (pci_slotmask & (1 << slot))) + pcibios_enable_slot(slot); + return(0); +} + +/*****************************************************************************/ + +void pcibios_update_resource(struct pci_dev *dev, struct resource *root, struct resource *r, int resource) +{ + printk(KERN_WARNING "%s(%d): no support for changing PCI resources...\n", + __FILE__, __LINE__); +} + + +/*****************************************************************************/ + +/* + * Local routines to interrcept the standard I/O and vector handling + * code. Don't include this 'till now - initialization code above needs + * access to the real code too. + */ +#include <asm/mcfpci.h> + +/*****************************************************************************/ + +void pci_outb(unsigned char val, unsigned int addr) +{ + volatile unsigned long *rp; + volatile unsigned char *bp; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_outb(val=%02x,addr=%x)\n", val, addr); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + bp = (volatile unsigned char *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IOWR | COMEM_DA_ADDR(addr); + addr = (addr & ~0x3) + (~addr & 0x03); + bp[(COMEM_PCIBUS + COMEM_DA_OFFSET(addr))] = val; +} + +/*****************************************************************************/ + +void pci_outw(unsigned short val, unsigned int addr) +{ + volatile unsigned long *rp; + volatile unsigned short *sp; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_outw(val=%04x,addr=%x)\n", val, addr); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + sp = (volatile unsigned short *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IOWR | COMEM_DA_ADDR(addr); + addr = (addr & ~0x3) + (~addr & 0x02); + if (pci_byteswap) + val = ((val & 0xff) << 8) | ((val >> 8) & 0xff); + sp[WREG(COMEM_PCIBUS + COMEM_DA_OFFSET(addr))] = val; +} + +/*****************************************************************************/ + +void pci_outl(unsigned int val, unsigned int addr) +{ + volatile unsigned long *rp; + volatile unsigned int *lp; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_outl(val=%08x,addr=%x)\n", val, addr); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + lp = (volatile unsigned int *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IOWR | COMEM_DA_ADDR(addr); + + if (pci_byteswap) + val = (val << 24) | ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | (val >> 24); + + lp[LREG(COMEM_PCIBUS + COMEM_DA_OFFSET(addr))] = val; +} + +/*****************************************************************************/ + +unsigned long pci_blmask[] = { + 0x000000e0, + 0x000000d0, + 0x000000b0, + 0x00000070 +}; + +unsigned char pci_inb(unsigned int addr) +{ + volatile unsigned long *rp; + volatile unsigned char *bp; + unsigned long r; + unsigned char val; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_inb(addr=%x)\n", addr); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + bp = (volatile unsigned char *) COMEM_BASE; + + r = COMEM_DA_IORD | COMEM_DA_ADDR(addr) | pci_blmask[(addr & 0x3)]; + rp[LREG(COMEM_DAHBASE)] = r; + + addr = (addr & ~0x3) + (~addr & 0x3); + val = bp[(COMEM_PCIBUS + COMEM_DA_OFFSET(addr))]; + return(val); +} + +/*****************************************************************************/ + +unsigned long pci_bwmask[] = { + 0x000000c0, + 0x000000c0, + 0x00000030, + 0x00000030 +}; + +unsigned short pci_inw(unsigned int addr) +{ + volatile unsigned long *rp; + volatile unsigned short *sp; + unsigned long r; + unsigned short val; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_inw(addr=%x)", addr); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + r = COMEM_DA_IORD | COMEM_DA_ADDR(addr) | pci_bwmask[(addr & 0x3)]; + rp[LREG(COMEM_DAHBASE)] = r; + + sp = (volatile unsigned short *) COMEM_BASE; + addr = (addr & ~0x3) + (~addr & 0x02); + val = sp[WREG(COMEM_PCIBUS + COMEM_DA_OFFSET(addr))]; + if (pci_byteswap) + val = ((val & 0xff) << 8) | ((val >> 8) & 0xff); +#ifdef DEBUGIO + printk(KERN_DEBUG "=%04x\n", val); +#endif + return(val); +} + +/*****************************************************************************/ + +unsigned int pci_inl(unsigned int addr) +{ + volatile unsigned long *rp; + volatile unsigned int *lp; + unsigned int val; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_inl(addr=%x)", addr); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + lp = (volatile unsigned int *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IORD | COMEM_DA_ADDR(addr); + val = lp[LREG(COMEM_PCIBUS + COMEM_DA_OFFSET(addr))]; + + if (pci_byteswap) + val = (val << 24) | ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | (val >> 24); + +#ifdef DEBUGIO + printk(KERN_DEBUG "=%08x\n", val); +#endif + return(val); +} + +/*****************************************************************************/ + +void pci_outsb(void *addr, void *buf, int len) +{ + volatile unsigned long *rp; + volatile unsigned char *bp; + unsigned char *dp = (unsigned char *) buf; + unsigned int a = (unsigned int) addr; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_outsb(addr=%x,buf=%x,len=%d)\n", (int)addr, (int)buf, len); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IOWR | COMEM_DA_ADDR(a); + + a = (a & ~0x3) + (~a & 0x03); + bp = (volatile unsigned char *) + (COMEM_BASE + COMEM_PCIBUS + COMEM_DA_OFFSET(a)); + + while (len--) + *bp = *dp++; +} + +/*****************************************************************************/ + +void pci_outsw(void *addr, void *buf, int len) +{ + volatile unsigned long *rp; + volatile unsigned short *wp; + unsigned short w, *dp = (unsigned short *) buf; + unsigned int a = (unsigned int) addr; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_outsw(addr=%x,buf=%x,len=%d)\n", (int)addr, (int)buf, len); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IOWR | COMEM_DA_ADDR(a); + + a = (a & ~0x3) + (~a & 0x2); + wp = (volatile unsigned short *) + (COMEM_BASE + COMEM_PCIBUS + COMEM_DA_OFFSET(a)); + + while (len--) { + w = *dp++; + if (pci_byteswap) + w = ((w & 0xff) << 8) | ((w >> 8) & 0xff); + *wp = w; + } +} + +/*****************************************************************************/ + +void pci_outsl(void *addr, void *buf, int len) +{ + volatile unsigned long *rp; + volatile unsigned long *lp; + unsigned long l, *dp = (unsigned long *) buf; + unsigned int a = (unsigned int) addr; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_outsl(addr=%x,buf=%x,len=%d)\n", (int)addr, (int)buf, len); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IOWR | COMEM_DA_ADDR(a); + + lp = (volatile unsigned long *) + (COMEM_BASE + COMEM_PCIBUS + COMEM_DA_OFFSET(a)); + + while (len--) { + l = *dp++; + if (pci_byteswap) + l = (l << 24) | ((l & 0x0000ff00) << 8) | + ((l & 0x00ff0000) >> 8) | (l >> 24); + *lp = l; + } +} + +/*****************************************************************************/ + +void pci_insb(void *addr, void *buf, int len) +{ + volatile unsigned long *rp; + volatile unsigned char *bp; + unsigned char *dp = (unsigned char *) buf; + unsigned int a = (unsigned int) addr; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_insb(addr=%x,buf=%x,len=%d)\n", (int)addr, (int)buf, len); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IORD | COMEM_DA_ADDR(a); + + a = (a & ~0x3) + (~a & 0x03); + bp = (volatile unsigned char *) + (COMEM_BASE + COMEM_PCIBUS + COMEM_DA_OFFSET(a)); + + while (len--) + *dp++ = *bp; +} + +/*****************************************************************************/ + +void pci_insw(void *addr, void *buf, int len) +{ + volatile unsigned long *rp; + volatile unsigned short *wp; + unsigned short w, *dp = (unsigned short *) buf; + unsigned int a = (unsigned int) addr; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_insw(addr=%x,buf=%x,len=%d)\n", (int)addr, (int)buf, len); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IORD | COMEM_DA_ADDR(a); + + a = (a & ~0x3) + (~a & 0x2); + wp = (volatile unsigned short *) + (COMEM_BASE + COMEM_PCIBUS + COMEM_DA_OFFSET(a)); + + while (len--) { + w = *wp; + if (pci_byteswap) + w = ((w & 0xff) << 8) | ((w >> 8) & 0xff); + *dp++ = w; + } +} + +/*****************************************************************************/ + +void pci_insl(void *addr, void *buf, int len) +{ + volatile unsigned long *rp; + volatile unsigned long *lp; + unsigned long l, *dp = (unsigned long *) buf; + unsigned int a = (unsigned int) addr; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_insl(addr=%x,buf=%x,len=%d)\n", (int)addr, (int)buf, len); +#endif + + rp = (volatile unsigned long *) COMEM_BASE; + rp[LREG(COMEM_DAHBASE)] = COMEM_DA_IORD | COMEM_DA_ADDR(a); + + lp = (volatile unsigned long *) + (COMEM_BASE + COMEM_PCIBUS + COMEM_DA_OFFSET(a)); + + while (len--) { + l = *lp; + if (pci_byteswap) + l = (l << 24) | ((l & 0x0000ff00) << 8) | + ((l & 0x00ff0000) >> 8) | (l >> 24); + *dp++ = l; + } +} + +/*****************************************************************************/ + +struct pci_localirqlist { + void (*handler)(int, void *, struct pt_regs *); + const char *device; + void *dev_id; +}; + +struct pci_localirqlist pci_irqlist[COMEM_MAXPCI]; + +/*****************************************************************************/ + +int pci_request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long flags, const char *device, void *dev_id) +{ + int i; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_request_irq(irq=%d,handler=%x,flags=%x,device=%s," + "dev_id=%x)\n", irq, (int) handler, (int) flags, device, + (int) dev_id); +#endif + + /* Check if this interrupt handler is already lodged */ + for (i = 0; (i < COMEM_MAXPCI); i++) { + if (pci_irqlist[i].handler == handler) + return(0); + } + + /* Find a free spot to put this handler */ + for (i = 0; (i < COMEM_MAXPCI); i++) { + if (pci_irqlist[i].handler == 0) { + pci_irqlist[i].handler = handler; + pci_irqlist[i].device = device; + pci_irqlist[i].dev_id = dev_id; + return(0); + } + } + + /* Couldn't fit?? */ + return(1); +} + +/*****************************************************************************/ + +void pci_free_irq(unsigned int irq, void *dev_id) +{ + int i; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_free_irq(irq=%d,dev_id=%x)\n", irq, (int) dev_id); +#endif + + if (dev_id == (void *) NULL) + return; + + /* Check if this interrupt handler is lodged */ + for (i = 0; (i < COMEM_MAXPCI); i++) { + if (pci_irqlist[i].dev_id == dev_id) { + pci_irqlist[i].handler = NULL; + pci_irqlist[i].device = NULL; + pci_irqlist[i].dev_id = NULL; + break; + } + } +} + +/*****************************************************************************/ + +void pci_interrupt(int irq, void *id, struct pt_regs *fp) +{ + int i; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_interrupt(irq=%d,id=%x,fp=%x)\n", irq, (int) id, (int) fp); +#endif + + for (i = 0; (i < COMEM_MAXPCI); i++) { + if (pci_irqlist[i].handler) + (*pci_irqlist[i].handler)(irq,pci_irqlist[i].dev_id,fp); + } +} + +/*****************************************************************************/ + +/* + * The shared memory region is broken up into contiguous 512 byte + * regions for easy allocation... This is not an optimal solution + * but it makes allocation and freeing regions really easy. + */ + +#define PCI_MEMSLOTSIZE 512 +#define PCI_MEMSLOTS (COMEM_SHMEMSIZE / PCI_MEMSLOTSIZE) + +char pci_shmemmap[PCI_MEMSLOTS]; + + +void *pci_bmalloc(int size) +{ + int i, j, nrslots; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_bmalloc(size=%d)\n", size); +#endif + + if (size <= 0) + return((void *) NULL); + + nrslots = (size - 1) / PCI_MEMSLOTSIZE; + + for (i = 0; (i < (PCI_MEMSLOTS-nrslots)); i++) { + if (pci_shmemmap[i] == 0) { + for (j = i+1; (j < (i+nrslots)); j++) { + if (pci_shmemmap[j]) + goto restart; + } + + for (j = i; (j <= i+nrslots); j++) + pci_shmemmap[j] = 1; + break; + } +restart: + } + + return((void *) (COMEM_BASE + COMEM_SHMEM + (i * PCI_MEMSLOTSIZE))); +} + +/*****************************************************************************/ + +void pci_bmfree(void *mp, int size) +{ + int i, j, nrslots; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_bmfree(mp=%x,size=%d)\n", (int) mp, size); +#endif + + nrslots = size / PCI_MEMSLOTSIZE; + i = (((unsigned long) mp) - (COMEM_BASE + COMEM_SHMEM)) / + PCI_MEMSLOTSIZE; + + for (j = i; (j < (i+nrslots)); j++) + pci_shmemmap[j] = 0; +} + +/*****************************************************************************/ + +unsigned long pci_virt_to_bus(volatile void *address) +{ + unsigned long l; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_virt_to_bus(address=%x)", (int) address); +#endif + + l = ((unsigned long) address) - COMEM_BASE; +#ifdef DEBUGIO + printk(KERN_DEBUG "=%x\n", (int) (l+pci_shmemaddr)); +#endif + return(l + pci_shmemaddr); +} + +/*****************************************************************************/ + +void *pci_bus_to_virt(unsigned long address) +{ + unsigned long l; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_bus_to_virt(address=%x)", (int) address); +#endif + + l = address - pci_shmemaddr; +#ifdef DEBUGIO + printk(KERN_DEBUG "=%x\n", (int) (address + COMEM_BASE)); +#endif + return((void *) (address + COMEM_BASE)); +} + +/*****************************************************************************/ + +void pci_bmcpyto(void *dst, void *src, int len) +{ + unsigned long *dp, *sp, val; + unsigned char *dcp, *scp; + int i, j; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_bmcpyto(dst=%x,src=%x,len=%d)\n", (int)dst, (int)src, len); +#endif + + dp = (unsigned long *) dst; + sp = (unsigned long *) src; + i = len >> 2; + +#if 0 + printk(KERN_INFO "DATA:"); + scp = (unsigned char *) sp; + for (i = 0; (i < len); i++) { + if ((i % 16) == 0) printk(KERN_INFO "\n%04x: ", i); + printk(KERN_INFO "%02x ", *scp++); + } + printk(KERN_INFO "\n"); +#endif + + for (j = 0; (i >= 0); i--, j++) { + val = *sp++; + val = (val << 24) | ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | (val >> 24); + *dp++ = val; + } + + if (len & 0x3) { + dcp = (unsigned char *) dp; + scp = ((unsigned char *) sp) + 3; + for (i = 0; (i < (len & 0x3)); i++) + *dcp++ = *scp--; + } +} + +/*****************************************************************************/ + +void pci_bmcpyfrom(void *dst, void *src, int len) +{ + unsigned long *dp, *sp, val; + unsigned char *dcp, *scp; + int i; + +#ifdef DEBUGIO + printk(KERN_DEBUG "pci_bmcpyfrom(dst=%x,src=%x,len=%d)\n",(int)dst,(int)src,len); +#endif + + dp = (unsigned long *) dst; + sp = (unsigned long *) src; + i = len >> 2; + + for (; (i >= 0); i--) { + val = *sp++; + val = (val << 24) | ((val & 0x0000ff00) << 8) | + ((val & 0x00ff0000) >> 8) | (val >> 24); + *dp++ = val; + } + + if (len & 0x3) { + dcp = ((unsigned char *) dp) + 3; + scp = (unsigned char *) sp; + for (i = 0; (i < (len & 0x3)); i++) + *dcp++ = *scp--; + } + +#if 0 + printk(KERN_INFO "DATA:"); + dcp = (unsigned char *) dst; + for (i = 0; (i < len); i++) { + if ((i % 16) == 0) printk(KERN_INFO "\n%04x: ", i); + printk(KERN_INFO "%02x ", *dcp++); + } + printk(KERN_INFO "\n"); +#endif +} + +/*****************************************************************************/ + +void *pci_alloc_consistent(struct pci_dev *dev, size_t size, dma_addr_t *dma_addr) +{ + void *mp; + if ((mp = pci_bmalloc(size)) != NULL) { + dma_addr = mp - (COMEM_BASE + COMEM_SHMEM); + return(mp); + } + *dma_addr = (dma_addr_t) NULL; + return(NULL); +} + +/*****************************************************************************/ + +void pci_free_consistent(struct pci_dev *dev, size_t size, void *cpu_addr, dma_addr_t dma_addr) +{ + pci_bmfree(cpu_addr, size); +} + +/*****************************************************************************/ diff --git a/arch/m68knommu/kernel/dma.c b/arch/m68knommu/kernel/dma.c new file mode 100644 index 00000000000..14b19c4161f --- /dev/null +++ b/arch/m68knommu/kernel/dma.c @@ -0,0 +1,36 @@ +/* + * Dynamic DMA mapping support. + * + * We never have any address translations to worry about, so this + * is just alloc/free. + */ + +#include <linux/types.h> +#include <linux/mm.h> +#include <linux/string.h> +#include <linux/pci.h> +#include <asm/io.h> + +void *dma_alloc_coherent(struct device *dev, size_t size, + dma_addr_t *dma_handle, int gfp) +{ + void *ret; + /* ignore region specifiers */ + gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); + + if (dev == NULL || (*dev->dma_mask < 0xffffffff)) + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_phys(ret); + } + return ret; +} + +void dma_free_coherent(struct device *dev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, get_order(size)); +} diff --git a/arch/m68knommu/kernel/entry.S b/arch/m68knommu/kernel/entry.S new file mode 100644 index 00000000000..8b1f47239b9 --- /dev/null +++ b/arch/m68knommu/kernel/entry.S @@ -0,0 +1,143 @@ +/* + * linux/arch/m68knommu/kernel/entry.S + * + * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) + * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, + * Kenneth Albanowski <kjahds@kjahds.com>, + * Copyright (C) 2000 Lineo Inc. (www.lineo.com) + * + * Based on: + * + * linux/arch/m68k/kernel/entry.S + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file README.legal in the main directory of this archive + * for more details. + * + * Linux/m68k support by Hamish Macdonald + * + * 68060 fixes by Jesper Skov + * ColdFire support by Greg Ungerer (gerg@snapgear.com) + * 5307 fixes by David W. Miller + * linux 2.4 support David McCullough <davidm@snapgear.com> + */ + +#include <linux/config.h> +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/thread_info.h> +#include <asm/errno.h> +#include <asm/setup.h> +#include <asm/segment.h> +#include <asm/asm-offsets.h> +#include <asm/entry.h> + +.text + +.globl buserr +.globl trap +.globl ret_from_exception +.globl ret_from_signal +.globl sys_fork +.globl sys_clone +.globl sys_vfork + +ENTRY(buserr) + SAVE_ALL + moveq #-1,%d0 + movel %d0,%sp@(PT_ORIG_D0) + movel %sp,%sp@- /* stack frame pointer argument */ + jsr buserr_c + addql #4,%sp + jra ret_from_exception + +ENTRY(trap) + SAVE_ALL + moveq #-1,%d0 + movel %d0,%sp@(PT_ORIG_D0) + movel %sp,%sp@- /* stack frame pointer argument */ + jsr trap_c + addql #4,%sp + jra ret_from_exception + +#ifdef TRAP_DBG_INTERRUPT + +.globl dbginterrupt +ENTRY(dbginterrupt) + SAVE_ALL + moveq #-1,%d0 + movel %d0,%sp@(PT_ORIG_D0) + movel %sp,%sp@- /* stack frame pointer argument */ + jsr dbginterrupt_c + addql #4,%sp + jra ret_from_exception +#endif + +ENTRY(reschedule) + /* save top of frame */ + pea %sp@ + jbsr set_esp0 + addql #4,%sp + pea ret_from_exception + jmp schedule + +ENTRY(ret_from_fork) + movel %d1,%sp@- + jsr schedule_tail + addql #4,%sp + jra ret_from_exception + +ENTRY(sys_fork) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr m68k_fork + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_vfork) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr m68k_vfork + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_clone) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr m68k_clone + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_sigsuspend) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr do_sigsuspend + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_rt_sigsuspend) + SAVE_SWITCH_STACK + pea %sp@(SWITCH_STACK_SIZE) + jbsr do_rt_sigsuspend + addql #4,%sp + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_sigreturn) + SAVE_SWITCH_STACK + jbsr do_sigreturn + RESTORE_SWITCH_STACK + rts + +ENTRY(sys_rt_sigreturn) + SAVE_SWITCH_STACK + jbsr do_rt_sigreturn + RESTORE_SWITCH_STACK + rts + diff --git a/arch/m68knommu/kernel/init_task.c b/arch/m68knommu/kernel/init_task.c new file mode 100644 index 00000000000..3897043a126 --- /dev/null +++ b/arch/m68knommu/kernel/init_task.c @@ -0,0 +1,43 @@ +/* + * linux/arch/m68knommu/kernel/init_task.c + */ +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/init_task.h> +#include <linux/fs.h> +#include <linux/mqueue.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +struct mm_struct init_mm = INIT_MM(init_mm); + +EXPORT_SYMBOL(init_mm); + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +__asm__(".align 4"); +struct task_struct init_task = INIT_TASK(init_task); + +EXPORT_SYMBOL(init_task); + +/* + * 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 having a special + * "init_task" linker map entry.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + diff --git a/arch/m68knommu/kernel/m68k_ksyms.c b/arch/m68knommu/kernel/m68k_ksyms.c new file mode 100644 index 00000000000..e93a5ad5649 --- /dev/null +++ b/arch/m68knommu/kernel/m68k_ksyms.c @@ -0,0 +1,104 @@ +#include <linux/module.h> +#include <linux/linkage.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/user.h> +#include <linux/elfcore.h> +#include <linux/in6.h> +#include <linux/interrupt.h> +#include <linux/config.h> + +#include <asm/setup.h> +#include <asm/machdep.h> +#include <asm/pgalloc.h> +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/semaphore.h> +#include <asm/checksum.h> +#include <asm/current.h> + +extern void dump_thread(struct pt_regs *, struct user *); +extern int dump_fpu(struct pt_regs *, elf_fpregset_t *); + +/* platform dependent support */ + +EXPORT_SYMBOL(__ioremap); +EXPORT_SYMBOL(iounmap); +EXPORT_SYMBOL(dump_fpu); +EXPORT_SYMBOL(dump_thread); +EXPORT_SYMBOL(strnlen); +EXPORT_SYMBOL(strrchr); +EXPORT_SYMBOL(strstr); +EXPORT_SYMBOL(strchr); +EXPORT_SYMBOL(strcat); +EXPORT_SYMBOL(strlen); +EXPORT_SYMBOL(strcmp); +EXPORT_SYMBOL(strncmp); + +EXPORT_SYMBOL(ip_fast_csum); + +EXPORT_SYMBOL(mach_enable_irq); +EXPORT_SYMBOL(mach_disable_irq); +EXPORT_SYMBOL(kernel_thread); + +/* Networking helper routines. */ +EXPORT_SYMBOL(csum_partial_copy); + +/* The following are special because they're not called + explicitly (the C compiler generates them). Fortunately, + their interface isn't gonna change any time soon now, so + it's OK to leave it out of version control. */ +EXPORT_SYMBOL(memcpy); +EXPORT_SYMBOL(memset); +EXPORT_SYMBOL(memcmp); +EXPORT_SYMBOL(memscan); +EXPORT_SYMBOL(memmove); + +EXPORT_SYMBOL(__down_failed); +EXPORT_SYMBOL(__down_failed_interruptible); +EXPORT_SYMBOL(__down_failed_trylock); +EXPORT_SYMBOL(__up_wakeup); + +EXPORT_SYMBOL(get_wchan); + +/* + * 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 __mulsi3(void); +extern void __udivsi3(void); +extern void __umodsi3(void); + + /* gcc lib functions */ +EXPORT_SYMBOL(__ashldi3); +EXPORT_SYMBOL(__ashrdi3); +EXPORT_SYMBOL(__divsi3); +EXPORT_SYMBOL(__lshrdi3); +EXPORT_SYMBOL(__modsi3); +EXPORT_SYMBOL(__muldi3); +EXPORT_SYMBOL(__mulsi3); +EXPORT_SYMBOL(__udivsi3); +EXPORT_SYMBOL(__umodsi3); + +EXPORT_SYMBOL(is_in_rom); + +#ifdef CONFIG_COLDFIRE +extern unsigned int *dma_device_address; +extern unsigned long dma_base_addr, _ramend; +EXPORT_SYMBOL(dma_base_addr); +EXPORT_SYMBOL(dma_device_address); +EXPORT_SYMBOL(_ramend); + +extern asmlinkage void trap(void); +extern void *_ramvec; +EXPORT_SYMBOL(trap); +EXPORT_SYMBOL(_ramvec); +#endif /* CONFIG_COLDFIRE */ diff --git a/arch/m68knommu/kernel/module.c b/arch/m68knommu/kernel/module.c new file mode 100644 index 00000000000..3b1a2ff61dd --- /dev/null +++ b/arch/m68knommu/kernel/module.c @@ -0,0 +1,128 @@ +#include <linux/moduleloader.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt...) +#endif + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +/* We don't need anything special. */ +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 relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + uint32_t *location; + + DEBUGP("Applying relocate section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rel[i].r_info); + + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_68K_32: + /* We add the value into the location given */ + *location += sym->st_value; + break; + case R_68K_PC32: + /* Add the value, subtract its postition */ + *location += sym->st_value - (uint32_t)location; + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +int apply_relocate_add(Elf32_Shdr *sechdrs, + const char *strtab, + unsigned int symindex, + unsigned int relsec, + struct module *me) +{ + unsigned int i; + Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; + Elf32_Sym *sym; + uint32_t *location; + + DEBUGP("Applying relocate_add section %u to %u\n", relsec, + sechdrs[relsec].sh_info); + for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { + /* This is where to make the change */ + location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + + rel[i].r_offset; + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + sym = (Elf32_Sym *)sechdrs[symindex].sh_addr + + ELF32_R_SYM(rel[i].r_info); + + switch (ELF32_R_TYPE(rel[i].r_info)) { + case R_68K_32: + /* We add the value into the location given */ + *location = rel[i].r_addend + sym->st_value; + break; + case R_68K_PC32: + /* Add the value, subtract its postition */ + *location = rel[i].r_addend + sym->st_value - (uint32_t)location; + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, ELF32_R_TYPE(rel[i].r_info)); + return -ENOEXEC; + } + } + return 0; +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/m68knommu/kernel/process.c b/arch/m68knommu/kernel/process.c new file mode 100644 index 00000000000..2b6c9d32b7a --- /dev/null +++ b/arch/m68knommu/kernel/process.c @@ -0,0 +1,442 @@ +/* + * linux/arch/m68knommu/kernel/process.c + * + * Copyright (C) 1995 Hamish Macdonald + * + * 68060 fixes by Jesper Skov + * + * uClinux changes + * Copyright (C) 2000-2002, David McCullough <davidm@snapgear.com> + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/interrupt.h> +#include <linux/reboot.h> + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/traps.h> +#include <asm/machdep.h> +#include <asm/setup.h> +#include <asm/pgtable.h> + +asmlinkage void ret_from_fork(void); + + +/* + * The idle loop on an m68knommu.. + */ +void default_idle(void) +{ + while(1) { + if (need_resched()) + __asm__("stop #0x2000" : : : "cc"); + schedule(); + } +} + +void (*idle)(void) = default_idle; + +/* + * The idle thread. There's no useful work to be + * done, so just try to conserve power and have a + * low exit latency (ie sit in a loop waiting for + * somebody to say that they'd like to reschedule) + */ +void cpu_idle(void) +{ + /* endless idle loop with no priority at all */ + idle(); +} + +void machine_restart(char * __unused) +{ + if (mach_reset) + mach_reset(); + for (;;); +} + +EXPORT_SYMBOL(machine_restart); + +void machine_halt(void) +{ + if (mach_halt) + mach_halt(); + for (;;); +} + +EXPORT_SYMBOL(machine_halt); + +void machine_power_off(void) +{ + if (mach_power_off) + mach_power_off(); + for (;;); +} + +EXPORT_SYMBOL(machine_power_off); + +void show_regs(struct pt_regs * regs) +{ + printk(KERN_NOTICE "\n"); + printk(KERN_NOTICE "Format %02x Vector: %04x PC: %08lx Status: %04x %s\n", + regs->format, regs->vector, regs->pc, regs->sr, print_tainted()); + printk(KERN_NOTICE "ORIG_D0: %08lx D0: %08lx A2: %08lx A1: %08lx\n", + regs->orig_d0, regs->d0, regs->a2, regs->a1); + printk(KERN_NOTICE "A0: %08lx D5: %08lx D4: %08lx\n", + regs->a0, regs->d5, regs->d4); + printk(KERN_NOTICE "D3: %08lx D2: %08lx D1: %08lx\n", + regs->d3, regs->d2, regs->d1); + if (!(regs->sr & PS_S)) + printk(KERN_NOTICE "USP: %08lx\n", rdusp()); +} + +/* + * Create a kernel thread + */ +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + int retval; + long clone_arg = flags | CLONE_VM; + mm_segment_t fs; + + fs = get_fs(); + set_fs(KERNEL_DS); + + __asm__ __volatile__ ( + "movel %%sp, %%d2\n\t" + "movel %5, %%d1\n\t" + "movel %1, %%d0\n\t" + "trap #0\n\t" + "cmpl %%sp, %%d2\n\t" + "jeq 1f\n\t" + "movel %3, %%sp@-\n\t" + "jsr %4@\n\t" + "movel %2, %%d0\n\t" + "trap #0\n" + "1:\n\t" + "movel %%d0, %0\n" + : "=d" (retval) + : "i" (__NR_clone), + "i" (__NR_exit), + "a" (arg), + "a" (fn), + "a" (clone_arg) + : "cc", "%d0", "%d1", "%d2"); + + set_fs(fs); + return retval; +} + +void flush_thread(void) +{ +#ifdef CONFIG_FPU + unsigned long zero = 0; +#endif + set_fs(USER_DS); + current->thread.fs = __USER_DS; +#ifdef CONFIG_FPU + if (!FPU_IS_EMU) + asm volatile (".chip 68k/68881\n\t" + "frestore %0@\n\t" + ".chip 68k" : : "a" (&zero)); +#endif +} + +/* + * "m68k_fork()".. By the time we get here, the + * non-volatile registers have also been saved on the + * stack. We do some ugly pointer stuff here.. (see + * also copy_thread) + */ + +asmlinkage int m68k_fork(struct pt_regs *regs) +{ + /* fork almost works, enough to trick you into looking elsewhere :-( */ + return(-EINVAL); +} + +asmlinkage int m68k_vfork(struct pt_regs *regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL); +} + +asmlinkage int m68k_clone(struct pt_regs *regs) +{ + unsigned long clone_flags; + unsigned long newsp; + + /* syscall2 puts clone_flags in d1 and usp in d2 */ + clone_flags = regs->d1; + newsp = regs->d2; + if (!newsp) + newsp = rdusp(); + return do_fork(clone_flags, newsp, regs, 0, NULL, NULL); +} + +int copy_thread(int nr, unsigned long clone_flags, + unsigned long usp, unsigned long topstk, + struct task_struct * p, struct pt_regs * regs) +{ + struct pt_regs * childregs; + struct switch_stack * childstack, *stack; + unsigned long stack_offset, *retp; + + stack_offset = THREAD_SIZE - sizeof(struct pt_regs); + childregs = (struct pt_regs *) ((unsigned long) p->thread_info + stack_offset); + + *childregs = *regs; + childregs->d0 = 0; + + retp = ((unsigned long *) regs); + stack = ((struct switch_stack *) retp) - 1; + + childstack = ((struct switch_stack *) childregs) - 1; + *childstack = *stack; + childstack->retpc = (unsigned long)ret_from_fork; + + p->thread.usp = usp; + p->thread.ksp = (unsigned long)childstack; + /* + * Must save the current SFC/DFC value, NOT the value when + * the parent was last descheduled - RGH 10-08-96 + */ + p->thread.fs = get_fs().seg; + +#ifdef CONFIG_FPU + if (!FPU_IS_EMU) { + /* Copy the current fpu state */ + asm volatile ("fsave %0" : : "m" (p->thread.fpstate[0]) : "memory"); + + if (p->thread.fpstate[0]) + asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" + "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" + : : "m" (p->thread.fp[0]), "m" (p->thread.fpcntl[0]) + : "memory"); + /* Restore the state in case the fpu was busy */ + asm volatile ("frestore %0" : : "m" (p->thread.fpstate[0])); + } +#endif + + return 0; +} + +/* Fill in the fpu structure for a core dump. */ + +int dump_fpu(struct pt_regs *regs, struct user_m68kfp_struct *fpu) +{ +#ifdef CONFIG_FPU + char fpustate[216]; + + if (FPU_IS_EMU) { + int i; + + memcpy(fpu->fpcntl, current->thread.fpcntl, 12); + memcpy(fpu->fpregs, current->thread.fp, 96); + /* Convert internal fpu reg representation + * into long double format + */ + for (i = 0; i < 24; i += 3) + fpu->fpregs[i] = ((fpu->fpregs[i] & 0xffff0000) << 15) | + ((fpu->fpregs[i] & 0x0000ffff) << 16); + return 1; + } + + /* First dump the fpu context to avoid protocol violation. */ + asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); + if (!fpustate[0]) + return 0; + + asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" + :: "m" (fpu->fpcntl[0]) + : "memory"); + asm volatile ("fmovemx %/fp0-%/fp7,%0" + :: "m" (fpu->fpregs[0]) + : "memory"); +#endif + return 1; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + struct switch_stack *sw; + + /* changed the size calculations - should hopefully work better. lbt */ + dump->magic = CMAGIC; + dump->start_code = 0; + dump->start_stack = rdusp() & ~(PAGE_SIZE - 1); + dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT; + dump->u_dsize = ((unsigned long) (current->mm->brk + + (PAGE_SIZE-1))) >> PAGE_SHIFT; + dump->u_dsize -= dump->u_tsize; + dump->u_ssize = 0; + + if (dump->start_stack < TASK_SIZE) + dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; + + dump->u_ar0 = (struct user_regs_struct *)((int)&dump->regs - (int)dump); + sw = ((struct switch_stack *)regs) - 1; + dump->regs.d1 = regs->d1; + dump->regs.d2 = regs->d2; + dump->regs.d3 = regs->d3; + dump->regs.d4 = regs->d4; + dump->regs.d5 = regs->d5; + dump->regs.d6 = sw->d6; + dump->regs.d7 = sw->d7; + dump->regs.a0 = regs->a0; + dump->regs.a1 = regs->a1; + dump->regs.a2 = regs->a2; + dump->regs.a3 = sw->a3; + dump->regs.a4 = sw->a4; + dump->regs.a5 = sw->a5; + dump->regs.a6 = sw->a6; + dump->regs.d0 = regs->d0; + dump->regs.orig_d0 = regs->orig_d0; + dump->regs.stkadj = regs->stkadj; + dump->regs.sr = regs->sr; + dump->regs.pc = regs->pc; + dump->regs.fmtvec = (regs->format << 12) | regs->vector; + /* dump floating point stuff */ + dump->u_fpvalid = dump_fpu (regs, &dump->m68kfp); +} + +/* + * Generic dumping code. Used for panic and debug. + */ +void dump(struct pt_regs *fp) +{ + unsigned long *sp; + unsigned char *tp; + int i; + + printk(KERN_EMERG "\nCURRENT PROCESS:\n\n"); + printk(KERN_EMERG "COMM=%s PID=%d\n", current->comm, current->pid); + + if (current->mm) { + printk(KERN_EMERG "TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n", + (int) current->mm->start_code, + (int) current->mm->end_code, + (int) current->mm->start_data, + (int) current->mm->end_data, + (int) current->mm->end_data, + (int) current->mm->brk); + printk(KERN_EMERG "USER-STACK=%08x KERNEL-STACK=%08x\n\n", + (int) current->mm->start_stack, + (int)(((unsigned long) current) + THREAD_SIZE)); + } + + printk(KERN_EMERG "PC: %08lx\n", fp->pc); + printk(KERN_EMERG "SR: %08lx SP: %08lx\n", (long) fp->sr, (long) fp); + printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + fp->d0, fp->d1, fp->d2, fp->d3); + printk(KERN_EMERG "d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", + fp->d4, fp->d5, fp->a0, fp->a1); + printk(KERN_EMERG "\nUSP: %08x TRAPFRAME: %08x\n", (unsigned int) rdusp(), + (unsigned int) fp); + + printk(KERN_EMERG "\nCODE:"); + tp = ((unsigned char *) fp->pc) - 0x20; + for (sp = (unsigned long *) tp, i = 0; (i < 0x40); i += 4) { + if ((i % 0x10) == 0) + printk(KERN_EMERG "\n%08x: ", (int) (tp + i)); + printk(KERN_EMERG "%08x ", (int) *sp++); + } + printk(KERN_EMERG "\n"); + + printk(KERN_EMERG "\nKERNEL STACK:"); + tp = ((unsigned char *) fp) - 0x40; + for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) { + if ((i % 0x10) == 0) + printk(KERN_EMERG "\n%08x: ", (int) (tp + i)); + printk(KERN_EMERG "%08x ", (int) *sp++); + } + printk(KERN_EMERG "\n"); + printk(KERN_EMERG "\n"); + + printk(KERN_EMERG "\nUSER STACK:"); + tp = (unsigned char *) (rdusp() - 0x10); + for (sp = (unsigned long *) tp, i = 0; (i < 0x80); i += 4) { + if ((i % 0x10) == 0) + printk(KERN_EMERG "\n%08x: ", (int) (tp + i)); + printk(KERN_EMERG "%08x ", (int) *sp++); + } + printk(KERN_EMERG "\n\n"); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(char *name, char **argv, char **envp) +{ + int error; + char * filename; + struct pt_regs *regs = (struct pt_regs *) &name; + + lock_kernel(); + filename = getname(name); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, argv, envp, regs); + putname(filename); +out: + unlock_kernel(); + return error; +} + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long fp, pc; + unsigned long stack_page; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + + stack_page = (unsigned long)p; + fp = ((struct switch_stack *)p->thread.ksp)->a6; + do { + if (fp < stack_page+sizeof(struct thread_info) || + fp >= 8184+stack_page) + return 0; + pc = ((unsigned long *)fp)[1]; + if (!in_sched_functions(pc)) + return pc; + fp = *(unsigned long *) fp; + } while (count++ < 16); + return 0; +} + +/* + * Return saved PC of a blocked thread. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + struct switch_stack *sw = (struct switch_stack *)tsk->thread.ksp; + + /* Check whether the thread is blocked in resume() */ + if (in_sched_functions(sw->retpc)) + return ((unsigned long *)sw->a6)[1]; + else + return sw->retpc; +} + diff --git a/arch/m68knommu/kernel/ptrace.c b/arch/m68knommu/kernel/ptrace.c new file mode 100644 index 00000000000..15cf79080b1 --- /dev/null +++ b/arch/m68knommu/kernel/ptrace.c @@ -0,0 +1,383 @@ +/* + * linux/arch/m68knommu/kernel/ptrace.c + * + * Copyright (C) 1994 by Hamish Macdonald + * Taken from linux/kernel/ptrace.c and modified for M680x0. + * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file COPYING in the main directory of + * this archive for more details. + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/config.h> + +#include <asm/uaccess.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/processor.h> + +/* + * does not yet catch signals sent when the child dies. + * in exit.c or in signal.c. + */ + +/* determines which bits in the SR the user has access to. */ +/* 1 = access 0 = no access */ +#define SR_MASK 0x001f + +/* sets the trace bits. */ +#define TRACE_BITS 0x8000 + +/* Find the stack offset for a register, relative to thread.esp0. */ +#define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) +#define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ + - sizeof(struct switch_stack)) +/* Mapping from PT_xxx to the stack offset at which the register is + saved. Notice that usp has no stack-slot and needs to be treated + specially (see get_reg/put_reg below). */ +static int regoff[] = { + PT_REG(d1), PT_REG(d2), PT_REG(d3), PT_REG(d4), + PT_REG(d5), SW_REG(d6), SW_REG(d7), PT_REG(a0), + PT_REG(a1), PT_REG(a2), SW_REG(a3), SW_REG(a4), + SW_REG(a5), SW_REG(a6), PT_REG(d0), -1, + PT_REG(orig_d0), PT_REG(sr), PT_REG(pc), +}; + +/* + * Get contents of register REGNO in task TASK. + */ +static inline long get_reg(struct task_struct *task, int regno) +{ + unsigned long *addr; + + if (regno == PT_USP) + addr = &task->thread.usp; + else if (regno < sizeof(regoff)/sizeof(regoff[0])) + addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); + else + return 0; + return *addr; +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, int regno, + unsigned long data) +{ + unsigned long *addr; + + if (regno == PT_USP) + addr = &task->thread.usp; + else if (regno < sizeof(regoff)/sizeof(regoff[0])) + addr = (unsigned long *) (task->thread.esp0 + regoff[regno]); + else + return -1; + *addr = data; + return 0; +} + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure the single step bit is not set. + */ +void ptrace_disable(struct task_struct *child) +{ + unsigned long tmp; + /* make sure the single step bit is not set. */ + tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16); + put_reg(child, PT_SR, tmp); +} + +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; + /* 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) + goto out_tsk; + + switch (request) { + /* when I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: { + unsigned long tmp; + int copied; + + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: { + unsigned long tmp; + + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + tmp = 0; /* Default return condition */ + addr = addr >> 2; /* temporary hack. */ + ret = -EIO; + if (addr < 19) { + tmp = get_reg(child, addr); + if (addr == PT_SR) + tmp >>= 16; + } else if (addr >= 21 && addr < 49) { + tmp = child->thread.fp[addr - 21]; +#ifdef CONFIG_M68KFPU_EMU + /* Convert internal fpu reg representation + * into long double format + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) + tmp = ((tmp & 0xffff0000) << 15) | + ((tmp & 0x0000ffff) << 16); +#endif + } else if (addr == 49) { + tmp = child->mm->start_code; + } else if (addr == 50) { + tmp = child->mm->start_data; + } else if (addr == 51) { + tmp = child->mm->end_code; + } else + break; + ret = put_user(tmp,(unsigned long *) data); + break; + } + + /* when I and D space are separate, this will have to be fixed. */ + case PTRACE_POKETEXT: /* write the word at location addr. */ + case PTRACE_POKEDATA: + ret = 0; + if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data)) + break; + ret = -EIO; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret = -EIO; + if ((addr & 3) || addr < 0 || + addr > sizeof(struct user) - 3) + break; + + addr = addr >> 2; /* temporary hack. */ + + if (addr == PT_SR) { + data &= SR_MASK; + data <<= 16; + data |= get_reg(child, PT_SR) & ~(SR_MASK << 16); + } + if (addr < 19) { + if (put_reg(child, addr, data)) + break; + ret = 0; + break; + } + if (addr >= 21 && addr < 48) + { +#ifdef CONFIG_M68KFPU_EMU + /* Convert long double format + * into internal fpu reg representation + */ + if (FPU_IS_EMU && (addr < 45) && !(addr % 3)) { + data = (unsigned long)data << 15; + data = (data & 0xffff0000) | + ((data & 0x0000ffff) >> 1); + } +#endif + child->thread.fp[addr - 21] = data; + ret = 0; + } + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: { /* restart after signal. */ + long tmp; + + 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 the single step bit is not set. */ + tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16); + put_reg(child, PT_SR, tmp); + 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: { + long tmp; + + ret = 0; + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + tmp = get_reg(child, PT_SR) & ~(TRACE_BITS << 16); + put_reg(child, PT_SR, tmp); + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + tmp = get_reg(child, PT_SR) | (TRACE_BITS << 16); + put_reg(child, PT_SR, tmp); + + child->exit_code = data; + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = ptrace_detach(child, data); + break; + + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + int i; + unsigned long tmp; + for (i = 0; i < 19; i++) { + tmp = get_reg(child, i); + if (i == PT_SR) + tmp >>= 16; + if (put_user(tmp, (unsigned long *) data)) { + ret = -EFAULT; + break; + } + data += sizeof(long); + } + ret = 0; + break; + } + + case PTRACE_SETREGS: { /* Set all gp regs in the child. */ + int i; + unsigned long tmp; + for (i = 0; i < 19; i++) { + if (get_user(tmp, (unsigned long *) data)) { + ret = -EFAULT; + break; + } + if (i == PT_SR) { + tmp &= SR_MASK; + tmp <<= 16; + tmp |= get_reg(child, PT_SR) & ~(SR_MASK << 16); + } + put_reg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + break; + } + +#ifdef PTRACE_GETFPREGS + case PTRACE_GETFPREGS: { /* Get the child FPU state. */ + ret = 0; + if (copy_to_user((void *)data, &child->thread.fp, + sizeof(struct user_m68kfp_struct))) + ret = -EFAULT; + break; + } +#endif + +#ifdef PTRACE_SETFPREGS + case PTRACE_SETFPREGS: { /* Set the child FPU state. */ + ret = 0; + if (copy_from_user(&child->thread.fp, (void *)data, + sizeof(struct user_m68kfp_struct))) + ret = -EFAULT; + break; + } +#endif + + default: + ret = -EIO; + break; + } +out_tsk: + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +asmlinkage void syscall_trace(void) +{ + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + 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; + } +} diff --git a/arch/m68knommu/kernel/semaphore.c b/arch/m68knommu/kernel/semaphore.c new file mode 100644 index 00000000000..c083f4772ad --- /dev/null +++ b/arch/m68knommu/kernel/semaphore.c @@ -0,0 +1,134 @@ +/* + * Generic semaphore code. Buyer beware. Do your own + * specific changes in <asm/semaphore-helper.h> + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/init.h> +#include <asm/semaphore-helper.h> + +#ifndef CONFIG_RMW_INSNS +spinlock_t semaphore_wake_lock; +#endif + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to sleep, while the "waking" variable is + * incremented when the "up()" code goes to wake up waiting + * processes. + * + * 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. + * + * waking_non_zero() (from asm/semaphore.h) must execute + * atomically. + * + * When __up() is called, the count was negative before + * incrementing it, and we need to wake up somebody. + * + * This routine adds one to the count of processes that need to + * wake up and exit. ALL waiting processes actually wake up but + * only the one that gets to the "waking" field first will gate + * through and acquire the semaphore. The others will go back + * to sleep. + * + * 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. + */ +void __up(struct semaphore *sem) +{ + wake_one_more(sem); + wake_up(&sem->wait); +} + +/* + * Perform the "down" function. Return zero for semaphore acquired, + * return negative for signalled out of the function. + * + * If called from __down, the return is ignored and the wait loop is + * not interruptible. This means that a task waiting on a semaphore + * using "down()" cannot be killed until someone does an "up()" on + * the semaphore. + * + * If called from __down_interruptible, the return value gets checked + * upon return. If the return value is negative then the task continues + * with the negative value in the return register (it can be tested by + * the caller). + * + * Either form may be used in conjunction with "up()". + * + */ + + +#define DOWN_HEAD(task_state) \ + \ + \ + current->state = (task_state); \ + add_wait_queue(&sem->wait, &wait); \ + \ + /* \ + * Ok, we're set up. sem->count is known to be less than zero \ + * so we must wait. \ + * \ + * We can let go the lock for purposes of waiting. \ + * We re-acquire it after awaking so as to protect \ + * all semaphore operations. \ + * \ + * If "up()" is called before we call waking_non_zero() then \ + * we will catch it right away. If it is called later then \ + * we will have to go through a wakeup cycle to catch it. \ + * \ + * Multiple waiters contend for the semaphore lock to see \ + * who gets to gate through and who has to wait some more. \ + */ \ + for (;;) { + +#define DOWN_TAIL(task_state) \ + current->state = (task_state); \ + } \ + current->state = TASK_RUNNING; \ + remove_wait_queue(&sem->wait, &wait); + +void __sched __down(struct semaphore * sem) +{ + DECLARE_WAITQUEUE(wait, current); + + DOWN_HEAD(TASK_UNINTERRUPTIBLE) + if (waking_non_zero(sem)) + break; + schedule(); + DOWN_TAIL(TASK_UNINTERRUPTIBLE) +} + +int __sched __down_interruptible(struct semaphore * sem) +{ + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + + DOWN_HEAD(TASK_INTERRUPTIBLE) + + ret = waking_non_zero_interruptible(sem, current); + if (ret) + { + if (ret == 1) + /* ret != 0 only if we get interrupted -arca */ + ret = 0; + break; + } + schedule(); + DOWN_TAIL(TASK_INTERRUPTIBLE) + return ret; +} + +int __down_trylock(struct semaphore * sem) +{ + return waking_non_zero_trylock(sem); +} diff --git a/arch/m68knommu/kernel/setup.c b/arch/m68knommu/kernel/setup.c new file mode 100644 index 00000000000..557238596dc --- /dev/null +++ b/arch/m68knommu/kernel/setup.c @@ -0,0 +1,347 @@ +/* + * linux/arch/m68knommu/kernel/setup.c + * + * Copyright (C) 1999-2004 Greg Ungerer (gerg@snapgear.com) + * Copyright (C) 1998,1999 D. Jeff Dionne <jeff@lineo.ca> + * Copyleft ()) 2000 James D. Schettine {james@telos-systems.com} + * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com> + * Copyright (C) 1995 Hamish Macdonald + * Copyright (C) 2000 Lineo Inc. (www.lineo.com) + * Copyright (C) 2001 Lineo, Inc. <www.lineo.com> + * + * 68VZ328 Fixes/support Evan Stawnyczy <e@lineo.ca> + */ + +/* + * This file handles the architecture-dependent parts of system setup + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/fs.h> +#include <linux/fb.h> +#include <linux/console.h> +#include <linux/genhd.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/major.h> +#include <linux/bootmem.h> +#include <linux/seq_file.h> +#include <linux/root_dev.h> +#include <linux/init.h> + +#include <asm/setup.h> +#include <asm/irq.h> +#include <asm/machdep.h> + +#ifdef CONFIG_BLK_DEV_INITRD +#include <asm/pgtable.h> +#endif + +unsigned long rom_length; +unsigned long memory_start; +unsigned long memory_end; + +char command_line[COMMAND_LINE_SIZE]; + +/* setup some dummy routines */ +static void dummy_waitbut(void) +{ +} + +void (*mach_sched_init) (irqreturn_t (*handler)(int, void *, struct pt_regs *)) = NULL; +void (*mach_tick)( void ) = NULL; +/* machine dependent keyboard functions */ +int (*mach_keyb_init) (void) = NULL; +int (*mach_kbdrate) (struct kbd_repeat *) = NULL; +void (*mach_kbd_leds) (unsigned int) = NULL; +/* machine dependent irq functions */ +void (*mach_init_IRQ) (void) = NULL; +irqreturn_t (*(*mach_default_handler)[]) (int, void *, struct pt_regs *) = NULL; +void (*mach_enable_irq) (unsigned int) = NULL; +void (*mach_disable_irq) (unsigned int) = NULL; +int (*mach_get_irq_list) (struct seq_file *, void *) = NULL; +void (*mach_process_int) (int irq, struct pt_regs *fp) = NULL; +void (*mach_trap_init) (void); +/* machine dependent timer functions */ +unsigned long (*mach_gettimeoffset) (void) = NULL; +void (*mach_gettod) (int*, int*, int*, int*, int*, int*) = NULL; +int (*mach_hwclk) (int, struct hwclk_time*) = NULL; +int (*mach_set_clock_mmss) (unsigned long) = NULL; +void (*mach_mksound)( unsigned int count, unsigned int ticks ) = NULL; +void (*mach_reset)( void ) = NULL; +void (*waitbut)(void) = dummy_waitbut; +void (*mach_debug_init)(void) = NULL; +void (*mach_halt)( void ) = NULL; +void (*mach_power_off)( void ) = NULL; + + +#ifdef CONFIG_M68000 + #define CPU "MC68000" +#endif +#ifdef CONFIG_M68328 + #define CPU "MC68328" +#endif +#ifdef CONFIG_M68EZ328 + #define CPU "MC68EZ328" +#endif +#ifdef CONFIG_M68VZ328 + #define CPU "MC68VZ328" +#endif +#ifdef CONFIG_M68332 + #define CPU "MC68332" +#endif +#ifdef CONFIG_M68360 + #define CPU "MC68360" +#endif +#if defined(CONFIG_M5206) + #define CPU "COLDFIRE(m5206)" +#endif +#if defined(CONFIG_M5206e) + #define CPU "COLDFIRE(m5206e)" +#endif +#if defined(CONFIG_M5249) + #define CPU "COLDFIRE(m5249)" +#endif +#if defined(CONFIG_M527x) + #define CPU "COLDFIRE(m5270/5271/5274/5275)" +#endif +#if defined(CONFIG_M5272) + #define CPU "COLDFIRE(m5272)" +#endif +#if defined(CONFIG_M528x) + #define CPU "COLDFIRE(m5280/5282)" +#endif +#if defined(CONFIG_M5307) + #define CPU "COLDFIRE(m5307)" +#endif +#if defined(CONFIG_M5407) + #define CPU "COLDFIRE(m5407)" +#endif +#ifndef CPU + #define CPU "UNKOWN" +#endif + +/* (es) */ +/* note: why is this defined here? the must be a better place to put this */ +#if defined( CONFIG_TELOS) || defined( CONFIG_UCDIMM ) || defined( CONFIG_UCSIMM ) || defined(CONFIG_DRAGEN2) || (defined( CONFIG_PILOT ) && defined( CONFIG_M68328 )) +#define CAT_ROMARRAY +#endif +/* (/es) */ + +extern int _stext, _etext, _sdata, _edata, _sbss, _ebss, _end; +extern int _ramstart, _ramend; + +void setup_arch(char **cmdline_p) +{ + int bootmap_size; + +#if defined(CAT_ROMARRAY) && defined(DEBUG) + extern int __data_rom_start; + extern int __data_start; + int *romarray = (int *)((int) &__data_rom_start + + (int)&_edata - (int)&__data_start); +#endif + + memory_start = PAGE_ALIGN(_ramstart); + memory_end = _ramend; /* by now the stack is part of the init task */ + + init_mm.start_code = (unsigned long) &_stext; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) 0; + + config_BSP(&command_line[0], sizeof(command_line)); + + printk(KERN_INFO "\x0F\r\n\nuClinux/" CPU "\n"); + +#ifdef CONFIG_UCDIMM + printk(KERN_INFO "uCdimm by Lineo, Inc. <www.lineo.com>\n"); +#endif +#ifdef CONFIG_M68VZ328 + printk(KERN_INFO "M68VZ328 support by Evan Stawnyczy <e@lineo.ca>\n"); +#endif +#ifdef CONFIG_COLDFIRE + printk(KERN_INFO "COLDFIRE port done by Greg Ungerer, gerg@snapgear.com\n"); +#ifdef CONFIG_M5307 + printk(KERN_INFO "Modified for M5307 by Dave Miller, dmiller@intellistor.com\n"); +#endif +#ifdef CONFIG_ELITE + printk(KERN_INFO "Modified for M5206eLITE by Rob Scott, rscott@mtrob.fdns.net\n"); +#endif +#ifdef CONFIG_TELOS + printk(KERN_INFO "Modified for Omnia ToolVox by James D. Schettine, james@telos-systems.com\n"); +#endif +#endif + printk(KERN_INFO "Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne\n"); + +#if defined( CONFIG_PILOT ) && defined( CONFIG_M68328 ) + printk(KERN_INFO "TRG SuperPilot FLASH card support <info@trgnet.com>\n"); +#endif + +#if defined( CONFIG_PILOT ) && defined( CONFIG_M68EZ328 ) + printk(KERN_INFO "PalmV support by Lineo Inc. <jeff@uclinux.com>\n"); +#endif + +#ifdef CONFIG_M68EZ328ADS + printk(KERN_INFO "M68EZ328ADS board support (C) 1999 Vladimir Gurevich <vgurevic@cisco.com>\n"); +#endif + +#ifdef CONFIG_ALMA_ANS + printk(KERN_INFO "Alma Electronics board support (C) 1999 Vladimir Gurevich <vgurevic@cisco.com>\n"); +#endif +#if defined (CONFIG_M68360) + printk(KERN_INFO "QUICC port done by SED Systems <hamilton@sedsystems.ca>,\n"); + printk(KERN_INFO "based on 2.0.38 port by Lineo Inc. <mleslie@lineo.com>.\n"); +#endif +#ifdef CONFIG_DRAGEN2 + printk(KERN_INFO "DragonEngine II board support by Georges Menie\n"); +#endif + +#ifdef DEBUG + printk(KERN_DEBUG "KERNEL -> TEXT=0x%06x-0x%06x DATA=0x%06x-0x%06x " + "BSS=0x%06x-0x%06x\n", (int) &_stext, (int) &_etext, + (int) &_sdata, (int) &_edata, + (int) &_sbss, (int) &_ebss); + printk(KERN_DEBUG "KERNEL -> ROMFS=0x%06x-0x%06x MEM=0x%06x-0x%06x " + "STACK=0x%06x-0x%06x\n", +#ifdef CAT_ROMARRAY + (int) romarray, ((int) romarray) + romarray[2], +#else + (int) &_ebss, (int) memory_start, +#endif + (int) memory_start, (int) memory_end, + (int) memory_end, (int) _ramend); +#endif + + /* Keep a copy of command line */ + *cmdline_p = &command_line[0]; + memcpy(saved_command_line, command_line, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = 0; + +#ifdef DEBUG + if (strlen(*cmdline_p)) + printk(KERN_DEBUG "Command line: '%s'\n", *cmdline_p); +#endif + +#if defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_DUMMY_CONSOLE) + conswitchp = &dummy_con; +#endif + + /* + * Give all the memory to the bootmap allocator, tell it to put the + * boot mem_map at the start of memory. + */ + bootmap_size = init_bootmem_node( + NODE_DATA(0), + memory_start >> PAGE_SHIFT, /* map goes here */ + PAGE_OFFSET >> PAGE_SHIFT, /* 0 on coldfire */ + memory_end >> PAGE_SHIFT); + /* + * Free the usable memory, we have to make sure we do not free + * the bootmem bitmap so we then reserve it after freeing it :-) + */ + free_bootmem(memory_start, memory_end - memory_start); + reserve_bootmem(memory_start, bootmap_size); + + /* + * Get kmalloc into gear. + */ + paging_init(); +} + +int get_cpuinfo(char * buffer) +{ + char *cpu, *mmu, *fpu; + u_long clockfreq; + + cpu = CPU; + mmu = "none"; + fpu = "none"; + +#ifdef CONFIG_COLDFIRE + clockfreq = (loops_per_jiffy*HZ)*3; +#else + clockfreq = (loops_per_jiffy*HZ)*16; +#endif + + return(sprintf(buffer, "CPU:\t\t%s\n" + "MMU:\t\t%s\n" + "FPU:\t\t%s\n" + "Clocking:\t%lu.%1luMHz\n" + "BogoMips:\t%lu.%02lu\n" + "Calibration:\t%lu loops\n", + cpu, mmu, fpu, + clockfreq/1000000,(clockfreq/100000)%10, + (loops_per_jiffy*HZ)/500000,((loops_per_jiffy*HZ)/5000)%100, + (loops_per_jiffy*HZ))); + +} + +/* + * Get CPU information for use by the procfs. + */ + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + char *cpu, *mmu, *fpu; + u_long clockfreq; + + cpu = CPU; + mmu = "none"; + fpu = "none"; + +#ifdef CONFIG_COLDFIRE + clockfreq = (loops_per_jiffy*HZ)*3; +#else + clockfreq = (loops_per_jiffy*HZ)*16; +#endif + + seq_printf(m, "CPU:\t\t%s\n" + "MMU:\t\t%s\n" + "FPU:\t\t%s\n" + "Clocking:\t%lu.%1luMHz\n" + "BogoMips:\t%lu.%02lu\n" + "Calibration:\t%lu loops\n", + cpu, mmu, fpu, + clockfreq/1000000,(clockfreq/100000)%10, + (loops_per_jiffy*HZ)/500000,((loops_per_jiffy*HZ)/5000)%100, + (loops_per_jiffy*HZ)); + + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? ((void *) 0x12345678) : NULL; +} + +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} + +static void c_stop(struct seq_file *m, void *v) +{ +} + +struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; + +void arch_gettod(int *year, int *mon, int *day, int *hour, + int *min, int *sec) +{ + if (mach_gettod) + mach_gettod(year, mon, day, hour, min, sec); + else + *year = *mon = *day = *hour = *min = *sec = 0; +} + diff --git a/arch/m68knommu/kernel/signal.c b/arch/m68knommu/kernel/signal.c new file mode 100644 index 00000000000..30dceb59a46 --- /dev/null +++ b/arch/m68knommu/kernel/signal.c @@ -0,0 +1,788 @@ +/* + * linux/arch/m68knommu/kernel/signal.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * Linux/m68k support by Hamish Macdonald + * + * 68060 fixes by Jesper Skov + * + * 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab + * + * mathemu support by Roman Zippel + * (Note: fpstate in the signal context is completely ignored for the emulator + * and the internal floating point format is put on stack) + */ + +/* + * ++roman (07/09/96): implemented signal stacks (specially for tosemu on + * Atari :-) Current limitation: Only one sigstack can be active at one time. + * If a second signal with SA_ONSTACK set arrives while working on a sigstack, + * SA_ONSTACK is ignored. This behaviour avoids lots of trouble with nested + * signal handlers! + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/syscalls.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <linux/highuid.h> +#include <linux/tty.h> +#include <linux/personality.h> +#include <linux/binfmts.h> + +#include <asm/setup.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/traps.h> +#include <asm/ucontext.h> + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs); + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int do_sigsuspend(struct pt_regs *regs) +{ + old_sigset_t mask = regs->d3; + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->d0 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return -EINTR; + } +} + +asmlinkage int +do_rt_sigsuspend(struct pt_regs *regs) +{ + sigset_t *unewset = (sigset_t *)regs->d1; + size_t sigsetsize = (size_t)regs->d2; + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + regs->d0 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(&saveset, regs)) + return -EINTR; + } +} + +asmlinkage int +sys_sigaction(int sig, const struct old_sigaction *act, + struct old_sigaction *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +asmlinkage int +sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + return do_sigaltstack(uss, uoss, rdusp()); +} + + +/* + * Do a signal return; undo the signal stack. + * + * Keep the return code on the stack quadword aligned! + * That makes the cache flush below easier. + */ + +struct sigframe +{ + char *pretcode; + int sig; + int code; + struct sigcontext *psc; + char retcode[8]; + unsigned long extramask[_NSIG_WORDS-1]; + struct sigcontext sc; +}; + +struct rt_sigframe +{ + char *pretcode; + int sig; + struct siginfo *pinfo; + void *puc; + char retcode[8]; + struct siginfo info; + struct ucontext uc; +}; + +#ifdef CONFIG_FPU + +static unsigned char fpu_version = 0; /* version number of fpu, set by setup_frame */ + +static inline int restore_fpu_state(struct sigcontext *sc) +{ + int err = 1; + + if (FPU_IS_EMU) { + /* restore registers */ + memcpy(current->thread.fpcntl, sc->sc_fpcntl, 12); + memcpy(current->thread.fp, sc->sc_fpregs, 24); + return 0; + } + + if (sc->sc_fpstate[0]) { + /* Verify the frame format. */ + if (sc->sc_fpstate[0] != fpu_version) + goto out; + + __asm__ volatile (".chip 68k/68881\n\t" + "fmovemx %0,%/fp0-%/fp1\n\t" + "fmoveml %1,%/fpcr/%/fpsr/%/fpiar\n\t" + ".chip 68k" + : /* no outputs */ + : "m" (*sc->sc_fpregs), "m" (*sc->sc_fpcntl)); + } + __asm__ volatile (".chip 68k/68881\n\t" + "frestore %0\n\t" + ".chip 68k" : : "m" (*sc->sc_fpstate)); + err = 0; + +out: + return err; +} + +#define FPCONTEXT_SIZE 216 +#define uc_fpstate uc_filler[0] +#define uc_formatvec uc_filler[FPCONTEXT_SIZE/4] +#define uc_extra uc_filler[FPCONTEXT_SIZE/4+1] + +static inline int rt_restore_fpu_state(struct ucontext *uc) +{ + unsigned char fpstate[FPCONTEXT_SIZE]; + int context_size = 0; + fpregset_t fpregs; + int err = 1; + + if (FPU_IS_EMU) { + /* restore fpu control register */ + if (__copy_from_user(current->thread.fpcntl, + &uc->uc_mcontext.fpregs.f_pcr, 12)) + goto out; + /* restore all other fpu register */ + if (__copy_from_user(current->thread.fp, + uc->uc_mcontext.fpregs.f_fpregs, 96)) + goto out; + return 0; + } + + if (__get_user(*(long *)fpstate, (long *)&uc->uc_fpstate)) + goto out; + if (fpstate[0]) { + context_size = fpstate[1]; + + /* Verify the frame format. */ + if (fpstate[0] != fpu_version) + goto out; + if (__copy_from_user(&fpregs, &uc->uc_mcontext.fpregs, + sizeof(fpregs))) + goto out; + __asm__ volatile (".chip 68k/68881\n\t" + "fmovemx %0,%/fp0-%/fp7\n\t" + "fmoveml %1,%/fpcr/%/fpsr/%/fpiar\n\t" + ".chip 68k" + : /* no outputs */ + : "m" (*fpregs.f_fpregs), + "m" (fpregs.f_pcr)); + } + if (context_size && + __copy_from_user(fpstate + 4, (long *)&uc->uc_fpstate + 1, + context_size)) + goto out; + __asm__ volatile (".chip 68k/68881\n\t" + "frestore %0\n\t" + ".chip 68k" : : "m" (*fpstate)); + err = 0; + +out: + return err; +} + +#endif + +static inline int +restore_sigcontext(struct pt_regs *regs, struct sigcontext *usc, void *fp, + int *pd0) +{ + int formatvec; + struct sigcontext context; + int err = 0; + + /* get previous context */ + if (copy_from_user(&context, usc, sizeof(context))) + goto badframe; + + /* restore passed registers */ + regs->d1 = context.sc_d1; + regs->a0 = context.sc_a0; + regs->a1 = context.sc_a1; + regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff); + regs->pc = context.sc_pc; + regs->orig_d0 = -1; /* disable syscall checks */ + wrusp(context.sc_usp); + formatvec = context.sc_formatvec; + regs->format = formatvec >> 12; + regs->vector = formatvec & 0xfff; + +#ifdef CONFIG_FPU + err = restore_fpu_state(&context); +#endif + + *pd0 = context.sc_d0; + return err; + +badframe: + return 1; +} + +static inline int +rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, + struct ucontext *uc, int *pd0) +{ + int temp; + greg_t *gregs = uc->uc_mcontext.gregs; + unsigned long usp; + int err; + + err = __get_user(temp, &uc->uc_mcontext.version); + if (temp != MCONTEXT_VERSION) + goto badframe; + /* restore passed registers */ + err |= __get_user(regs->d0, &gregs[0]); + err |= __get_user(regs->d1, &gregs[1]); + err |= __get_user(regs->d2, &gregs[2]); + err |= __get_user(regs->d3, &gregs[3]); + err |= __get_user(regs->d4, &gregs[4]); + err |= __get_user(regs->d5, &gregs[5]); + err |= __get_user(sw->d6, &gregs[6]); + err |= __get_user(sw->d7, &gregs[7]); + err |= __get_user(regs->a0, &gregs[8]); + err |= __get_user(regs->a1, &gregs[9]); + err |= __get_user(regs->a2, &gregs[10]); + err |= __get_user(sw->a3, &gregs[11]); + err |= __get_user(sw->a4, &gregs[12]); + err |= __get_user(sw->a5, &gregs[13]); + err |= __get_user(sw->a6, &gregs[14]); + err |= __get_user(usp, &gregs[15]); + wrusp(usp); + err |= __get_user(regs->pc, &gregs[16]); + err |= __get_user(temp, &gregs[17]); + regs->sr = (regs->sr & 0xff00) | (temp & 0xff); + regs->orig_d0 = -1; /* disable syscall checks */ + regs->format = temp >> 12; + regs->vector = temp & 0xfff; + + if (do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT) + goto badframe; + + *pd0 = regs->d0; + return err; + +badframe: + return 1; +} + +asmlinkage int do_sigreturn(unsigned long __unused) +{ + struct switch_stack *sw = (struct switch_stack *) &__unused; + struct pt_regs *regs = (struct pt_regs *) (sw + 1); + unsigned long usp = rdusp(); + struct sigframe *frame = (struct sigframe *)(usp - 4); + sigset_t set; + int d0; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.sc_mask) || + (_NSIG_WORDS > 1 && + __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigcontext(regs, &frame->sc, frame + 1, &d0)) + goto badframe; + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int do_rt_sigreturn(unsigned long __unused) +{ + struct switch_stack *sw = (struct switch_stack *) &__unused; + struct pt_regs *regs = (struct pt_regs *) (sw + 1); + unsigned long usp = rdusp(); + struct rt_sigframe *frame = (struct rt_sigframe *)(usp - 4); + sigset_t set; + int d0; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (rt_restore_ucontext(regs, sw, &frame->uc, &d0)) + goto badframe; + return d0; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +#ifdef CONFIG_FPU +/* + * Set up a signal frame. + */ + +static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) +{ + if (FPU_IS_EMU) { + /* save registers */ + memcpy(sc->sc_fpcntl, current->thread.fpcntl, 12); + memcpy(sc->sc_fpregs, current->thread.fp, 24); + return; + } + + __asm__ volatile (".chip 68k/68881\n\t" + "fsave %0\n\t" + ".chip 68k" + : : "m" (*sc->sc_fpstate) : "memory"); + + if (sc->sc_fpstate[0]) { + fpu_version = sc->sc_fpstate[0]; + __asm__ volatile (".chip 68k/68881\n\t" + "fmovemx %/fp0-%/fp1,%0\n\t" + "fmoveml %/fpcr/%/fpsr/%/fpiar,%1\n\t" + ".chip 68k" + : /* no outputs */ + : "m" (*sc->sc_fpregs), + "m" (*sc->sc_fpcntl) + : "memory"); + } +} + +static inline int rt_save_fpu_state(struct ucontext *uc, struct pt_regs *regs) +{ + unsigned char fpstate[FPCONTEXT_SIZE]; + int context_size = 0; + int err = 0; + + if (FPU_IS_EMU) { + /* save fpu control register */ + err |= copy_to_user(&uc->uc_mcontext.fpregs.f_pcr, + current->thread.fpcntl, 12); + /* save all other fpu register */ + err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs, + current->thread.fp, 96); + return err; + } + + __asm__ volatile (".chip 68k/68881\n\t" + "fsave %0\n\t" + ".chip 68k" + : : "m" (*fpstate) : "memory"); + + err |= __put_user(*(long *)fpstate, (long *)&uc->uc_fpstate); + if (fpstate[0]) { + fpregset_t fpregs; + context_size = fpstate[1]; + fpu_version = fpstate[0]; + __asm__ volatile (".chip 68k/68881\n\t" + "fmovemx %/fp0-%/fp7,%0\n\t" + "fmoveml %/fpcr/%/fpsr/%/fpiar,%1\n\t" + ".chip 68k" + : /* no outputs */ + : "m" (*fpregs.f_fpregs), + "m" (fpregs.f_pcr) + : "memory"); + err |= copy_to_user(&uc->uc_mcontext.fpregs, &fpregs, + sizeof(fpregs)); + } + if (context_size) + err |= copy_to_user((long *)&uc->uc_fpstate + 1, fpstate + 4, + context_size); + return err; +} + +#endif + +static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, + unsigned long mask) +{ + sc->sc_mask = mask; + sc->sc_usp = rdusp(); + sc->sc_d0 = regs->d0; + sc->sc_d1 = regs->d1; + sc->sc_a0 = regs->a0; + sc->sc_a1 = regs->a1; + sc->sc_sr = regs->sr; + sc->sc_pc = regs->pc; + sc->sc_formatvec = regs->format << 12 | regs->vector; +#ifdef CONFIG_FPU + save_fpu_state(sc, regs); +#endif +} + +static inline int rt_setup_ucontext(struct ucontext *uc, struct pt_regs *regs) +{ + struct switch_stack *sw = (struct switch_stack *)regs - 1; + greg_t *gregs = uc->uc_mcontext.gregs; + int err = 0; + + err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); + err |= __put_user(regs->d0, &gregs[0]); + err |= __put_user(regs->d1, &gregs[1]); + err |= __put_user(regs->d2, &gregs[2]); + err |= __put_user(regs->d3, &gregs[3]); + err |= __put_user(regs->d4, &gregs[4]); + err |= __put_user(regs->d5, &gregs[5]); + err |= __put_user(sw->d6, &gregs[6]); + err |= __put_user(sw->d7, &gregs[7]); + err |= __put_user(regs->a0, &gregs[8]); + err |= __put_user(regs->a1, &gregs[9]); + err |= __put_user(regs->a2, &gregs[10]); + err |= __put_user(sw->a3, &gregs[11]); + err |= __put_user(sw->a4, &gregs[12]); + err |= __put_user(sw->a5, &gregs[13]); + err |= __put_user(sw->a6, &gregs[14]); + err |= __put_user(rdusp(), &gregs[15]); + err |= __put_user(regs->pc, &gregs[16]); + err |= __put_user(regs->sr, &gregs[17]); +#ifdef CONFIG_FPU + err |= rt_save_fpu_state(uc, regs); +#endif + return err; +} + +static inline void push_cache (unsigned long vaddr) +{ +} + +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) +{ + unsigned long usp; + + /* Default to using normal stack. */ + usp = rdusp(); + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (!on_sig_stack(usp)) + usp = current->sas_ss_sp + current->sas_ss_size; + } + return (void *)((usp - frame_size) & -8UL); +} + +static void setup_frame (int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs *regs) +{ + struct sigframe *frame; + struct sigcontext context; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + err |= __put_user((current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + + err |= __put_user(regs->vector, &frame->code); + err |= __put_user(&frame->sc, &frame->psc); + + if (_NSIG_WORDS > 1) + err |= copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + + setup_sigcontext(&context, regs, set->sig[0]); + err |= copy_to_user (&frame->sc, &context, sizeof(context)); + + /* Set up to return from userspace. */ + err |= __put_user(frame->retcode, &frame->pretcode); + /* moveq #,d0; trap #0 */ + err |= __put_user(0x70004e40 + (__NR_sigreturn << 16), + (long *)(frame->retcode)); + + if (err) + goto give_sigsegv; + + push_cache ((unsigned long) &frame->retcode); + + /* Set up registers for signal handler */ + wrusp ((unsigned long) frame); + regs->pc = (unsigned long) ka->sa.sa_handler; + +adjust_stack: + /* Prepare to skip over the extra stuff in the exception frame. */ + if (regs->stkadj) { + struct pt_regs *tregs = + (struct pt_regs *)((ulong)regs + regs->stkadj); +#if DEBUG + printk(KERN_DEBUG "Performing stackadjust=%04x\n", regs->stkadj); +#endif + /* This must be copied with decreasing addresses to + handle overlaps. */ + tregs->vector = 0; + tregs->format = 0; + tregs->pc = regs->pc; + tregs->sr = regs->sr; + } + return; + +give_sigsegv: + force_sigsegv(sig, current); + goto adjust_stack; +} + +static void setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs *regs) +{ + struct rt_sigframe *frame; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + err |= __put_user((current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32 + ? current_thread_info()->exec_domain->signal_invmap[sig] + : sig), + &frame->sig); + err |= __put_user(&frame->info, &frame->pinfo); + err |= __put_user(&frame->uc, &frame->puc); + err |= copy_siginfo_to_user(&frame->info, info); + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user((void *)current->sas_ss_sp, + &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(rdusp()), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= rt_setup_ucontext(&frame->uc, regs); + err |= copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set)); + + /* Set up to return from userspace. */ + err |= __put_user(frame->retcode, &frame->pretcode); + /* moveq #,d0; notb d0; trap #0 */ + err |= __put_user(0x70004600 + ((__NR_rt_sigreturn ^ 0xff) << 16), + (long *)(frame->retcode + 0)); + err |= __put_user(0x4e40, (short *)(frame->retcode + 4)); + + if (err) + goto give_sigsegv; + + push_cache ((unsigned long) &frame->retcode); + + /* Set up registers for signal handler */ + wrusp ((unsigned long) frame); + regs->pc = (unsigned long) ka->sa.sa_handler; + +adjust_stack: + /* Prepare to skip over the extra stuff in the exception frame. */ + if (regs->stkadj) { + struct pt_regs *tregs = + (struct pt_regs *)((ulong)regs + regs->stkadj); +#if DEBUG + printk(KERN_DEBUG "Performing stackadjust=%04x\n", regs->stkadj); +#endif + /* This must be copied with decreasing addresses to + handle overlaps. */ + tregs->vector = 0; + tregs->format = 0; + tregs->pc = regs->pc; + tregs->sr = regs->sr; + } + return; + +give_sigsegv: + force_sigsegv(sig, current); + goto adjust_stack; +} + +static inline void +handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) +{ + switch (regs->d0) { + case -ERESTARTNOHAND: + if (!has_handler) + goto do_restart; + regs->d0 = -EINTR; + break; + + case -ERESTARTSYS: + if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { + regs->d0 = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + do_restart: + regs->d0 = regs->orig_d0; + regs->pc -= 2; + break; + } +} + +/* + * OK, we're invoking a handler + */ +static void +handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *oldset, struct pt_regs *regs) +{ + /* are we from a system call? */ + if (regs->orig_d0 >= 0) + /* If so, check system call restarting.. */ + handle_restart(regs, ka, 1); + + /* set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame(sig, ka, info, oldset, regs); + else + setup_frame(sig, ka, oldset, regs); + + if (ka->sa.sa_flags & SA_ONESHOT) + ka->sa.sa_handler = SIG_DFL; + + if (!(ka->sa.sa_flags & SA_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + */ +asmlinkage int do_signal(sigset_t *oldset, struct pt_regs *regs) +{ + 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 1; + + if (!oldset) + oldset = ¤t->blocked; + + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + if (signr > 0) { + /* Whee! Actually deliver the signal. */ + handle_signal(signr, &ka, &info, oldset, regs); + return 1; + } + + /* Did we come from a system call? */ + if (regs->orig_d0 >= 0) { + /* Restart the system call - no handlers present */ + if (regs->d0 == -ERESTARTNOHAND + || regs->d0 == -ERESTARTSYS + || regs->d0 == -ERESTARTNOINTR) { + regs->d0 = regs->orig_d0; + regs->pc -= 2; + } else if (regs->d0 == -ERESTART_RESTARTBLOCK) { + regs->d0 = __NR_restart_syscall; + regs->pc -= 2; + } + } + return 0; +} diff --git a/arch/m68knommu/kernel/sys_m68k.c b/arch/m68knommu/kernel/sys_m68k.c new file mode 100644 index 00000000000..d87e1e0a133 --- /dev/null +++ b/arch/m68knommu/kernel/sys_m68k.c @@ -0,0 +1,208 @@ +/* + * linux/arch/m68knommu/kernel/sys_m68k.c + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/m68k + * platform. + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/stat.h> +#include <linux/syscalls.h> +#include <linux/mman.h> +#include <linux/file.h> +#include <linux/utsname.h> + +#include <asm/setup.h> +#include <asm/uaccess.h> +#include <asm/cachectl.h> +#include <asm/traps.h> +#include <asm/ipc.h> +#include <asm/cacheflush.h> + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way unix traditionally does this, though. + */ +asmlinkage int sys_pipe(unsigned long * fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +/* common code for old and new mmaps */ +static inline long do_mmap2( + unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + int error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, + unsigned long prot, unsigned long flags, + unsigned long fd, unsigned long pgoff) +{ + return do_mmap2(addr, len, prot, flags, fd, pgoff); +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/m68k cloned Linux/i386, which didn't use to be able to + * handle more than 4 system call parameters, so these system calls + * used a memory block for parameter passing.. + */ + +struct mmap_arg_struct { + unsigned long addr; + unsigned long len; + unsigned long prot; + unsigned long flags; + unsigned long fd; + unsigned long offset; +}; + +asmlinkage int old_mmap(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + int error = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + + error = -EINVAL; + if (a.offset & ~PAGE_MASK) + goto out; + + a.flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + + error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT); +out: + return error; +} + +struct sel_arg_struct { + unsigned long n; + fd_set *inp, *outp, *exp; + struct timeval *tvp; +}; + +asmlinkage int old_select(struct sel_arg_struct *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage int sys_ipc (uint call, int first, int second, + int third, void *ptr, long fifth) +{ + int version; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + if (call <= SEMCTL) + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + default: + return -EINVAL; + } + if (call <= MSGCTL) + switch (call) { + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + if (copy_from_user (&tmp, + (struct ipc_kludge *)ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, + (struct msqid_ds *) ptr); + default: + return -EINVAL; + } + + return -EINVAL; +} + +/* sys_cacheflush -- flush (part of) the processor cache. */ +asmlinkage int +sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len) +{ + flush_cache_all(); + return(0); +} + +asmlinkage int sys_getpagesize(void) +{ + return PAGE_SIZE; +} + diff --git a/arch/m68knommu/kernel/syscalltable.S b/arch/m68knommu/kernel/syscalltable.S new file mode 100644 index 00000000000..897deaa06b0 --- /dev/null +++ b/arch/m68knommu/kernel/syscalltable.S @@ -0,0 +1,308 @@ +/* + * linux/arch/m68knommu/kernel/syscalltable.S + * + * Copyright (C) 2002, Greg Ungerer (gerg@snapgear.com) + * + * Based on older entry.S files, the following copyrights apply: + * + * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, + * Kenneth Albanowski <kjahds@kjahds.com>, + * Copyright (C) 2000 Lineo Inc. (www.lineo.com) + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include <linux/config.h> +#include <linux/sys.h> +#include <linux/linkage.h> +#include <asm/unistd.h> + +.text +ALIGN +ENTRY(sys_call_table) + .long sys_ni_syscall /* 0 - old "setup()" system call*/ + .long sys_exit + .long sys_fork + .long sys_read + .long sys_write + .long sys_open /* 5 */ + .long sys_close + .long sys_waitpid + .long sys_creat + .long sys_link + .long sys_unlink /* 10 */ + .long sys_execve + .long sys_chdir + .long sys_time + .long sys_mknod + .long sys_chmod /* 15 */ + .long sys_chown16 + .long sys_ni_syscall /* old break syscall holder */ + .long sys_stat + .long sys_lseek + .long sys_getpid /* 20 */ + .long sys_mount + .long sys_oldumount + .long sys_setuid16 + .long sys_getuid16 + .long sys_stime /* 25 */ + .long sys_ptrace + .long sys_alarm + .long sys_fstat + .long sys_pause + .long sys_utime /* 30 */ + .long sys_ni_syscall /* old stty syscall holder */ + .long sys_ni_syscall /* old gtty syscall holder */ + .long sys_access + .long sys_nice + .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ + .long sys_sync + .long sys_kill + .long sys_rename + .long sys_mkdir + .long sys_rmdir /* 40 */ + .long sys_dup + .long sys_pipe + .long sys_times + .long sys_ni_syscall /* old prof syscall holder */ + .long sys_brk /* 45 */ + .long sys_setgid16 + .long sys_getgid16 + .long sys_signal + .long sys_geteuid16 + .long sys_getegid16 /* 50 */ + .long sys_acct + .long sys_umount /* recycled never used phys() */ + .long sys_ni_syscall /* old lock syscall holder */ + .long sys_ioctl + .long sys_fcntl /* 55 */ + .long sys_ni_syscall /* old mpx syscall holder */ + .long sys_setpgid + .long sys_ni_syscall /* old ulimit syscall holder */ + .long sys_ni_syscall + .long sys_umask /* 60 */ + .long sys_chroot + .long sys_ustat + .long sys_dup2 + .long sys_getppid + .long sys_getpgrp /* 65 */ + .long sys_setsid + .long sys_sigaction + .long sys_sgetmask + .long sys_ssetmask + .long sys_setreuid16 /* 70 */ + .long sys_setregid16 + .long sys_sigsuspend + .long sys_sigpending + .long sys_sethostname + .long sys_setrlimit /* 75 */ + .long sys_old_getrlimit + .long sys_getrusage + .long sys_gettimeofday + .long sys_settimeofday + .long sys_getgroups16 /* 80 */ + .long sys_setgroups16 + .long old_select + .long sys_symlink + .long sys_lstat + .long sys_readlink /* 85 */ + .long sys_uselib + .long sys_ni_syscall /* sys_swapon */ + .long sys_reboot + .long old_readdir + .long old_mmap /* 90 */ + .long sys_munmap + .long sys_truncate + .long sys_ftruncate + .long sys_fchmod + .long sys_fchown16 /* 95 */ + .long sys_getpriority + .long sys_setpriority + .long sys_ni_syscall /* old profil syscall holder */ + .long sys_statfs + .long sys_fstatfs /* 100 */ + .long sys_ni_syscall /* ioperm for i386 */ + .long sys_socketcall + .long sys_syslog + .long sys_setitimer + .long sys_getitimer /* 105 */ + .long sys_newstat + .long sys_newlstat + .long sys_newfstat + .long sys_ni_syscall + .long sys_ni_syscall /* iopl for i386 */ /* 110 */ + .long sys_vhangup + .long sys_ni_syscall /* obsolete idle() syscall */ + .long sys_ni_syscall /* vm86old for i386 */ + .long sys_wait4 + .long sys_ni_syscall /* 115 */ /* sys_swapoff */ + .long sys_sysinfo + .long sys_ipc + .long sys_fsync + .long sys_sigreturn + .long sys_clone /* 120 */ + .long sys_setdomainname + .long sys_newuname + .long sys_cacheflush /* modify_ldt for i386 */ + .long sys_adjtimex + .long sys_ni_syscall /* 125 */ /* sys_mprotect */ + .long sys_sigprocmask + .long sys_ni_syscall /* old "creat_module" */ + .long sys_init_module + .long sys_delete_module + .long sys_ni_syscall /* 130: old "get_kernel_syms" */ + .long sys_quotactl + .long sys_getpgid + .long sys_fchdir + .long sys_bdflush + .long sys_sysfs /* 135 */ + .long sys_personality + .long sys_ni_syscall /* for afs_syscall */ + .long sys_setfsuid16 + .long sys_setfsgid16 + .long sys_llseek /* 140 */ + .long sys_getdents + .long sys_select + .long sys_flock + .long sys_ni_syscall /* sys_msync */ + .long sys_readv /* 145 */ + .long sys_writev + .long sys_getsid + .long sys_fdatasync + .long sys_sysctl + .long sys_ni_syscall /* 150 */ /* sys_mlock */ + .long sys_ni_syscall /* sys_munlock */ + .long sys_ni_syscall /* sys_mlockall */ + .long sys_ni_syscall /* sys_munlockall */ + .long sys_sched_setparam + .long sys_sched_getparam /* 155 */ + .long sys_sched_setscheduler + .long sys_sched_getscheduler + .long sys_sched_yield + .long sys_sched_get_priority_max + .long sys_sched_get_priority_min /* 160 */ + .long sys_sched_rr_get_interval + .long sys_nanosleep + .long sys_ni_syscall /* sys_mremap */ + .long sys_setresuid16 + .long sys_getresuid16 /* 165 */ + .long sys_getpagesize /* sys_getpagesize */ + .long sys_ni_syscall /* old "query_module" */ + .long sys_poll + .long sys_ni_syscall /* sys_nfsservctl */ + .long sys_setresgid16 /* 170 */ + .long sys_getresgid16 + .long sys_prctl + .long sys_rt_sigreturn + .long sys_rt_sigaction + .long sys_rt_sigprocmask /* 175 */ + .long sys_rt_sigpending + .long sys_rt_sigtimedwait + .long sys_rt_sigqueueinfo + .long sys_rt_sigsuspend + .long sys_pread64 /* 180 */ + .long sys_pwrite64 + .long sys_lchown16 + .long sys_getcwd + .long sys_capget + .long sys_capset /* 185 */ + .long sys_sigaltstack + .long sys_sendfile + .long sys_ni_syscall /* streams1 */ + .long sys_ni_syscall /* streams2 */ + .long sys_vfork /* 190 */ + .long sys_getrlimit + .long sys_mmap2 + .long sys_truncate64 + .long sys_ftruncate64 + .long sys_stat64 /* 195 */ + .long sys_lstat64 + .long sys_fstat64 + .long sys_chown + .long sys_getuid + .long sys_getgid /* 200 */ + .long sys_geteuid + .long sys_getegid + .long sys_setreuid + .long sys_setregid + .long sys_getgroups /* 205 */ + .long sys_setgroups + .long sys_fchown + .long sys_setresuid + .long sys_getresuid + .long sys_setresgid /* 210 */ + .long sys_getresgid + .long sys_lchown + .long sys_setuid + .long sys_setgid + .long sys_setfsuid /* 215 */ + .long sys_setfsgid + .long sys_pivot_root + .long sys_ni_syscall + .long sys_ni_syscall + .long sys_getdents64 /* 220 */ + .long sys_gettid + .long sys_tkill + .long sys_setxattr + .long sys_lsetxattr + .long sys_fsetxattr /* 225 */ + .long sys_getxattr + .long sys_lgetxattr + .long sys_fgetxattr + .long sys_listxattr + .long sys_llistxattr /* 230 */ + .long sys_flistxattr + .long sys_removexattr + .long sys_lremovexattr + .long sys_fremovexattr + .long sys_futex /* 235 */ + .long sys_sendfile64 + .long sys_ni_syscall /* sys_mincore */ + .long sys_ni_syscall /* sys_madvise */ + .long sys_fcntl64 + .long sys_readahead /* 240 */ + .long sys_io_setup + .long sys_io_destroy + .long sys_io_getevents + .long sys_io_submit + .long sys_io_cancel /* 245 */ + .long sys_fadvise64 + .long sys_exit_group + .long sys_lookup_dcookie + .long sys_epoll_create + .long sys_epoll_ctl /* 250 */ + .long sys_epoll_wait + .long sys_ni_syscall /* sys_remap_file_pages */ + .long sys_set_tid_address + .long sys_timer_create + .long sys_timer_settime /* 255 */ + .long sys_timer_gettime + .long sys_timer_getoverrun + .long sys_timer_delete + .long sys_clock_settime + .long sys_clock_gettime /* 260 */ + .long sys_clock_getres + .long sys_clock_nanosleep + .long sys_statfs64 + .long sys_fstatfs64 + .long sys_tgkill /* 265 */ + .long sys_utimes + .long sys_fadvise64_64 + .long sys_mbind + .long sys_get_mempolicy + .long sys_set_mempolicy /* 270 */ + .long sys_mq_open + .long sys_mq_unlink + .long sys_mq_timedsend + .long sys_mq_timedreceive + .long sys_mq_notify /* 275 */ + .long sys_mq_getsetattr + .long sys_waitid + .long sys_ni_syscall /* sys_setaltroot */ + .long sys_ni_syscall /* sys_add_key */ + .long sys_ni_syscall /* 280 */ /* sys_request_key */ + .long sys_ni_syscall /* sys_keyctl */ + + .rept NR_syscalls-(.-sys_call_table)/4 + .long sys_ni_syscall + .endr + diff --git a/arch/m68knommu/kernel/time.c b/arch/m68knommu/kernel/time.c new file mode 100644 index 00000000000..5c3ca671627 --- /dev/null +++ b/arch/m68knommu/kernel/time.c @@ -0,0 +1,198 @@ +/* + * linux/arch/m68knommu/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * + * This file contains the m68k-specific time handling details. + * Most of the stuff is located in the machine specific files. + * + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/profile.h> +#include <linux/time.h> +#include <linux/timex.h> + +#include <asm/machdep.h> +#include <asm/io.h> + +#define TICK_SIZE (tick_nsec / 1000) + +u64 jiffies_64 = INITIAL_JIFFIES; + +EXPORT_SYMBOL(jiffies_64); + +extern unsigned long wall_jiffies; + + +static inline int set_rtc_mmss(unsigned long nowtime) +{ + if (mach_set_clock_mmss) + return mach_set_clock_mmss (nowtime); + return -1; +} + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +static irqreturn_t timer_interrupt(int irq, void *dummy, struct pt_regs * regs) +{ + /* last time the cmos clock got updated */ + static long last_rtc_update=0; + + /* may need to kick the hardware timer */ + if (mach_tick) + mach_tick(); + + write_seqlock(&xtime_lock); + + do_timer(regs); +#ifndef CONFIG_SMP + update_process_times(user_mode(regs)); +#endif + if (current->pid) + profile_tick(CPU_PROFILING, regs); + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if ((time_status & STA_UNSYNC) == 0 && + xtime.tv_sec > last_rtc_update + 660 && + (xtime.tv_nsec / 1000) >= 500000 - ((unsigned) TICK_SIZE) / 2 && + (xtime.tv_nsec / 1000) <= 500000 + ((unsigned) TICK_SIZE) / 2) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */ + } +#ifdef CONFIG_HEARTBEAT + /* use power LED as a heartbeat instead -- much more useful + for debugging -- based on the version for PReP by Cort */ + /* acts like an actual heart beat -- ie thump-thump-pause... */ + if (mach_heartbeat) { + static unsigned cnt = 0, period = 0, dist = 0; + + if (cnt == 0 || cnt == dist) + mach_heartbeat( 1 ); + else if (cnt == 7 || cnt == dist+7) + mach_heartbeat( 0 ); + + if (++cnt > period) { + cnt = 0; + /* The hyperbolic function below modifies the heartbeat period + * length in dependency of the current (5min) load. It goes + * through the points f(0)=126, f(1)=86, f(5)=51, + * f(inf)->30. */ + period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30; + dist = period / 4; + } + } +#endif /* CONFIG_HEARTBEAT */ + + write_sequnlock(&xtime_lock); + return(IRQ_HANDLED); +} + +void time_init(void) +{ + unsigned int year, mon, day, hour, min, sec; + + extern void arch_gettod(int *year, int *mon, int *day, int *hour, + int *min, int *sec); + + arch_gettod(&year, &mon, &day, &hour, &min, &sec); + + if ((year += 1900) < 1970) + year += 100; + xtime.tv_sec = mktime(year, mon, day, hour, min, sec); + xtime.tv_nsec = 0; + wall_to_monotonic.tv_sec = -xtime.tv_sec; + + mach_sched_init(timer_interrupt); +} + +/* + * This version of gettimeofday has near microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long lost, seq; + unsigned long usec, sec; + + do { + seq = read_seqbegin_irqsave(&xtime_lock, flags); + usec = mach_gettimeoffset ? mach_gettimeoffset() : 0; + lost = jiffies - wall_jiffies; + if (lost) + usec += lost * (1000000 / HZ); + sec = xtime.tv_sec; + usec += (xtime.tv_nsec / 1000); + } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); + + 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 the xtime.tv_usec + * correctly. However, the value in this location is + * is value at the last tick. + * Discover what correction gettimeofday + * would have done, and then undo it! + */ + if (mach_gettimeoffset) + nsec -= (mach_gettimeoffset() * 1000); + + 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; +} + +/* + * Scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + return (unsigned long long)jiffies * (1000000000 / HZ); +} + +EXPORT_SYMBOL(do_settimeofday); diff --git a/arch/m68knommu/kernel/traps.c b/arch/m68knommu/kernel/traps.c new file mode 100644 index 00000000000..ad7dc6347f1 --- /dev/null +++ b/arch/m68knommu/kernel/traps.c @@ -0,0 +1,320 @@ +/* + * linux/arch/m68knommu/kernel/traps.c + * + * Copyright (C) 1993, 1994 by Hamish Macdonald + * + * 68040 fixes by Michael Rausch + * 68040 fixes by Martin Apel + * 68060 fixes by Roman Hodek + * 68060 fixes by Jesper Skov + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive + * for more details. + */ + +/* + * Sets up all exception vectors + */ +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/types.h> +#include <linux/a.out.h> +#include <linux/user.h> +#include <linux/string.h> +#include <linux/linkage.h> +#include <linux/init.h> +#include <linux/ptrace.h> + +#include <asm/setup.h> +#include <asm/fpu.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/traps.h> +#include <asm/pgtable.h> +#include <asm/machdep.h> +#include <asm/siginfo.h> + +static char *vec_names[] = { + "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR", + "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc", + "PRIVILEGE VIOLATION", "TRACE", "LINE 1010", "LINE 1111", + "UNASSIGNED RESERVED 12", "COPROCESSOR PROTOCOL VIOLATION", + "FORMAT ERROR", "UNINITIALIZED INTERRUPT", + "UNASSIGNED RESERVED 16", "UNASSIGNED RESERVED 17", + "UNASSIGNED RESERVED 18", "UNASSIGNED RESERVED 19", + "UNASSIGNED RESERVED 20", "UNASSIGNED RESERVED 21", + "UNASSIGNED RESERVED 22", "UNASSIGNED RESERVED 23", + "SPURIOUS INTERRUPT", "LEVEL 1 INT", "LEVEL 2 INT", "LEVEL 3 INT", + "LEVEL 4 INT", "LEVEL 5 INT", "LEVEL 6 INT", "LEVEL 7 INT", + "SYSCALL", "TRAP #1", "TRAP #2", "TRAP #3", + "TRAP #4", "TRAP #5", "TRAP #6", "TRAP #7", + "TRAP #8", "TRAP #9", "TRAP #10", "TRAP #11", + "TRAP #12", "TRAP #13", "TRAP #14", "TRAP #15", + "FPCP BSUN", "FPCP INEXACT", "FPCP DIV BY 0", "FPCP UNDERFLOW", + "FPCP OPERAND ERROR", "FPCP OVERFLOW", "FPCP SNAN", + "FPCP UNSUPPORTED OPERATION", + "MMU CONFIGURATION ERROR" +}; + +void __init trap_init(void) +{ + if (mach_trap_init) + mach_trap_init(); +} + +void die_if_kernel(char *str, struct pt_regs *fp, int nr) +{ + if (!(fp->sr & PS_S)) + return; + + console_verbose(); + printk(KERN_EMERG "%s: %08x\n",str,nr); + printk(KERN_EMERG "PC: [<%08lx>]\nSR: %04x SP: %p a2: %08lx\n", + fp->pc, fp->sr, fp, fp->a2); + printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", + fp->d0, fp->d1, fp->d2, fp->d3); + printk(KERN_EMERG "d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", + fp->d4, fp->d5, fp->a0, fp->a1); + + printk(KERN_EMERG "Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, PAGE_SIZE+(unsigned long)current); + show_stack(NULL, (unsigned long *)fp); + do_exit(SIGSEGV); +} + +asmlinkage void buserr_c(struct frame *fp) +{ + /* Only set esp0 if coming from user mode */ + if (user_mode(&fp->ptregs)) + current->thread.esp0 = (unsigned long) fp; + +#if DEBUG + printk (KERN_DEBUG "*** Bus Error *** Format is %x\n", fp->ptregs.format); +#endif + + die_if_kernel("bad frame format",&fp->ptregs,0); +#if DEBUG + printk(KERN_DEBUG "Unknown SIGSEGV - 4\n"); +#endif + force_sig(SIGSEGV, current); +} + + +int kstack_depth_to_print = 48; + +void show_stack(struct task_struct *task, unsigned long *esp) +{ + unsigned long *stack, *endstack, addr; + extern char _start, _etext; + int i; + + if (esp == NULL) + esp = (unsigned long *) &esp; + + stack = esp; + addr = (unsigned long) esp; + endstack = (unsigned long *) PAGE_ALIGN(addr); + + printk(KERN_EMERG "Stack from %08lx:", (unsigned long)stack); + for (i = 0; i < kstack_depth_to_print; i++) { + if (stack + 1 > endstack) + break; + if (i % 8 == 0) + printk(KERN_EMERG "\n "); + printk(KERN_EMERG " %08lx", *stack++); + } + + printk(KERN_EMERG "\nCall Trace:"); + i = 0; + while (stack + 1 <= endstack) { + addr = *stack++; + /* + * If the address is either in the text segment of the + * kernel, or in the region which contains vmalloc'ed + * memory, it *may* be the address of a calling + * routine; if so, print it so that someone tracing + * down the cause of the crash will be able to figure + * out the call path that was taken. + */ + if (((addr >= (unsigned long) &_start) && + (addr <= (unsigned long) &_etext))) { + if (i % 4 == 0) + printk(KERN_EMERG "\n "); + printk(KERN_EMERG " [<%08lx>]", addr); + i++; + } + } + printk(KERN_EMERG "\n"); +} + +void bad_super_trap(struct frame *fp) +{ + console_verbose(); + if (fp->ptregs.vector < 4*sizeof(vec_names)/sizeof(vec_names[0])) + printk (KERN_WARNING "*** %s *** FORMAT=%X\n", + vec_names[(fp->ptregs.vector) >> 2], + fp->ptregs.format); + else + printk (KERN_WARNING "*** Exception %d *** FORMAT=%X\n", + (fp->ptregs.vector) >> 2, + fp->ptregs.format); + printk (KERN_WARNING "Current process id is %d\n", current->pid); + die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0); +} + +asmlinkage void trap_c(struct frame *fp) +{ + int sig; + siginfo_t info; + + if (fp->ptregs.sr & PS_S) { + if ((fp->ptregs.vector >> 2) == VEC_TRACE) { + /* traced a trapping instruction */ + current->ptrace |= PT_DTRACE; + } else + bad_super_trap(fp); + return; + } + + /* send the appropriate signal to the user program */ + switch ((fp->ptregs.vector) >> 2) { + case VEC_ADDRERR: + info.si_code = BUS_ADRALN; + sig = SIGBUS; + break; + case VEC_ILLEGAL: + case VEC_LINE10: + case VEC_LINE11: + info.si_code = ILL_ILLOPC; + sig = SIGILL; + break; + case VEC_PRIV: + info.si_code = ILL_PRVOPC; + sig = SIGILL; + break; + case VEC_COPROC: + info.si_code = ILL_COPROC; + sig = SIGILL; + break; + case VEC_TRAP1: /* gdbserver breakpoint */ + fp->ptregs.pc -= 2; + info.si_code = TRAP_TRACE; + sig = SIGTRAP; + break; + case VEC_TRAP2: + case VEC_TRAP3: + case VEC_TRAP4: + case VEC_TRAP5: + case VEC_TRAP6: + case VEC_TRAP7: + case VEC_TRAP8: + case VEC_TRAP9: + case VEC_TRAP10: + case VEC_TRAP11: + case VEC_TRAP12: + case VEC_TRAP13: + case VEC_TRAP14: + info.si_code = ILL_ILLTRP; + sig = SIGILL; + break; + case VEC_FPBRUC: + case VEC_FPOE: + case VEC_FPNAN: + info.si_code = FPE_FLTINV; + sig = SIGFPE; + break; + case VEC_FPIR: + info.si_code = FPE_FLTRES; + sig = SIGFPE; + break; + case VEC_FPDIVZ: + info.si_code = FPE_FLTDIV; + sig = SIGFPE; + break; + case VEC_FPUNDER: + info.si_code = FPE_FLTUND; + sig = SIGFPE; + break; + case VEC_FPOVER: + info.si_code = FPE_FLTOVF; + sig = SIGFPE; + break; + case VEC_ZERODIV: + info.si_code = FPE_INTDIV; + sig = SIGFPE; + break; + case VEC_CHK: + case VEC_TRAP: + info.si_code = FPE_INTOVF; + sig = SIGFPE; + break; + case VEC_TRACE: /* ptrace single step */ + info.si_code = TRAP_TRACE; + sig = SIGTRAP; + break; + case VEC_TRAP15: /* breakpoint */ + info.si_code = TRAP_BRKPT; + sig = SIGTRAP; + break; + default: + info.si_code = ILL_ILLOPC; + sig = SIGILL; + break; + } + info.si_signo = sig; + info.si_errno = 0; + switch (fp->ptregs.format) { + default: + info.si_addr = (void *) fp->ptregs.pc; + break; + case 2: + info.si_addr = (void *) fp->un.fmt2.iaddr; + break; + case 7: + info.si_addr = (void *) fp->un.fmt7.effaddr; + break; + case 9: + info.si_addr = (void *) fp->un.fmt9.iaddr; + break; + case 10: + info.si_addr = (void *) fp->un.fmta.daddr; + break; + case 11: + info.si_addr = (void *) fp->un.fmtb.daddr; + break; + } + force_sig_info (sig, &info, current); +} + +asmlinkage void set_esp0(unsigned long ssp) +{ + current->thread.esp0 = ssp; +} + + +/* + * The architecture-independent backtrace generator + */ +void dump_stack(void) +{ + unsigned long stack; + + show_stack(current, &stack); +} + +#ifdef CONFIG_M68KFPU_EMU +asmlinkage void fpemu_signal(int signal, int code, void *addr) +{ + siginfo_t info; + + info.si_signo = signal; + info.si_errno = 0; + info.si_code = code; + info.si_addr = addr; + force_sig_info(signal, &info, current); +} +#endif diff --git a/arch/m68knommu/kernel/vmlinux.lds.S b/arch/m68knommu/kernel/vmlinux.lds.S new file mode 100644 index 00000000000..31cb12892da --- /dev/null +++ b/arch/m68knommu/kernel/vmlinux.lds.S @@ -0,0 +1,348 @@ +/* + * vmlinux.lds.S -- master linker script for m68knommu arch + * + * (C) Copyright 2002-2004, Greg Ungerer <gerg@snapgear.com> + * + * This ends up looking compilcated, because of the number of + * address variations for ram and rom/flash layouts. The real + * work of the linker script is all at the end, and reasonably + * strait forward. + */ + +#include <linux/config.h> +#include <asm-generic/vmlinux.lds.h> + +/* + * Original Palm pilot (same for Xcopilot). + * There is really only a rom target for this. + */ +#ifdef CONFIG_PILOT3 +#define ROMVEC_START 0x10c00000 +#define ROMVEC_LENGTH 0x10400 +#define ROM_START 0x10c10400 +#define ROM_LENGTH 0xfec00 +#define ROM_END 0x10d00000 +#define RAMVEC_START 0x00000000 +#define RAMVEC_LENGTH 0x400 +#define RAM_START 0x10000400 +#define RAM_LENGTH 0xffc00 +#define RAM_END 0x10100000 +#define _ramend _ram_end_notused +#define DATA_ADDR RAM_START +#endif + +/* + * Same setup on both the uCsimm and uCdimm. + */ +#if defined(CONFIG_UCSIMM) || defined(CONFIG_UCDIMM) +#ifdef CONFIG_RAMKERNEL +#define ROMVEC_START 0x10c10000 +#define ROMVEC_LENGTH 0x400 +#define ROM_START 0x10c10400 +#define ROM_LENGTH 0x1efc00 +#define ROM_END 0x10e00000 +#define RAMVEC_START 0x00000000 +#define RAMVEC_LENGTH 0x400 +#define RAM_START 0x00020400 +#define RAM_LENGTH 0x7dfc00 +#define RAM_END 0x00800000 +#endif +#ifdef CONFIG_ROMKERNEL +#define ROMVEC_START 0x10c10000 +#define ROMVEC_LENGTH 0x400 +#define ROM_START 0x10c10400 +#define ROM_LENGTH 0x1efc00 +#define ROM_END 0x10e00000 +#define RAMVEC_START 0x00000000 +#define RAMVEC_LENGTH 0x400 +#define RAM_START 0x00020000 +#define RAM_LENGTH 0x600000 +#define RAM_END 0x00800000 +#endif +#ifdef CONFIG_HIMEMKERNEL +#define ROMVEC_START 0x00600000 +#define ROMVEC_LENGTH 0x400 +#define ROM_START 0x00600400 +#define ROM_LENGTH 0x1efc00 +#define ROM_END 0x007f0000 +#define RAMVEC_START 0x00000000 +#define RAMVEC_LENGTH 0x400 +#define RAM_START 0x00020000 +#define RAM_LENGTH 0x5e0000 +#define RAM_END 0x00600000 +#endif +#endif + +#ifdef CONFIG_DRAGEN2 +#define RAM_START 0x10000 +#define RAM_LENGTH 0x7f0000 +#endif + +#ifdef CONFIG_UCQUICC +#define ROMVEC_START 0x00000000 +#define ROMVEC_LENGTH 0x404 +#define ROM_START 0x00000404 +#define ROM_LENGTH 0x1ff6fc +#define ROM_END 0x00200000 +#define RAMVEC_START 0x00200000 +#define RAMVEC_LENGTH 0x404 +#define RAM_START 0x00200404 +#define RAM_LENGTH 0x1ff6fc +#define RAM_END 0x00400000 +#endif + +/* + * The standard Arnewsh 5206 board only has 1MiB of ram. Not normally + * enough to be useful. Assume the user has fitted something larger, + * at least 4MiB in size. No point in not letting the kernel completely + * link, it will be obvious if it is too big when they go to load it. + */ +#if defined(CONFIG_ARN5206) +#define RAM_START 0x10000 +#define RAM_LENGTH 0x3f0000 +#endif + +/* + * The Motorola 5206eLITE board only has 1MiB of static RAM. + */ +#if defined(CONFIG_ELITE) +#define RAM_START 0x30020000 +#define RAM_END 0xe0000 +#endif + +/* + * All the Motorola eval boards have the same basic arrangement. + * The end of RAM will vary depending on how much ram is fitted, + * but this isn't important here, we assume at least 4MiB. + */ +#if defined(CONFIG_M5206eC3) || defined(CONFIG_M5249C3) || \ + defined(CONFIG_M5272C3) || defined(CONFIG_M5307C3) || \ + defined(CONFIG_ARN5307) || defined(CONFIG_M5407C3) || \ + defined(CONFIG_M5271EVB) || defined(CONFIG_M5275EVB) +#define RAM_START 0x20000 +#define RAM_LENGTH 0x3e0000 +#endif + +/* + * The senTec COBRA5272 board has nearly the same memory layout as + * the M5272C3. We assume 16MiB ram. + */ +#if defined(CONFIG_COBRA5272) +#define RAM_START 0x20000 +#define RAM_LENGTH 0xfe0000 +#endif + +#if defined(CONFIG_M5282EVB) +#define RAM_START 0x10000 +#define RAM_LENGTH 0x3f0000 +#endif + +/* + * The senTec COBRA5282 board has the same memory layout as the M5282EVB. + */ +#if defined(CONFIG_COBRA5282) +#define RAM_START 0x10000 +#define RAM_LENGTH 0x3f0000 +#endif + +/* + * These flash boot boards use all of ram for operation. Again the + * actual memory size is not important here, assume at least 4MiB. + * They currently have no support for running in flash. + */ +#if defined(CONFIG_NETtel) || defined(CONFIG_eLIA) || \ + defined(CONFIG_DISKtel) || defined(CONFIG_SECUREEDGEMP3) || \ + defined(CONFIG_HW_FEITH) +#define RAM_START 0x400 +#define RAM_LENGTH 0x3ffc00 +#endif + +/* + * Sneha Boards mimimun memmory + * The end of RAM will vary depending on how much ram is fitted, + * but this isn't important here, we assume at least 4MiB. + */ +#if defined(CONFIG_CPU16B) +#define RAM_START 0x20000 +#define RAM_LENGTH 0x3e0000 +#endif + + +#if defined(CONFIG_RAMKERNEL) +#define TEXT ram +#define DATA ram +#define INIT ram +#define BSS ram +#endif +#if defined(CONFIG_ROMKERNEL) || defined(CONFIG_HIMEMKERNEL) +#define TEXT rom +#define DATA ram +#define INIT ram +#define BSS ram +#endif + +#ifndef DATA_ADDR +#define DATA_ADDR +#endif + + +OUTPUT_ARCH(m68k) +ENTRY(_start) + +MEMORY { +#ifdef RAMVEC_START + ramvec : ORIGIN = RAMVEC_START, LENGTH = RAMVEC_LENGTH +#endif + ram : ORIGIN = RAM_START, LENGTH = RAM_LENGTH +#ifdef RAM_END + eram : ORIGIN = RAM_END, LENGTH = 0 +#endif +#ifdef ROM_START + romvec : ORIGIN = ROMVEC_START, LENGTH = ROMVEC_LENGTH + rom : ORIGIN = ROM_START, LENGTH = ROM_LENGTH + erom : ORIGIN = ROM_END, LENGTH = 0 +#endif +} + +jiffies = jiffies_64 + 4; + +SECTIONS { + +#ifdef ROMVEC_START + . = ROMVEC_START ; + .romvec : { + __rom_start = . ; + _romvec = .; + *(.data.initvect) + } > romvec +#endif + + .text : { + _stext = . ; + *(.text) + SCHED_TEXT + *(.text.lock) + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + *(__ex_table) + __stop___ex_table = .; + + *(.rodata) *(.rodata.*) + *(__vermagic) /* Kernel version magic */ + *(.rodata1) + *(.rodata.str1.1) + + /* Kernel symbol table: Normal symbols */ + . = ALIGN(4); + __start___ksymtab = .; + *(__ksymtab) + __stop___ksymtab = .; + + /* Kernel symbol table: GPL-only symbols */ + __start___ksymtab_gpl = .; + *(__ksymtab_gpl) + __stop___ksymtab_gpl = .; + + /* Kernel symbol table: Normal symbols */ + __start___kcrctab = .; + *(__kcrctab) + __stop___kcrctab = .; + + /* Kernel symbol table: GPL-only symbols */ + __start___kcrctab_gpl = .; + *(__kcrctab_gpl) + __stop___kcrctab_gpl = .; + + /* Kernel symbol table: strings */ + *(__ksymtab_strings) + + /* Built-in module parameters */ + __start___param = .; + *(__param) + __stop___param = .; + + . = ALIGN(4) ; + _etext = . ; + } > TEXT + +#ifdef ROM_END + . = ROM_END ; + .erom : { + __rom_end = . ; + } > erom +#endif +#ifdef RAMVEC_START + . = RAMVEC_START ; + .ramvec : { + __ramvec = .; + } > ramvec +#endif + + .data DATA_ADDR : { + . = ALIGN(4); + _sdata = . ; + *(.data) + . = ALIGN(8192) ; + *(.data.init_task) + _edata = . ; + } > DATA + + .init : { + . = ALIGN(4096); + __init_begin = .; + _sinittext = .; + *(.init.text) + _einittext = .; + *(.init.data) + . = ALIGN(16); + __setup_start = .; + *(.init.setup) + __setup_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(4); + __initramfs_start = .; + *(.init.ramfs) + __initramfs_end = .; + . = ALIGN(4096); + __init_end = .; + } > INIT + + /DISCARD/ : { + *(.exit.text) + *(.exit.data) + *(.exitcall.exit) + } + + .bss : { + . = ALIGN(4); + _sbss = . ; + *(.bss) + *(COMMON) + . = ALIGN(4) ; + _ebss = . ; + } > BSS + +#ifdef RAM_END + . = RAM_END ; + .eram : { + __ramend = . ; + _ramend = . ; + } > eram +#endif +} + |