diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2001-03-09 20:33:35 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2001-03-09 20:33:35 +0000 |
commit | 116674acc97ba75a720329996877077d988443a2 (patch) | |
tree | 6a3f2ff0b612ae2ee8a3f3509370c9e6333a53b3 /arch | |
parent | 71118c319fcae4a138f16e35b4f7e0a6d53ce2ca (diff) |
Merge with Linux 2.4.2.
Diffstat (limited to 'arch')
398 files changed, 42198 insertions, 7178 deletions
diff --git a/arch/alpha/kernel/Makefile b/arch/alpha/kernel/Makefile index abc04cca2..08ec1d613 100644 --- a/arch/alpha/kernel/Makefile +++ b/arch/alpha/kernel/Makefile @@ -30,9 +30,7 @@ ifdef CONFIG_VGA_HOSE obj-y += console.o endif - obj-$(CONFIG_SMP) += smp.o irq_smp.o - obj-$(CONFIG_PCI) += pci.o pci_iommu.o ifdef CONFIG_ALPHA_GENERIC @@ -76,7 +74,7 @@ obj-y += sys_eb64p.o endif obj-$(CONFIG_ALPHA_EIGER) += sys_eiger.o -obj-$(CONFIG_ALPHA_JENSEN) += sys_jensen.o +obj-$(CONFIG_ALPHA_JENSEN) += sys_jensen.o pci-noop.o obj-$(CONFIG_ALPHA_MIATA) += sys_miata.o obj-$(CONFIG_ALPHA_MIKASA) += sys_mikasa.o obj-$(CONFIG_ALPHA_NAUTILUS) += sys_nautilus.o diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c index 17285ac26..d7bf13ec3 100644 --- a/arch/alpha/kernel/alpha_ksyms.c +++ b/arch/alpha/kernel/alpha_ksyms.c @@ -98,9 +98,13 @@ EXPORT_SYMBOL(__memcpy); EXPORT_SYMBOL(__memset); EXPORT_SYMBOL(__memsetw); EXPORT_SYMBOL(__constant_c_memset); +EXPORT_SYMBOL(copy_page); +EXPORT_SYMBOL(clear_page); EXPORT_SYMBOL(__direct_map_base); EXPORT_SYMBOL(__direct_map_size); + +#ifdef CONFIG_PCI EXPORT_SYMBOL(pci_alloc_consistent); EXPORT_SYMBOL(pci_free_consistent); EXPORT_SYMBOL(pci_map_single); @@ -108,6 +112,7 @@ EXPORT_SYMBOL(pci_unmap_single); EXPORT_SYMBOL(pci_map_sg); EXPORT_SYMBOL(pci_unmap_sg); EXPORT_SYMBOL(pci_dma_supported); +#endif EXPORT_SYMBOL(dump_thread); EXPORT_SYMBOL(dump_fpu); @@ -166,6 +171,7 @@ EXPORT_SYMBOL(__down_failed_interruptible); EXPORT_SYMBOL(__up_wakeup); EXPORT_SYMBOL(down); EXPORT_SYMBOL(down_interruptible); +EXPORT_SYMBOL(down_trylock); EXPORT_SYMBOL(up); EXPORT_SYMBOL(__down_read_failed); EXPORT_SYMBOL(__down_write_failed); diff --git a/arch/alpha/kernel/irq.c b/arch/alpha/kernel/irq.c index 080e48e43..825eaf2d5 100644 --- a/arch/alpha/kernel/irq.c +++ b/arch/alpha/kernel/irq.c @@ -18,7 +18,7 @@ #include <linux/signal.h> #include <linux/sched.h> #include <linux/interrupt.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/init.h> #include <linux/irq.h> diff --git a/arch/alpha/kernel/irq_alpha.c b/arch/alpha/kernel/irq_alpha.c index 59f102496..91e99f573 100644 --- a/arch/alpha/kernel/irq_alpha.c +++ b/arch/alpha/kernel/irq_alpha.c @@ -18,7 +18,7 @@ unsigned long __irq_attempt[NR_IRQS]; #endif -/* Hack minimum IPL during interupt processing for broken hardware. */ +/* Hack minimum IPL during interrupt processing for broken hardware. */ #ifdef CONFIG_ALPHA_BROKEN_IRQ_MASK int __min_ipl; #endif diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index cd28b07fc..6159457d9 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -19,7 +19,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/utsname.h> @@ -74,8 +74,10 @@ asmlinkage int osf_set_program_attributes( mm = current->mm; mm->end_code = bss_start + bss_len; mm->brk = bss_start + bss_len; +#if 0 printk("set_program_attributes(%lx %lx %lx %lx)\n", text_start, text_len, bss_start, bss_len); +#endif unlock_kernel(); return 0; } diff --git a/arch/alpha/kernel/pci-noop.c b/arch/alpha/kernel/pci-noop.c new file mode 100644 index 000000000..d530ebb82 --- /dev/null +++ b/arch/alpha/kernel/pci-noop.c @@ -0,0 +1,104 @@ +/* + * linux/arch/alpha/kernel/pci-noop.c + * + * Stub PCI interfaces for Jensen-specific kernels. + */ + +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/bootmem.h> +#include <linux/errno.h> +#include <linux/sched.h> + +#include "proto.h" + + +/* + * The PCI controler list. + */ + +struct pci_controler *hose_head, **hose_tail = &hose_head; +struct pci_controler *pci_isa_hose; + + +struct pci_controler * __init +alloc_pci_controler(void) +{ + struct pci_controler *hose; + + hose = alloc_bootmem(sizeof(*hose)); + + *hose_tail = hose; + hose_tail = &hose->next; + + return hose; +} + +struct resource * __init +alloc_resource(void) +{ + struct resource *res; + + res = alloc_bootmem(sizeof(*res)); + + return res; +} + +asmlinkage long +sys_pciconfig_iobase(long which, unsigned long bus, unsigned long dfn) +{ + struct pci_controler *hose; + struct pci_dev *dev; + + /* from hose or from bus.devfn */ + if (which & IOBASE_FROM_HOSE) { + for (hose = hose_head; hose; hose = hose->next) + if (hose->index == bus) + break; + if (!hose) + return -ENODEV; + } else { + /* Special hook for ISA access. */ + if (bus == 0 && dfn == 0) + hose = pci_isa_hose; + else + return -ENODEV; + } + + switch (which & ~IOBASE_FROM_HOSE) { + case IOBASE_HOSE: + return hose->index; + case IOBASE_SPARSE_MEM: + return hose->sparse_mem_base; + case IOBASE_DENSE_MEM: + return hose->dense_mem_base; + case IOBASE_SPARSE_IO: + return hose->sparse_io_base; + case IOBASE_DENSE_IO: + return hose->dense_io_base; + case IOBASE_ROOT_BUS: + return hose->bus->number; + } + + return -EOPNOTSUPP; +} + +asmlinkage long +sys_pciconfig_read(unsigned long bus, unsigned long dfn, + unsigned long off, unsigned long len, void *buf) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + else + return -ENODEV; +} + +asmlinkage long +sys_pciconfig_write(unsigned long bus, unsigned long dfn, + unsigned long off, unsigned long len, void *buf) +{ + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + else + return -ENODEV; +} diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c index 508e278b0..2dd505e10 100644 --- a/arch/alpha/kernel/process.c +++ b/arch/alpha/kernel/process.c @@ -18,7 +18,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/utsname.h> @@ -416,22 +416,20 @@ dump_fpu(struct pt_regs * regs, elf_fpregset_t *r) * Don't do this at home. */ asmlinkage int -sys_execve(unsigned long a0, unsigned long a1, unsigned long a2, - unsigned long a3, unsigned long a4, unsigned long a5, - struct pt_regs regs) +sys_execve(char *ufilename, char **argv, char **envp, + unsigned long a3, unsigned long a4, unsigned long a5, + struct pt_regs regs) { int error; - char * filename; + char *filename; - lock_kernel(); - filename = getname((char *) a0); + filename = getname(ufilename); error = PTR_ERR(filename); if (IS_ERR(filename)) goto out; - error = do_execve(filename, (char **) a1, (char **) a2, ®s); + error = do_execve(filename, argv, envp, ®s); putname(filename); out: - unlock_kernel(); return error; } diff --git a/arch/alpha/kernel/ptrace.c b/arch/alpha/kernel/ptrace.c index 09fcfd787..a919e7c2f 100644 --- a/arch/alpha/kernel/ptrace.c +++ b/arch/alpha/kernel/ptrace.c @@ -12,7 +12,7 @@ #include <linux/errno.h> #include <linux/ptrace.h> #include <linux/user.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/uaccess.h> #include <asm/pgtable.h> diff --git a/arch/alpha/kernel/setup.c b/arch/alpha/kernel/setup.c index 2acf56d96..a6443da53 100644 --- a/arch/alpha/kernel/setup.c +++ b/arch/alpha/kernel/setup.c @@ -16,7 +16,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> diff --git a/arch/alpha/kernel/smc37c669.c b/arch/alpha/kernel/smc37c669.c index 0b2db18ab..3ffb611f2 100644 --- a/arch/alpha/kernel/smc37c669.c +++ b/arch/alpha/kernel/smc37c669.c @@ -3,7 +3,7 @@ */ #include <linux/kernel.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/delay.h> diff --git a/arch/alpha/kernel/smc37c93x.c b/arch/alpha/kernel/smc37c93x.c index 5448305a3..b0e15d307 100644 --- a/arch/alpha/kernel/smc37c93x.c +++ b/arch/alpha/kernel/smc37c93x.c @@ -5,7 +5,7 @@ #include <linux/config.h> #include <linux/kernel.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mm.h> #include <linux/init.h> #include <linux/delay.h> diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c index e91e77895..4f877b10b 100644 --- a/arch/alpha/kernel/smp.c +++ b/arch/alpha/kernel/smp.c @@ -837,7 +837,7 @@ smp_call_function (void (*func) (void *info), void *info, int retry, int wait) atomic_set(&data.unstarted_count, smp_num_cpus - 1); atomic_set(&data.unfinished_count, smp_num_cpus - 1); - /* Aquire the smp_call_function_data mutex. */ + /* Acquire the smp_call_function_data mutex. */ if (pointer_lock(&smp_call_function_data, &data, retry)) return -EBUSY; diff --git a/arch/alpha/kernel/sys_ruffian.c b/arch/alpha/kernel/sys_ruffian.c index c9a2e79a4..0d9713377 100644 --- a/arch/alpha/kernel/sys_ruffian.c +++ b/arch/alpha/kernel/sys_ruffian.c @@ -92,14 +92,80 @@ ruffian_kill_arch (int mode) #endif } +/* + * Interrupt routing: + * + * Primary bus + * IdSel INTA INTB INTC INTD + * 21052 13 - - - - + * SIO 14 23 - - - + * 21143 15 44 - - - + * Slot 0 17 43 42 41 40 + * + * Secondary bus + * IdSel INTA INTB INTC INTD + * Slot 0 8 (18) 19 18 17 16 + * Slot 1 9 (19) 31 30 29 28 + * Slot 2 10 (20) 27 26 25 24 + * Slot 3 11 (21) 39 38 37 36 + * Slot 4 12 (22) 35 34 33 32 + * 53c875 13 (23) 20 - - - + * + */ + static int __init ruffian_map_irq(struct pci_dev *dev, u8 slot, u8 pin) { - /* We don't know anything about the PCI routing, so leave - the IRQ unchanged. */ - return dev->irq; + static char irq_tab[11][5] __initdata = { + /*INT INTA INTB INTC INTD */ + {-1, -1, -1, -1, -1}, /* IdSel 13, 21052 */ + {-1, -1, -1, -1, -1}, /* IdSel 14, SIO */ + {44, 44, 44, 44, 44}, /* IdSel 15, 21143 */ + {-1, -1, -1, -1, -1}, /* IdSel 16, none */ + {43, 43, 42, 41, 40}, /* IdSel 17, 64-bit slot */ + /* the next 6 are actually on PCI bus 1, across the bridge */ + {19, 19, 18, 17, 16}, /* IdSel 8, slot 0 */ + {31, 31, 30, 29, 28}, /* IdSel 9, slot 1 */ + {27, 27, 26, 25, 24}, /* IdSel 10, slot 2 */ + {39, 39, 38, 37, 36}, /* IdSel 11, slot 3 */ + {35, 35, 34, 33, 32}, /* IdSel 12, slot 4 */ + {20, 20, 20, 20, 20}, /* IdSel 13, 53c875 */ + }; + const long min_idsel = 13, max_idsel = 23, irqs_per_slot = 5; + return COMMON_TABLE_LOOKUP; } +static u8 __init +ruffian_swizzle(struct pci_dev *dev, u8 *pinp) +{ + int slot, pin = *pinp; + + if (dev->bus->number == 0) { + slot = PCI_SLOT(dev->devfn); + } + /* Check for the built-in bridge. */ + else if (PCI_SLOT(dev->bus->self->devfn) == 13) { + slot = PCI_SLOT(dev->devfn) + 10; + } + else + { + /* Must be a card-based bridge. */ + do { + if (PCI_SLOT(dev->bus->self->devfn) == 13) { + slot = PCI_SLOT(dev->devfn) + 10; + break; + } + pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + + /* Move up the chain of bridges. */ + dev = dev->bus->self; + /* Slot of the next bridge. */ + slot = PCI_SLOT(dev->devfn); + } while (dev->bus->self); + } + *pinp = pin; + return slot; +} #ifdef BUILDING_FOR_MILO /* @@ -164,6 +230,6 @@ struct alpha_machine_vector ruffian_mv __initmv = { init_pci: cia_init_pci, kill_arch: ruffian_kill_arch, pci_map_irq: ruffian_map_irq, - pci_swizzle: common_swizzle, + pci_swizzle: ruffian_swizzle, }; ALIAS_MV(ruffian) diff --git a/arch/alpha/kernel/traps.c b/arch/alpha/kernel/traps.c index 7b2f8be03..ed7d2f68e 100644 --- a/arch/alpha/kernel/traps.c +++ b/arch/alpha/kernel/traps.c @@ -1093,7 +1093,9 @@ alpha_ni_syscall(unsigned long a0, unsigned long a1, unsigned long a2, { /* We only get here for OSF system calls, minus #112; the rest go to sys_ni_syscall. */ +#if 0 printk("<sc %ld(%lx,%lx,%lx)>", regs.r0, a0, a1, a2); +#endif return -ENOSYS; } diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile index 1e3e485b5..d95ace0d7 100644 --- a/arch/alpha/lib/Makefile +++ b/arch/alpha/lib/Makefile @@ -42,6 +42,8 @@ OBJS = __divqu.o __remqu.o __divlu.o __remlu.o \ $(ev6)strncpy_from_user.o \ $(ev67)strlen_user.o \ $(ev6)csum_ipv6_magic.o \ + $(ev6)clear_page.o \ + $(ev6)copy_page.o \ strcasecmp.o \ fpreg.o \ callback_srm.o srm_puts.o srm_printk.o diff --git a/arch/alpha/lib/clear_page.S b/arch/alpha/lib/clear_page.S new file mode 100644 index 000000000..a221ae266 --- /dev/null +++ b/arch/alpha/lib/clear_page.S @@ -0,0 +1,39 @@ +/* + * arch/alpha/lib/clear_page.S + * + * Zero an entire page. + */ + + .text + .align 4 + .global clear_page + .ent clear_page +clear_page: + .prologue 0 + + lda $0,128 + nop + unop + nop + +1: stq $31,0($16) + stq $31,8($16) + stq $31,16($16) + stq $31,24($16) + + stq $31,32($16) + stq $31,40($16) + stq $31,48($16) + subq $0,1,$0 + + stq $31,56($16) + addq $16,64,$16 + unop + bne $0,1b + + ret + nop + unop + nop + + .end clear_page diff --git a/arch/alpha/lib/copy_page.S b/arch/alpha/lib/copy_page.S new file mode 100644 index 000000000..9f3b97459 --- /dev/null +++ b/arch/alpha/lib/copy_page.S @@ -0,0 +1,49 @@ +/* + * arch/alpha/lib/copy_page.S + * + * Copy an entire page. + */ + + .text + .align 4 + .global copy_page + .ent copy_page +copy_page: + .prologue 0 + + lda $18,128 + nop + unop + nop + +1: ldq $0,0($17) + ldq $1,8($17) + ldq $2,16($17) + ldq $3,24($17) + + ldq $4,32($17) + ldq $5,40($17) + ldq $6,48($17) + ldq $7,56($17) + + stq $0,0($16) + subq $18,1,$18 + stq $1,8($16) + addq $17,64,$17 + + stq $2,16($16) + stq $3,24($16) + stq $4,32($16) + stq $5,40($16) + + stq $6,48($16) + stq $7,56($16) + addq $16,64,$16 + bne $18, 1b + + ret + nop + unop + nop + + .end copy_page diff --git a/arch/alpha/lib/ev6-clear_page.S b/arch/alpha/lib/ev6-clear_page.S new file mode 100644 index 000000000..adf4f7be0 --- /dev/null +++ b/arch/alpha/lib/ev6-clear_page.S @@ -0,0 +1,54 @@ +/* + * arch/alpha/lib/ev6-clear_page.S + * + * Zero an entire page. + */ + + .text + .align 4 + .global clear_page + .ent clear_page +clear_page: + .prologue 0 + + lda $0,128 + lda $1,125 + addq $16,64,$2 + addq $16,128,$3 + + addq $16,192,$17 + wh64 ($16) + wh64 ($2) + wh64 ($3) + +1: wh64 ($17) + stq $31,0($16) + subq $0,1,$0 + subq $1,1,$1 + + stq $31,8($16) + stq $31,16($16) + addq $17,64,$2 + nop + + stq $31,24($16) + stq $31,32($16) + cmovgt $1,$2,$17 + nop + + stq $31,40($16) + stq $31,48($16) + nop + nop + + stq $31,56($16) + addq $16,64,$16 + nop + bne $0,1b + + ret + nop + nop + nop + + .end clear_page diff --git a/arch/alpha/lib/ev6-copy_page.S b/arch/alpha/lib/ev6-copy_page.S new file mode 100644 index 000000000..b789db192 --- /dev/null +++ b/arch/alpha/lib/ev6-copy_page.S @@ -0,0 +1,203 @@ +/* + * arch/alpha/lib/ev6-copy_page.S + * + * Copy an entire page. + */ + +/* The following comparison of this routine vs the normal copy_page.S + was written by an unnamed ev6 hardware designer and forwarded to me + via Steven Hobbs <hobbs@steven.zko.dec.com>. + + First Problem: STQ overflows. + ----------------------------- + + It would be nice if EV6 handled every resource overflow efficiently, + but for some it doesn't. Including store queue overflows. It causes + a trap and a restart of the pipe. + + To get around this we sometimes use (to borrow a term from a VSSAD + researcher) "aeration". The idea is to slow the rate at which the + processor receives valid instructions by inserting nops in the fetch + path. In doing so, you can prevent the overflow and actually make + the code run faster. You can, of course, take advantage of the fact + that the processor can fetch at most 4 aligned instructions per cycle. + + I inserted enough nops to force it to take 10 cycles to fetch the + loop code. In theory, EV6 should be able to execute this loop in + 9 cycles but I was not able to get it to run that fast -- the initial + conditions were such that I could not reach this optimum rate on + (chaotic) EV6. I wrote the code such that everything would issue + in order. + + Second Problem: Dcache index matches. + ------------------------------------- + + If you are going to use this routine on random aligned pages, there + is a 25% chance that the pages will be at the same dcache indices. + This results in many nasty memory traps without care. + + The solution is to schedule the prefetches to avoid the memory + conflicts. I schedule the wh64 prefetches farther ahead of the + read prefetches to avoid this problem. + + Third Problem: Needs more prefetching. + -------------------------------------- + + In order to improve the code I added deeper prefetching to take the + most advantage of EV6's bandwidth. + + I also prefetched the read stream. Note that adding the read prefetch + forced me to add another cycle to the inner-most kernel - up to 11 + from the original 8 cycles per iteration. We could improve performance + further by unrolling the loop and doing multiple prefetches per cycle. + + I think that the code below will be very robust and fast code for the + purposes of copying aligned pages. It is slower when both source and + destination pages are in the dcache, but it is my guess that this is + less important than the dcache miss case. */ + + + .text + .align 4 + .global copy_page + .ent copy_page +copy_page: + .prologue 0 + + /* Prefetch 5 read cachelines; write-hint 10 cache lines. */ + wh64 ($16) + ldl $31,0($17) + ldl $31,64($17) + lda $1,1*64($16) + + wh64 ($1) + ldl $31,128($17) + ldl $31,192($17) + lda $1,2*64($16) + + wh64 ($1) + ldl $31,256($17) + lda $18,118 + lda $1,3*64($16) + + wh64 ($1) + nop + lda $1,4*64($16) + lda $2,5*64($16) + + wh64 ($1) + wh64 ($2) + lda $1,6*64($16) + lda $2,7*64($16) + + wh64 ($1) + wh64 ($2) + lda $1,8*64($16) + lda $2,9*64($16) + + wh64 ($1) + wh64 ($2) + lda $19,10*64($16) + nop + + /* Main prefetching/write-hinting loop. */ +1: ldq $0,0($17) + ldq $1,8($17) + unop + unop + + unop + unop + ldq $2,16($17) + ldq $3,24($17) + + ldq $4,32($17) + ldq $5,40($17) + unop + unop + + unop + unop + ldq $6,48($17) + ldq $7,56($17) + + ldl $31,320($17) + unop + unop + unop + + /* This gives the extra cycle of aeration above the minimum. */ + unop + unop + unop + unop + + wh64 ($19) + unop + unop + unop + + stq $0,0($16) + subq $18,1,$18 + stq $1,8($16) + unop + + unop + stq $2,16($16) + addq $17,64,$17 + stq $3,24($16) + + stq $4,32($16) + stq $5,40($16) + addq $19,64,$19 + unop + + stq $6,48($16) + stq $7,56($16) + addq $16,64,$16 + bne $18, 1b + + /* Prefetch the final 5 cache lines of the read stream. */ + lda $18,10 + ldl $31,320($17) + ldl $31,384($17) + ldl $31,448($17) + + ldl $31,512($17) + ldl $31,576($17) + nop + nop + + /* Non-prefetching, non-write-hinting cleanup loop for the + final 10 cache lines. */ +2: ldq $0,0($17) + ldq $1,8($17) + ldq $2,16($17) + ldq $3,24($17) + + ldq $4,32($17) + ldq $5,40($17) + ldq $6,48($17) + ldq $7,56($17) + + stq $0,0($16) + subq $18,1,$18 + stq $1,8($16) + addq $17,64,$17 + + stq $2,16($16) + stq $3,24($16) + stq $4,32($16) + stq $5,40($16) + + stq $6,48($16) + stq $7,56($16) + addq $16,64,$16 + bne $18, 2b + + ret + nop + unop + nop + + .end copy_page diff --git a/arch/arm/Makefile b/arch/arm/Makefile index c19ccddec..0320dba31 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -12,25 +12,18 @@ # # Copyright (C) 1995-2000 by Russell King -OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S -CPP := $(CC) -E -LINKFLAGS := -p -X -T arch/arm/vmlinux.lds -ARCHCC := $(word 1,$(CC)) - -AFLAGS += -mno-fpu -CFLAGS_PIPE := -pipe -CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) -msoft-float +LINKFLAGS :=-p -X -T arch/arm/vmlinux.lds +GZFLAGS :=-9 +CFLAGS +=-fno-common -pipe ifdef CONFIG_FRAME_POINTER -CFLAGS := $(CFLAGS:-fomit-frame-pointer=) +CFLAGS :=$(CFLAGS:-fomit-frame-pointer=) endif ifdef CONFIG_DEBUG_INFO -CFLAGS += -g +CFLAGS +=-g endif -GZFLAGS = -9 - # Ensure this is ld "2.9.4" or later NEW_LINKER := $(shell $(LD) --gc-sections --version >/dev/null 2>&1; echo $$?) @@ -40,61 +33,42 @@ dummy:; @echo '*** ${VERSION}.${PATCHLEVEL} kernels no longer build correctly w @false endif -# GCC 2.7 uses different options to later compilers; sort out which we have -NEW_GCC := $(shell $(CC) --version 2>&1 | grep '^2\.7' > /dev/null; echo $$?) - -# -# select flags depending on the compiler +# Select CPU dependent flags. Note that order of declaration is important; +# the options further down the list override previous items. # -ifneq ($(NEW_GCC),0) -CFLAGS += -mshort-load-bytes -CFLAGS_PROC_CPU_26 := -mcpu=arm3 -mapcs-26 -Os -CFLAGS_PROC_CPU_32v3 := -march=armv3 -CFLAGS_PROC_CPU_32v4 := -march=armv4 -else -CFLAGS += -DNO_TEXT_SECTIONS -CFLAGS_PROC_CPU_26 := -m3 -CFLAGS_PROC_CPU_32v3 := -m6 -CFLAGS_PROC_CPU_32v4 := -m6 -endif +apcs-$(CONFIG_CPU_26) :=-mapcs-26 -mcpu=arm3 -Os +apcs-$(CONFIG_CPU_32) :=-mapcs-32 + +arch-$(CONFIG_CPU_32v3) :=-march=armv3 +arch-$(CONFIG_CPU_32v4) :=-march=armv4 +arch-$(CONFIG_CPU_32v5) :=-march=armv5 + +proc-$(CONFIG_CPU_32v3) :=-marmv3m +proc-$(CONFIG_CPU_32v4) :=-marmv4 +proc-$(CONFIG_CPU_32v5) :=-marmv5 + +tune-$(CONFIG_CPU_ARM610) :=-mtune=arm610 +tune-$(CONFIG_CPU_ARM710) :=-mtune=arm710 +tune-$(CONFIG_CPU_ARM720T) :=-mtune=arm7tdmi +tune-$(CONFIG_CPU_ARM920T) :=-mtune=arm9tdmi +tune-$(CONFIG_CPU_SA110) :=-mtune=strongarm110 +tune-$(CONFIG_CPU_SA1100) :=-mtune=strongarm1100 + +CFLAGS += $(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float +AFLAGS += $(apcs-y) $(proc-y) -mno-fpu + +LIBGCC := $(shell $(CC) $(CFLAGS) --print-libgcc-file-name) -# -# Select CPU dependent flags -# ifeq ($(CONFIG_CPU_26),y) - PROCESSOR = armo - TEXTADDR = 0x02080000 - CFLAGS += $(CFLAGS_PROC_CPU_26) - AFLAGS += -mapcs-26 +PROCESSOR = armo +TEXTADDR = 0x02080000 endif ifeq ($(CONFIG_CPU_32),y) - PROCESSOR = armv - TEXTADDR = 0xC0008000 - ifeq ($(CONFIG_CPU_32v4),y) - CFLAGS += $(CFLAGS_PROC_CPU_32v4) - AFLAGS += -mapcs-32 -marmv4 - else - CFLAGS += $(CFLAGS_PROC_CPU_32v3) - AFLAGS += -mapcs-32 -marmv3m - endif - - opt-$(CONFIG_CPU_ARM6) := -mtune=arm6 - opt-$(CONFIG_CPU_ARM7) := -mtune=arm7 - opt-$(CONFIG_CPU_ARM720) := -mtune=arm7tdmi - opt-$(CONFIG_CPU_ARM920) := -mtune=arm9tdmi - opt-$(CONFIG_CPU_SA110) := -mtune=strongarm110 - opt-$(CONFIG_CPU_SA1100) := -mtune=strongarm110 - - ifneq ($(NEW_GCC),0) - CFLAGS += $(opt-y) - endif +PROCESSOR = armv +TEXTADDR = 0xC0008000 endif -LIBGCC := $(shell $(CC) $(CFLAGS) --print-libgcc-file-name) - -export LIBGCC MACHINE PROCESSOR TEXTADDR GZFLAGS - ifeq ($(CONFIG_ARCH_ARCA5K),y) MACHINE = arc endif @@ -132,6 +106,10 @@ MACHINE = shark endif ifeq ($(CONFIG_ARCH_SA1100),y) +ifeq ($(CONFIG_SA1111),y) +# SA1111 DMA bug: we don't want the kernel to live in precious DMA-able memory +TEXTADDR = 0xc0208000 +endif MACHINE = sa1100 endif @@ -143,6 +121,13 @@ ifeq ($(CONFIG_ARCH_INTEGRATOR),y) MACHINE = integrator endif +ifeq ($(CONFIG_ARCH_CLPS711X),y) +TEXTADDR = 0xc0018000 +MACHINE = clps711x +endif + +export LIBGCC MACHINE PROCESSOR TEXTADDR GZFLAGS + # Only set INCDIR if its not already defined above # Grr, ?= doesn't work as all the other assignment operators do. Make bug? ifeq ($(origin INCDIR), undefined) @@ -158,8 +143,7 @@ endif HEAD := arch/arm/kernel/head-$(PROCESSOR).o \ arch/arm/kernel/init_task.o -SUBDIRS += arch/arm/kernel arch/arm/mm arch/arm/lib \ - arch/arm/nwfpe +SUBDIRS += arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES) LIBS := arch/arm/lib/lib.o arch/arm/lib/lib.a $(LIBS) $(LIBGCC) @@ -167,14 +151,6 @@ ifeq ($(CONFIG_NWFPE),y) LIBS := arch/arm/nwfpe/math-emu.o $(LIBS) endif -ifeq ($(CONFIG_ARCH_ACORN),y) -SUBDIRS += drivers/acorn -DRIVERS += drivers/acorn/block/acorn-block.a -DRIVERS += drivers/acorn/char/acorn-char.o -DRIVERS += drivers/acorn/net/acorn-net.o -DRIVERS += drivers/acorn/scsi/acorn-scsi.a -endif - ifeq ($(CONFIG_ARCH_CLPS7500),y) SUBDIRS += drivers/acorn/char DRIVERS += drivers/acorn/char/acorn-char.o @@ -230,6 +206,7 @@ zImg:; @$(MAKEBOOT) zImage Img:; @$(MAKEBOOT) Image i:; @$(MAKEBOOT) install zi:; @$(MAKEBOOT) zinstall +bp:; @$(MAKEBOOT) bootpImage # # Configuration targets. Use these to select a @@ -237,9 +214,10 @@ zi:; @$(MAKEBOOT) zinstall CFGS= a5k_config ebsa110_config \ footbridge_config rpc_config \ brutus_config victor_config \ - empeg_config thinclient_config \ + empeg_config graphicsclient_config \ assabet_config lart_config \ - cerf_config lusl7200_config + cerf_config lusl7200_config \ + sherman_config pangolin_config $(CFGS): @( \ diff --git a/arch/arm/boot/Makefile b/arch/arm/boot/Makefile index f74033b36..7b13b1bcb 100644 --- a/arch/arm/boot/Makefile +++ b/arch/arm/boot/Makefile @@ -83,15 +83,18 @@ ZTEXTADDR = 0xc0008000 ZRELADDR = 0xc0008000 ifeq ($(CONFIG_SA1100_VICTOR),y) ZTEXTADDR = 0x00002000 - ZBSSADDR = 0xc0100000 + ZBSSADDR = 0xc0200000 endif ifeq ($(CONFIG_SA1100_SHERMAN),y) ZTEXTADDR = 0x00050000 - ZBSSADDR = 0xc0100000 + ZBSSADDR = 0xc0200000 endif ifeq ($(CONFIG_SA1100_GRAPHICSCLIENT),y) ZTEXTADDR = 0xC0200000 endif +ifeq ($(CONFIG_SA1111),y) + ZRELADDR = 0xc0208000 +endif endif # @@ -105,15 +108,15 @@ endif export SYSTEM ZTEXTADDR ZBSSADDR ZRELADDR INITRD_PHYS INITRD_VIRT PARAMS_PHYS Image: $(CONFIGURE) $(SYSTEM) - $(OBJCOPY) $(SYSTEM) $@ + $(OBJCOPY) -O binary -R .note -R .comment -S $(SYSTEM) $@ bzImage: zImage zImage: $(CONFIGURE) compressed/vmlinux - $(OBJCOPY) compressed/vmlinux $@ + $(OBJCOPY) -O binary -R .note -R .comment -S compressed/vmlinux $@ bootpImage: bootp/bootp - $(OBJCOPY) bootp/bootp $@ + $(OBJCOPY) -O binary -R .note -R .comment -S bootp/bootp $@ compressed/vmlinux: $(TOPDIR)/vmlinux dep @$(MAKE) -C compressed vmlinux diff --git a/arch/arm/boot/bootp/Makefile b/arch/arm/boot/bootp/Makefile index 918e3eb9e..adeeeddc5 100644 --- a/arch/arm/boot/bootp/Makefile +++ b/arch/arm/boot/bootp/Makefile @@ -7,7 +7,8 @@ INITRD =$(ZSYSTEM) ZLDFLAGS =-p -X -T bootp.lds \ --defsym initrd_addr=$(INITRD_PHYS) \ --defsym initrd_virt=$(INITRD_VIRT) \ - --defsym params=$(PARAMS_PHYS) + --defsym params=$(PARAMS_PHYS) \ + --defsym kernel_addr=$(ZTEXTADDR) all: bootp diff --git a/arch/arm/boot/bootp/init.S b/arch/arm/boot/bootp/init.S index 2b498b7bb..9963fbc2b 100644 --- a/arch/arm/boot/bootp/init.S +++ b/arch/arm/boot/bootp/init.S @@ -12,33 +12,59 @@ */ .section .start,#alloc,#execinstr .type _entry, #function -_entry: -kernel_addr: adr r10, initdata - ldmia r10, {r11, r12} +_entry: adr r10, initdata + ldr r11, initdata sub r11, r10, r11 @ work out exec offset - add r12, r12, r11 @ correct "splitify" - mov pc, r12 @ jump to splitify + b splitify .size _entry,. - _entry .type initdata, #object initdata: .word initdata @ compiled address of this - .word splitify .size initdata,. - initdata .text splitify: adr r13, data - ldmia r13!, {r4-r6} @ move the kernel + ldmia r13!, {r4-r6} @ move the initrd add r4, r4, r11 @ correction - mov r12, r5 bl move - ldmia r13!, {r4-r6} @ then the initrd + ldmia r13!, {r4-r6} @ then the kernel + mov r12, r5 add r4, r4, r11 @ correction bl move - ldmib r13, {r5,r6,r7} @ get size and addr of initrd - add r7, r7, #16*4 @ offset of initrd_start in param_struct - stmia r7, {r5,r6} @ save in param_struct +/* + * Setup the initrd parameters to pass to the kernel. This can either be + * passed in via a param_struct or a tag list. We spot the param_struct + * method by looking at the first word; this should either indicate a page + * size of 4K, 16K or 32K. + */ + ldmia r13, {r5-r8} @ get size and addr of initrd + @ r5 = ATAG_INITRD + @ r6 = initrd start + @ r7 = initrd end + @ r8 = param_struct address + ldr r9, [r8, #0] @ no param struct? + teq r9, #0x1000 @ 4K? + teqne r9, #0x4000 @ 16K? + teqne r9, #0x8000 @ 32K? + beq no_taglist + +/* + * find the end of the tag list, and then add an INITRD tag on the end. + */ +taglist: ldr r9, [r8, #0] @ tag length + teq r9, #0 @ last tag? + addne r8, r8, r9 + bne taglist + + mov r4, #16 @ length of initrd tag + mov r9, #0 @ end of tag list terminator + stmia r8, {r4, r5, r6, r7, r9} + mov pc, r12 @ call kernel + +no_taglist: add r8, r8, #16*4 + stmia r8, {r6,r7} @ save in param_struct mov pc, r12 @ call kernel move: ldmia r4!, {r7 - r10} @ move 32-bytes at a time @@ -49,17 +75,18 @@ move: ldmia r4!, {r7 - r10} @ move 32-bytes at a time bcs move mov pc, lr -data: .word kernel_start - .word kernel_addr - .word kernel_len - - .word initrd_start +data: .word initrd_start .word initrd_addr .word initrd_len - .word initrd_virt - .word initrd_len - .word params + .word kernel_start + .word kernel_addr + .word kernel_len + + .word 0x54410005 @ r5 = ATAG_INITRD + .word initrd_virt @ r6 + .word initrd_len @ r7 + .word params @ r8 .type kernel_start,#object .type initrd_start,#object diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index bf8f1c947..7f077cd21 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -66,7 +66,7 @@ $(HEAD): $(HEAD:.o=.S) $(CC) $(AFLAGS) -traditional -c $(HEAD:.o=.S) piggy.o: $(SYSTEM) - $(OBJCOPY) $(SYSTEM) piggy + $(OBJCOPY) -O binary -R .note -R .comment -S $(SYSTEM) piggy gzip $(GZFLAGS) < piggy > piggy.gz $(LD) -r -o $@ -b binary piggy.gz rm -f piggy piggy.gz diff --git a/arch/arm/boot/compressed/head-ftvpci.S b/arch/arm/boot/compressed/head-ftvpci.S index a8c806ef3..8e10667ca 100644 --- a/arch/arm/boot/compressed/head-ftvpci.S +++ b/arch/arm/boot/compressed/head-ftvpci.S @@ -6,6 +6,13 @@ * Special startup code for FTV PCI board. */ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + .section ".start", #alloc, #execinstr ftv_start: mcr p15, 0, r0, c7, c5, 0 @ flush I cache diff --git a/arch/arm/boot/compressed/head-l7200.S b/arch/arm/boot/compressed/head-l7200.S index ecf4d6881..8ab9eafdb 100644 --- a/arch/arm/boot/compressed/head-l7200.S +++ b/arch/arm/boot/compressed/head-l7200.S @@ -3,7 +3,7 @@ * * Copyright (C) 2000 Steve Hill <sjhill@cotw.com> * - * Some code borrowed from Nicola Pitre's 'head-sa1100.S' file. This + * Some code borrowed from Nicolas Pitre's 'head-sa1100.S' file. This * is merged with head.S by the linker. */ @@ -16,15 +16,14 @@ .section ".start", #alloc, #execinstr __L7200_start: - mov r0, #0x00100000 @ FLASH address of initrd mov r2, #0xf1000000 @ RAM address of initrd - add r1, r2, #0x00700000 @ Size of initrd + add r3, r2, #0x00700000 @ Size of initrd 1: - ldmia r0!, {r3, r4, r5, r6} - stmia r2!, {r3, r4, r5, r6} - cmp r2, r1 + ldmia r0!, {r4, r5, r6, r7} + stmia r2!, {r4, r5, r6, r7} + cmp r2, r3 ble 1b - + mov r8, #0 @ Zero it out mov r7, #19 @ Set architecture ID diff --git a/arch/arm/boot/compressed/head-sa1100.S b/arch/arm/boot/compressed/head-sa1100.S index 73cef4bd8..f49ceb5e6 100644 --- a/arch/arm/boot/compressed/head-sa1100.S +++ b/arch/arm/boot/compressed/head-sa1100.S @@ -11,10 +11,6 @@ #include <linux/linkage.h> #include <asm/mach-types.h> -#ifndef CONFIG_ARCH_SA1100 -#error What am I doing here... -#endif - .section ".start", #alloc, #execinstr __SA1100_start: @@ -54,6 +50,8 @@ __SA1100_start: bic r0, r0, #0x0d @ clear WB, DC, MMU bic r0, r0, #0x1000 @ clear Icache mcr p15, 0, r0, c1, c0, 0 + +#ifdef CONFIG_ANGELBOOT /* * Pause for a short time so that we give enough time * for the host to start a terminal up. @@ -61,3 +59,5 @@ __SA1100_start: mov r0, #0x00200000 1: subs r0, r0, #1 bne 1b +#endif + diff --git a/arch/arm/boot/compressed/head-shark.S b/arch/arm/boot/compressed/head-shark.S new file mode 100644 index 000000000..817b75366 --- /dev/null +++ b/arch/arm/boot/compressed/head-shark.S @@ -0,0 +1,121 @@ +/* The head-file for the Shark + * by Alexander.Schulz@stud.uni-karlsruhe.de + * + * Does the following: + * - get the memory layout from firmware. This can only be done as long as the mmu + * is still on. + * - switch the mmu off, so we have physical addresses + * - copy the kernel to 0x08508000. This is done to have a fixed address where the + * C-parts (misc.c) are executed. This address must be known at compile-time, + * but the load-address of the kernel depends on how much memory is installed. + * - Jump to this location. + * - Set r8 with 0, r7 with the architecture ID for head.S + */ + +#include <linux/linkage.h> + +#include <asm/assembler.h> + + .section ".start", #alloc, #execinstr + + b __beginning + +__serial_addr: .long 0xf7eff3f8 + .long 0 @ space +__ofw_data: .long 0 @ the number of memory blocks + .space 128 @ (startaddr,size) ... + .space 128 @ bootargs + .align + +__beginning: mov r4, r0 @ save the entry to the firmware + + mov r0, #0xC0 @ disable irq and fiq + mov r1, r0 + mrs r3, cpsr_all + bic r2, r3, r0 + eor r2, r2, r1 + msr cpsr_all, r2 + + ldr r0, __serial_addr @ disable serial interrupt + mov r1, #0 @ hangs the machine, I don t know why. + strb r1, [r0, #0x01] + + mov r0, r4 @ get the Memory layout from firmware + adr r1, __ofw_data + add r2, r1, #4 + mov lr, pc + b SYMBOL_NAME(ofw_init) + mov r1, #0 + + adr r2, __mmu_off @ calculate physical address + sub r2, r2, #0xf0000000 @ openprom maps us at f000 virt, 0e50 phys + adr r0, __ofw_data + ldr r0, [r0, #4] + add r2, r2, r0 + add r2, r2, #0x00500000 + + mrc p15, 0, r3, c1, c0 + bic r3, r3, #0xC @ Write Buffer and DCache + bic r3, r3, #0x1000 @ ICache + mcr p15, 0, r3, c1, c0 @ disabled + + mov r0, #0 + mcr p15, 0, r0, c7, c7 @ flush I,D caches on v4 + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 + mcr p15, 0, r0, c8, c7 @ flush I,D TLBs on v4 + + bic r3, r3, #0x1 @ MMU + mcr p15, 0, r3, c1, c0 @ disabled + + mov pc, r2 + +__copy_target: .long 0x08508000 +__copy_end: .long 0x08608000 + + .word _start + .word __bss_start + + .align +__temp_stack: .space 128 + +__mmu_off: + adr r0, __ofw_data + ldr r0, [r0, #4] + orr r0, r0, #0x00600000 + + ldr r1, __copy_end + ldr r3, __copy_target + +/* r0 = 0x0e600000 (current end of kernelcode) + * r3 = 0x08508000 (where it should begin) + * r1 = 0x08608000 (end of copying area, 1MB) + * The kernel is compressed, so 1 MB should be enough. + * copy the kernel to the beginning of physical memory + * We start from the highest address, so we can copy + * from 0x08500000 to 0x08508000 if we have only 8MB + */ + + +__Copy: ldr r2, [r0], #-4 + str r2, [r1], #-4 + teq r1, r3 + bne __Copy + /* and jump to it */ + adr r2, __go_on + adr r0, __ofw_data + ldr r0, [r0, #4] + sub r2, r2, r0 + sub r2, r2, #0x00500000 + ldr r0, __copy_target + add r2, r2, r0 + mov pc, r2 + +__go_on: + adr sp, __temp_stack + add sp, sp, #128 + adr r0, __ofw_data + mov lr, pc + b SYMBOL_NAME(create_params) + + mov r8, #0 + mov r7, #15 diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index 21bdb43c0..0e384ec72 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -112,8 +112,7 @@ start: */ .text -1: mrc p15, 0, r6, c0, c0 @ get processor ID - adr r2, LC0 +1: adr r2, LC0 ldmia r2, {r2, r3, r4, r5, sp} mov r0, #0 @@ -124,14 +123,15 @@ start: cmp r2, r3 blt 1b + mrc p15, 0, r6, c0, c0 @ get processor ID bl cache_on mov r1, sp @ malloc space above stack add r2, sp, #0x10000 @ 64k max teq r4, r5 @ will we overwrite ourselves? - moveq r5, r2 - movne r5, r4 + moveq r5, r2 @ decompress after image + movne r5, r4 @ decompress to final location mov r0, r5 mov r3, r7 diff --git a/arch/arm/boot/compressed/ofw-shark.c b/arch/arm/boot/compressed/ofw-shark.c new file mode 100644 index 000000000..0b4474c59 --- /dev/null +++ b/arch/arm/boot/compressed/ofw-shark.c @@ -0,0 +1,216 @@ +/* + * linux/arch/arm/boot/compressed/ofw-shark.c + * + * by Alexander.Schulz@stud.uni-karlsruhe.de + * + * This file is used to get some basic information + * about the memory layout of the shark we are running + * on. Memory is usually divided in blocks a 8 MB. + * And bootargs are copied from OpenFirmware. + */ + + +#include <linux/kernel.h> +#include <asm/setup.h> +#include <asm/page.h> + + +asmlinkage void +create_params (unsigned long *buffer) +{ + /* Is there a better address? Also change in kernel/arch.c */ + struct param_struct *params = (struct param_struct *) 0x08003000; + int j,i,m,k,nr_banks,size; + + for (j=0;j<256;j++) params->u1.unused[j]=0; + + size=0; + nr_banks=(unsigned int) buffer[0]; + if (nr_banks > NR_BANKS) nr_banks = NR_BANKS; + for (j=0;j<nr_banks;j++){ + /* search the lowest address and put it into the next entry */ + /* not a fast sort algorithm, but there are at most 8 entries */ + /* and this is used only once anyway */ + m=0xffffffff; + for (i=0;i<(unsigned int) buffer[0];i++){ + if (buffer[2*i+1]<m) { + m=buffer[2*i+1]; + k=i; + } + } + + /* using pages_in_bank for startaddress and size */ + /* start is in bytes, size in pages and not bigger than 0x2000 */ + /* This is decoded in fixup_shark in arch/arm/kernel/arch.c */ + params->u1.s.pages_in_bank[j]=buffer[2*k+1]|(buffer[2*k+2]/PAGE_SIZE); + size += buffer[2*k+2]; + + buffer[2*k+1]=0xffffffff; /* mark as copied */ + } + + params->u1.s.page_size = PAGE_SIZE; + params->u1.s.nr_pages = size/PAGE_SIZE; + params->u1.s.flags = FLAG_READONLY; + + /* Copy over the bootargs */ + for (j=0;j<128/4;j++) { + ((unsigned long *) params->commandline)[j]=buffer[33+j]; + } +} + + +typedef int (*ofw_handle_t)(void *); + +/* Everything below is called with a wrong MMU setting. + * This means: no string constants, no initialization of + * arrays, no global variables! This is ugly but I didn't + * want to write this in assembler :-) + */ + +int +of_decode_int(const unsigned char *p) +{ + unsigned int i = *p++ << 8; + i = (i + *p++) << 8; + i = (i + *p++) << 8; + return (i + *p); +} + +int +OF_finddevice(ofw_handle_t openfirmware, char *name) +{ + unsigned int args[8]; + char service[12]; + + service[0]='f'; + service[1]='i'; + service[2]='n'; + service[3]='d'; + service[4]='d'; + service[5]='e'; + service[6]='v'; + service[7]='i'; + service[8]='c'; + service[9]='e'; + service[10]='\0'; + + args[0]=(unsigned int)service; + args[1]=1; + args[2]=1; + args[3]=(unsigned int)name; + + if (openfirmware(args) == -1) + return -1; + return args[4]; +} + +int +OF_getproplen(ofw_handle_t openfirmware, int handle, char *prop) +{ + unsigned int args[8]; + char service[12]; + + service[0]='g'; + service[1]='e'; + service[2]='t'; + service[3]='p'; + service[4]='r'; + service[5]='o'; + service[6]='p'; + service[7]='l'; + service[8]='e'; + service[9]='n'; + service[10]='\0'; + + args[0] = (unsigned int)service; + args[1] = 2; + args[2] = 1; + args[3] = (unsigned int)handle; + args[4] = (unsigned int)prop; + + if (openfirmware(args) == -1) + return -1; + return args[5]; +} + +int +OF_getprop(ofw_handle_t openfirmware, int handle, char *prop, void *buf, unsigned int buflen) +{ + unsigned int args[8]; + char service[8]; + + service[0]='g'; + service[1]='e'; + service[2]='t'; + service[3]='p'; + service[4]='r'; + service[5]='o'; + service[6]='p'; + service[7]='\0'; + + args[0] = (unsigned int)service; + args[1] = 4; + args[2] = 1; + args[3] = (unsigned int)handle; + args[4] = (unsigned int)prop; + args[5] = (unsigned int)buf; + args[6] = buflen; + + if (openfirmware(args) == -1) + return -1; + return args[7]; +} + +asmlinkage void ofw_init(ofw_handle_t o, int *nomr, int *pointer) +{ + int phandle,i,mem_len,buffer[32]; + char temp[12]; + + temp[0]='/'; + temp[1]='m'; + temp[2]='e'; + temp[3]='m'; + temp[4]='o'; + temp[5]='r'; + temp[6]='y'; + temp[7]='\0'; + + phandle=OF_finddevice(o,temp); + + temp[0]='r'; + temp[1]='e'; + temp[2]='g'; + temp[3]='\0'; + + mem_len = OF_getproplen(o,phandle, temp); + OF_getprop(o,phandle, temp, buffer, mem_len); + *nomr=mem_len >> 3; + + for (i=0; i<=mem_len/4; i++) pointer[i]=of_decode_int((const unsigned char *)&buffer[i]); + + temp[0]='/'; + temp[1]='c'; + temp[2]='h'; + temp[3]='o'; + temp[4]='s'; + temp[5]='e'; + temp[6]='n'; + temp[7]='\0'; + + phandle=OF_finddevice(o,temp); + + temp[0]='b'; + temp[1]='o'; + temp[2]='o'; + temp[3]='t'; + temp[4]='a'; + temp[5]='r'; + temp[6]='g'; + temp[7]='s'; + temp[8]='\0'; + + mem_len = OF_getproplen(o,phandle, temp); + OF_getprop(o,phandle, temp, buffer, mem_len); + for (i=0; i<=mem_len/4; i++) pointer[i+32]=buffer[i]; + +} diff --git a/arch/arm/boot/compressed/setup-sa1100.S b/arch/arm/boot/compressed/setup-sa1100.S index 3babde180..32e77c285 100644 --- a/arch/arm/boot/compressed/setup-sa1100.S +++ b/arch/arm/boot/compressed/setup-sa1100.S @@ -33,13 +33,11 @@ UART3_BASE: .long 0x80050000 #define UTSR0 0x1c #define UTSR1 0x20 -#define BAUD_DIV_230400 0x000 -#define BAUD_DIV_115200 0x001 -#define BAUD_DIV_57600 0x003 -#define BAUD_DIV_38400 0x005 -#define BAUD_DIV_19200 0x00b -#define BAUD_DIV_9600 0x017 -#define BAUD_DIV BAUD_DIV_9600 +#ifndef CONFIG_SA1100_DEFAULT_BAUDRATE +#define CONFIG_SA1100_DEFAULT_BAUDRATE 9600 +#endif + +#define BAUD_DIV ((230400/CONFIG_SA1100_DEFAULT_BAUDRATE)-1) SCR_loc: .long SYMBOL_NAME(SCR_value) #define GPIO_2_9 0x3fc @@ -92,7 +90,7 @@ skip_SCR: bne skip_uart @ UART3 if Assabet is used with Neponset - teq r3, #25 @ if Assabet + teq r3, #MACH_TYPE_ASSABET @ if Assabet tsteq r2, #(1 << 9) @ ... and Neponset present ldreq r0, UART3_BASE beq uart_init diff --git a/arch/arm/boot/compressed/vmlinux.lds.in b/arch/arm/boot/compressed/vmlinux.lds.in index 86ed5e6e3..8608996e9 100644 --- a/arch/arm/boot/compressed/vmlinux.lds.in +++ b/arch/arm/boot/compressed/vmlinux.lds.in @@ -19,12 +19,13 @@ SECTIONS .text : { _start = .; - head.o(.start) *(.start) - head.o(.text) *(.text) *(.fixup) *(.gnu.warning) + *(.rodata) + *(.glue_7) + *(.glue_7t) input_data = .; piggy.o input_data_end = .; diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index ed5115c53..45956ea07 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -10,16 +10,14 @@ USE_STANDARD_AS_RULE := true HEAD_OBJ = head-$(PROCESSOR).o ENTRY_OBJ = entry-$(PROCESSOR).o -AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -traditional -AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) -traditional +AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) +AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) O_OBJS_arc = dma-arc.o oldlatches.o O_OBJS_rpc = dma-rpc.o O_OBJS_footbridge = dma-footbridge.o isa.o O_OBJS_l7200 = fiq.o -leds-ebsa110 = leds-ebsa110.o - pci-nexuspci = plx90x0.o pci-footbridge = dec21285.o pci-shark = via82c505.o @@ -31,37 +29,29 @@ O_TARGET := kernel.o # Object file lists. -obj-y := arch.o dma.o $(ENTRY_OBJ) irq.o process.o ptrace.o \ - semaphore.o setup.o signal.o sys_arm.o time.o \ - traps.o $(O_OBJS_$(MACHINE)) -obj-m := -obj-n := -obj- := +obj-y := arch.o dma.o $(ENTRY_OBJ) irq.o process.o ptrace.o \ + semaphore.o setup.o signal.o sys_arm.o time.o traps.o \ + $(O_OBJS_$(MACHINE)) +obj-m := +obj-n := +obj- := + +export-objs := armksyms.o dma.o ecard.o fiq.o oldlatches.o time.o -export-objs := armksyms.o dma.o ecard.o \ - $(leds-$(MACHINE)) oldlatches.o \ - time.o +no-irq-arch := $(CONFIG_ARCH_INTEGRATOR) $(CONFIG_ARCH_CLPS711X) \ + $(CONFIG_ARCH_FOOTBRIDGE) $(CONFIG_ARCH_EBSA110) + +ifneq ($(findstring y,$(no-irq-arch)),y) + obj-y += irq-arch.o +endif obj-$(CONFIG_ARCH_ACORN) += ecard.o fiq.o time-acorn.o obj-$(CONFIG_DEBUG_LL) += debug-$(PROCESSOR).o obj-$(CONFIG_MODULES) += armksyms.o -obj-$(CONFIG_LEDS) += $(leds-$(MACHINE)) obj-$(CONFIG_ARTHUR) += arthur.o obj-$(CONFIG_ISA_DMA) += dma-isa.o - obj-$(CONFIG_PCI) += bios32.o $(pci-$(MACHINE)) $(pci-y) -# Files that are both resident and modular; remove from modular. - -obj-m := $(filter-out $(obj-y), $(obj-m)) - -# Translate to Rules.make lists. - -O_OBJS := $(filter-out $(export-objs), $(obj-y)) -OX_OBJS := $(filter $(export-objs), $(obj-y)) -M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) -MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) - all: kernel.o $(HEAD_OBJ) init_task.o include $(TOPDIR)/Rules.make diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 868fbd4a7..bbc43c509 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -257,11 +257,30 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) (struct arm_pci_sysdata *)bus->sysdata; struct arm_bus_sysdata *busdata; - if (bus->number < MAX_NR_BUS) - busdata = sysdata->bus + bus->number; - else + if (bus->number >= MAX_NR_BUS) BUG(); + if (bus->self) { + struct pci_dev *dev = bus->self; + int i; + + for (i = 0; i < 3; i++) { + bus->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES + i]; + bus->resource[i]->name = bus->name; + } + bus->resource[0]->start = ioport_resource.start; + bus->resource[0]->end = ioport_resource.end; + bus->resource[0]->flags |= pci_bridge_check_io(dev); + bus->resource[1]->start = iomem_resource.start; + bus->resource[1]->end = iomem_resource.end; + bus->resource[1]->flags |= IORESOURCE_MEM; + + /* Turn off downsteam prefetchable memory address range */ + bus->resource[2]->start = 1024*1024; + bus->resource[2]->end = bus->resource[2]->start - 1; + } + + busdata = sysdata->bus + bus->number; busdata->max_lat = 255; /* @@ -363,10 +382,6 @@ void __init pcibios_fixup_bus(struct pci_bus *bus) void __init pcibios_fixup_pbus_ranges(struct pci_bus *bus, struct pbus_set_ranges_data *ranges) { - ranges->io_start -= bus->resource[0]->start; - ranges->io_end -= bus->resource[0]->start; - ranges->mem_start -= bus->resource[1]->start; - ranges->mem_end -= bus->resource[1]->start; } u8 __init no_swizzle(struct pci_dev *dev, u8 *pin) @@ -442,7 +457,8 @@ void __init pcibios_init(void) hw_pci->init(&sysdata); /* - * Other architectures don't seem to do this... should we? + * Claim the currently allocated resources. This ensures + * that we will not allocate an already inuse region. */ pcibios_claim_resources(); diff --git a/arch/arm/kernel/dma-arc.c b/arch/arm/kernel/dma-arc.c index 121a2792b..ad9ca3de5 100644 --- a/arch/arm/kernel/dma-arc.c +++ b/arch/arm/kernel/dma-arc.c @@ -40,7 +40,7 @@ static void arc_floppy_data_enable_dma(dmach_t channel, dma_t *dma) memcpy ((void *)0x1c, (void *)&fdc1772_dma_read, &fdc1772_dma_read_end - &fdc1772_dma_read); fdc1772_setupdma(dma->buf.length, dma->buf.address); /* Sets data pointer up */ - enable_irq (64); + enable_fiq(FIQ_FLOPPYDATA); restore_flags(flags); } break; @@ -55,7 +55,7 @@ static void arc_floppy_data_enable_dma(dmach_t channel, dma_t *dma) memcpy ((void *)0x1c, (void *)&fdc1772_dma_write, &fdc1772_dma_write_end - &fdc1772_dma_write); fdc1772_setupdma(dma->buf.length, dma->buf.address); /* Sets data pointer up */ - enable_irq (64); + enable_fiq(FIQ_FLOPPYDATA; restore_flags(flags); } @@ -102,7 +102,7 @@ static int arc_floppy_cmdend_get_dma_residue(dmach_t channel, dma_t *dma) static void arc_disable_dma(dmach_t channel, dma_t *dma) { - disable_irq(dma->dma_irq); + disable_fiq(dma->dma_irq); } static struct dma_ops arc_floppy_data_dma_ops = { @@ -158,12 +158,12 @@ static void a5k_floppy_enable_dma(dmach_t channel, dma_t *dma) regs.ARM_r10 = dma->buf.address; regs.ARM_fp = FLOPPYDMA_BASE; set_fiq_regs(®s); - enable_irq(dma->dma_irq); + enable_fiq(dma->dma_irq); } static void a5k_floppy_disable_dma(dmach_t channel, dma_t *dma) { - disable_irq(dma->dma_irq); + disable_fiq(dma->dma_irq); release_fiq(&fh); } @@ -192,15 +192,15 @@ void __init arch_dma_init(dma_t *dma) { #if defined(CONFIG_BLK_DEV_FD1772) || defined(CONFIG_BLK_DEV_FD1772_MODULE) if (machine_is_archimedes()) { - dma[DMA_VIRTUAL_FLOPPY0].dma_irq = 64; + dma[DMA_VIRTUAL_FLOPPY0].dma_irq = FIQ_FLOPPYDATA; dma[DMA_VIRTUAL_FLOPPY0].d_ops = &arc_floppy_data_dma_ops; - dma[DMA_VIRTUAL_FLOPPY1].dma_irq = 65; + dma[DMA_VIRTUAL_FLOPPY1].dma_irq = 1; dma[DMA_VIRTUAL_FLOPPY1].d_ops = &arc_floppy_cmdend_dma_ops; } #endif #ifdef CONFIG_ARCH_A5K if (machine_is_a5k()) { - dma[DMA_VIRTUAL_FLOPPY0].dma_irq = 64; + dma[DMA_VIRTUAL_FLOPPY0].dma_irq = FIQ_FLOPPYDATA; dma[DMA_VIRTUAL_FLOPPY0].d_ops = &a5k_floppy_dma_ops; } #endif diff --git a/arch/arm/kernel/dma-rpc.c b/arch/arm/kernel/dma-rpc.c index b46ca7753..402b71cdb 100644 --- a/arch/arm/kernel/dma-rpc.c +++ b/arch/arm/kernel/dma-rpc.c @@ -10,7 +10,7 @@ * DMA functions specific to RiscPC architecture */ #include <linux/sched.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mman.h> #include <linux/init.h> #include <linux/pci.h> @@ -42,11 +42,11 @@ typedef struct { #define TRANSFER_SIZE 2 #define CURA (0) -#define ENDA ((IOMD_IO0ENDA - IOMD_IO0CURA) << 2) -#define CURB ((IOMD_IO0CURB - IOMD_IO0CURA) << 2) -#define ENDB ((IOMD_IO0ENDB - IOMD_IO0CURA) << 2) -#define CR ((IOMD_IO0CR - IOMD_IO0CURA) << 2) -#define ST ((IOMD_IO0ST - IOMD_IO0CURA) << 2) +#define ENDA (IOMD_IO0ENDA - IOMD_IO0CURA) +#define CURB (IOMD_IO0CURB - IOMD_IO0CURA) +#define ENDB (IOMD_IO0ENDB - IOMD_IO0CURA) +#define CR (IOMD_IO0CR - IOMD_IO0CURA) +#define ST (IOMD_IO0ST - IOMD_IO0CURA) #define state_prog_a 0 #define state_wait_a 1 @@ -93,14 +93,14 @@ static void iomd_get_next_sg(struct scatterlist *sg, dma_t *dma) static inline void iomd_setup_dma_a(struct scatterlist *sg, dma_t *dma) { - outl_t(sg->dma_address, dma->dma_base + CURA); - outl_t(sg->length, dma->dma_base + ENDA); + iomd_writel(sg->dma_address, dma->dma_base + CURA); + iomd_writel(sg->length, dma->dma_base + ENDA); } static inline void iomd_setup_dma_b(struct scatterlist *sg, dma_t *dma) { - outl_t(sg->dma_address, dma->dma_base + CURB); - outl_t(sg->length, dma->dma_base + ENDB); + iomd_writel(sg->dma_address, dma->dma_base + CURB); + iomd_writel(sg->length, dma->dma_base + ENDB); } static void iomd_dma_handle(int irq, void *dev_id, struct pt_regs *regs) @@ -116,7 +116,7 @@ static void iomd_dma_handle(int irq, void *dev_id, struct pt_regs *regs) dma->state = state_wait_a; case state_wait_a: - status = inb_t(dma->dma_base + ST); + status = iomd_readb(dma->dma_base + ST); switch (status & (DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB)) { case DMA_ST_OFL|DMA_ST_INT: iomd_get_next_sg(&dma->cur_sg, dma); @@ -137,7 +137,7 @@ static void iomd_dma_handle(int irq, void *dev_id, struct pt_regs *regs) break; case state_wait_b: - status = inb_t(dma->dma_base + ST); + status = iomd_readb(dma->dma_base + ST); switch (status & (DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB)) { case DMA_ST_OFL|DMA_ST_INT|DMA_ST_AB: iomd_get_next_sg(&dma->cur_sg, dma); @@ -193,14 +193,14 @@ static void iomd_enable_dma(dmach_t channel, dma_t *dma) PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); } - outb_t(DMA_CR_C, dma_base + CR); + iomd_writeb(DMA_CR_C, dma_base + CR); dma->state = state_prog_a; } if (dma->dma_mode == DMA_MODE_READ) ctrl |= DMA_CR_D; - outb_t(ctrl, dma_base + CR); + iomd_writeb(ctrl, dma_base + CR); enable_irq(dma->dma_irq); } @@ -210,8 +210,8 @@ static void iomd_disable_dma(dmach_t channel, dma_t *dma) unsigned int ctrl; disable_irq(dma->dma_irq); - ctrl = inb_t(dma_base + CR); - outb_t(ctrl & ~DMA_CR_E, dma_base + CR); + ctrl = iomd_readb(dma_base + CR); + iomd_writeb(ctrl & ~DMA_CR_E, dma_base + CR); } static int iomd_set_dma_speed(dmach_t channel, dma_t *dma, int cycle) @@ -227,7 +227,7 @@ static int iomd_set_dma_speed(dmach_t channel, dma_t *dma, int cycle) else speed = 0; - tcr = inb(IOMD_DMATCR); + tcr = iomd_readb(IOMD_DMATCR); speed &= 3; switch (channel) { @@ -251,7 +251,7 @@ static int iomd_set_dma_speed(dmach_t channel, dma_t *dma, int cycle) break; } - outb(tcr, IOMD_DMATCR); + iomd_writeb(tcr, IOMD_DMATCR); return speed; } @@ -287,7 +287,7 @@ static void floppy_enable_dma(dmach_t channel, dma_t *dma) regs.ARM_r9 = dma->buf.length; regs.ARM_r10 = (unsigned long)dma->buf.address; - regs.ARM_fp = (unsigned long)PCIO_FLOPPYDMABASE; + regs.ARM_fp = FLOPPYDMA_BASE; if (claim_fiq(&fh)) { printk("floppydma: couldn't claim FIQ.\n"); @@ -296,12 +296,12 @@ static void floppy_enable_dma(dmach_t channel, dma_t *dma) set_fiq_handler(fiqhandler_start, fiqhandler_length); set_fiq_regs(®s); - enable_irq(dma->dma_irq); + enable_fiq(dma->dma_irq); } static void floppy_disable_dma(dmach_t channel, dma_t *dma) { - disable_irq(dma->dma_irq); + disable_fiq(dma->dma_irq); release_fiq(&fh); } @@ -334,32 +334,32 @@ static struct dma_ops sound_dma_ops = { void __init arch_dma_init(dma_t *dma) { - outb(0, IOMD_IO0CR); - outb(0, IOMD_IO1CR); - outb(0, IOMD_IO2CR); - outb(0, IOMD_IO3CR); + iomd_writeb(0, IOMD_IO0CR); + iomd_writeb(0, IOMD_IO1CR); + iomd_writeb(0, IOMD_IO2CR); + iomd_writeb(0, IOMD_IO3CR); - outb(0xa0, IOMD_DMATCR); + iomd_writeb(0xa0, IOMD_DMATCR); - dma[DMA_0].dma_base = ioaddr(IOMD_IO0CURA); + dma[DMA_0].dma_base = IOMD_IO0CURA; dma[DMA_0].dma_irq = IRQ_DMA0; dma[DMA_0].d_ops = &iomd_dma_ops; - dma[DMA_1].dma_base = ioaddr(IOMD_IO1CURA); + dma[DMA_1].dma_base = IOMD_IO1CURA; dma[DMA_1].dma_irq = IRQ_DMA1; dma[DMA_1].d_ops = &iomd_dma_ops; - dma[DMA_2].dma_base = ioaddr(IOMD_IO2CURA); + dma[DMA_2].dma_base = IOMD_IO2CURA; dma[DMA_2].dma_irq = IRQ_DMA2; dma[DMA_2].d_ops = &iomd_dma_ops; - dma[DMA_3].dma_base = ioaddr(IOMD_IO3CURA); + dma[DMA_3].dma_base = IOMD_IO3CURA; dma[DMA_3].dma_irq = IRQ_DMA3; dma[DMA_3].d_ops = &iomd_dma_ops; - dma[DMA_S0].dma_base = ioaddr(IOMD_SD0CURA); + dma[DMA_S0].dma_base = IOMD_SD0CURA; dma[DMA_S0].dma_irq = IRQ_DMAS0; dma[DMA_S0].d_ops = &iomd_dma_ops; - dma[DMA_S1].dma_base = ioaddr(IOMD_SD1CURA); + dma[DMA_S1].dma_base = IOMD_SD1CURA; dma[DMA_S1].dma_irq = IRQ_DMAS1; dma[DMA_S1].d_ops = &iomd_dma_ops; - dma[DMA_VIRTUAL_FLOPPY].dma_irq = 64; + dma[DMA_VIRTUAL_FLOPPY].dma_irq = FIQ_FLOPPYDATA; dma[DMA_VIRTUAL_FLOPPY].d_ops = &floppy_dma_ops; dma[DMA_VIRTUAL_SOUND].d_ops = &sound_dma_ops; @@ -367,5 +367,5 @@ void __init arch_dma_init(dma_t *dma) * Setup DMA channels 2,3 to be for podules * and channels 0,1 for internal devices */ - outb(DMA_EXT_IO3|DMA_EXT_IO2, IOMD_DMAEXT); + iomd_writeb(DMA_EXT_IO3|DMA_EXT_IO2, IOMD_DMAEXT); } diff --git a/arch/arm/kernel/dma.c b/arch/arm/kernel/dma.c index ad311020f..7f0106953 100644 --- a/arch/arm/kernel/dma.c +++ b/arch/arm/kernel/dma.c @@ -12,7 +12,7 @@ * DMA facilities. */ #include <linux/module.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/sched.h> #include <linux/mman.h> #include <linux/init.h> diff --git a/arch/arm/kernel/ecard.c b/arch/arm/kernel/ecard.c index 3584c3227..07de0170c 100644 --- a/arch/arm/kernel/ecard.c +++ b/arch/arm/kernel/ecard.c @@ -35,7 +35,7 @@ #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/proc_fs.h> #include <linux/init.h> diff --git a/arch/arm/kernel/entry-armo.S b/arch/arm/kernel/entry-armo.S index e4df9fe1a..65f70c727 100644 --- a/arch/arm/kernel/entry-armo.S +++ b/arch/arm/kernel/entry-armo.S @@ -197,8 +197,7 @@ irq_prio_h: .byte 0, 8, 9, 8,10,10,10,10,11,11,11,11,10,10,10,10 #endif #define FAULT_CODE_PREFETCH 0x04 #define FAULT_CODE_WRITE 0x02 -#define FAULT_CODE_USER 0x01 - +#define FAULT_CODE_FORCECOW 0x01 #define SVC_SAVE_ALL \ str sp, [sp, #-16]! ;\ @@ -487,7 +486,6 @@ vector_data: sub lr, lr, #8 @ Correct lr save_user_regs teqp pc, #0x00000003 @ NOT a problem - doesnt change mode mask_pc r0, lr - mov r2, #FAULT_CODE_USER bl Ldata_do b ret_from_exception @@ -499,7 +497,6 @@ Ldata_not_user: tst lr, #0x08000000 teqeqp pc, #0x00000003 @ NOT a problem - doesnt change mode mask_pc r0, lr - mov r2, #0 bl Ldata_do SVC_RESTORE_ALL @@ -510,6 +507,7 @@ Ldata_illegal_mode: Ldata_do: mov r3, sp ldr r4, [r0] @ Get instruction + mov r2, #0 tst r4, #1 << 20 @ Check to see if it is a write instruction orreq r2, r2, #FAULT_CODE_WRITE @ Indicate write instruction mov r1, r4, lsr #22 @ Now branch to the relevent processing routine diff --git a/arch/arm/kernel/fiq.c b/arch/arm/kernel/fiq.c index 1be23e8fb..da32d69f9 100644 --- a/arch/arm/kernel/fiq.c +++ b/arch/arm/kernel/fiq.c @@ -36,12 +36,14 @@ * 6. Goto 3 */ #include <linux/config.h> +#include <linux/module.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/init.h> #include <asm/fiq.h> #include <asm/io.h> +#include <asm/irq.h> #include <asm/pgalloc.h> #include <asm/system.h> #include <asm/uaccess.h> @@ -71,7 +73,7 @@ static inline void protect_page_0(void) * - we always relinquish FIQ control * - we always reacquire FIQ control */ -int fiq_def_op(void *ref, int relinquish) +static int fiq_def_op(void *ref, int relinquish) { if (!relinquish) { unprotect_page_0(); @@ -214,6 +216,24 @@ void release_fiq(struct fiq_handler *f) while (current_fiq->fiq_op(current_fiq->dev_id, 0)); } +void enable_fiq(int fiq) +{ + enable_irq(fiq + FIQ_START); +} + +void disable_fiq(int fiq) +{ + disable_irq(fiq + FIQ_START); +} + +EXPORT_SYMBOL(set_fiq_handler); +EXPORT_SYMBOL(set_fiq_regs); +EXPORT_SYMBOL(get_fiq_regs); +EXPORT_SYMBOL(claim_fiq); +EXPORT_SYMBOL(release_fiq); +EXPORT_SYMBOL(enable_fiq); +EXPORT_SYMBOL(disable_fiq); + void __init init_FIQ(void) { no_fiq_insn = *(unsigned long *)FIQ_VECTOR; diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 3c6899264..ae599d255 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -24,7 +24,7 @@ #include <linux/sched.h> #include <linux/ioport.h> #include <linux/interrupt.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/smp.h> #include <linux/init.h> diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c index 9f50ab6fd..6b045845f 100644 --- a/arch/arm/kernel/process.c +++ b/arch/arm/kernel/process.c @@ -17,7 +17,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/delay.h> #include <linux/reboot.h> @@ -92,8 +92,10 @@ void cpu_idle(void) void (*idle)(void) = pm_idle; if (!idle) idle = arch_idle; + leds_event(led_idle_start); while (!current->need_resched) idle(); + leds_event(led_idle_end); schedule(); #ifndef CONFIG_NO_PGT_CACHE check_pgt_cache(); @@ -364,20 +366,23 @@ void dump_thread(struct pt_regs * regs, struct user * dump) */ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) { - extern long sys_exit(int) __attribute__((noreturn)); pid_t __ret; __asm__ __volatile__( - "mov r0, %1 @ kernel_thread sys_clone + "orr r0, %1, %2 @ kernel_thread sys_clone mov r1, #0 "__syscall(clone)" - teq r0, #0 @ if we are the child - moveq fp, #0 @ ensure that fp is zero - mov %0, r0" + movs %0, r0 @ if we are the child + bne 1f + mov fp, #0 @ ensure that fp is zero + mov r0, %4 + mov lr, pc + mov pc, %3 + b sys_exit +1: " : "=r" (__ret) - : "Ir" (flags | CLONE_VM) : "r0", "r1"); - if (__ret == 0) - sys_exit((fn)(arg)); + : "Ir" (flags), "I" (CLONE_VM), "r" (fn), "r" (arg) + : "r0", "r1", "lr"); return __ret; } diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c index 36bbf3ac8..16b8e59ea 100644 --- a/arch/arm/kernel/sys_arm.c +++ b/arch/arm/kernel/sys_arm.c @@ -14,7 +14,7 @@ */ #include <linux/errno.h> #include <linux/sched.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mm.h> #include <linux/sem.h> #include <linux/msg.h> diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 926cdff2f..be24bdcfc 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -134,32 +134,17 @@ void (*leds_event)(led_event_t) = dummy_leds_event; #ifdef CONFIG_MODULES EXPORT_SYMBOL(leds_event); #endif +#endif +#ifdef CONFIG_LEDS_TIMER static void do_leds(void) { -#ifdef CONFIG_LEDS_CPU - { - static int last_pid; - - if (current->pid != last_pid) { - last_pid = current->pid; - if (last_pid) - leds_event(led_idle_end); - else - leds_event(led_idle_start); - } - } -#endif -#ifdef CONFIG_LEDS_TIMER - { - static unsigned int count = 50; + static unsigned int count = 50; - if (--count == 0) { - count = 50; - leds_event(led_timer); - } + if (--count == 0) { + count = 50; + leds_event(led_timer); } -#endif } #else #define do_leds() diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index 9d0cc5c69..8455caf78 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -107,7 +107,7 @@ static void dump_instr(struct pt_regs *regs) bad = __get_user(val, &((u32 *)addr)[i]); if (!bad) - printk(i == 0 ? "(%0*x) " : "%0*x", width, val); + printk(i == 0 ? "(%0*x) " : "%0*x ", width, val); else { printk("bad PC value."); break; @@ -133,7 +133,7 @@ static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) printk("no frame pointer"); ok = 0; } else if (verify_stack(fp)) { - printk("invalid frame pointer %08lx", fp); + printk("invalid frame pointer 0x%08x", fp); ok = 0; } else if (fp < 4096+(unsigned long)tsk) printk("frame pointer underflow"); diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile index fa7f10117..ee111801f 100644 --- a/arch/arm/lib/Makefile +++ b/arch/arm/lib/Makefile @@ -1,58 +1,58 @@ # # linux/arch/arm/lib/Makefile # -# Copyright (C) 1995-1999 Russell King +# Copyright (C) 1995-2000 Russell King # USE_STANDARD_AS_RULE := true -L_TARGET := lib.a -L_OBJS := changebit.o csumipv6.o csumpartial.o csumpartialcopy.o \ - csumpartialcopyuser.o clearbit.o copy_page.o findbit.o \ - memchr.o memcpy.o memset.o memzero.o setbit.o \ - strncpy_from_user.o strnlen_user.o strchr.o strrchr.o \ - testchangebit.o testclearbit.o testsetbit.o uaccess.o - -l-obj-y := -l-obj-n := - -O_TARGET := lib.o -O_OBJS := backtrace.o delay.o - -ifeq ($(CONFIG_ARCH_ACORN),y) - half := n - full := y +L_TARGET := lib.a + +obj-y := backtrace.o changebit.o csumipv6.o csumpartial.o \ + csumpartialcopy.o csumpartialcopyuser.o clearbit.o \ + copy_page.o delay.o findbit.o memchr.o memcpy.o \ + memset.o memzero.o setbit.o strncpy_from_user.o \ + strnlen_user.o strchr.o strrchr.o testchangebit.o \ + testclearbit.o testsetbit.o uaccess.o +obj-m := +obj-n := + +export-objs := io.o + +obj-arc := ecard.o io-acorn.o floppydma.o +obj-rpc := ecard.o io-acorn.o floppydma.o +obj-clps7500 := io-acorn.o +obj-footbridge := io-pcio.o +obj-l7200 := io-acorn.o +obj-nexuspci := io-pcio.o +obj-sa1100 := io-pcio.o +obj-shark := io-shark.o +obj-integrator := io-pcio.o +obj-clps711x := io-shark.o + +obj-y += $(obj-$(MACHINE)) + +ifeq ($(CONFIG_CPU_32v4),y) + v3 := n + v4 := y else - half := y - full := n + v3 := y + v4 := n endif -L_OBJS_arc := ecard.o io-acorn.o floppydma.o -L_OBJS_rpc := ecard.o io-acorn.o floppydma.o -L_OBJS_clps7500 := io-acorn.o -L_OBJS_footbridge := io-pcio.o -L_OBJS_l7200 := io-acorn.o -L_OBJS_nexuspci := io-pcio.o -L_OBJS_sa1100 := io-pcio.o -L_OBJS_shark := io-shark.o -L_OBJS_integrator := io-pcio.o -L_OBJS_clps711x := io-shark.o - -l-obj-y += io-readsb.o io-writesb.o -l-obj-$(full) += io-readsw-armv3.o io-writesw-armv3.o -l-obj-$(half) += io-readsw-armv4.o io-writesw-armv4.o -l-obj-y += io-readsl.o io-writesl.o +obj-y += io-readsb.o io-writesb.o +obj-$(v3) += io-readsw-armv3.o io-writesw-armv3.o io-readsl-armv3.o +obj-$(v4) += io-readsw-armv4.o io-writesw-armv4.o io-readsl-armv4.o +obj-y += io-writesl.o ifeq ($(PROCESSOR),armo) - L_OBJS += uaccess-armo.o + obj-y += uaccess-armo.o endif ifneq ($(MACHINE),ebsa110) - OX_OBJS += io.o + obj-y += io.o endif -L_OBJS += $(L_OBJS_$(MACHINE)) $(l-obj-y) - include $(TOPDIR)/Rules.make constants.h: getconsdata.o extractconstants.pl diff --git a/arch/arm/lib/io-acorn.S b/arch/arm/lib/io-acorn.S index a40272fdb..db19d932e 100644 --- a/arch/arm/lib/io-acorn.S +++ b/arch/arm/lib/io-acorn.S @@ -60,32 +60,32 @@ ENTRY(outsl) */ ENTRY(insw) - teq r2, #0 - RETINSTR(moveq, pc, lr) addr r0 - b __arch_readsw + teq r2, #0 + bne __arch_readsw + RETINSTR(mov, pc, lr) ENTRY(insb) - teq r2, #0 - RETINSTR(moveq, pc, lr) addr r0 - b __arch_readsb + teq r2, #0 + bne __arch_readsb + RETINSTR(mov, pc, lr) @ Purpose: write a block of data from memory to a hardware register. @ Proto : outsw(int to_reg, void *from, int len_in_words); @ Notes : increments from ENTRY(outsw) - teq r2, #0 - RETINSTR(moveq, pc, lr) addr r0 - b __arch_writesw + teq r2, #0 + bne __arch_writesw + RETINSTR(mov, pc, lr) ENTRY(outsb) - teq r2, #0 - RETINSTR(moveq, pc, lr) addr r0 - b __arch_writesb + teq r2, #0 + bne __arch_writesb + RETINSTR(mov, pc, lr) @ Purpose: write a memc register @ Proto : void memc_write(int register, int value); diff --git a/arch/arm/lib/io-pcio.S b/arch/arm/lib/io-pcio.S index f57abfc10..243366b86 100644 --- a/arch/arm/lib/io-pcio.S +++ b/arch/arm/lib/io-pcio.S @@ -1,4 +1,5 @@ #include <linux/linkage.h> +#include <asm/assembler.h> #include <asm/hardware.h> .equ pcio_high, PCIO_BASE & 0xff000000 @@ -13,26 +14,37 @@ ENTRY(insl) ioaddr r0, r0 - b __arch_readsl + teq r2, #0 + bne __arch_readsl + RETINSTR(mov, pc, lr) ENTRY(outsl) ioaddr r0, r0 - b __arch_writesl - - /* Nobody could say these are optimal, but not to worry. */ + teq r2, #0 + bne __arch_writesl + RETINSTR(mov, pc, lr) ENTRY(outsw) ioaddr r0, r0 - b __arch_writesw + teq r2, #0 + bne __arch_writesw + RETINSTR(mov, pc, lr) ENTRY(insw) ioaddr r0, r0 - b __arch_readsw + teq r2, #0 + bne __arch_readsw + RETINSTR(mov, pc, lr) ENTRY(insb) ioaddr r0, r0 - b __arch_readsb + teq r2, #0 + bne __arch_readsb + RETINSTR(mov, pc, lr) ENTRY(outsb) ioaddr r0, r0 - b __arch_writesb + teq r2, #0 + bne __arch_writesb + RETINSTR(mov, pc, lr) + diff --git a/arch/arm/lib/io-readsb.S b/arch/arm/lib/io-readsb.S index 23cca8929..a31381fd5 100644 --- a/arch/arm/lib/io-readsb.S +++ b/arch/arm/lib/io-readsb.S @@ -62,6 +62,7 @@ ENTRY(__arch_readsb) ldrb ip, [r0] orr r6, r6, ip, lsl #24 stmia r1!, {r3 - r6} + subs r2, r2, #16 bpl .insb_16_lp @@ -88,7 +89,7 @@ ENTRY(__arch_readsb) stmia r1!, {r3, r4} .insb_no_8: tst r2, #4 - bne .insb_no_4 + beq .insb_no_4 ldrb r3, [r0] ldrb r4, [r0] @@ -101,6 +102,7 @@ ENTRY(__arch_readsb) .insb_no_4: ands r2, r2, #3 LOADREGS(eqfd, sp!, {r4 - r6, pc}) + cmp r2, #2 ldrb r3, [r0] strb r3, [r1], #1 @@ -108,4 +110,5 @@ ENTRY(__arch_readsb) strgeb r3, [r1], #1 ldrgtb r3, [r0] strgtb r3, [r1] + LOADREGS(fd, sp!, {r4 - r6, pc}) diff --git a/arch/arm/lib/io-readsl-armv3.S b/arch/arm/lib/io-readsl-armv3.S new file mode 100644 index 000000000..0751c3157 --- /dev/null +++ b/arch/arm/lib/io-readsl-armv3.S @@ -0,0 +1,76 @@ +/* + * linux/arch/arm/lib/io-readsl-armv3.S + * + * Copyright (C) 1995-2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <asm/assembler.h> +#include <asm/hardware.h> + +/* + * Note that some reads can be aligned on half-word boundaries. + */ +ENTRY(__arch_readsl) + ands ip, r1, #3 + bne 2f + +1: ldr r3, [r0] + str r3, [r1], #4 + subs r2, r2, #1 + bne 1b + mov pc, lr + +2: cmp ip, #2 + ldr ip, [r0] + blt 4f + bgt 6f + + strb ip, [r1], #1 + mov ip, ip, lsr #8 + strb ip, [r1], #1 + mov ip, ip, lsr #8 +3: subs r2, r2, #1 + ldrne r3, [r0] + orrne ip, ip, r3, lsl #16 + strne ip, [r1], #4 + movne ip, r3, lsr #16 + bne 3b + strb ip, [r1], #1 + mov ip, ip, lsr #8 + strb ip, [r1], #1 + mov pc, lr + +4: strb ip, [r1], #1 + mov ip, ip, lsr #8 + strb ip, [r1], #1 + mov ip, ip, lsr #8 + strb ip, [r1], #1 + mov ip, ip, lsr #8 +5: subs r2, r2, #1 + ldrne r3, [r0] + orrne ip, ip, r3, lsl #8 + strne ip, [r1], #4 + movne ip, r3, lsr #24 + bne 5b + strb ip, [r1], #1 + mov pc, lr + +6: strb ip, [r1], #1 + mov ip, ip, lsr #8 +7: subs r2, r2, #1 + ldrne r3, [r0] + orrne ip, ip, r3, lsl #24 + strne ip, [r1], #4 + movne ip, r3, lsr #8 + bne 7b + strb ip, [r1], #1 + mov ip, ip, lsr #8 + strb ip, [r1], #1 + mov ip, ip, lsr #8 + strb ip, [r1], #1 + mov pc, lr + diff --git a/arch/arm/lib/io-readsl.S b/arch/arm/lib/io-readsl-armv4.S index 530175485..08b3d350f 100644 --- a/arch/arm/lib/io-readsl.S +++ b/arch/arm/lib/io-readsl-armv4.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/lib/io-readsb.S + * linux/arch/arm/lib/io-readsl-armv4.S * * Copyright (C) 1995-2000 Russell King * @@ -11,6 +11,9 @@ #include <asm/assembler.h> #include <asm/hardware.h> +/* + * Note that some reads can be aligned on half-word boundaries. + */ ENTRY(__arch_readsl) ands ip, r1, #3 bne 2f diff --git a/arch/arm/lib/io-readsw-armv3.S b/arch/arm/lib/io-readsw-armv3.S index 65fb94e2f..cb2d5cee9 100644 --- a/arch/arm/lib/io-readsw-armv3.S +++ b/arch/arm/lib/io-readsw-armv3.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/lib/io-readsb.S + * linux/arch/arm/lib/io-readsw-armv3.S * * Copyright (C) 1995-2000 Russell King * @@ -62,8 +62,10 @@ ENTRY(__arch_readsw) orr r6, r6, lr, lsl #16 stmia r1!, {r3 - r6} + subs r2, r2, #8 bpl .insw_8_lp + tst r2, #7 LOADREGS(eqfd, sp!, {r4, r5, r6, pc}) @@ -97,6 +99,7 @@ ENTRY(__arch_readsw) strneb r3, [r1], #1 movne r3, r3, lsr #8 strneb r3, [r1] + LOADREGS(fd, sp!, {r4, r5, r6, pc}) diff --git a/arch/arm/lib/io-readsw-armv4.S b/arch/arm/lib/io-readsw-armv4.S index 6f1750a49..10eeee99f 100644 --- a/arch/arm/lib/io-readsw-armv4.S +++ b/arch/arm/lib/io-readsw-armv4.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/lib/io-readsb.S + * linux/arch/arm/lib/io-readsw-armv4.S * * Copyright (C) 1995-2000 Russell King * @@ -46,16 +46,18 @@ ENTRY(__arch_readsw) orr r4, r4, r5, lsl #16 ldrh r5, [r0] - ldrh r6, [r0] - orr r5, r5, r6, lsl #16 + ldrh ip, [r0] + orr r5, r5, ip, lsl #16 ldrh ip, [r0] ldrh lr, [r0] orr ip, ip, lr, lsl #16 stmia r1!, {r3 - r5, ip} + subs r2, r2, #8 bpl .insw_8_lp + tst r2, #7 LOADREGS(eqfd, sp!, {r4, r5, pc}) @@ -84,4 +86,5 @@ ENTRY(__arch_readsw) .no_insw_2: tst r2, #1 ldrneh r3, [r0] strneh r3, [r1] + LOADREGS(fd, sp!, {r4, r5, pc}) diff --git a/arch/arm/lib/io-writesb.S b/arch/arm/lib/io-writesb.S index b919fdaad..a9a8c4af9 100644 --- a/arch/arm/lib/io-writesb.S +++ b/arch/arm/lib/io-writesb.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/lib/io-readsb.S + * linux/arch/arm/lib/io-writesb.S * * Copyright (C) 1995-2000 Russell King * @@ -13,7 +13,7 @@ .outsb_align: rsb ip, ip, #4 cmp ip, r2 - mov ip, r2 + movgt ip, r2 cmp ip, #2 ldrb r3, [r1], #1 strb r3, [r0] @@ -34,6 +34,7 @@ ENTRY(__arch_writesb) bmi .outsb_no_16 .outsb_16_lp: ldmia r1!, {r3 - r6} + strb r3, [r0] mov r3, r3, lsr #8 strb r3, [r0] @@ -65,6 +66,7 @@ ENTRY(__arch_writesb) strb r6, [r0] mov r6, r6, lsr #8 strb r6, [r0] + subs r2, r2, #16 bpl .outsb_16_lp @@ -74,7 +76,8 @@ ENTRY(__arch_writesb) .outsb_no_16: tst r2, #8 beq .outsb_no_8 - ldmia r1, {r3, r4} + ldmia r1!, {r3, r4} + strb r3, [r0] mov r3, r3, lsr #8 strb r3, [r0] @@ -92,7 +95,7 @@ ENTRY(__arch_writesb) strb r4, [r0] .outsb_no_8: tst r2, #4 - bne .outsb_no_4 + beq .outsb_no_4 ldr r3, [r1], #4 strb r3, [r0] @@ -105,6 +108,7 @@ ENTRY(__arch_writesb) .outsb_no_4: ands r2, r2, #3 LOADREGS(eqfd, sp!, {r4 - r6, pc}) + cmp r2, #2 ldrb r3, [r1], #1 strb r3, [r0] @@ -112,4 +116,5 @@ ENTRY(__arch_writesb) strgeb r3, [r0] ldrgtb r3, [r1] strgtb r3, [r0] + LOADREGS(fd, sp!, {r4 - r6, pc}) diff --git a/arch/arm/lib/io-writesl.S b/arch/arm/lib/io-writesl.S index ccda08c39..aa3340bdd 100644 --- a/arch/arm/lib/io-writesl.S +++ b/arch/arm/lib/io-writesl.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/lib/io-readsb.S + * linux/arch/arm/lib/io-writesl.S * * Copyright (C) 1995-2000 Russell King * diff --git a/arch/arm/lib/io-writesw-armv3.S b/arch/arm/lib/io-writesw-armv3.S index 29c94cc3d..fce7d5ef5 100644 --- a/arch/arm/lib/io-writesw-armv3.S +++ b/arch/arm/lib/io-writesw-armv3.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/lib/io-readsb.S + * linux/arch/arm/lib/io-writesw-armv3.S * * Copyright (C) 1995-2000 Russell King * @@ -39,6 +39,7 @@ ENTRY(__arch_writesw) subs r2, r2, #8 bmi .no_outsw_8 + .outsw_8_lp: ldmia r1!, {r3, r4, r5, r6} mov ip, r3, lsl #16 @@ -75,6 +76,7 @@ ENTRY(__arch_writesw) subs r2, r2, #8 bpl .outsw_8_lp + tst r2, #7 LOADREGS(eqfd, sp!, {r4, r5, r6, pc}) @@ -121,5 +123,3 @@ ENTRY(__arch_writesw) strne ip, [r0] LOADREGS(fd, sp!, {r4, r5, r6, pc}) - - diff --git a/arch/arm/lib/io-writesw-armv4.S b/arch/arm/lib/io-writesw-armv4.S index 0f96d1325..bac67a42e 100644 --- a/arch/arm/lib/io-writesw-armv4.S +++ b/arch/arm/lib/io-writesw-armv4.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/lib/io-readsb.S + * linux/arch/arm/lib/io-writesw-armv4.S * * Copyright (C) 1995-2000 Russell King * @@ -32,11 +32,11 @@ ENTRY(__arch_writesw) tst r1, #3 bne .outsw_align + stmfd sp!, {r4, r5, lr} + subs r2, r2, #8 bmi .no_outsw_8 - stmfd sp!, {r4, r5, lr} - .outsw_8_lp: ldmia r1!, {r3, r4, r5, ip} strh r3, [r0] @@ -54,26 +54,31 @@ ENTRY(__arch_writesw) subs r2, r2, #8 bpl .outsw_8_lp - ldmfd sp!, {r4, r5, lr} + tst r2, #7 - RETINSTR(moveq, pc, lr) + LOADREGS(eqfd, sp!, {r4, r5, pc}) .no_outsw_8: tst r2, #4 - ldmneia r1!, {r3, ip} - strneh r3, [r0] - movne r3, r3, lsr #16 - strneh r3, [r0] - strneh ip, [r0] - movne ip, ip, lsr #16 - strneh ip, [r0] - tst r2, #2 - ldrne r3, [r1], #4 - strneh r3, [r0] - movne r3, r3, lsr #16 - strneh r3, [r0] - tst r2, #1 + beq .no_outsw_4 + + ldmia r1!, {r3, ip} + strh r3, [r0] + mov r3, r3, lsr #16 + strh r3, [r0] + strh ip, [r0] + mov ip, ip, lsr #16 + strh ip, [r0] + +.no_outsw_4: tst r2, #2 + beq .no_outsw_2 + + ldr r3, [r1], #4 + strh r3, [r0] + mov r3, r3, lsr #16 + strh r3, [r0] + +.no_outsw_2: tst r2, #1 ldrneh r3, [r1] strneh r3, [r0] - RETINSTR(mov, pc, lr) - + LOADREGS(fd, sp!, {r4, r5, pc}) diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile index 441608295..3a61edf39 100644 --- a/arch/arm/mach-sa1100/Makefile +++ b/arch/arm/mach-sa1100/Makefile @@ -20,15 +20,4 @@ export-objs := hw.o leds.o obj-$(CONFIG_LEDS) += leds.o -# Files that are both resident and modular; remove from modular. - -obj-m := $(filter-out $(obj-y), $(obj-m)) - -# Translate to Rules.make lists. - -O_OBJS := $(filter-out $(export-objs), $(obj-y)) -OX_OBJS := $(filter $(export-objs), $(obj-y)) -M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) -MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) - include $(TOPDIR)/Rules.make diff --git a/arch/arm/mach-sa1100/hw.c b/arch/arm/mach-sa1100/hw.c index 1ccc61fe3..a28b28177 100644 --- a/arch/arm/mach-sa1100/hw.c +++ b/arch/arm/mach-sa1100/hw.c @@ -90,12 +90,12 @@ static int bitsy_egpio = EGPIO_BITSY_RS232_ON; void clr_bitsy_egpio(unsigned long x) { bitsy_egpio &= ~x; - *(volatile int *)0xdc000000 = bitsy_egpio; + BITSY_EGPIO = bitsy_egpio; } void set_bitsy_egpio(unsigned long x) { bitsy_egpio |= x; - *(volatile int *)0xdc000000 = bitsy_egpio; + BITSY_EGPIO = bitsy_egpio; } EXPORT_SYMBOL(clr_bitsy_egpio); EXPORT_SYMBOL(set_bitsy_egpio); @@ -119,8 +119,8 @@ static void __init sa1111_init(void){ /* First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111: * (SA-1110 Developer's Manual, section 9.1.2.1) */ - GAFR |= GPIO_GPIO27; - GPDR |= GPIO_GPIO27; + GAFR |= GPIO_32_768kHz; + GPDR |= GPIO_32_768kHz; TUCR = TUCR_3_6864MHz; /* Now, set up the PLL and RCLK in the SA-1111: */ @@ -133,32 +133,32 @@ static void __init sa1111_init(void){ * using the SKPCR. */ - { - /* - * SA1111 DMA bus master setup + /* If the system is going to use the SA-1111 DMA engines, set up + * the memory bus request/grant pins. Also configure the shared + * memory controller on the SA-1111 (SA-1111 Developer's Manual, + * section 3.2.3) and power up the DMA bus clock: */ - int cas; - - /* SA1111 side */ - switch ( (MDCNFG>>12) & 0x03 ) { - case 0x02: - cas = 0; break; - case 0x03: - cas = 1; break; - default: - cas = 1; break; - } - SMCR = 1 /* 1: memory is SDRAM */ - | ( 1 << 1 ) /* 1:MBGNT is enable */ - | ( ((MDCNFG >> 4) & 0x07) << 2 ) /* row address lines */ - | ( cas << 5 ); /* CAS latency */ - - /* SA1110 side */ - GPDR |= 1<<21; - GPDR &= ~(1<<22); - GAFR |= ( (1<<21) | (1<<22) ); - - TUCR |= (1<<10); + if(machine_is_assabet()){ + + GAFR |= (GPIO_MBGNT | GPIO_MBREQ); + GPDR |= GPIO_MBGNT; + GPDR &= ~GPIO_MBREQ; + TUCR |= TUCR_MR; + + /* Assabet is populated by default with two Samsung KM416S8030T-G8 + * 128Mb SDRAMs, which are organized as 12-bit (row addr) x 9-bit + * (column addr), according to the data sheet. Apparently, the + * bank selects factor into the row address, as Angel sets up the + * SA-1110 to use 14x9 addresses. The SDRAM datasheet specifies + * that when running at 100-125MHz, the CAS latency for -8 parts + * is 3 cycles, which is consistent with Angel. + */ + + SMCR = (SMCR_DTIM | SMCR_MBGE | + FInsrt(FExtr(MDCNFG, MDCNFG_SA1110_DRAC0), SMCR_DRAC) | + ((FExtr(MDCNFG, MDCNFG_SA1110_TDL0)==3) ? SMCR_CLAT : 0)); + + SKPCR |= SKPCR_DCLKEN; } } diff --git a/arch/arm/mach-shark/Makefile b/arch/arm/mach-shark/Makefile index 414d0c2e1..c6a4d4023 100644 --- a/arch/arm/mach-shark/Makefile +++ b/arch/arm/mach-shark/Makefile @@ -5,6 +5,8 @@ # removes any old dependencies. DON'T put your own dependencies here # unless it's something special (ie not a .c file). +USE_STANDARD_AS_RULE := true + O_TARGET := shark.o # Object file lists. @@ -18,15 +20,4 @@ export-objs := #obj-$(CONFIG_LEDS) += leds.o -# Files that are both resident and modular; remove from modular. - -obj-m := $(filter-out $(obj-y), $(obj-m)) - -# Translate to Rules.make lists. - -O_OBJS := $(filter-out $(export-objs), $(obj-y)) -OX_OBJS := $(filter $(export-objs), $(obj-y)) -M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) -MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) - include $(TOPDIR)/Rules.make diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile index fe3cf5eac..5cc3ff520 100644 --- a/arch/arm/mm/Makefile +++ b/arch/arm/mm/Makefile @@ -9,12 +9,11 @@ USE_STANDARD_AS_RULE := true -EXTRA_AFLAGS := -traditional O_TARGET := mm.o # Object file lists. -obj-y := extable.o fault-$(PROCESSOR).o init.o \ +obj-y := extable.o fault-common.o fault-$(PROCESSOR).o init.o \ mm-$(PROCESSOR).o small_page.o obj-m := obj-n := @@ -22,10 +21,10 @@ obj- := export-objs := proc-syms.o p-$(CONFIG_CPU_26) += proc-arm2,3.o -p-$(CONFIG_CPU_ARM6) += proc-arm6,7.o -p-$(CONFIG_CPU_ARM7) += proc-arm6,7.o -p-$(CONFIG_CPU_ARM720) += proc-arm720.o -p-$(CONFIG_CPU_ARM920) += proc-arm920.o +p-$(CONFIG_CPU_ARM610) += proc-arm6,7.o +p-$(CONFIG_CPU_ARM710) += proc-arm6,7.o +p-$(CONFIG_CPU_ARM720T) += proc-arm720.o +p-$(CONFIG_CPU_ARM920T) += proc-arm920.o p-$(CONFIG_CPU_ARM10) += proc-arm10.o p-$(CONFIG_CPU_SA110) += proc-sa110.o p-$(CONFIG_CPU_SA1100) += proc-sa110.o @@ -37,29 +36,16 @@ endif # Integrator follows "new style" # Soon, others will do too, and we can get rid of this -MMMACH := mm-$(MACHINE).o +MMMACH := mm-$(MACHINE).c ifeq ($(MMMACH),$(wildcard $(MMMACH))) -obj-$(CONFIG_CPU_32) += $(MMMACH) +obj-$(CONFIG_CPU_32) += $(MMMACH:.c=.o) endif obj-y += $(sort $(p-y)) -# Files that are both resident and modular; remove from modular. - -obj-m := $(filter-out $(obj-y), $(obj-m)) - -# Translate to Rules.make lists. - -O_OBJS := $(filter-out $(export-objs), $(obj-y)) -OX_OBJS := $(filter $(export-objs), $(obj-y)) -M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) -MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) - include $(TOPDIR)/Rules.make # Special dependencies -fault-armv.o: fault-common.c -fault-armo.o: fault-common.c proc-arm2,3.o: ../lib/constants.h proc-arm6,7.o: ../lib/constants.h proc-arm720.o: ../lib/constants.h diff --git a/arch/arm/mm/fault-armo.c b/arch/arm/mm/fault-armo.c index 85b2dc2f4..a37c625f4 100644 --- a/arch/arm/mm/fault-armo.c +++ b/arch/arm/mm/fault-armo.c @@ -23,14 +23,20 @@ #include <asm/uaccess.h> #include <asm/pgtable.h> -#define FAULT_CODE_FORCECOW 0x80 +#define FAULT_CODE_LDRSTRPOST 0x80 +#define FAULT_CODE_LDRSTRPRE 0x40 +#define FAULT_CODE_LDRSTRREG 0x20 +#define FAULT_CODE_LDMSTM 0x10 +#define FAULT_CODE_LDCSTC 0x08 #define FAULT_CODE_PREFETCH 0x04 #define FAULT_CODE_WRITE 0x02 +#define FAULT_CODE_FORCECOW 0x01 #define DO_COW(m) ((m) & (FAULT_CODE_WRITE|FAULT_CODE_FORCECOW)) #define READ_FAULT(m) (!((m) & FAULT_CODE_WRITE)) -#include "fault-common.c" +extern int do_page_fault(unsigned long addr, int mode, struct pt_regs *regs); +extern void show_pte(struct mm_struct *mm, unsigned long addr); /* * Handle a data abort. Note that we have to handle a range of addresses diff --git a/arch/arm/mm/fault-armv.c b/arch/arm/mm/fault-armv.c index f184b4e68..a6a45f091 100644 --- a/arch/arm/mm/fault-armv.c +++ b/arch/arm/mm/fault-armv.c @@ -27,27 +27,9 @@ #include <asm/pgtable.h> #include <asm/unaligned.h> -#define FAULT_CODE_READ 0x02 - -#define DO_COW(m) (!((m) & FAULT_CODE_READ)) -#define READ_FAULT(m) ((m) & FAULT_CODE_READ) - extern void die_if_kernel(const char *str, struct pt_regs *regs, int err); - -#include "fault-common.c" - -#ifdef DEBUG -static int sp_valid(unsigned long *sp) -{ - unsigned long addr = (unsigned long) sp; - - if (addr >= 0xb0000000 && addr < 0xd0000000) - return 1; - if (addr >= 0x03ff0000 && addr < 0x04000000) - return 1; - return 0; -} -#endif +extern void show_pte(struct mm_struct *mm, unsigned long addr); +extern int do_page_fault(unsigned long addr, int mode, struct pt_regs *regs); #ifdef CONFIG_ALIGNMENT_TRAP /* @@ -178,6 +160,15 @@ do_alignment(unsigned long addr, int error_code, struct pt_regs *regs) eaddr -= offset.un; } + /* + * This is a "hint" - we already have eaddr worked out by the + * processor for us. + */ + if (addr != eaddr) + printk(KERN_ERR "LDRHSTRH: PC = %08lx, instr = %08x, " + "addr = %08lx, eaddr = %08lx\n", + instruction_pointer(regs), instr, addr, eaddr); + if (LDST_L_BIT(instr)) regs->uregs[rd] = get_unaligned((unsigned short *)eaddr); else @@ -253,8 +244,15 @@ do_alignment(unsigned long addr, int error_code, struct pt_regs *regs) } } -if (addr != eaddr) -printk("PC = %08lx, instr = %08x, addr = %08lx, eaddr = %08lx\n", instruction_pointer(regs), instr, addr, eaddr); + /* + * This is a "hint" - we already have eaddr worked out by the + * processor for us. + */ + if (addr != eaddr) + printk(KERN_ERR "LDRSTR: PC = %08lx, instr = %08x, " + "addr = %08lx, eaddr = %08lx\n", + instruction_pointer(regs), instr, addr, eaddr); + if (LDST_L_BIT(instr)) { regs->uregs[rd] = get_unaligned((unsigned long *)eaddr); if (rd == 15) @@ -284,6 +282,15 @@ printk("PC = %08lx, instr = %08x, addr = %08lx, eaddr = %08lx\n", instruction_po if (!LDST_U_BIT(instr)) eaddr -= nr_regs; + /* + * This is a "hint" - we already have eaddr worked out by the + * processor for us. + */ + if (addr != eaddr) + printk(KERN_ERR "LDMSTM: PC = %08lx, instr = %08x, " + "addr = %08lx, eaddr = %08lx\n", + instruction_pointer(regs), instr, addr, eaddr); + if ((LDST_U_BIT(instr) == 0 && LDST_P_BIT(instr) == 0) || (LDST_U_BIT(instr) && LDST_P_BIT(instr))) eaddr += 4; @@ -322,39 +329,39 @@ printk("PC = %08lx, instr = %08x, addr = %08lx, eaddr = %08lx\n", instruction_po #endif -#ifdef CONFIG_DEBUG_USER - +/* + * Some section permission faults need to be handled gracefully, for + * instance, when they happen due to a __{get,put}_user during an oops). + * In this case, we should return an error to the __{get,put}_user caller + * instead of causing another oops. We should also fixup this fault as + * the user could pass a pointer to a section to the kernel. + */ static int do_sect_fault(unsigned long addr, int error_code, struct pt_regs *regs) { + unsigned long fixup; + if (user_mode(regs)) { +#ifdef CONFIG_DEBUG_USER printk("%s: permission fault on section, " "address=0x%08lx, code %d\n", current->comm, addr, error_code); +#endif + goto fail; + } + + fixup = search_exception_table(instruction_pointer(regs)); + if (fixup != 0) { #ifdef DEBUG - { - unsigned int i, j; - unsigned long *sp; - - sp = (unsigned long *) (regs->ARM_sp - 128); - for (j = 0; j < 20 && sp_valid(sp); j++) { - printk("%p: ", sp); - for (i = 0; i < 8 && sp_valid(sp); i += 1, sp++) - printk("%08lx ", *sp); - printk("\n"); - } - show_regs(regs); - c_backtrace(regs->ARM_fp, regs->ARM_cpsr); - } + printk(KERN_DEBUG "%s: Exception at [<%lx>] addr=%lx (fixup: %lx)\n", + tsk->comm, regs->ARM_pc, addr, fixup); #endif + regs->ARM_pc = fixup; + return 0; } +fail: return 1; /* not fixed up */ } -#else - -#define do_sect_fault NULL - -#endif static const struct fsr_info { int (*fn)(unsigned long addr, int error_code, struct pt_regs *regs); @@ -382,18 +389,19 @@ static const struct fsr_info { /* * Currently dropped down to debug level */ -#define BUG_PROC_MSG \ - KERN_DEBUG "Weird data abort (%08X).\n" \ - KERN_DEBUG "Please see http://www.arm.linux.org.uk/state.html for " \ - "more information\n" - asmlinkage void do_DataAbort(unsigned long addr, int error_code, struct pt_regs *regs, int fsr) { const struct fsr_info *inf = fsr_info + (fsr & 15); +#if defined(CONFIG_CPU_SA110) || defined(CONFIG_CPU_SA1100) if (addr == regs->ARM_pc) - goto weirdness; + goto sa1_weirdness; +#endif +#if defined(CONFIG_CPU_ARM720T) && defined(CONFIG_ALIGNMENT_TRAP) + if (addr & 3 && (fsr & 13) != 1) + goto arm720_weirdness; +#endif if (!inf->fn) goto bad; @@ -409,26 +417,51 @@ bad: die_if_kernel("Oops", regs, 0); return; -weirdness: +#if defined(CONFIG_CPU_SA110) || defined(CONFIG_CPU_SA1100) +sa1_weirdness: if (user_mode(regs)) { static int first = 1; if (first) - /* - * I want statistical information on this problem, - * but we don't want to hastle the users too much. - */ - printk(BUG_PROC_MSG, fsr); + printk(KERN_DEBUG "Weird data abort detected\n"); first = 0; return; } if (!inf->fn || inf->fn(addr, error_code, regs)) goto bad; + return; +#endif +#if defined(CONFIG_CPU_ARM720T) && defined(CONFIG_ALIGNMENT_TRAP) +arm720_weirdness: + if (!user_mode(regs)) { + unsigned long instr; + + instr = *(unsigned long *)instruction_pointer(regs); + + if ((instr & 0x04400000) != 0x04400000) { + static int first = 1; + if (first) + printk("Mis-reported alignment fault at " + "0x%08lx, fsr 0x%02x, code 0x%02x, " + "PC = 0x%08lx, instr = 0x%08lx\n", + addr, fsr, error_code, regs->ARM_pc, + instr); + first = 0; + cpu_tlb_invalidate_all(); + cpu_cache_clean_invalidate_all(); + return; + } + } + + if (!inf->fn || inf->fn(addr, error_code, regs)) + goto bad; + return; +#endif } asmlinkage int do_PrefetchAbort(unsigned long addr, struct pt_regs *regs) { - do_page_fault(addr, FAULT_CODE_READ, regs); + do_page_fault(addr, 0, regs); return 1; } diff --git a/arch/arm/mm/fault-common.c b/arch/arm/mm/fault-common.c index 1cbdd7790..f79b618a2 100644 --- a/arch/arm/mm/fault-common.c +++ b/arch/arm/mm/fault-common.c @@ -9,8 +9,41 @@ * published by the Free Software Foundation. */ #include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/proc_fs.h> +#include <linux/init.h> + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/unaligned.h> + +#ifdef CONFIG_CPU_26 +#define FAULT_CODE_WRITE 0x02 +#define FAULT_CODE_FORCECOW 0x01 +#define DO_COW(m) ((m) & (FAULT_CODE_WRITE|FAULT_CODE_FORCECOW)) +#define READ_FAULT(m) (!((m) & FAULT_CODE_WRITE)) +#else +/* + * On 32-bit processors, we define "mode" to be zero when reading, + * non-zero when writing. This now ties up nicely with the polarity + * of the 26-bit machines, and also means that we avoid the horrible + * gcc code for "int val = !other_val;". + */ +#define DO_COW(m) (m) +#define READ_FAULT(m) (!(m)) +#endif -extern void die(const char *msg, struct pt_regs *regs, int err); +NORET_TYPE void die(const char *msg, struct pt_regs *regs, int err) ATTRIB_NORET; /* * This is useful to dump out the page tables associated with @@ -60,7 +93,9 @@ void show_pte(struct mm_struct *mm, unsigned long addr) printk("\n"); } -static int __do_page_fault(struct mm_struct *mm, unsigned long addr, int mode, struct task_struct *tsk) +static int +__do_page_fault(struct mm_struct *mm, unsigned long addr, int mode, + struct task_struct *tsk) { struct vm_area_struct *vma; int fault, mask; @@ -159,7 +194,7 @@ bad_area: return -2; } -static int do_page_fault(unsigned long addr, int mode, struct pt_regs *regs) +int do_page_fault(unsigned long addr, int mode, struct pt_regs *regs) { struct task_struct *tsk; struct mm_struct *mm; @@ -278,8 +313,10 @@ no_context: * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. */ - printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n", - (addr < PAGE_SIZE) ? "NULL pointer dereference" : "paging request", addr); + printk(KERN_ALERT + "Unable to handle kernel %s at virtual address %08lx\n", + (addr < PAGE_SIZE) ? "NULL pointer dereference" : + "paging request", addr); show_pte(mm, addr); die("Oops", regs, mode); diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c index bfa085a52..6ceec5a22 100644 --- a/arch/arm/mm/init.c +++ b/arch/arm/mm/init.c @@ -49,7 +49,7 @@ #define TABLE_SIZE ((TABLE_OFFSET + PTRS_PER_PTE) * sizeof(void *)) static unsigned long totalram_pages; -pgd_t swapper_pg_dir[PTRS_PER_PGD]; +extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; extern char _stext, _text, _etext, _end, __init_begin, __init_end; /* @@ -418,7 +418,7 @@ static __init void reserve_node_zero(unsigned int bootmap_pfn, unsigned int boot if (machine_is_archimedes() || machine_is_a5k()) reserve_bootmem_node(pgdat, 0x02000000, 0x00080000); if (machine_is_p720t()) - reserve_bootmem_node(pgdat, 0xc0000000, 0x00014000); + reserve_bootmem_node(pgdat, PAGE_OFFSET, 0x00014000); } /* @@ -450,8 +450,28 @@ void __init bootmem_init(struct meminfo *mi) initrd_node = check_initrd(mi); map_pg = bootmap_pfn; - - for (node = 0; node < numnodes; node++, np++) { + + /* + * Initialise the bootmem nodes. + * + * What we really want to do is: + * + * unmap_all_regions_except_kernel(); + * for_each_node_in_reverse_order(node) { + * map_node(node); + * allocate_bootmem_map(node); + * init_bootmem_node(node); + * free_bootmem_node(node); + * } + * + * but this is a 2.5-type change. For now, we just set + * the nodes up in reverse order. + * + * (we could also do with rolling bootmem_init and paging_init + * into one generic "memory_init" type function). + */ + np += numnodes - 1; + for (node = numnodes - 1; node >= 0; node--, np--) { /* * If there are no pages in this node, ignore it. * Note that node 0 must always have some pages. diff --git a/arch/arm/mm/mm-armv.c b/arch/arm/mm/mm-armv.c index d96fcbccb..a2fc69ea4 100644 --- a/arch/arm/mm/mm-armv.c +++ b/arch/arm/mm/mm-armv.c @@ -275,7 +275,7 @@ static void __init create_mapping(struct map_desc *md) off = md->physical - virt; length = md->length; - while ((virt & 1048575 || (virt + off) & 1048575) && length >= PAGE_SIZE) { + while ((virt & 0xfffff || (virt + off) & 0xfffff) && length >= PAGE_SIZE) { alloc_init_page(virt, virt + off, md->domain, prot_pte); virt += PAGE_SIZE; diff --git a/arch/arm/mm/proc-arm6,7.S b/arch/arm/mm/proc-arm6,7.S index 61e135e19..d26a98f75 100644 --- a/arch/arm/mm/proc-arm6,7.S +++ b/arch/arm/mm/proc-arm6,7.S @@ -7,10 +7,8 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * MMU functions for ARM6 - * * These are the low level assembler for performing cache and TLB - * functions on the ARM6 & ARM7. + * functions on the ARM610 & ARM710. */ #include <linux/linkage.h> #include <asm/assembler.h> @@ -105,9 +103,9 @@ ENTRY(cpu_arm7_tlb_invalidate_page) ENTRY(cpu_arm6_data_abort) ldr r4, [r0] @ read instruction causing problem - mov r1, r4, lsr #19 @ r1 b1 = L + tst r4, r4, lsr #21 @ C = bit 20 + sbc r1, r1, r1 @ r1 = C - 1 and r2, r4, #14 << 24 - and r1, r1, #2 @ check read/write bit teq r2, #8 << 24 @ was it ldm/stm bne Ldata_simple @@ -138,9 +136,9 @@ Ldata_simple: mrc p15, 0, r0, c6, c0, 0 @ get FAR ENTRY(cpu_arm7_data_abort) ldr r4, [r0] @ read instruction causing problem - mov r1, r4, lsr #19 @ r1 b1 = L + tst r4, r4, lsr #21 @ C = bit 20 + sbc r1, r1, r1 @ r1 = C - 1 and r2, r4, #15 << 24 - and r1, r1, #2 @ check read/write bit add pc, pc, r2, lsr #22 @ Now branch to the relevent processing routine movs pc, lr diff --git a/arch/arm/mm/proc-arm720.S b/arch/arm/mm/proc-arm720.S index e6fc86bd1..77b689bdf 100644 --- a/arch/arm/mm/proc-arm720.S +++ b/arch/arm/mm/proc-arm720.S @@ -144,9 +144,9 @@ Ldata_simple: mrc p15, 0, r0, c6, c0, 0 @ get FAR ENTRY(cpu_arm720_data_abort) ldr r4, [r0] @ read instruction causing problem - mov r1, r4, lsr #19 @ r1 b1 = L + tst r4, r4, lsr #21 @ C = bit 20 + sbc r1, r1, r1 @ r1 = C - 1 and r2, r4, #15 << 24 - and r1, r1, #2 @ check read/write bit add pc, pc, r2, lsr #22 @ Now branch to the relevent processing routine movs pc, lr @@ -283,23 +283,11 @@ ENTRY(cpu_arm720_proc_fin) ldmfd sp!, {pc} /* - * Function: arm720_proc_do_idle (void) - * - * Params : r0 = call type: - * 0 = slow idle - * 1 = fast idle - * 2 = switch to slow processor clock - * 3 = switch to fast processor clock - * + * Function: arm720_proc_do_idle(void) + * Params : r0 = unused * Purpose : put the processer in proper idle mode */ ENTRY(cpu_arm720_do_idle) -#if 0 /* FIXME: is this part of the processor? */ - ldr r2, =IO_BASE @ Virt addr of IO - add r2, r2, #0x00050000 @ Start of PMU regs - mov r1, #0x01 @ Idle mode - str r1, [r2, #4] -#endif mov pc, lr /* diff --git a/arch/arm/mm/proc-arm920.S b/arch/arm/mm/proc-arm920.S index a4306f08a..6f1f11601 100644 --- a/arch/arm/mm/proc-arm920.S +++ b/arch/arm/mm/proc-arm920.S @@ -68,9 +68,9 @@ ENTRY(cpu_arm920_data_abort) ldr r1, [r0] @ read aborted instruction mrc p15, 0, r0, c6, c0, 0 @ get FAR - mov r1, r1, lsr #19 @ b1 = L + tst r1, r1, lsr #21 @ C = bit 20 mrc p15, 0, r3, c5, c0, 0 @ get FSR - and r1, r1, #2 + sbc r1, r1, r1 @ r1 = C - 1 and r3, r3, #255 mov pc, lr @@ -330,7 +330,7 @@ ENTRY(cpu_arm920_icache_invalidate_range) mov r0, #0 mcr p15, 0, r0, c7, c10, 4 @ drain WB ENTRY(cpu_arm920_icache_invalidate_page) - /* why no invalidate I cache --rmk */ + mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache mov pc, lr diff --git a/arch/arm/mm/proc-sa110.S b/arch/arm/mm/proc-sa110.S index 031ba648a..45b0a31fe 100644 --- a/arch/arm/mm/proc-sa110.S +++ b/arch/arm/mm/proc-sa110.S @@ -83,9 +83,9 @@ ENTRY(cpu_sa110_data_abort) ENTRY(cpu_sa1100_data_abort) ldr r1, [r0] @ read aborted instruction mrc p15, 0, r0, c6, c0, 0 @ get FAR - mov r1, r1, lsr #19 @ b1 = L + tst r1, r1, lsr #21 @ C = bit 20 mrc p15, 0, r3, c5, c0, 0 @ get FSR - and r1, r1, #2 + sbc r1, r1, r1 @ r1 = C - 1 and r3, r3, #255 mov pc, lr diff --git a/arch/arm/nwfpe/Makefile b/arch/arm/nwfpe/Makefile index a7f69d405..16c7c0aed 100644 --- a/arch/arm/nwfpe/Makefile +++ b/arch/arm/nwfpe/Makefile @@ -6,27 +6,27 @@ USE_STANDARD_AS_RULE := true -NWFPE_OBJS := fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o \ - fpmodule.o fpopcode.o softfloat.o \ - single_cpdo.o double_cpdo.o extended_cpdo.o +O_TARGET := math-emu.o -ifeq ($(CONFIG_CPU_26),y) -NWFPE_OBJS += entry26.o -else -NWFPE_OBJS += entry.o -endif +obj-y := +obj-m := +obj-n := + +list-multi := nwfpe.o -ifeq ($(CONFIG_NWFPE),y) -O_TARGET := math-emu.o -O_OBJS = $(NWFPE_OBJS) +obj-$(CONFIG_NWFPE) += nwfpe.o + +nwfpe-objs := fpa11.o fpa11_cpdo.o fpa11_cpdt.o fpa11_cprt.o \ + fpmodule.o fpopcode.o softfloat.o \ + single_cpdo.o double_cpdo.o extended_cpdo.o + +ifeq ($(CONFIG_CPU_26),y) +nwfpe-objs += entry26.o else - ifeq ($(CONFIG_NWFPE),m) - M_OBJS = nwfpe.o - MI_OBJS = $(NWFPE_OBJS) - endif +nwfpe-objs += entry.o endif include $(TOPDIR)/Rules.make -nwfpe.o: $(MI_OBJS) $(MIX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ $(MI_OBJS) $(MIX_OBJS) +nwfpe.o: $(nwfpe-objs) + $(LD) -r -o $@ $(nwfpe-objs) diff --git a/arch/arm/vmlinux-armo.lds.in b/arch/arm/vmlinux-armo.lds.in index 0c0e720aa..f42deb72c 100644 --- a/arch/arm/vmlinux-armo.lds.in +++ b/arch/arm/vmlinux-armo.lds.in @@ -47,6 +47,8 @@ SECTIONS *(.gnu.warning) *(.text.lock) /* out-of-line lock text */ *(.rodata) + *(.glue_7) + *(.glue_7t) *(.kstrtab) . = ALIGN(16); /* Exception table */ __start___ex_table = .; diff --git a/arch/arm/vmlinux-armv.lds.in b/arch/arm/vmlinux-armv.lds.in index b0398852d..10661f414 100644 --- a/arch/arm/vmlinux-armv.lds.in +++ b/arch/arm/vmlinux-armv.lds.in @@ -42,6 +42,8 @@ SECTIONS *(.gnu.warning) *(.text.lock) /* out-of-line lock text */ *(.rodata) + *(.glue_7) + *(.glue_7t) *(.kstrtab) . = ALIGN(16); __start___ex_table = .; /* Exception table */ diff --git a/arch/cris/Makefile b/arch/cris/Makefile new file mode 100644 index 000000000..b63a44711 --- /dev/null +++ b/arch/cris/Makefile @@ -0,0 +1,96 @@ +# $Id: Makefile,v 1.11 2000/11/27 17:58:30 bjornw Exp $ +# cris/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# +# 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. + +LD_SCRIPT=$(TOPDIR)/arch/cris/cris.ld + +# A bug in ld prevents us from having a (constant-value) symbol in a +# "ORIGIN =" or "LENGTH =" expression. We fix that by generating a +# linker file with the symbolic part of those expressions evaluated. +# Unfortunately, there is trouble making vmlinux depend on anything we +# generate here, so we *always* regenerate the final linker script and +# replace the LD macro to get what we want. Thankfully(?) vmlinux is +# always rebuilt (due to calling make recursively and not knowing if +# anything was rebuilt). +# The shell script to build in some kind of dependency is really not +# necessary for reasons of speed. It's there because always +# regenerating stuff (even for incremental linking of subsystems!) is +# even more nauseating. +LD = if [ ! -e $(LD_SCRIPT).tmp -o $(LD_SCRIPT) -nt $(LD_SCRIPT).tmp ]; then \ + sed -e s/@ETRAX_DRAM_BASE@/0x$(ETRAX_DRAM_BASE)/ \ + -e s/@ETRAX_DRAM_SIZE_M@/$(ETRAX_DRAM_SIZE)/ \ + < $(LD_SCRIPT) > $(LD_SCRIPT).tmp; \ + else true; \ + fi && $(CROSS_COMPILE)ld -mcriself + +LINKFLAGS =-qmagic -mcriself -T $(LD_SCRIPT).tmp + +# objcopy is used to make binary images from the resulting linked file + +OBJCOPY := $(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S + +# normally, gcc on a linux box adds __linux__ but we do it "manually" +# gcc-cris defaults to a.out, we need ELF, so -melf + +CFLAGS := $(CFLAGS) -march=v10 -fno-strict-aliasing -pipe -D__linux__ + +ifdef CONFIG_KGDB +CFLAGS := $(subst -fomit-frame-pointer,,$(CFLAGS)) -g +CFLAGS += -fno-omit-frame-pointer +endif + +HEAD := arch/cris/kernel/head.o + +SUBDIRS += arch/cris/kernel arch/cris/mm arch/cris/lib arch/cris/drivers +CORE_FILES += arch/cris/kernel/kernel.o arch/cris/mm/mm.o arch/cris/drivers/drivers.o +LIBGCC = $(shell $(CC) $(CFLAGS) -print-file-name=libgcc.a) +LIBS := $(TOPDIR)/arch/cris/lib/lib.a $(LIBS) $(TOPDIR)/arch/cris/lib/lib.a $(LIBGCC) + +arch/cris/kernel: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/cris/kernel + +arch/cris/mm: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/cris/mm + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +vmlinux.bin: vmlinux + $(OBJCOPY) vmlinux vmlinux.bin + +timage: vmlinux.bin + cat vmlinux.bin cramfs.img >timage + +simimage: timage + cp vmlinux.bin simvmlinux.bin + +# the following will remake timage without compiling the kernel +# it does of course require that all object files exist... + +cramfs: +## cramfs - Creates a cramfs image + mkcramfs -p 8192 root cramfs.img + cat vmlinux.bin cramfs.img >timage + +zImage: vmlinux +## zImage - Compressed kernel (gzip) + @$(MAKEBOOT) zImage + +compressed: zImage + +archclean: + @$(MAKEBOOT) clean + rm -f timage vmlinux.bin cramfs.img + rm -rf $(LD_SCRIPT).tmp + +archmrproper: + +archdep: + @$(MAKEBOOT) dep diff --git a/arch/cris/README.mm b/arch/cris/README.mm new file mode 100644 index 000000000..6de4e7077 --- /dev/null +++ b/arch/cris/README.mm @@ -0,0 +1,241 @@ +Memory management for CRIS/MMU +------------------------------ +HISTORY: + +$Log: README.mm,v $ +Revision 1.1 2000/07/10 16:25:21 bjornw +Initial revision + +Revision 1.4 2000/01/17 02:31:59 bjornw +Added discussion of paging and VM. + +Revision 1.3 1999/12/03 16:43:23 hp +Blurb about that the 3.5G-limitation is not a MMU limitation + +Revision 1.2 1999/12/03 16:04:21 hp +Picky comment about not mapping the first page + +Revision 1.1 1999/12/03 15:41:30 bjornw +First version of CRIS/MMU memory layout specification. + + + + + +------------------------------ + +See the ETRAX-NG HSDD for reference. + +We use the page-size of 8 kbytes, as opposed to the i386 page-size of 4 kbytes. + +The MMU can, apart from the normal mapping of pages, also do a top-level +segmentation of the kernel memory space. We use this feature to avoid having +to use page-tables to map the physical memory into the kernel's address +space. We also use it to keep the user-mode virtual mapping in the same +map during kernel-mode, so that the kernel easily can access the corresponding +user-mode process' data. + +As a comparision, the Linux/i386 2.0 puts the kernel and physical RAM at +address 0, overlapping with the user-mode virtual space, so that descriptor +registers are needed for each memory access to specify which MMU space to +map through. That changed in 2.2, putting the kernel/physical RAM at +0xc0000000, to co-exist with the user-mode mapping. We will do something +quite similar, but with the additional complexity of having to map the +internal chip I/O registers and the flash memory area (including SRAM +and peripherial chip-selets). + +The kernel-mode segmentation map: + + ------------------------ ------------------------ +FFFFFFFF| | => cached | | + | kernel seg_f | flash | | +F0000000|______________________| | | +EFFFFFFF| | => uncached | | + | kernel seg_e | flash | | +E0000000|______________________| | DRAM | +DFFFFFFF| | paged to any | Un-cached | + | kernel seg_d | =======> | | +D0000000|______________________| | | +CFFFFFFF| | | | + | kernel seg_c |==\ | | +C0000000|______________________| \ |______________________| +BFFFFFFF| | uncached | | + | kernel seg_b |=====\=========>| Registers | +B0000000|______________________| \c |______________________| +AFFFFFFF| | \a | | + | | \c | FLASH/SRAM/Peripheral| + | | \h |______________________| + | | \e | | + | | \d | | + | kernel seg_0 - seg_a | \==>| DRAM | + | | | Cached | + | | paged to any | | + | | =======> |______________________| + | | | | + | | | Illegal | + | | |______________________| + | | | | + | | | FLASH/SRAM/Peripheral| +00000000|______________________| |______________________| + +In user-mode it looks the same except that only the space 0-AFFFFFFF is +available. Therefore, in this model, the virtual address space per process +is limited to 0xb0000000 bytes (minus 8192 bytes, since the first page, +0..8191, is never mapped, in order to trap NULL references). + +It also means that the total physical RAM that can be mapped is 256 MB +(kseg_c above). More RAM can be mapped by choosing a different segmentation +and shrinking the user-mode memory space. + +The MMU can map all 4 GB in user mode, but doing that would mean that a +few extra instructions would be needed for each access to user mode +memory. + +The kernel needs access to both cached and uncached flash. Uncached is +necessary because of the special write/erase sequences. Also, the +peripherial chip-selects are decoded from that region. + +The kernel also needs its own virtual memory space. That is kseg_d. It +is used by the vmalloc() kernel function to allocate virtual contiguous +chunks of memory not possible using the normal kmalloc physical RAM +allocator. + +The setting of the actual MMU control registers to use this layout would +be something like this: + +R_MMU_KSEG = ( ( seg_f, seg ) | // Flash cached + ( seg_e, seg ) | // Flash uncached + ( seg_d, page ) | // kernel vmalloc area + ( seg_c, seg ) | // kernel linear segment + ( seg_b, seg ) | // kernel linear segment + ( seg_a, page ) | + ( seg_9, page ) | + ( seg_8, page ) | + ( seg_7, page ) | + ( seg_6, page ) | + ( seg_5, page ) | + ( seg_4, page ) | + ( seg_3, page ) | + ( seg_2, page ) | + ( seg_1, page ) | + ( seg_0, page ) ); + +R_MMU_KBASE_HI = ( ( base_f, 0x0 ) | // flash/sram/periph cached + ( base_e, 0x8 ) | // flash/sram/periph uncached + ( base_d, 0x0 ) | // don't care + ( base_c, 0x4 ) | // physical RAM cached area + ( base_b, 0xb ) | // uncached on-chip registers + ( base_a, 0x0 ) | // don't care + ( base_9, 0x0 ) | // don't care + ( base_8, 0x0 ) ); // don't care + +R_MMU_KBASE_LO = ( ( base_7, 0x0 ) | // don't care + ( base_6, 0x0 ) | // don't care + ( base_5, 0x0 ) | // don't care + ( base_4, 0x0 ) | // don't care + ( base_3, 0x0 ) | // don't care + ( base_2, 0x0 ) | // don't care + ( base_1, 0x0 ) | // don't care + ( base_0, 0x0 ) ); // don't care + +NOTE: while setting up the MMU, we run in a non-mapped mode in the DRAM (0x40 +segment) and need to setup the seg_4 to a unity mapping, so that we don't get +a fault before we have had time to jump into the real kernel segment (0xc0). This +is done in head.S temporarily, but fixed by the kernel later in paging_init. + + +Paging - PTE's, PMD's and PGD's +------------------------------- + +[ References: asm/pgtable.h, asm/page.h, asm/mmu.h ] + +The paging mechanism uses virtual addresses to split a process memory-space into +pages, a page being the smallest unit that can be freely remapped in memory. On +Linux/CRIS, a page is 8192 bytes (for technical reasons not equal to 4096 as in +most other 32-bit architectures). It would be inefficient to let a virtual memory +mapping be controlled by a long table of page mappings, so it is broken down into +a 2-level structure with a Page Directory containing pointers to Page Tables which +each have maps of up to 2048 pages (8192 / sizeof(void *)). Linux can actually +handle 3-level structures as well, with a Page Middle Directory in between, but +in many cases, this is folded into a two-level structure by excluding the Middle +Directory. + +We'll take a look at how an address is translated while we discuss how it's handled +in the Linux kernel. + +The example address is 0xd004000c; in binary this is: + +31 23 15 7 0 +11010000 00000100 00000000 00001100 + +|______| |__________||____________| + PGD PTE page offset + +Given the top-level Page Directory, the offset in that directory is calculated +using the upper 8 bits: + +extern inline pgd_t * pgd_offset(struct mm_struct * mm, unsigned long address) +{ + return mm->pgd + (address >> PGDIR_SHIFT); +} + +PGDIR_SHIFT is the log2 of the amount of memory an entry in the PGD can map; in our +case it is 24, corresponding to 16 MB. This means that each entry in the PGD +corresponds to 16 MB of virtual memory. + +The pgd_t from our example will therefore be the 208'th (0xd0) entry in mm->pgd. + +Since the Middle Directory does not exist, it is a unity mapping: + +extern inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address) +{ + return (pmd_t *) dir; +} + +The Page Table provides the final lookup by using bits 13 to 23 as index: + +extern inline pte_t * pte_offset(pmd_t * dir, unsigned long address) +{ + return (pte_t *) pmd_page(*dir) + ((address >> PAGE_SHIFT) & + (PTRS_PER_PTE - 1)); +} + +PAGE_SHIFT is the log2 of the size of a page; 13 in our case. PTRS_PER_PTE is +the number of pointers that fit in a Page Table and is used to mask off the +PGD-part of the address. + +The so-far unused bits 0 to 12 are used to index inside a page linearily. + +The VM system +------------- + +The kernels own page-directory is the swapper_pg_dir, cleared in paging_init, +and contains the kernels virtual mappings (the kernel itself is not paged - it +is mapped linearily using kseg_c as described above). Architectures without +kernel segments like the i386, need to setup swapper_pg_dir directly in head.S +to map the kernel itself. swapper_pg_dir is pointed to by init_mm.pgd as the +init-task's PGD. + +To see what support functions are used to setup a page-table, let's look at the +kernel's internal paged memory system, vmalloc/vfree. + +void * vmalloc(unsigned long size) + +The vmalloc-system keeps a paged segment in kernel-space at 0xd0000000. What +happens first is that a virtual address chunk is allocated to the request using +get_vm_area(size). After that, physical RAM pages are allocated and put into +the kernel's page-table using alloc_area_pages(addr, size). + +static int alloc_area_pages(unsigned long address, unsigned long size) + +First the PGD entry is found using init_mm.pgd. This is passed to +alloc_area_pmd (remember the 3->2 folding). It uses pte_alloc_kernel to +check if the PGD entry points anywhere - if not, a page table page is +allocated and the PGD entry updated. Then the alloc_area_pte function is +used just like alloc_area_pmd to check which page table entry is desired, +and a physical page is allocated and the table entry updated. All of this +is repeated at the top-level until the entire address range specified has +been mapped. + + + diff --git a/arch/cris/boot/Makefile b/arch/cris/boot/Makefile new file mode 100644 index 000000000..77b59012e --- /dev/null +++ b/arch/cris/boot/Makefile @@ -0,0 +1,14 @@ +# +# arch/cris/boot/Makefile +# + +zImage: compressed/vmlinuz + +compressed/vmlinuz: $(TOPDIR)/vmlinux + @$(MAKE) -C compressed vmlinuz + +dep: + +clean: + rm -f zImage tools/build compressed/vmlinux.out + @$(MAKE) -C compressed clean diff --git a/arch/cris/boot/compressed/Makefile b/arch/cris/boot/compressed/Makefile new file mode 100644 index 000000000..fbf2bc36b --- /dev/null +++ b/arch/cris/boot/compressed/Makefile @@ -0,0 +1,37 @@ +# +# linux/arch/etrax100/boot/compressed/Makefile +# +# create a compressed vmlinux image from the original vmlinux files and romfs +# + +CC = gcc-cris -melf -I $(TOPDIR)/include +CFLAGS = -O2 +LD = ld-cris +OBJCOPY = objcopy-cris + +OBJECTS = head.o misc.o + +# files to compress +SYSTEM = $(TOPDIR)/vmlinux.bin + +all: vmlinuz + +vmlinuz: piggy.img $(OBJECTS) + $(LD) -mcriself -T decompress.ld -o decompress.o $(OBJECTS) + $(OBJCOPY) -O binary --remove-section=.bss decompress.o decompress.bin +# save it for mkprod in the topdir. + cp decompress.bin $(TOPDIR) + cat decompress.bin piggy.img $(TOPDIR)/cramfs.img > vmlinuz + rm -f piggy.img + +head.o: head.S + $(CC) -D__ASSEMBLY__ -traditional -c head.S -o head.o + +# gzip the kernel image + +piggy.img: $(SYSTEM) + cat $(SYSTEM) | gzip -f -9 > piggy.img + +clean: + rm -f piggy.img vmlinuz vmlinuz.o + diff --git a/arch/cris/boot/compressed/README b/arch/cris/boot/compressed/README new file mode 100644 index 000000000..83e1f1e51 --- /dev/null +++ b/arch/cris/boot/compressed/README @@ -0,0 +1,25 @@ +Creation of the self-extracting compressed kernel image (vmlinuz) +----------------------------------------------------------------- +$Id: README,v 1.1 2000/11/22 17:20:46 bjornw Exp $ + +This can be slightly confusing because it's a process with many steps. + +The kernel object built by the arch/etrax100/Makefile, vmlinux, is split +by that makefile into text and data binary files, vmlinux.text and +vmlinux.data. + +Those files together with a ROM filesystem can be catted together and +burned into a flash or executed directly at the DRAM origin. + +They can also be catted together and compressed with gzip, which is what +happens in this makefile. Together they make up piggy.img. + +The decompressor is built into the file decompress.o. It is turned into +the binary file decompress.bin, which is catted together with piggy.img +into the file vmlinuz. It can be executed in an arbitrary place in flash. + +Be careful - it assumes some things about free locations in DRAM. It +assumes the DRAM starts at 0x40000000 and that it is at least 8 MB, +so it puts its code at 0x40700000, and initial stack at 0x40800000. + +-Bjorn diff --git a/arch/cris/boot/compressed/decompress.ld b/arch/cris/boot/compressed/decompress.ld new file mode 100644 index 000000000..8cdef6a60 --- /dev/null +++ b/arch/cris/boot/compressed/decompress.ld @@ -0,0 +1,26 @@ +MEMORY + { + dram : ORIGIN = 0x40700000, + LENGTH = 0x00100000 + } + +SECTIONS +{ + .text : + { + _stext = . ; + *(.text) + *(.rodata) + _etext = . ; + } > dram + .data : + { + *(.data) + _edata = . ; + } > dram + .bss : + { + *(.bss) + _end = ALIGN( 0x10 ) ; + } > dram +} diff --git a/arch/cris/boot/compressed/head.S b/arch/cris/boot/compressed/head.S new file mode 100644 index 000000000..97243ee13 --- /dev/null +++ b/arch/cris/boot/compressed/head.S @@ -0,0 +1,100 @@ +/* + * arch/etrax100/boot/compressed/head.S + * + * Copyright (C) 1999 Axis Communications AB + * + * Code that sets up the DRAM registers, calls the + * decompressor to unpack the piggybacked kernel, and jumps. + * + */ + +#include <linux/config.h> +#define ASSEMBLER_MACROS_ONLY +#include <asm/sv_addr_ag.h> + + ;; Exported symbols + + .globl _input_data + + + .text + + nop + di + +#ifndef CONFIG_SVINTO_SIM + + ;; We need to setup the bus registers before we start using the DRAM + + move.d DEF_R_WAITSTATES, r0 + move.d r0, [R_WAITSTATES] + + move.d DEF_R_BUS_CONFIG, r0 + move.d r0, [R_BUS_CONFIG] + + move.d DEF_R_DRAM_CONFIG, r0 + move.d r0, [R_DRAM_CONFIG] + + move.d DEF_R_DRAM_TIMING, r0 + move.d r0, [R_DRAM_TIMING] + +#endif + + ;; Setup the stack to a suitably high address. + ;; We assume 8 MB is the minimum DRAM in an eLinux + ;; product and put the sp at the top for now. + + move.d 0x40800000, sp + + ;; Figure out where the compressed piggyback image is + ;; in the flash (since we wont try to copy it to DRAM + ;; before unpacking). It is at _edata, but in flash. + ;; Use (_edata - basse) as offset to the current PC. + +basse: move.d pc, r5 + and.d 0x7fffffff, r5 ; strip any non-cache bit + subq 2, r5 ; compensate for the move.d pc instr + move.d r5, r0 ; save for later - flash address of 'basse' + add.d _edata, r5 + sub.d basse, r5 ; r5 = flash address of '_edata' + + ;; Copy text+data to DRAM + + move.d basse, r1 ; destination + move.d _edata, r2 ; end destination +1: move.w [r0+], r3 + move.w r3, [r1+] + cmp.d r2, r1 + bcs 1b + nop + + move.d r5, [_input_data] ; for the decompressor + + ;; Clear the decompressors BSS (between _edata and _end) + + moveq 0, r0 + move.d _edata, r1 + move.d _end, r2 +1: move.w r0, [r1+] + cmp.d r2, r1 + bcs 1b + nop + + ;; Do the decompression and save compressed size in _inptr + + jsr _decompress_kernel + + ;; Put start address of cramfs in r9 so the kernel can use it + ;; when mounting from flash + + move.d [_input_data], r9 ; flash address of compressed kernel + add.d [_inptr], r9 ; size of compressed kernel + + ;; Enter the decompressed kernel + + jump 0x40004000 ; kernel is linked to this address + + .data + +_input_data: + .dword 0 ; used by the decompressor diff --git a/arch/cris/boot/compressed/misc.c b/arch/cris/boot/compressed/misc.c new file mode 100644 index 000000000..87ce3d8a0 --- /dev/null +++ b/arch/cris/boot/compressed/misc.c @@ -0,0 +1,260 @@ +/* + * misc.c + * + * $Id: misc.c,v 1.3 2001/01/17 15:54:18 jonashg Exp $ + * + * This is a collection of several routines from gzip-1.0.3 + * adapted for Linux. + * + * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994 + * puts by Nick Holloway 1993, better puts by Martin Mares 1995 + * adoptation for Linux/CRIS Axis Communications AB, 1999 + * + */ + +/* where the piggybacked kernel image expects itself to live. + * it is the same address we use when we network load an uncompressed + * image into DRAM, and it is the address the kernel is linked to live + * at by etrax100.ld. + */ + +#define KERNEL_LOAD_ADR 0x40004000 + +#include <linux/config.h> +#include <linux/types.h> +#include <asm/svinto.h> + +/* + * gzip declarations + */ + +#define OF(args) args +#define STATIC static + +void* memset(void* s, int c, size_t n); +void* memcpy(void* __dest, __const void* __src, + size_t __n); + +#define memzero(s, n) memset ((s), 0, (n)) + + +typedef unsigned char uch; +typedef unsigned short ush; +typedef unsigned long ulg; + +#define WSIZE 0x8000 /* Window size must be at least 32k, */ + /* and a power of two */ + +static uch *inbuf; /* input buffer */ +static uch window[WSIZE]; /* Sliding window buffer */ + +unsigned inptr = 0; /* index of next byte to be processed in inbuf + * After decompression it will contain the + * compressed size, and head.S will read it. + */ + +static unsigned outcnt = 0; /* bytes in output buffer */ + +/* gzip flag byte */ +#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ +#define CONTINUATION 0x02 /* bit 1 set: continuation of multi-part gzip file */ +#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ +#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ +#define COMMENT 0x10 /* bit 4 set: file comment present */ +#define ENCRYPTED 0x20 /* bit 5 set: file is encrypted */ +#define RESERVED 0xC0 /* bit 6,7: reserved */ + +#define get_byte() inbuf[inptr++] + +/* Diagnostic functions */ +#ifdef DEBUG +# define Assert(cond,msg) {if(!(cond)) error(msg);} +# define Trace(x) fprintf x +# define Tracev(x) {if (verbose) fprintf x ;} +# define Tracevv(x) {if (verbose>1) fprintf x ;} +# define Tracec(c,x) {if (verbose && (c)) fprintf x ;} +# define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;} +#else +# define Assert(cond,msg) +# define Trace(x) +# define Tracev(x) +# define Tracevv(x) +# define Tracec(c,x) +# define Tracecv(c,x) +#endif + +static int fill_inbuf(void); +static void flush_window(void); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +extern char *input_data; /* lives in head.S */ + +static long bytes_out = 0; +static uch *output_data; +static unsigned long output_ptr = 0; + +static void *malloc(int size); +static void free(void *where); +static void error(char *m); +static void gzip_mark(void **); +static void gzip_release(void **); + +static void puts(const char *); + +/* the "heap" is put directly after the BSS ends, at end */ + +extern int end; +static long free_mem_ptr = (long)&end; + +#include "../../../../lib/inflate.c" + +static void *malloc(int size) +{ + void *p; + + if (size <0) error("Malloc error\n"); + + free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */ + + p = (void *)free_mem_ptr; + free_mem_ptr += size; + + return p; +} + +static void free(void *where) +{ /* Don't care */ +} + +static void gzip_mark(void **ptr) +{ + *ptr = (void *) free_mem_ptr; +} + +static void gzip_release(void **ptr) +{ + free_mem_ptr = (long) *ptr; +} + +/* decompressor info and error messages to serial console */ + +static void +puts(const char *s) +{ +#ifndef CONFIG_DEBUG_PORT_NULL + while(*s) { +#ifdef CONFIG_DEBUG_PORT0 + while(!(*R_SERIAL0_STATUS & (1 << 5))) ; + *R_SERIAL0_TR_DATA = *s++; +#endif +#ifdef CONFIG_DEBUG_PORT1 + while(!(*R_SERIAL1_STATUS & (1 << 5))) ; + *R_SERIAL1_TR_DATA = *s++; +#endif +#ifdef CONFIG_DEBUG_PORT2 + while(!(*R_SERIAL2_STATUS & (1 << 5))) ; + *R_SERIAL2_TR_DATA = *s++; +#endif +#ifdef CONFIG_DEBUG_PORT3 + while(!(*R_SERIAL3_STATUS & (1 << 5))) ; + *R_SERIAL3_TR_DATA = *s++; +#endif + } +#endif +} + +void* +memset(void* s, int c, size_t n) +{ + int i; + char *ss = (char*)s; + + for (i=0;i<n;i++) ss[i] = c; +} + +void* +memcpy(void* __dest, __const void* __src, + size_t __n) +{ + int i; + char *d = (char *)__dest, *s = (char *)__src; + + for (i=0;i<__n;i++) d[i] = s[i]; +} + +/* =========================================================================== + * Write the output window window[0..outcnt-1] and update crc and bytes_out. + * (Used for the decompressed data only.) + */ + +static void +flush_window() +{ + ulg c = crc; /* temporary variable */ + unsigned n; + uch *in, *out, ch; + + in = window; + out = &output_data[output_ptr]; + for (n = 0; n < outcnt; n++) { + ch = *out++ = *in++; + c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8); + } + crc = c; + bytes_out += (ulg)outcnt; + output_ptr += (ulg)outcnt; + outcnt = 0; +} + +static void +error(char *x) +{ + puts("\n\n"); + puts(x); + puts("\n\n -- System halted\n"); + + while(1); /* Halt */ +} + +void +setup_normal_output_buffer() +{ + output_data = (char *)KERNEL_LOAD_ADR; +} + +void +decompress_kernel() +{ + /* input_data is set in head.S */ + inbuf = input_data; + +#ifdef CONFIG_DEBUG_PORT0 + *R_SERIAL0_XOFF = 0; + *R_SERIAL0_BAUD = 0x99; + *R_SERIAL0_TR_CTRL = 0x40; +#endif +#ifdef CONFIG_DEBUG_PORT1 + *R_SERIAL1_XOFF = 0; + *R_SERIAL1_BAUD = 0x99; + *R_SERIAL1_TR_CTRL = 0x40; +#endif +#ifdef CONFIG_DEBUG_PORT2 + *R_SERIAL2_XOFF = 0; + *R_SERIAL2_BAUD = 0x99; + *R_SERIAL2_TR_CTRL = 0x40; +#endif +#ifdef CONFIG_DEBUG_PORT3 + *R_SERIAL3_XOFF = 0; + *R_SERIAL3_BAUD = 0x99; + *R_SERIAL3_TR_CTRL = 0x40; +#endif + + setup_normal_output_buffer(); + + makecrc(); + puts("Uncompressing Linux...\n"); + gunzip(); + puts("Done. Now booting the kernel.\n"); +} diff --git a/arch/cris/config.in b/arch/cris/config.in new file mode 100644 index 000000000..3e234ec43 --- /dev/null +++ b/arch/cris/config.in @@ -0,0 +1,223 @@ +# +# For a description of the syntax of this configuration file, +# see the Configure script. +# +mainmenu_name "Linux/CRIS Kernel Configuration" + +define_bool CONFIG_UID16 y + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'General setup' + +bool 'Networking support' CONFIG_NET +bool 'System V IPC' CONFIG_SYSVIPC + +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate 'Kernel support for JAVA binaries' CONFIG_BINFMT_JAVA +fi + +bool 'Use kernel gdb debugger' CONFIG_KGDB + +bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG + +bool 'Use serial console (on the debug port)' CONFIG_USE_SERIAL_CONSOLE + +bool 'Use in-kernel ifconfig/route setup' CONFIG_KERNEL_IFCONFIG + +endmenu + +mainmenu_option next_comment +comment 'Hardware setup' + +choice 'Processor type' \ + "Etrax-100-LX CONFIG_ETRAX100LX \ + Etrax-100-LX-for-xsim-simulator CONFIG_SVINTO_SIM" Etrax-100-LX + +# For both LX version 1 and the current simulator we enable the low VM mapping +# Later when LX version 2 and above exist, this should be done with an if + +define_bool CONFIG_CRIS_LOW_MAP y + +hex 'DRAM base (hex)' ETRAX_DRAM_BASE 40000000 +int 'DRAM size (dec, in MB)' ETRAX_DRAM_SIZE 8 + +int 'Max possible flash size (dec, in MB)' CONFIG_ETRAX_FLASH_LENGTH 2 +int 'Buswidth of flash in bytes' CONFIG_ETRAX_FLASH_BUSWIDTH 2 + +choice 'Product LED port' \ + "Port-PA-LEDs CONFIG_ETRAX_PA_LEDS \ + Port-PB-LEDs CONFIG_ETRAX_PB_LEDS \ + Mem-0x90000000-LEDs CONFIG_ETRAX_90000000_LEDS \ + None CONFIG_ETRAX_NO_LEDS" Port-PA-LEDs + +if [ "$CONFIG_ETRAX_NO_LEDS" != "y" ]; then + int ' First green LED bit' CONFIG_ETRAX_LED1G 2 + int ' First red LED bit' CONFIG_ETRAX_LED1R 3 + int ' Second green LED bit' CONFIG_ETRAX_LED2G 4 + int ' Second red LED bit' CONFIG_ETRAX_LED2R 5 + int ' Third green LED bit' CONFIG_ETRAX_LED3R 2 + int ' Third red LED bit' CONFIG_ETRAX_LED3G 2 +fi + +choice 'Product debug-port' \ + "Serial-0 CONFIG_DEBUG_PORT0 \ + Serial-1 CONFIG_DEBUG_PORT1 \ + Serial-2 CONFIG_DEBUG_PORT2 \ + Serial-3 CONFIG_DEBUG_PORT3" Serial-0 + +hex 'R_WAITSTATES' DEF_R_WAITSTATES 95a6 +hex 'R_BUS_CONFIG' DEF_R_BUS_CONFIG 104 +hex 'R_DRAM_CONFIG' DEF_R_DRAM_CONFIG 1a200040 +hex 'R_DRAM_TIMING' DEF_R_DRAM_TIMING 5611 +hex 'R_PORT_PA_DIR' DEF_R_PORT_PA_DIR 1c +hex 'R_PORT_PA_DATA' DEF_R_PORT_PA_DATA 00 +hex 'R_PORT_PB_CONFIG' DEF_R_PORT_PB_CONFIG 00 +hex 'R_PORT_PB_DIR' DEF_R_PORT_PB_DIR 00 +hex 'R_PORT_PB_DATA' DEF_R_PORT_PB_DATA ff + +endmenu + +# only configure IP numbers if the kernel ifconfig/route setup is enabled + +if [ "$CONFIG_KERNEL_IFCONFIG" = "y" ]; then + mainmenu_option next_comment + comment 'IP address selection' + + comment 'All addresses are in hexadecimal form without 0x prefix' + + hex 'IP address' ELTEST_IPADR ab1005af + hex 'Network' ELTEST_NETWORK ab100000 + hex 'Netmask' ELTEST_NETMASK ffff0000 + hex 'Broadcast' ELTEST_BROADCAST ab10ffff + hex 'Gateway' ELTEST_GATEWAY ab100101 + hwaddr 'Ethernet address' ELTEST_ETHADR 00408ccd0000 + + endmenu +fi + +# bring in Etrax built-in drivers + +source arch/cris/drivers/Config.in + +# standard linux drivers + +source drivers/mtd/Config.in + +source drivers/parport/Config.in + +source drivers/pnp/Config.in + +source drivers/block/Config.in + +source drivers/md/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +source drivers/telephony/Config.in + +mainmenu_option next_comment +comment 'ATA/IDE/MFM/RLL support' + +tristate 'ATA/IDE/MFM/RLL support' CONFIG_IDE + +if [ "$CONFIG_IDE" != "n" ]; then + source drivers/ide/Config.in +else + define_bool CONFIG_BLK_DEV_IDE_MODES n + define_bool CONFIG_BLK_DEV_HD n +fi +endmenu + +mainmenu_option next_comment +comment 'SCSI support' + +tristate 'SCSI support' CONFIG_SCSI + +if [ "$CONFIG_SCSI" != "n" ]; then + source drivers/scsi/Config.in +fi +endmenu + +source drivers/ieee1394/Config.in + +source drivers/i2o/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' + + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + source drivers/net/Config.in + if [ "$CONFIG_ATM" = "y" ]; then + source drivers/atm/Config.in + fi + fi + endmenu +fi + +source net/ax25/Config.in + +source net/irda/Config.in + +mainmenu_option next_comment +comment 'ISDN subsystem' +if [ "$CONFIG_NET" != "n" ]; then + tristate 'ISDN support' CONFIG_ISDN + if [ "$CONFIG_ISDN" != "n" ]; then + source drivers/isdn/Config.in + fi +fi +endmenu + +mainmenu_option next_comment +comment 'Old CD-ROM drivers (not SCSI, not IDE)' + +bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI +if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/cdrom/Config.in +fi +endmenu + +# +# input before char - char/joystick depends on it. As does USB. +# +source drivers/input/Config.in +source drivers/char/Config.in + +#source drivers/misc/Config.in + +source drivers/media/Config.in + +source fs/Config.in + +source drivers/char/Config.in + +mainmenu_option next_comment +comment 'Sound' + +tristate 'Sound card support' CONFIG_SOUND +if [ "$CONFIG_SOUND" != "n" ]; then + source drivers/sound/Config.in +fi +endmenu + +source drivers/usb/Config.in + +mainmenu_option next_comment +comment 'Kernel hacking' + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +bool 'Kernel profiling support' CONFIG_PROFILE +if [ "$CONFIG_PROFILE" = "y" ]; then + int ' Profile shift count' CONFIG_PROFILE_SHIFT 2 +fi +endmenu diff --git a/arch/cris/cris.ld b/arch/cris/cris.ld new file mode 100644 index 000000000..5ba215959 --- /dev/null +++ b/arch/cris/cris.ld @@ -0,0 +1,81 @@ +/* ld script to make the Linux/CRIS kernel + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * For now, on Etrax-100 LX, the DRAM starts virtually at 0x6. Normally + * it should be at 0xc. + */ + +SECTIONS +{ + . = 0x60000000; /* DRAM starts virtually at 0x60000000 */ + _dram_start = .; + _ibr_start = .; + . = . + 0x4000; /* see head.S and pages reserved at the start */ + + _text = .; /* Text and read-only data */ + _text_start = .; /* lots of aliases */ + _stext = .; + __stext = .; + .text : { + *(.text) + *(.fixup) + *(.text.__*) + *(.rodata) + } + + . = ALIGN(4); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + _etext = . ; /* End of text section */ + __etext = .; + + . = ALIGN (4); + ___data_rom_start = . ; + ___data_start = . ; + __Sdata = . ; + .data : { /* Data */ + *(.data) + } + __edata = . ; /* End of data section */ + _edata = . ; + + . = ALIGN(8192); /* init_task and stack, must be aligned */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(8192); /* Init code and data */ + ___init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(16); + ___setup_start = .; + .setup.init : { *(.setup.init) } + ___setup_end = .; + ___initcall_start = .; + .initcall.init : { *(.initcall.init) } + ___initcall_end = .; + __vmlinux_end = .; /* last address of the physical file */ + . = ALIGN(8192); + ___init_end = .; + + __data_end = . ; /* Move to _edata ? */ + __bss_start = .; /* BSS */ + .bss : { + *(COMMON) + *(.bss) + } + + . = ALIGN (0x20); + _end = .; + __end = .; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.text.exit) + *(.data.exit) + *(.exitcall.exit) + } + + _dram_end = 0x60000000 + @ETRAX_DRAM_SIZE_M@*1024*1024; +} diff --git a/arch/cris/defconfig b/arch/cris/defconfig new file mode 100644 index 000000000..91a25ee23 --- /dev/null +++ b/arch/cris/defconfig @@ -0,0 +1,319 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_UID16=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# General setup +# +CONFIG_NET=y +CONFIG_SYSVIPC=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_JAVA is not set +# CONFIG_KGDB is not set +# CONFIG_ETRAX_WATCHDOG is not set +CONFIG_USE_SERIAL_CONSOLE=y +# CONFIG_KERNEL_IFCONFIG is not set + +# +# Hardware setup +# +CONFIG_ETRAX100LX=y +# CONFIG_SVINTO_SIM is not set +CONFIG_CRIS_LOW_MAP=y +ETRAX_DRAM_BASE=40000000 +ETRAX_DRAM_SIZE=8 +CONFIG_ETRAX_PA_LEDS=y +# CONFIG_ETRAX_PB_LEDS is not set +# CONFIG_ETRAX_90000000_LEDS is not set +# CONFIG_ETRAX_NO_LEDS is not set +CONFIG_ETRAX_LED1G=2 +CONFIG_ETRAX_LED1R=2 +CONFIG_ETRAX_LED2G=2 +CONFIG_ETRAX_LED2R=2 +CONFIG_DEBUG_PORT0=y +# CONFIG_DEBUG_PORT1 is not set +# CONFIG_DEBUG_PORT2 is not set +# CONFIG_DEBUG_PORT3 is not set +DEF_R_WAITSTATES=95a6 +DEF_R_BUS_CONFIG=104 +DEF_R_DRAM_CONFIG=1a200040 +DEF_R_DRAM_TIMING=5611 +DEF_R_PORT_PA_DIR=1d +DEF_R_PORT_PA_DATA=f0 +DEF_R_PORT_PB_CONFIG=00 +DEF_R_PORT_PB_DIR=1e +DEF_R_PORT_PB_DATA=f3 + +# +# Drivers for Etrax built-in interfaces +# +CONFIG_ETRAX_ETHERNET=y +CONFIG_NET_ETHERNET=y +CONFIG_ETRAX_SERIAL=y + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +# CONFIG_BLK_DEV_XD is not set +# CONFIG_PARIDE is not set +# CONFIG_BLK_CPQ_DA is not set +# CONFIG_BLK_CPQ_CISS_DA is not set +# CONFIG_BLK_DEV_DAC960 is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set + +# +# Networking options +# +# CONFIG_PACKET is not set +# CONFIG_NETLINK is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set + +# +# +# +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# SCSI support +# +# CONFIG_SCSI is not set + +# +# Network device support +# +CONFIG_NETDEVICES=y + +# +# ARCnet devices +# +# CONFIG_ARCNET is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_NET_SB1000 is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_LANCE is not set +# CONFIG_NET_VENDOR_SMC is not set +# CONFIG_NET_VENDOR_RACAL is not set +# CONFIG_AT1700 is not set +# CONFIG_DEPCA is not set +# CONFIG_NET_ISA is not set +# CONFIG_NET_PCI is not set +# CONFIG_NET_POCKET is not set + +# +# Ethernet (1000 Mbit) +# +# CONFIG_ACENIC is not set +# CONFIG_HAMACHI is not set +# CONFIG_YELLOWFIN is not set +# CONFIG_SK98LIN is not set +# CONFIG_FDDI is not set +# CONFIG_HIPPI is not set +# CONFIG_PLIP is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_TR is not set +# CONFIG_NET_FC is not set +# CONFIG_RCPCI is not set +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# CD-ROM drivers (not for SCSI or IDE/ATAPI drives) +# +# CONFIG_CD_NO_IDESCSI is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_CRAMFS=y +CONFIG_RAMFS=y +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_EXT2_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +# CONFIG_NFS_FS is not set +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +# CONFIG_SUNRPC is not set +# CONFIG_LOCKD is not set +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set +# CONFIG_NCPFS_NDS_DOMAINS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_NLS is not set + +# +# Character devices +# +# CONFIG_VT is not set +# CONFIG_SERIAL is not set +# CONFIG_SERIAL_EXTENDED is not set +# CONFIG_SERIAL_NONSTANDARD is not set +# CONFIG_UNIX98_PTYS is not set +# CONFIG_PRINTER is not set +# CONFIG_PPDEV is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_MOUSE is not set + +# +# Joysticks +# +# CONFIG_JOYSTICK is not set + +# +# Input core support is needed for joysticks +# +# CONFIG_QIC02_TAPE is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_INTEL_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Kernel hacking +# +# CONFIG_PROFILE is not set diff --git a/arch/cris/drivers/Config.in b/arch/cris/drivers/Config.in new file mode 100644 index 000000000..36055ae04 --- /dev/null +++ b/arch/cris/drivers/Config.in @@ -0,0 +1,49 @@ +mainmenu_option next_comment +comment 'Drivers for Etrax built-in interfaces' + +bool 'Ethernet support' CONFIG_ETRAX_ETHERNET y +if [ "$CONFIG_ETRAX_ETHERNET" = "y" ]; then +# this is just so that the user does not have to go into the +# normal ethernet driver section just to enable ethernetworking + define_bool CONFIG_NET_ETHERNET y +fi + +bool 'Serial-port support' CONFIG_ETRAX_SERIAL y + +bool 'ATA/IDE support' CONFIG_ETRAX_IDE n + +if [ "$CONFIG_ETRAX_IDE" = "y" ]; then +# here we should add the CONFIG_'s necessary to enable the basic +# general ide drivers so the common case does not need to go +# into that config submenu. enable disk and CD support. others +# need to go fiddle in the submenu.. + define_bool CONFIG_IDE y + + define_bool CONFIG_BLK_DEV_IDE y + define_bool CONFIG_BLK_DEV_IDEDISK y + define_bool CONFIG_BLK_DEV_IDECD y + + define_bool CONFIG_BLK_DEV_IDEDMA y + define_bool CONFIG_DMA_NONPCI y + + choice 'IDE reset pin' \ + "Port_PB_Bit_7 CONFIG_ETRAX_IDE_PB7_RESET\ + Port_G_Bit_27 CONFIG_ETRAX_IDE_G27_RESET\ + Port_CSE1_Bit_16 CONFIG_ETRAX_IDE_CSE1_16_RESET" Port_PB_Bit_7 +fi + +bool 'Axis flash-map support' CONFIG_ETRAX_AXISFLASHMAP n + +if [ "$CONFIG_ETRAX_AXISFLASHMAP" = "y" ]; then +# here we define the CONFIG_'s necessary to enable MTD support +# for the flash + define_bool CONFIG_MTD y + + define_bool CONFIG_MTD_CFI y + define_bool CONFIG_MTD_CFI_INTELEXT n + define_bool CONFIG_MTD_CFI_AMDSTD y + + define_bool CONFIG_MTD_CHAR y +fi + +endmenu diff --git a/arch/cris/drivers/Makefile b/arch/cris/drivers/Makefile new file mode 100644 index 000000000..47754f647 --- /dev/null +++ b/arch/cris/drivers/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for Etrax-specific drivers +# + +O_TARGET := drivers.o + +obj-y := + +obj-$(CONFIG_ETRAX_ETHERNET) += ethernet.o +obj-$(CONFIG_ETRAX_SERIAL) += serial.o +obj-$(CONFIG_ETRAX_IDE) += ide.o +obj-$(CONFIG_ETRAX_AXISFLASHMAP) += axisflashmap.o + +include $(TOPDIR)/Rules.make diff --git a/arch/cris/drivers/axisflashmap.c b/arch/cris/drivers/axisflashmap.c new file mode 100644 index 000000000..15b0be262 --- /dev/null +++ b/arch/cris/drivers/axisflashmap.c @@ -0,0 +1,311 @@ +/* + * Physical mapping layer for MTD using the Axis partitiontable format + * + * Copyright (c) 2001 Axis Communications AB + * + * This file is under the GPL. + * + * First partition is always sector 0 regardless of if we find a partitiontable + * or not. In the start of the next sector, there can be a partitiontable that + * tells us what other partitions to define. If there isn't, we use a default + * partition split defined below. + * + * $Log: axisflashmap.c,v $ + * Revision 1.1 2001/01/12 17:01:18 bjornw + * * Added axisflashmap.c, a physical mapping for MTD that reads and understands + * Axis partition-table format. + * + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/config.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/axisflashmap.h> +#include <asm/mmu.h> + +#ifdef CONFIG_CRIS_LOW_MAP +#define FLASH_UNCACHED_ADDR KSEG_E +#define FLASH_CACHED_ADDR KSEG_5 +#else +#define FLASH_UNCACHED_ADDR KSEG_E +#define FLASH_CACHED_ADDR KSEG_F +#endif + +/* + * WINDOW_SIZE is the total size where the flash chips are mapped, + * my guess is that this can be the total memory area even if there + * are many flash chips inside the area or if they are not all mounted. + * So possibly we can get rid of the CONFIG_ here and just write something + * like 32 MB always. + */ + +#define WINDOW_SIZE (CONFIG_ETRAX_FLASH_LENGTH * 1024 * 1024) + +/* Byte-offset where the partition-table is placed in the first chip + */ + +#define PTABLE_SECTOR 65536 + +/* + * Map driver + * + * Ok this is the scoop - we need to access the flash both with and without + * the cache - without when doing all the fancy flash interfacing, and with + * when we do actual copying because otherwise it will be slow like molasses. + * I hope this works the way it's intended, so that there won't be any cases + * of non-synchronicity because of the different access modes below... + */ + +static __u8 flash_read8(struct map_info *map, unsigned long ofs) +{ + return *(__u8 *)(FLASH_UNCACHED_ADDR + ofs); +} + +static __u16 flash_read16(struct map_info *map, unsigned long ofs) +{ + return *(__u16 *)(FLASH_UNCACHED_ADDR + ofs); +} + +static __u32 flash_read32(struct map_info *map, unsigned long ofs) +{ + return *(volatile unsigned int *)(FLASH_UNCACHED_ADDR + ofs); +} + +static void flash_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + memcpy(to, (void *)(FLASH_CACHED_ADDR + from), len); +} + +static void flash_write8(struct map_info *map, __u8 d, unsigned long adr) +{ + *(__u8 *)(FLASH_UNCACHED_ADDR + adr) = d; +} + +static void flash_write16(struct map_info *map, __u16 d, unsigned long adr) +{ + *(__u16 *)(FLASH_UNCACHED_ADDR + adr) = d; +} + +static void flash_write32(struct map_info *map, __u32 d, unsigned long adr) +{ + *(__u32 *)(FLASH_UNCACHED_ADDR + adr) = d; +} + +static void flash_copy_to(struct map_info *map, unsigned long to, + const void *from, ssize_t len) +{ + memcpy((void *)(FLASH_CACHED_ADDR + to), from, len); +} + +static struct map_info axis_map = { + name: "Axis flash", + size: WINDOW_SIZE, + buswidth: CONFIG_ETRAX_FLASH_BUSWIDTH, + read8: flash_read8, + read16: flash_read16, + read32: flash_read32, + copy_from: flash_copy_from, + write8: flash_write8, + write16: flash_write16, + write32: flash_write32, + copy_to: flash_copy_to +}; + +/* If no partition-table was found, we use this default-set. + */ + +#define MAX_PARTITIONS 7 +#define NUM_DEFAULT_PARTITIONS 3 + +static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = { + { + name: "boot firmware", + size: PTABLE_SECTOR, + offset: 0 + }, + { + name: "kernel", + size: 0x1a0000, + offset: PTABLE_SECTOR + }, + { + name: "filesystem", + size: 0x50000, + offset: (0x1a0000 + PTABLE_SECTOR) + } +}; + +static struct mtd_partition axis_partitions[MAX_PARTITIONS] = { + { + name: "part0", + size: 0, + offset: 0 + }, + { + name: "part1", + size: 0, + offset: 0 + }, + { + name: "part2", + size: 0, + offset: 0 + }, + { + name: "part3", + size: 0, + offset: 0 + }, + { + name: "part4", + size: 0, + offset: 0 + }, + { + name: "part5", + size: 0, + offset: 0 + }, + { + name: "part6", + size: 0, + offset: 0 + }, +}; + +/* + * This is the master MTD device for which all the others are just + * auto-relocating aliases. + */ +static struct mtd_info *mymtd; + +/* CFI-scan the flash, and if there was a chip, read the partition-table + * and register the partitions with MTD. + */ + +static int __init +init_axis_flash(void) +{ + int pidx = 0; + struct partitiontable_head *ptable_head; + struct partitiontable_entry *ptable; + int use_default_ptable = 1; /* Until proven otherwise */ + const char *pmsg = " /dev/flash%d at 0x%x, size 0x%x\n"; + + printk(KERN_NOTICE "Axis flash mapping: %x at %x\n", + WINDOW_SIZE, FLASH_CACHED_ADDR); + + mymtd = do_cfi_probe(&axis_map); + + if(!mymtd) { + printk("cfi_probe erred %d\n", mymtd); + return -ENXIO; + } + + mymtd->module = THIS_MODULE; + + /* The partition-table is at an offset within the second + * sector of the flash. We _define_ this to be at offset 64k + * even if the actual sector-size in the flash changes.. for + * now at least. + */ + + ptable_head = FLASH_CACHED_ADDR + PTABLE_SECTOR + PARTITION_TABLE_OFFSET; + pidx++; /* first partition is always set to the default */ + + if ((ptable_head->magic == PARTITION_TABLE_MAGIC) + && (ptable_head->size + < (MAX_PARTITIONS + * sizeof(struct partitiontable_entry) + 4)) + && (*(unsigned long*) + ((void*)ptable_head + + sizeof(*ptable_head) + + ptable_head->size - 4) + == PARTITIONTABLE_END_MARKER)) { + /* Looks like a start, sane length and end of a + * partition table, lets check csum etc. + */ + int ptable_ok = 0; + struct partitiontable_entry *max_addr = + (struct partitiontable_entry *) + ((unsigned long)ptable_head + sizeof(*ptable_head) + + ptable_head->size); + unsigned long offset = PTABLE_SECTOR; + unsigned char *p; + unsigned long csum = 0; + + ptable = (struct partitiontable_entry *) + ((unsigned long)ptable_head + sizeof(*ptable_head)); + + /* Lets be PARANOID, and check the checksum. */ + p = (unsigned char*) ptable; + + while (p <= (unsigned char*)max_addr) { + csum += *p++; + csum += *p++; + csum += *p++; + csum += *p++; + } + /* printk(" total csum: 0x%08X 0x%08X\n", + csum, ptable_head->checksum); */ + ptable_ok = (csum == ptable_head->checksum); + + /* Read the entries and use/show the info. */ + ptable = (struct partitiontable_entry *) + ((unsigned long)ptable_head + sizeof(*ptable_head)); + + printk(" Found %s partition table at 0x%08lX-0x%08lX.\n", + (ptable_ok ? "valid" : "invalid"), + (unsigned long)ptable_head, + (unsigned long)max_addr); + + /* We have found a working bootblock. Now read the + partition table. Scan the table. It ends when + there is 0xffffffff, that is, empty flash. */ + + while (ptable_ok + && ptable->offset != 0xffffffff + && ptable < max_addr + && pidx < MAX_PARTITIONS) { +#if 0 + /* wait with multi-chip support until we know + * how mtd detects multiple chips + */ + if ((offset + ptable->offset) >= chips[0].size) { + partitions[pidx].start + = offset + chips[1].start + + ptable->offset - chips[0].size; + } +#endif + axis_partitions[pidx].offset = offset + ptable->offset; + axis_partitions[pidx].size = ptable->size; + + printk(pmsg, pidx, axis_partitions[pidx].offset, + axis_partitions[pidx].size); + pidx++; + ptable++; + } + use_default_ptable = !ptable_ok; + } + + if(use_default_ptable) { + printk(" Using default partition table\n"); + return add_mtd_partitions(mymtd, axis_default_partitions, + NUM_DEFAULT_PARTITIONS); + } else { + return add_mtd_partitions(mymtd, axis_partitions, pidx); + } +} + +/* This adds the above to the kernels init-call chain */ + +module_init(init_axis_flash); + diff --git a/arch/cris/drivers/ethernet.c b/arch/cris/drivers/ethernet.c new file mode 100644 index 000000000..53a9798ae --- /dev/null +++ b/arch/cris/drivers/ethernet.c @@ -0,0 +1,1041 @@ +/* $Id: ethernet.c,v 1.5 2000/11/29 17:22:22 bjornw Exp $ + * + * e100net.c: A network driver for the ETRAX 100LX network controller. + * + * Copyright (c) 1998-2000 Axis Communications AB. + * + * The outline of this driver comes from skeleton.c. + * + * $Log: ethernet.c,v $ + * Revision 1.5 2000/11/29 17:22:22 bjornw + * Get rid of the udword types legacy stuff + * + * Revision 1.4 2000/11/22 16:36:09 bjornw + * Please marketing by using the correct case when spelling Etrax. + * + * Revision 1.3 2000/11/21 16:43:04 bjornw + * Minor short->int change + * + * Revision 1.2 2000/11/08 14:27:57 bjornw + * 2.4 port + * + * Revision 1.1 2000/11/06 13:56:00 bjornw + * Verbatim copy of the 1.24 version of e100net.c from elinux + * + * Revision 1.24 2000/10/04 15:55:23 bjornw + * * Use virt_to_phys etc. for DMA addresses + * * Removed bogus CHECKSUM_UNNECESSARY + * + * + */ + +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/types.h> +#include <linux/fcntl.h> +#include <linux/interrupt.h> +#include <linux/ptrace.h> +#include <linux/ioport.h> +#include <linux/in.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <asm/system.h> +#include <asm/bitops.h> +#include <linux/spinlock.h> +#include <asm/io.h> +#include <asm/dma.h> +#include <linux/errno.h> +#include <linux/init.h> + +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> + +#include <asm/svinto.h> + +//#define ETHDEBUG + +#define D(x) + +/* + * The name of the card. Is used for messages and in the requests for + * io regions, irqs and dma channels + */ + +static const char* cardname = "ETRAX 100LX built-in ethernet controller"; + +/* A default ethernet address. Highlevel SW will set the real one later */ + +static struct sockaddr default_mac = { + 0, + { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 } +}; + +/* Information that need to be kept for each board. */ +struct net_local { + struct net_device_stats stats; + + /* Tx control lock. This protects the transmit buffer ring + * state along with the "tx full" state of the driver. This + * means all netif_queue flow control actions are protected + * by this lock as well. + */ + spinlock_t lock; +}; + +#define NETWORK_DMARX_IRQ 17 /* irq 17 is the DMA1 irq */ +#define NETWORK_DMATX_IRQ 16 /* irq 16 is the DMA0 irq */ +#define NETWORK_STATUS_IRQ 6 /* irq 6 is the network irq */ + +/* Dma descriptors etc. */ + +#define RX_BUF_SIZE 32768 + +#define MAX_MEDIA_DATA_SIZE 1518 + +#define MIN_PACKET_LEN 46 +#define ETHER_HEAD_LEN 14 + +/* +** MDIO constants. +*/ +#define MDIO_BASE_STATUS_REG 0x1 +#define MDIO_BASE_CONTROL_REG 0x0 +#define MDIO_LINK_UP_MASK 0x4 +#define MDIO_START 0x1 +#define MDIO_READ 0x2 +#define MDIO_WRITE 0x1 +#define MDIO_PREAMBLE 0xfffffffful + +/* Broadcom specific */ +#define MDIO_AUX_CTRL_STATUS_REG 0x18 +#define MDIO_SPEED 0x2 +#define MDIO_PHYS_ADDR 0x0 + +/* Network flash constants */ +#define NET_FLASH_TIME 2 /* 20 ms */ +#define NET_LINK_UP_CHECK_INTERVAL 200 /* 2 s */ + +/* RX_DESC_BUF_SIZE should be a multiple of four to avoid the buffer + * alignment bug in Etrax 100 release 1 + */ + +#define RX_DESC_BUF_SIZE 256 +#define NBR_OF_RX_DESC (RX_BUF_SIZE / \ + RX_DESC_BUF_SIZE) + +#define GET_BIT(bit,val) (((val) >> (bit)) & 0x01) + +static etrax_dma_descr *myNextRxDesc; /* Points to the next descriptor to + to be processed */ +static etrax_dma_descr *myLastRxDesc; /* The last processed descriptor */ +static etrax_dma_descr *myPrevRxDesc; /* The descriptor right before myNextRxDesc */ + +static unsigned char RxBuf[RX_BUF_SIZE]; + +static etrax_dma_descr RxDescList[NBR_OF_RX_DESC]; +static etrax_dma_descr TxDesc; + +static struct sk_buff *tx_skb; + +/* Network speed indication. */ +static struct timer_list speed_timer; +static struct timer_list clear_led_timer; +static int current_speed; +static int led_clear_time; +static int nolink; + +/* Index to functions, as function prototypes. */ + +static int etrax_ethernet_init(struct net_device *dev); + +static int e100_open(struct net_device *dev); +static int e100_set_mac_address(struct net_device *dev, void *addr); +static int e100_send_packet(struct sk_buff *skb, struct net_device *dev); +static void e100rx_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void e100tx_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void e100nw_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static void e100_rx(struct net_device *dev); +static int e100_close(struct net_device *dev); +static struct net_device_stats *e100_get_stats(struct net_device *dev); +static void set_multicast_list(struct net_device *dev); +static void e100_hardware_send_packet(char *buf, int length); +static void update_rx_stats(struct net_device_stats *); +static void update_tx_stats(struct net_device_stats *); + +static void e100_check_speed(unsigned long dummy); +static unsigned short e100_get_mdio_reg(unsigned char reg_num); +static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd); +static void e100_send_mdio_bit(unsigned char bit); +static unsigned char e100_receive_mdio_bit(void); +static void e100_reset_tranceiver(void); + +static void e100_clear_network_leds(unsigned long dummy); + +#define tx_done(dev) (*R_DMA_CH0_CMD == 0) + +/* + * Check for a network adaptor of this type, and return '0' if one exists. + * If dev->base_addr == 0, probe all likely locations. + * If dev->base_addr == 1, always return failure. + * If dev->base_addr == 2, allocate space for the device and return success + * (detachable devices only). + */ + +static int __init +etrax_ethernet_init(struct net_device *dev) +{ + int i; + int anOffset = 0; + + printk("ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000 Axis Communications AB\n"); + + dev->base_addr = (unsigned int)R_NETWORK_SA_0; /* just to have something to show */ + + printk("%s initialized\n", dev->name); + + /* make Linux aware of the new hardware */ + + if (!dev) { + printk("dev == NULL. Should this happen?\n"); + dev = init_etherdev(dev, sizeof(struct net_local)); + } + + /* setup generic handlers and stuff in the dev struct */ + + ether_setup(dev); + + /* make room for the local structure containing stats etc */ + + dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL); + if (dev->priv == NULL) + return -ENOMEM; + memset(dev->priv, 0, sizeof(struct net_local)); + + /* now setup our etrax specific stuff */ + + dev->irq = NETWORK_DMARX_IRQ; /* we really use DMATX as well... */ + dev->dma = 1; + + /* fill in our handlers so the network layer can talk to us in the future */ + + dev->open = e100_open; + dev->hard_start_xmit = e100_send_packet; + dev->stop = e100_close; + dev->get_stats = e100_get_stats; + dev->set_multicast_list = set_multicast_list; + dev->set_mac_address = e100_set_mac_address; + + /* set the default MAC address */ + + e100_set_mac_address(dev, &default_mac); + + /* Initialise the list of Etrax DMA-descriptors */ + + /* Initialise receive descriptors */ + + for(i = 0; i < (NBR_OF_RX_DESC - 1); i++) { + RxDescList[i].ctrl = 0; + RxDescList[i].sw_len = RX_DESC_BUF_SIZE; + RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]); + RxDescList[i].buf = virt_to_phys(RxBuf + anOffset); + RxDescList[i].status = 0; + RxDescList[i].hw_len = 0; + anOffset += RX_DESC_BUF_SIZE; + } + + RxDescList[i].ctrl = d_eol; + RxDescList[i].sw_len = RX_DESC_BUF_SIZE; + RxDescList[i].next = virt_to_phys(&RxDescList[0]); + RxDescList[i].buf = virt_to_phys(RxBuf + anOffset); + RxDescList[i].status = 0; + RxDescList[i].hw_len = 0; + + /* Initialise initial pointers */ + + myNextRxDesc = &RxDescList[0]; + myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; + myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; + + /* Initialize speed indicator stuff. */ + + nolink = 0; + current_speed = 10; + speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL; + speed_timer.function = e100_check_speed; + add_timer(&speed_timer); + clear_led_timer.function = e100_clear_network_leds; + clear_led_timer.expires = jiffies + 10; + add_timer(&clear_led_timer); + + return 0; +} + +/* set MAC address of the interface. called from the core after a + * SIOCSIFADDR ioctl, and from the bootup above. + */ + +static int +e100_set_mac_address(struct net_device *dev, void *p) +{ + struct sockaddr *addr = p; + int i; + + /* remember it */ + + memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); + + /* Write it to the hardware. + * Note the way the address is wrapped: + * *R_NETWORK_SA_0 = a0_0 | (a0_1 << 8) | (a0_2 << 16) | (a0_3 << 24); + * *R_NETWORK_SA_1 = a0_4 | (a0_5 << 8); + */ + + *R_NETWORK_SA_0 = dev->dev_addr[0] | (dev->dev_addr[1] << 8) | + (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24); + *R_NETWORK_SA_1 = dev->dev_addr[4] | (dev->dev_addr[5] << 8); + *R_NETWORK_SA_2 = 0; + + /* show it in the log as well */ + + printk("%s: changed MAC to ", dev->name); + + for (i = 0; i < 5; i++) + printk("%02X:", dev->dev_addr[i]); + + printk("%02X\n", dev->dev_addr[i]); + + return 0; +} + +/* + * Open/initialize the board. This is called (in the current kernel) + * sometime after booting when the 'ifconfig' program is run. + * + * This routine should set everything up anew at each open, even + * registers that "should" only need to be set once at boot, so that + * there is non-reboot way to recover if something goes wrong. + */ + +static int +e100_open(struct net_device *dev) +{ + unsigned long flags; + + /* disable the ethernet interface while we configure it */ + + *R_NETWORK_GEN_CONFIG = + IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) | + IO_STATE(R_NETWORK_GEN_CONFIG, enable, off); + + /* enable the MDIO output pin */ + + *R_NETWORK_MGM_CTRL = IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable); + + *R_IRQ_MASK0_CLR = 0xe0000; /* clear excessive_col, over/underrun irq mask */ + *R_IRQ_MASK2_CLR = 0xf; /* clear dma0 and 1 eop and descr irq masks */ + + /* Reset and wait for the DMA channels */ + + RESET_DMA(0); + RESET_DMA(1); + WAIT_DMA(0); + WAIT_DMA(1); + + /* Initialise the etrax network controller */ + + /* allocate the irq corresponding to the receiving DMA */ + + if (request_irq(NETWORK_DMARX_IRQ, e100rx_interrupt, 0, + cardname, (void *)dev)) { + goto grace_exit; + } + + /* allocate the irq corresponding to the transmitting DMA */ + + if (request_irq(NETWORK_DMATX_IRQ, e100tx_interrupt, 0, + cardname, (void *)dev)) { + goto grace_exit; + } + + /* allocate the irq corresponding to the network errors etc */ + + if (request_irq(NETWORK_STATUS_IRQ, e100nw_interrupt, 0, + cardname, (void *)dev)) { + goto grace_exit; + } + + /* + * Always allocate the DMA channels after the IRQ, + * and clean up on failure. + */ + + if(request_dma(0, cardname)) { + goto grace_exit; + } + + if(request_dma(1, cardname)) { + grace_exit: + /* this will cause some 'trying to free free irq' but what the heck... */ + free_dma(0); + free_irq(NETWORK_DMARX_IRQ, NULL); + free_irq(NETWORK_DMATX_IRQ, NULL); + free_irq(NETWORK_STATUS_IRQ, NULL); + return -EAGAIN; + } + + /* give the HW an idea of what MAC address we want */ + + *R_NETWORK_SA_0 = dev->dev_addr[0] | (dev->dev_addr[1] << 8) | + (dev->dev_addr[2] << 16) | (dev->dev_addr[3] << 24); + *R_NETWORK_SA_1 = dev->dev_addr[4] | (dev->dev_addr[5] << 8); + *R_NETWORK_SA_2 = 0; + +#if 0 + /* use promiscuous mode for testing */ + *R_NETWORK_GA_0 = 0xffffffff; + *R_NETWORK_GA_1 = 0xffffffff; + + *R_NETWORK_REC_CONFIG = 0xd; /* broadcast rec, individ. rec, ma0 enabled */ +#else + *R_NETWORK_REC_CONFIG = + IO_STATE(R_NETWORK_REC_CONFIG, broadcast, receive) | + IO_STATE(R_NETWORK_REC_CONFIG, ma0, enable); +#endif + + *R_NETWORK_GEN_CONFIG = + IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) | + IO_STATE(R_NETWORK_GEN_CONFIG, enable, on); + + save_flags(flags); + cli(); + + /* enable the irq's for ethernet DMA */ + + *R_IRQ_MASK2_SET = + IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) | + IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set); + + *R_IRQ_MASK0_SET = + IO_STATE(R_IRQ_MASK0_SET, overrun, set) | + IO_STATE(R_IRQ_MASK0_SET, underrun, set) | + IO_STATE(R_IRQ_MASK0_SET, excessive_col, set); + + tx_skb = 0; + + /* make sure the irqs are cleared */ + + *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do); + *R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do); + + /* make sure the rec and transmit error counters are cleared */ + + (void)*R_REC_COUNTERS; /* dummy read */ + (void)*R_TR_COUNTERS; /* dummy read */ + + /* start the receiving DMA channel so we can receive packets from now on */ + + *R_DMA_CH1_FIRST = virt_to_phys(myNextRxDesc); + *R_DMA_CH1_CMD = IO_STATE(R_DMA_CH1_CMD, cmd, start); + + restore_flags(flags); + + /* We are now ready to accept transmit requeusts from + * the queueing layer of the networking. + */ + netif_start_queue(dev); + + return 0; +} + + +static void +e100_check_speed(unsigned long dummy) +{ + unsigned long data; + data = e100_get_mdio_reg(MDIO_BASE_STATUS_REG); + if (!(data & MDIO_LINK_UP_MASK)) { + nolink = 1; + LED_NETWORK_TX_SET(1); /* Make it red, link is down. */ + } else { + nolink = 0; + LED_NETWORK_TX_SET(0); /* Link is up again, clear red LED. */ + data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG); + if (data & MDIO_SPEED) { + current_speed = 100; + } else { + current_speed = 10; + } + } + + /* Reinitialize the timer. */ + speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL; + add_timer(&speed_timer); +} + +static unsigned short +e100_get_mdio_reg(unsigned char reg_num) +{ + unsigned long flags; + unsigned short cmd; /* Data to be sent on MDIO port */ + unsigned short data; /* Data read from MDIO */ + int bitCounter; + + save_flags(flags); + cli(); + + /* Start of frame, OP Code, Physical Address, Register Address */ + cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (MDIO_PHYS_ADDR << 7) | + (reg_num << 2); + + e100_send_mdio_cmd(cmd, 0); + + data = 0; + + /* Data... */ + for(bitCounter=15; bitCounter>=0 ; bitCounter--) { + data |= (e100_receive_mdio_bit() << bitCounter); + } + + restore_flags(flags); + return data; +} + +static void +e100_send_mdio_cmd(unsigned short cmd, int write_cmd) +{ + int bitCounter; + unsigned char data = 0x2; + + /* Preamble */ + for(bitCounter = 31; bitCounter>= 0; bitCounter--) + e100_send_mdio_bit(GET_BIT(bitCounter, MDIO_PREAMBLE)); + + for(bitCounter = 15; bitCounter >= 2; bitCounter--) + e100_send_mdio_bit(GET_BIT(bitCounter, cmd)); + + /* Turnaround */ + for(bitCounter = 1; bitCounter >= 0 ; bitCounter--) + if (write_cmd) + e100_send_mdio_bit(GET_BIT(bitCounter, data)); + else + e100_receive_mdio_bit(); +} + +static void +e100_send_mdio_bit(unsigned char bit) +{ + volatile int i; + *R_NETWORK_MGM_CTRL = 2 | bit&1; + for (i=40; i; i--); + *R_NETWORK_MGM_CTRL = 6 | bit&1; + for (i=40; i; i--); +} + +static unsigned char +e100_receive_mdio_bit() +{ + unsigned char bit; + volatile int i; + *R_NETWORK_MGM_CTRL = 0; + bit = *R_NETWORK_STAT & 1; + for (i=40; i; i--); + *R_NETWORK_MGM_CTRL = 4; + for (i=40; i; i--); + return bit; +} + +static void +e100_reset_tranceiver(void) +{ + unsigned long flags; + unsigned short cmd; + unsigned short data; + int bitCounter; + + save_flags(flags); + cli(); + + data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG); + + cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | (MDIO_BASE_CONTROL_REG << 2); + + e100_send_mdio_cmd(cmd, 0); + + data |= 0x8000; + + for(bitCounter = 15; bitCounter >= 0 ; bitCounter--) { + e100_send_mdio_bit(GET_BIT(bitCounter, data)); + } + + restore_flags(flags); +} + +/* Called by upper layers if they decide it took too long to complete + * sending a packet - we need to reset and stuff. + */ + +static void +e100_tx_timeout(struct net_device *dev) +{ + struct net_local *np = (struct net_local *)dev->priv; + + printk(KERN_WARNING "%s: transmit timed out, %s?\n", dev->name, + tx_done(dev) ? "IRQ problem" : "network cable problem"); + + /* remember we got an error */ + + np->stats.tx_errors++; + + /* reset the TX DMA in case it has hung on something */ + + RESET_DMA(0); + WAIT_DMA(0); + + /* Reset the tranceiver. */ + + e100_reset_tranceiver(); + + /* and get rid of the packet that never got an interrupt */ + + dev_kfree_skb(tx_skb); + tx_skb = 0; + + /* tell the upper layers we're ok again */ + + netif_wake_queue(dev); +} + + +/* This will only be invoked if the driver is _not_ in XOFF state. + * What this means is that we need not check it, and that this + * invariant will hold if we make sure that the netif_*_queue() + * calls are done at the proper times. + */ + +static int +e100_send_packet(struct sk_buff *skb, struct net_device *dev) +{ + struct net_local *np = (struct net_local *)dev->priv; + int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; + unsigned char *buf = skb->data; + +#ifdef ETHDEBUG + printk("send packet len %d\n", length); +#endif + spin_lock_irq(&np->lock); /* protect from tx_interrupt */ + + tx_skb = skb; /* remember it so we can free it in the tx irq handler later */ + dev->trans_start = jiffies; + + e100_hardware_send_packet(buf, length); + + /* this simple TX driver has only one send-descriptor so we're full + * directly. If this had a send-ring instead, we would only do this if + * the ring got full. + */ + + netif_stop_queue(dev); + + spin_unlock_irq(&np->lock); + + return 0; +} + +/* + * The typical workload of the driver: + * Handle the network interface interrupts. + */ + +static void +e100rx_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + unsigned long irqbits = *R_IRQ_MASK2_RD; + + if(irqbits & (1U << 3)) { + + /* acknowledge the eop interrupt */ + + *R_DMA_CH1_CLR_INTR = IO_STATE(R_DMA_CH1_CLR_INTR, clr_eop, do); + + /* check if one or more complete packets were indeed received */ + + while(*R_DMA_CH1_FIRST != virt_to_phys(myNextRxDesc)) { + /* Take out the buffer and give it to the OS, then + * allocate a new buffer to put a packet in. + */ + e100_rx(dev); + ((struct net_local *)dev->priv)->stats.rx_packets++; + *R_DMA_CH1_CMD = 3; /* restart/continue on the channel, for safety */ + *R_DMA_CH1_CLR_INTR = 3; /* clear dma channel 1 eop/descr irq bits */ + /* now, we might have gotten another packet so we have to loop back + and check if so */ + } + } +} + +/* the transmit dma channel interrupt + * + * this is supposed to free the skbuff which was pending during transmission, + * and inform the kernel that we can send one more buffer + */ + +static void +e100tx_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + unsigned long irqbits = *R_IRQ_MASK2_RD; + struct net_local *np = (struct net_local *)dev->priv; + + if(irqbits & 2) { /* check for a dma0_eop interrupt */ + + /* This protects us from concurrent execution of + * our dev->hard_start_xmit function above. + */ + + spin_lock(&np->lock); + + /* acknowledge the eop interrupt */ + + *R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do); + + if(*R_DMA_CH0_FIRST == 0 && tx_skb) { + np->stats.tx_bytes += tx_skb->len; + np->stats.tx_packets++; + /* dma is ready with the transmission of the data in tx_skb, so now + we can release the skb memory */ + dev_kfree_skb_irq(tx_skb); + tx_skb = 0; + netif_wake_queue(dev); + } else { + printk("tx weird interrupt\n"); + } + + spin_unlock(&np->lock); + } +} + +static void +e100nw_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct net_local *np = (struct net_local *)dev->priv; + unsigned long irqbits = *R_IRQ_MASK0_RD; + + if(irqbits & (1 << 19)) { /* check for overrun irq */ + update_rx_stats(&np->stats); /* this will ack the irq */ + D(printk("ethernet receiver overrun!\n")); + } + if(irqbits & (1 << 17)) { /* check for excessive collision irq */ + *R_NETWORK_TR_CTRL = 1 << 8; /* clear the interrupt */ + np->stats.tx_errors++; + D(printk("ethernet excessive collisions!\n")); + } + +} + +/* We have a good packet(s), get it/them out of the buffers. */ +static void +e100_rx(struct net_device *dev) +{ + struct sk_buff *skb; + int length=0; + int i; + struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc; + unsigned char *skb_data_ptr; + + /* light the network rx packet depending on the current speed. + ** But only if link has been detected. + */ + if (!nolink) + if (current_speed == 10) { + LED_NETWORK_TX_SET(1); + LED_NETWORK_RX_SET(1); + } else + LED_NETWORK_RX_SET(1); + + /* Set the earliest time we may clear the LED */ + + led_clear_time = jiffies + NET_FLASH_TIME; + + /* If the packet is broken down in many small packages then merge + * count how much space we will need to alloc with skb_alloc() for + * it to fit. + */ + + while (!(myNextRxDesc->status & d_eop)) { + length += myNextRxDesc->sw_len; /* use sw_len for the first descs */ + myNextRxDesc->status = 0; + myNextRxDesc = phys_to_virt(myNextRxDesc->next); + } + + length += myNextRxDesc->hw_len; /* use hw_len for the last descr */ + +#ifdef ETHDEBUG + printk("Got a packet of length %d:\n", length); + /* dump the first bytes in the packet */ + skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf); + for(i = 0; i < 8; i++) { + printk("%d: %.2x %.2x %.2x %.2x %.2x %.2x %.2x %.2x\n", i * 8, + skb_data_ptr[0],skb_data_ptr[1],skb_data_ptr[2],skb_data_ptr[3], + skb_data_ptr[4],skb_data_ptr[5],skb_data_ptr[6],skb_data_ptr[7]); + skb_data_ptr += 8; + } +#endif + + skb = dev_alloc_skb(length - ETHER_HEAD_LEN); + if (!skb) { + printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", + dev->name); + return; + } + + skb_put(skb, length - ETHER_HEAD_LEN); /* allocate room for the packet body */ + skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */ + +#if 0 + printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n", + skb->head, skb->data, skb->tail, skb->end); + printk("copying packet to 0x%x.\n", skb_data_ptr); +#endif + + /* this loop can be made using max two memcpy's if optimized */ + + while(mySaveRxDesc != myNextRxDesc) { + memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf), + mySaveRxDesc->sw_len); + skb_data_ptr += mySaveRxDesc->sw_len; + mySaveRxDesc = phys_to_virt(mySaveRxDesc->next); + } + + memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf), + mySaveRxDesc->hw_len); + + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + + /* Send the packet to the upper layers */ + + netif_rx(skb); + + /* Prepare for next packet */ + + myNextRxDesc->status = 0; + myPrevRxDesc = myNextRxDesc; + myNextRxDesc = phys_to_virt(myNextRxDesc->next); + + myPrevRxDesc->ctrl |= d_eol; + myLastRxDesc->ctrl &= ~d_eol; + myLastRxDesc = myPrevRxDesc; + + return; +} + +/* The inverse routine to net_open(). */ +static int +e100_close(struct net_device *dev) +{ + struct net_local *np = (struct net_local *)dev->priv; + + printk("Closing %s.\n", dev->name); + + netif_stop_queue(dev); + + *R_NETWORK_GEN_CONFIG = + IO_STATE(R_NETWORK_GEN_CONFIG, phy, mii_clk) | + IO_STATE(R_NETWORK_GEN_CONFIG, enable, off); + + *R_IRQ_MASK0_CLR = 0xe0000; /* clear excessive_col, over/underrun irq mask */ + *R_IRQ_MASK2_CLR = 0xf; /* clear dma0 and 1 eop and descr irq masks */ + + /* Stop the receiver and the transmitter */ + + RESET_DMA(0); + RESET_DMA(1); + + /* Flush the Tx and disable Rx here. */ + + free_irq(NETWORK_DMARX_IRQ, NULL); + free_irq(NETWORK_DMATX_IRQ, NULL); + free_irq(NETWORK_STATUS_IRQ, NULL); + + free_dma(0); + free_dma(1); + + /* Update the statistics here. */ + + update_rx_stats(&np->stats); + update_tx_stats(&np->stats); + + return 0; +} + +static void +update_rx_stats(struct net_device_stats *es) +{ + unsigned long r = *R_REC_COUNTERS; + /* update stats relevant to reception errors */ + es->rx_fifo_errors += r >> 24; /* fifo overrun */ + es->rx_crc_errors += r & 0xff; /* crc error */ + es->rx_frame_errors += (r >> 8) & 0xff; /* alignment error */ + es->rx_length_errors += (r >> 16) & 0xff; /* oversized frames */ +} + +static void +update_tx_stats(struct net_device_stats *es) +{ + unsigned long r = *R_TR_COUNTERS; + /* update stats relevant to transmission errors */ + es->collisions += (r & 0xff) + ((r >> 8) & 0xff); /* single_col + multiple_col */ + es->tx_errors += (r >> 24) & 0xff; /* deferred transmit frames */ +} + +/* + * Get the current statistics. + * This may be called with the card open or closed. + */ +static struct net_device_stats * +e100_get_stats(struct net_device *dev) +{ + struct net_local *lp = (struct net_local *)dev->priv; + + update_rx_stats(&lp->stats); + update_tx_stats(&lp->stats); + + return &lp->stats; +} + +/* + * Set or clear the multicast filter for this adaptor. + * num_addrs == -1 Promiscuous mode, receive all packets + * num_addrs == 0 Normal mode, clear multicast list + * num_addrs > 0 Multicast mode, receive normal and MC packets, + * and do best-effort filtering. + */ +static void +set_multicast_list(struct net_device *dev) +{ + int num_addr = dev->mc_count; + unsigned long int lo_bits; + unsigned long int hi_bits; + if (num_addr == -1) + { + /* promiscuous mode */ + lo_bits = 0xfffffffful; + hi_bits = 0xfffffffful; + } else if (num_addr == 0) { + /* Normal, clear the mc list */ + lo_bits = 0x00000000ul; + hi_bits = 0x00000000ul; + } else { + /* MC mode, receive normal and MC packets */ + char hash_ix; + struct dev_mc_list *dmi = dev->mc_list; + int i; + char *baddr; + lo_bits = 0x00000000ul; + hi_bits = 0x00000000ul; + for (i=0; i<num_addr; i++) { + /* Calculate the hash index for the GA registers */ + + hash_ix = 0; + baddr = dmi->dmi_addr; + hash_ix ^= (*baddr) & 0x3f; + hash_ix ^= ((*baddr) >> 6) & 0x03; + ++baddr; + hash_ix ^= ((*baddr) << 2) & 0x03c; + hash_ix ^= ((*baddr) >> 4) & 0xf; + ++baddr; + hash_ix ^= ((*baddr) << 4) & 0x30; + hash_ix ^= ((*baddr) >> 2) & 0x3f; + ++baddr; + hash_ix ^= (*baddr) & 0x3f; + hash_ix ^= ((*baddr) >> 6) & 0x03; + ++baddr; + hash_ix ^= ((*baddr) << 2) & 0x03c; + hash_ix ^= ((*baddr) >> 4) & 0xf; + ++baddr; + hash_ix ^= ((*baddr) << 4) & 0x30; + hash_ix ^= ((*baddr) >> 2) & 0x3f; + + hash_ix &= 0x3f; + + if (hash_ix > 32) { + hi_bits |= (1 << (hash_ix-32)); + } + else { + lo_bits |= (1 << hash_ix); + } + dmi = dmi->next; + } + } + *R_NETWORK_GA_0 = lo_bits; + *R_NETWORK_GA_1 = hi_bits; +} + +void +e100_hardware_send_packet(char *buf, int length) +{ + D(printk("e100 send pack, buf 0x%x len %d\n", buf, length)); + /* light the network leds depending on the current speed. + ** But only if link has been detected. + */ + if (!nolink) { + if (current_speed == 10) { + LED_NETWORK_TX_SET(1); + LED_NETWORK_RX_SET(1); + } else { + LED_NETWORK_RX_SET(1); + } + } + + /* Set the earliest time we may clear the LED */ + + led_clear_time = jiffies + NET_FLASH_TIME; + + /* configure the tx dma descriptor */ + + TxDesc.sw_len = length; + TxDesc.ctrl = d_eop | d_eol | d_wait; + TxDesc.buf = virt_to_phys(buf); + + /* setup the dma channel and start it */ + + *R_DMA_CH0_FIRST = virt_to_phys(&TxDesc); + *R_DMA_CH0_CMD = IO_STATE(R_DMA_CH0_CMD, cmd, start); +} + +static void +e100_clear_network_leds(unsigned long dummy) +{ + if (jiffies > led_clear_time) { + if (nolink) + LED_NETWORK_TX_SET(1); + else + LED_NETWORK_TX_SET(0); + LED_NETWORK_RX_SET(0); + } + + clear_led_timer.expires = jiffies + 10; + add_timer(&clear_led_timer); +} + +static struct net_device dev_etrax_ethernet; /* only got one */ + +static int +etrax_init_module(void) +{ + struct net_device *d = &dev_etrax_ethernet; + + d->init = etrax_ethernet_init; + + if(register_netdev(d) == 0) + return 0; + else + return -ENODEV; +} + +module_init(etrax_init_module); diff --git a/arch/cris/drivers/ide.c b/arch/cris/drivers/ide.c new file mode 100644 index 000000000..e352fdf32 --- /dev/null +++ b/arch/cris/drivers/ide.c @@ -0,0 +1,818 @@ +/* $Id: ide.c,v 1.4 2001/01/10 21:14:32 bjornw Exp $ + * + * Etrax specific IDE functions, like init and PIO-mode setting etc. + * Almost the entire ide.c is used for the rest of the Etrax ATA driver. + * Copyright (c) 2000, 2001 Axis Communications AB + * + * Authors: Bjorn Wesen (initial version) + * Mikael Starvik (pio setup stuff) + * + * $Log: ide.c,v $ + * Revision 1.4 2001/01/10 21:14:32 bjornw + * Initialize hwif->ideproc, for the new way of handling ide_xxx_data + * + * Revision 1.3 2000/12/01 17:48:18 bjornw + * - atapi_output_bytes now uses DMA + * - dma_active check removed - the kernel does proper serializing and it had + * a race-condition anyway + * - ide_build_dmatable had a nameclash + * - re-added the RESET_DMA thingys because sometimes the interface can get + * stuck apparently + * - added ide_release_dma + * + * Revision 1.2 2000/11/29 17:31:29 bjornw + * 2.4 port + * + * - The "register addresses" stored in the hwif are now 32-bit fields that + * don't need to be shifted into correct positions in R_ATA_CTRL_DATA + * - PIO-mode detection temporarily disabled since ide-modes.c is not compiled + * - All DMA uses virt_to_phys conversions for DMA buffers and descriptor ptrs + * - Probably correct ide_dma_begin semantics in dmaproc now for ATAPI devices + * - Removed RESET_DMA when starting a new transfer - why was this necessary ? + * - Indentation fix + * + * + */ + +/* Regarding DMA: + * + * There are two forms of DMA - "DMA handshaking" between the interface and the drive, + * and DMA between the memory and the interface. We can ALWAYS use the latter, since it's + * something built-in in the Etrax. However only some drives support the DMA-mode handshaking + * on the ATA-bus. The normal PC driver and Triton interface disables memory-if DMA when the + * device can't do DMA handshaking for some stupid reason. We don't need to do that. + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include <linux/config.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/blkdev.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/init.h> + +#include <asm/io.h> +#include <asm/svinto.h> + +/* number of Etrax DMA descriptors */ +#define MAX_DMA_DESCRS 64 + +#define LOWDB(x) +#define D(x) + +void OUT_BYTE(unsigned char data, ide_ioreg_t reg) { + LOWDB(printk("ob: data 0x%x, reg 0x%x\n", data, reg)); + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */ + *R_ATA_CTRL_DATA = reg | data; /* write data to the drive's register */ + while(!(*R_ATA_STATUS_DATA & + IO_MASK(R_ATA_STATUS_DATA, tr_rdy))); /* wait for transmitter ready */ +} + +unsigned char IN_BYTE(ide_ioreg_t reg) { + int status; + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */ + *R_ATA_CTRL_DATA = reg | IO_STATE(R_ATA_CTRL_DATA, rw, read); /* read data */ + while(!((status = *R_ATA_STATUS_DATA) & + IO_MASK(R_ATA_STATUS_DATA, dav))); /* wait for available */ + LOWDB(printk("inb: 0x%x from reg 0x%x\n", status & 0xff, reg)); + return (unsigned char)status; /* data was in the lower 16 bits in the status reg */ +} + +/* PIO timing (in R_ATA_CONFIG) + * + * _____________________________ + * ADDRESS : ________/ + * + * _______________ + * DIOR : ____________/ \__________ + * + * _______________ + * DATA : XXXXXXXXXXXXXXXX_______________XXXXXXXX + * + * + * DIOR is unbuffered while address and data is buffered. + * This creates two problems: + * 1. The DIOR pulse is to early (because it is unbuffered) + * 2. The rise time of DIOR is long + * + * There are at least three different plausible solutions + * 1. Use a pad capable of larger currents in Etrax + * 2. Use an external buffer + * 3. Make the strobe pulse longer + * + * Some of the strobe timings below are modified to compensate + * for this. This implies a slight performance decrease. + * + * THIS SHOULD NEVER BE CHANGED! + * + * TODO: Is this true for the latest LX boards still ? + */ + +#define ATA_DMA2_STROBE 4 +#define ATA_DMA2_HOLD 0 +#define ATA_DMA1_STROBE 4 +#define ATA_DMA1_HOLD 1 +#define ATA_DMA0_STROBE 12 +#define ATA_DMA0_HOLD 9 +#define ATA_PIO4_SETUP 1 +#define ATA_PIO4_STROBE 5 +#define ATA_PIO4_HOLD 0 +#define ATA_PIO3_SETUP 1 +#define ATA_PIO3_STROBE 5 +#define ATA_PIO3_HOLD 1 +#define ATA_PIO2_SETUP 1 +#define ATA_PIO2_STROBE 6 +#define ATA_PIO2_HOLD 2 +#define ATA_PIO1_SETUP 2 +#define ATA_PIO1_STROBE 11 +#define ATA_PIO1_HOLD 4 +#define ATA_PIO0_SETUP 4 +#define ATA_PIO0_STROBE 19 +#define ATA_PIO0_HOLD 4 + +/* + * good_dma_drives() lists the model names (from "hdparm -i") + * of drives which do not support mword2 DMA but which are + * known to work fine with this interface under Linux. + */ + +const char *good_dma_drives[] = {"Micropolis 2112A", + "CONNER CTMA 4000", + "CONNER CTT8000-A", + NULL}; + +static void tune_e100_ide(ide_drive_t *drive, byte pio) +{ + unsigned long flags; + + pio = 4; + //pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + + save_flags(flags); + cli(); + + /* set pio mode! */ + + switch(pio) { + case 0: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO0_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO0_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO0_HOLD ) ); + break; + case 1: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO1_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO1_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO1_HOLD ) ); + break; + case 2: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO2_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO2_HOLD ) ); + break; + case 3: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO3_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO3_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO3_HOLD ) ); + break; + case 4: + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) ); + break; + } + restore_flags(flags); +} + +static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive); /* defined below */ +static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive, + void *buffer, unsigned int length); /* defined below */ + +void __init init_e100_ide (void) +{ + volatile unsigned int dummy; + int h; + + printk("ide: ETRAX 100LX built-in ATA DMA controller\n"); + + /* first fill in some stuff in the ide_hwifs fields */ + + for(h = 0; h < MAX_HWIFS; h++) { + ide_hwif_t *hwif = &ide_hwifs[h]; + hwif->chipset = ide_etrax100; + hwif->tuneproc = &tune_e100_ide; + hwif->dmaproc = &e100_dmaproc; + hwif->ideproc = &e100_ideproc; + } + /* actually reset and configure the etrax100 ide/ata interface */ + + /* de-assert bus-reset */ +#ifdef CONFIG_ETRAX_IDE_PB7_RESET + port_pb_dir_shadow = port_pb_dir_shadow | + IO_STATE(R_PORT_PB_DIR, dir7, output); + *R_PORT_PB_DIR = port_pb_dir_shadow; + REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, 7, 1); +#endif +#ifdef CONFIG_ETRAX_IDE_G27_RESET + *R_PORT_G_DATA = 0; +#endif + + *R_ATA_CTRL_DATA = 0; + *R_ATA_TRANSFER_CNT = 0; + *R_ATA_CONFIG = 0; + + genconfig_shadow = (genconfig_shadow & + ~IO_MASK(R_GEN_CONFIG, dma2) & + ~IO_MASK(R_GEN_CONFIG, dma3) & + ~IO_MASK(R_GEN_CONFIG, ata)) | + ( IO_STATE( R_GEN_CONFIG, dma3, ata ) | + IO_STATE( R_GEN_CONFIG, dma2, ata ) | + IO_STATE( R_GEN_CONFIG, ata, select ) ); + + *R_GEN_CONFIG = genconfig_shadow; + +#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET + *(volatile long *)(MEM_CSE1_START | MEM_NON_CACHEABLE) = 0; +#endif + + /* wait some */ + + dummy = 1; + dummy = 2; + dummy = 3; + +#ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET + *(volatile long *)(MEM_CSE1_START | MEM_NON_CACHEABLE) = 1 << 16; + *R_PORT_G_DATA = 0; /* de-assert bus-reset */ +#endif + + /* make a dummy read to set the ata controller in a proper state */ + dummy = *R_ATA_STATUS_DATA; + + *R_ATA_CONFIG = ( IO_FIELD( R_ATA_CONFIG, enable, 1 ) | + IO_FIELD( R_ATA_CONFIG, dma_strobe, ATA_DMA2_STROBE ) | + IO_FIELD( R_ATA_CONFIG, dma_hold, ATA_DMA2_HOLD ) | + IO_FIELD( R_ATA_CONFIG, pio_setup, ATA_PIO4_SETUP ) | + IO_FIELD( R_ATA_CONFIG, pio_strobe, ATA_PIO4_STROBE ) | + IO_FIELD( R_ATA_CONFIG, pio_hold, ATA_PIO4_HOLD ) ); + + *R_ATA_CTRL_DATA = ( IO_STATE( R_ATA_CTRL_DATA, rw, read) | + IO_FIELD( R_ATA_CTRL_DATA, addr, 1 ) ); + + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag*/ + + *R_IRQ_MASK0_SET = ( IO_STATE( R_IRQ_MASK0_SET, ata_irq0, set ) | + IO_STATE( R_IRQ_MASK0_SET, ata_irq1, set ) | + IO_STATE( R_IRQ_MASK0_SET, ata_irq2, set ) | + IO_STATE( R_IRQ_MASK0_SET, ata_irq3, set ) ); + + printk("ide: waiting 10 seconds for drives to regain consciousness\n"); + + h = jiffies + 1000; + while(jiffies < h) ; + + /* reset the dma channels we will use */ + + RESET_DMA(2); + RESET_DMA(3); + WAIT_DMA(2); + WAIT_DMA(3); + +} + +static etrax_dma_descr mydescr; + +/* + * The following routines are mainly used by the ATAPI drivers. + * + * These routines will round up any request for an odd number of bytes, + * so if an odd bytecount is specified, be sure that there's at least one + * extra byte allocated for the buffer. + */ +static void +e100_atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + ide_ioreg_t data_reg = IDE_DATA_REG; + unsigned long status; + + D(printk("atapi_input_bytes, dreg 0x%x, buffer 0x%x, count %d\n", + data_reg, buffer, bytecount)); + + if(bytecount & 1) { + printk("warning, odd bytecount in cdrom_in_bytes = %d.\n", bytecount); + bytecount++; /* to round off */ + } + + /* make sure the DMA channel is available */ + RESET_DMA(3); + WAIT_DMA(3); + + /* setup DMA descriptor */ + + mydescr.sw_len = bytecount; + mydescr.ctrl = d_eol; + mydescr.buf = virt_to_phys(buffer); + + /* start the dma channel */ + + *R_DMA_CH3_FIRST = virt_to_phys(&mydescr); + *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start); + + /* initiate a multi word dma read using PIO handshaking */ + + *R_ATA_TRANSFER_CNT = bytecount >> 1; + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* wait for completion */ + + LED_DISK_READ(1); + WAIT_DMA(3); + LED_DISK_READ(0); + +#if 0 + /* old polled transfer code */ + + /* initiate a multi word read */ + + *R_ATA_TRANSFER_CNT = wcount << 1; + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* svinto has a latency until the busy bit actually is set */ + + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + nop(); nop(); + + /* unit should be busy during multi transfer */ + while((status = *R_ATA_STATUS_DATA) & IO_MASK(R_ATA_STATUS_DATA, busy)) { + while(!(status & IO_MASK(R_ATA_STATUS_DATA, dav))) + status = *R_ATA_STATUS_DATA; + *ptr++ = (unsigned short)(status & 0xffff); + } +#endif +} + +static void +e100_atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + ide_ioreg_t data_reg = IDE_DATA_REG; + unsigned short *ptr = (unsigned short *)buffer; + unsigned long ctrl; + + D(printk("atapi_output_bytes, dreg 0x%x, buffer 0x%x, count %d\n", + data_reg, buffer, bytecount)); + + if(bytecount & 1) { + printk("odd bytecount %d in atapi_out_bytes!\n", bytecount); + bytecount++; + } + + /* make sure the DMA channel is available */ + RESET_DMA(2); + WAIT_DMA(2); + + /* setup DMA descriptor */ + + mydescr.sw_len = bytecount; + mydescr.ctrl = d_eol; + mydescr.buf = virt_to_phys(buffer); + + /* start the dma channel */ + + *R_DMA_CH2_FIRST = virt_to_phys(&mydescr); + *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start); + + /* initiate a multi word dma write using PIO handshaking */ + + *R_ATA_TRANSFER_CNT = bytecount >> 1; + + *R_ATA_CTRL_DATA = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + /* wait for completion */ + + LED_DISK_WRITE(1); + WAIT_DMA(2); + LED_DISK_WRITE(0); + +#if 0 + /* old polled write code */ + + while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); /* wait for busy flag */ + + /* initiate a multi word write */ + + *R_ATA_TRANSFER_CNT = bytecount >> 1; + + ctrl = data_reg | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, register) | + IO_STATE(R_ATA_CTRL_DATA, handsh, pio) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_WRITE(1); + + /* Etrax will set busy = 1 until the multi pio transfer has finished + * and tr_rdy = 1 after each succesful word transfer. + * When the last byte has been transferred Etrax will first set tr_tdy = 1 + * and then busy = 0 (not in the same cycle). If we read busy before it + * has been set to 0 we will think that we should transfer more bytes + * and then tr_rdy would be 0 forever. This is solved by checking busy + * in the inner loop. + */ + + do { + *R_ATA_CTRL_DATA = ctrl | *ptr++; + while(!(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, tr_rdy)) && + (*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy))); + } while(*R_ATA_STATUS_DATA & IO_MASK(R_ATA_STATUS_DATA, busy)); + + LED_DISK_WRITE(0); +#endif +} + +/* + * This is used for most PIO data transfers *from* the IDE interface + */ +static void +e100_ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + e100_atapi_input_bytes(drive, buffer, wcount << 2); +} + +/* + * This is used for most PIO data transfers *to* the IDE interface + */ +static void +e100_ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + e100_atapi_output_bytes(drive, buffer, wcount << 2); +} + +/* + * The multiplexor for ide_xxxput_data and atapi calls + */ +static void +e100_ideproc (ide_ide_action_t func, ide_drive_t *drive, + void *buffer, unsigned int length) +{ + switch (func) { + case ideproc_ide_input_data: + e100_ide_input_data(drive, buffer, length); + break; + case ideproc_ide_output_data: + e100_ide_input_data(drive, buffer, length); + break; + case ideproc_atapi_input_bytes: + e100_atapi_input_bytes(drive, buffer, length); + break; + case ideproc_atapi_output_bytes: + e100_atapi_output_bytes(drive, buffer, length); + break; + default: + printk("e100_ideproc: unsupported func %d!\n", func); + break; + } +} + +/* we only have one DMA channel on the chip for ATA, so we can keep these statically */ +static etrax_dma_descr ata_descrs[MAX_DMA_DESCRS]; +static unsigned int ata_tot_size; + +/* + * e100_ide_build_dmatable() prepares a dma request. + * Returns 0 if all went okay, returns 1 otherwise. + */ +static int e100_ide_build_dmatable (ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + struct buffer_head *bh = rq->bh; + unsigned long size, addr; + unsigned int count = 0; + + ata_tot_size = 0; + + do { + /* + * Determine addr and size of next buffer area. We assume that + * individual virtual buffers are always composed linearly in + * physical memory. For example, we assume that any 8kB buffer + * is always composed of two adjacent physical 4kB pages rather + * than two possibly non-adjacent physical 4kB pages. + */ + if (bh == NULL) { /* paging and tape requests have (rq->bh == NULL) */ + addr = virt_to_phys (rq->buffer); + size = rq->nr_sectors << 9; + } else { + /* group sequential buffers into one large buffer */ + addr = virt_to_phys (bh->b_data); + size = bh->b_size; + while ((bh = bh->b_reqnext) != NULL) { + if ((addr + size) != virt_to_phys (bh->b_data)) + break; + size += bh->b_size; + } + } + + /* did we run out of descriptors? */ + + if(count >= MAX_DMA_DESCRS) { + printk("%s: too few DMA descriptors\n", drive->name); + return 1; + } + + /* uh.. I'm lazy.. if size >= 65536, it should loop below and split it in + more than one descriptor */ + + if(size >= 65536) { + printk("too large ATA DMA request block, size = %d!\n", size); + return 1; + } + + /* however, this case is more difficult - R_ATA_TRANSFER_CNT cannot be more + than 65536 words per transfer, so in that case we need to either + 1) use a DMA interrupt to re-trigger R_ATA_TRANSFER_CNT and continue with + the descriptors, or + 2) simply do the request here, and get dma_intr to only ide_end_request on + those blocks that were actually set-up for transfer. + */ + + if(ata_tot_size + size >= 131072) { + printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, size); + return 1; + } + + /* ok we want to do IO at addr, size bytes. set up a new descriptor entry */ + + ata_descrs[count].sw_len = size; + ata_descrs[count].ctrl = 0; + ata_descrs[count].buf = addr; + ata_descrs[count].next = virt_to_phys(&ata_descrs[count + 1]); + count++; + ata_tot_size += size; + + } while (bh != NULL); + + if (count) { + /* set the end-of-list flag on the last descriptor */ + ata_descrs[count - 1].ctrl |= d_eol; + /* return and say all is ok */ + return 0; + } + + printk("%s: empty DMA table?\n", drive->name); + return 1; /* let the PIO routines handle this weirdness */ +} + +static int config_drive_for_dma (ide_drive_t *drive) +{ + const char **list; + struct hd_driveid *id = drive->id; + + if (id && (id->capability & 1)) { + /* Enable DMA on any drive that supports mword2 DMA */ + if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) { + drive->using_dma = 1; + return 0; /* DMA enabled */ + } + + /* Consult the list of known "good" drives */ + list = good_dma_drives; + while (*list) { + if (!strcmp(*list++,id->model)) { + drive->using_dma = 1; + return 0; /* DMA enabled */ + } + } + } + return 1; /* DMA not enabled */ +} + +/* + * etrax_dma_intr() is the handler for disk read/write DMA interrupts + */ +static ide_startstop_t etrax_dma_intr (ide_drive_t *drive) +{ + int i, dma_stat; + byte stat; + + LED_DISK_READ(0); + LED_DISK_WRITE(0); + + dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive); + stat = GET_STAT(); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + if (!dma_stat) { + struct request *rq; + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_end_request(1, HWGROUP(drive)); + } + return ide_stopped; + } + printk("%s: bad DMA status\n", drive->name); + } + return ide_error(drive, "dma_intr", stat); +} + +/* + * e100_dmaproc() initiates/aborts DMA read/write operations on a drive. + * + * The caller is assumed to have selected the drive and programmed the drive's + * sector address using CHS or LBA. All that remains is to prepare for DMA + * and then issue the actual read/write DMA/PIO command to the drive. + * + * For ATAPI devices, we just prepare for DMA and return. The caller should + * then issue the packet command to the drive and call us again with + * ide_dma_begin afterwards. + * + * Returns 0 if all went well. + * Returns 1 if DMA read/write could not be started, in which case + * the caller should revert to PIO for the current request. + */ + +static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + static unsigned int reading; /* static to support ide_dma_begin semantics */ + int atapi = 0; + + D(printk("e100_dmaproc func %d\n", func)); + + switch (func) { + case ide_dma_verbose: + return 0; + case ide_dma_check: + return config_drive_for_dma (drive); + case ide_dma_off: + case ide_dma_off_quietly: + /* ok.. we don't really need to do anything I think. */ + return 0; + case ide_dma_write: + reading = 0; + break; + case ide_dma_read: + reading = 1; + break; + case ide_dma_begin: + /* begin DMA, used by ATAPI devices which want to issue the + * appropriate IDE command themselves. + * + * they have already called ide_dma_read/write to set the + * static reading flag, now they call ide_dma_begin to do + * the real stuff. we tell our code below not to issue + * any IDE commands itself and jump into it. + */ + atapi++; + goto dma_begin; + case ide_dma_end: /* returns 1 on error, 0 otherwise */ + /* TODO: check if something went wrong with the DMA */ + return 0; + + default: + printk("e100_dmaproc: unsupported func %d\n", func); + return 1; + } + + /* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction + * then they call ide_dma_begin after they have issued the appropriate drive command + * themselves to actually start the chipset DMA. so we just return here if we're + * not a diskdrive. + */ + + if (drive->media != ide_disk) + return 0; + + dma_begin: + + if(reading) { + + RESET_DMA(3); /* sometimes the DMA channel get stuck so we need to do this */ + WAIT_DMA(3); + + /* set up the Etrax DMA descriptors */ + + if(e100_ide_build_dmatable (drive)) + return 1; + + if(!atapi) { + /* set the irq handler which will finish the request when DMA is done */ + + ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL); + + /* issue cmd to drive */ + + OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG); + } + + /* begin DMA */ + + *R_DMA_CH3_FIRST = virt_to_phys(ata_descrs); + *R_DMA_CH3_CMD = IO_STATE(R_DMA_CH3_CMD, cmd, start); + + /* initiate a multi word dma read using DMA handshaking */ + + *R_ATA_TRANSFER_CNT = ata_tot_size >> 1; + + *R_ATA_CTRL_DATA = IDE_DATA_REG | + IO_STATE(R_ATA_CTRL_DATA, rw, read) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_READ(1); + + D(printk("dma read of %d bytes.\n", ata_tot_size)); + + } else { + /* writing */ + + RESET_DMA(2); /* sometimes the DMA channel get stuck so we need to do this */ + WAIT_DMA(2); + + /* set up the Etrax DMA descriptors */ + + if(e100_ide_build_dmatable (drive)) + return 1; + + if(!atapi) { + /* set the irq handler which will finish the request when DMA is done */ + + ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL); + + /* issue cmd to drive */ + + OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG); + } + + /* begin DMA */ + + *R_DMA_CH2_FIRST = virt_to_phys(ata_descrs); + *R_DMA_CH2_CMD = IO_STATE(R_DMA_CH2_CMD, cmd, start); + + /* initiate a multi word dma write using DMA handshaking */ + + *R_ATA_TRANSFER_CNT = ata_tot_size >> 1; + + *R_ATA_CTRL_DATA = IDE_DATA_REG | + IO_STATE(R_ATA_CTRL_DATA, rw, write) | + IO_STATE(R_ATA_CTRL_DATA, src_dst, dma) | + IO_STATE(R_ATA_CTRL_DATA, handsh, dma) | + IO_STATE(R_ATA_CTRL_DATA, multi, on) | + IO_STATE(R_ATA_CTRL_DATA, dma_size, word); + + LED_DISK_WRITE(1); + + D(printk("dma write of %d bytes.\n", ata_tot_size)); + } + + /* DMA started successfully */ + return 0; +} + +/* ide.c calls this, but we don't need to do anything particular */ + +int ide_release_dma (ide_hwif_t *hwif) +{ + return 1; +} diff --git a/arch/cris/drivers/serial.c b/arch/cris/drivers/serial.c new file mode 100644 index 000000000..2dcb17a5e --- /dev/null +++ b/arch/cris/drivers/serial.c @@ -0,0 +1,3039 @@ +/* $Id: serial.c,v 1.6 2000/11/22 16:36:09 bjornw Exp $ + * + * Serial port driver for the ETRAX 100LX chip + * + * Copyright (C) 1998, 1999, 2000 Axis Communications AB + * + * Many, many authors. Based once upon a time on serial.c for 16x50. + * + * $Log: serial.c,v $ + * Revision 1.6 2000/11/22 16:36:09 bjornw + * Please marketing by using the correct case when spelling Etrax. + * + * Revision 1.5 2000/11/21 16:43:37 bjornw + * Fixed so it compiles under CONFIG_SVINTO_SIM + * + * Revision 1.4 2000/11/15 17:34:12 bjornw + * Added a timeout timer for flushing input channels. The interrupt-based + * fast flush system should be easy to merge with this later (works the same + * way, only with an irq instead of a system timer_list) + * + * Revision 1.3 2000/11/13 17:19:57 bjornw + * * Incredibly, this almost complete rewrite of serial.c worked (at least + * for output) the first time. + * + * Items worth noticing: + * + * No Etrax100 port 1 workarounds (does only compile on 2.4 anyway now) + * RS485 is not ported (why cant it be done in userspace as on x86 ?) + * Statistics done through async_icount - if any more stats are needed, + * that's the place to put them or in an arch-dep version of it. + * timeout_interrupt and the other fast timeout stuff not ported yet + * There be dragons in this 3k+ line driver + * + * Revision 1.2 2000/11/10 16:50:28 bjornw + * First shot at a 2.4 port, does not compile totally yet + * + * Revision 1.1 2000/11/10 16:47:32 bjornw + * Added verbatim copy of rev 1.49 etrax100ser.c from elinux + * + * Revision 1.49 2000/10/30 15:47:14 tobiasa + * Changed version number. + * + * Revision 1.48 2000/10/25 11:02:43 johana + * Changed %ul to %lu in printf's + * + * Revision 1.47 2000/10/18 15:06:53 pkj + * Compile correctly with CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST and + * CONFIG_SERIAL_PROC_ENTRY together. + * Some clean-up of the /proc/serial file. + * + * Revision 1.46 2000/10/16 12:59:40 johana + * Added CONFIG_SERIAL_PROC_ENTRY for statistics and debug info. + * + * Revision 1.45 2000/10/13 17:10:59 pkj + * Do not flush DMAs while flipping TTY buffers. + * + * Revision 1.44 2000/10/13 16:34:29 pkj + * Added a delay in ser_interrupt() for 2.3ms when an error is detected. + * We do not know why this delay is required yet, but without it the + * irmaflash program does not work (this was the program that needed + * the ser_interrupt() to be needed in the first place). This should not + * affect normal use of the serial ports. + * + * Revision 1.43 2000/10/13 16:30:44 pkj + * New version of the fast flush of serial buffers code. This time + * it is localized to the serial driver and uses a fast timer to + * do the work. + * + * Revision 1.42 2000/10/13 14:54:26 bennyo + * Fix for switching RTS when using rs485 + * + * Revision 1.41 2000/10/12 11:43:44 pkj + * Cleaned up a number of comments. + * + * Revision 1.40 2000/10/10 11:58:39 johana + * Made RS485 support generic for all ports. + * Toggle rts in interrupt if no delay wanted. + * WARNING: No true transmitter empty check?? + * Set d_wait bit when sending data so interrupt is delayed until + * fifo flushed. (Fix tcdrain() problem) + * + * Revision 1.39 2000/10/04 16:08:02 bjornw + * * Use virt_to_phys etc. for DMA addresses + * * Removed CONFIG_FLUSH_DMA_FAST hacks + * * Indentation fix + * + * Revision 1.38 2000/10/02 12:27:10 mattias + * * added variable used when using fast flush on serial dma. + * (CONFIG_FLUSH_DMA_FAST) + * + * Revision 1.37 2000/09/27 09:44:24 pkj + * Uncomment definition of SERIAL_HANDLE_EARLY_ERRORS. + * + * Revision 1.36 2000/09/20 13:12:52 johana + * Support for CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS: + * Number of timer ticks between flush of receive fifo (1 tick = 10ms). + * Try 0-3 for low latency applications. Approx 5 for high load + * applications (e.g. PPP). Maybe this should be more adaptive some day... + * + * Revision 1.35 2000/09/20 10:36:08 johana + * Typo in get_lsr_info() + * + * Revision 1.34 2000/09/20 10:29:59 johana + * Let rs_chars_in_buffer() check fifo content as well. + * get_lsr_info() might work now (not tested). + * Easier to change the port to debug. + * + * Revision 1.33 2000/09/13 07:52:11 torbjore + * Support RS485 + * + * Revision 1.32 2000/08/31 14:45:37 bjornw + * After sending a break we need to reset the transmit DMA channel + * + * Revision 1.31 2000/06/21 12:13:29 johana + * Fixed wait for all chars sent when closing port. + * (Used to always take 1 second!) + * Added shadows for directions of status/ctrl signals. + * + * Revision 1.30 2000/05/29 16:27:55 bjornw + * Simulator ifdef moved a bit + * + * Revision 1.29 2000/05/09 09:40:30 mattias + * * Added description of dma registers used in timeout_interrupt + * * Removed old code + * + * Revision 1.28 2000/05/08 16:38:58 mattias + * * Bugfix for flushing fifo in timeout_interrupt + * Problem occurs when bluetooth stack waits for a small number of bytes + * containing an event acknowledging free buffers in bluetooth HW + * As before, data was stuck in fifo until more data came on uart and + * flushed it up to the stack. + * + * Revision 1.27 2000/05/02 09:52:28 jonasd + * Added fix for peculiar etrax behaviour when eop is forced on an empty + * fifo. This is used when flashing the IRMA chip. Disabled by default. + * + * Revision 1.26 2000/03/29 15:32:02 bjornw + * 2.0.34 updates + * + * Revision 1.25 2000/02/16 16:59:36 bjornw + * * Receive DMA directly into the flip-buffer, eliminating an intermediary + * receive buffer and a memcpy. Will avoid some overruns. + * * Error message on debug port if an overrun or flip buffer overrun occurs. + * * Just use the first byte in the flag flip buffer for errors. + * * Check for timeout on the serial ports only each 5/100 s, not 1/100. + * + * Revision 1.24 2000/02/09 18:02:28 bjornw + * * Clear serial errors (overrun, framing, parity) correctly. Before, the + * receiver would get stuck if an error occured and we did not restart + * the input DMA. + * * Cosmetics (indentation, some code made into inlines) + * * Some more debug options + * * Actually shut down the serial port (DMA irq, DMA reset, receiver stop) + * when the last open is closed. Corresponding fixes in startup(). + * * rs_close() "tx FIFO wait" code moved into right place, bug & -> && fixed + * and make a special case out of port 1 (R_DMA_CHx_STATUS is broken for that) + * * e100_disable_rx/enable_rx just disables/enables the receiver, not RTS + * + * Revision 1.23 2000/01/24 17:46:19 johana + * Wait for flush of DMA/FIFO when closing port. + * + * Revision 1.22 2000/01/20 18:10:23 johana + * Added TIOCMGET ioctl to return modem status. + * Implemented modem status/control that works with the extra signals + * (DTR, DSR, RI,CD) as well. + * 3 different modes supported: + * ser0 on PB (Bundy), ser1 on PB (Lisa) and ser2 on PA (Bundy) + * Fixed DEF_TX value that caused the serial transmitter pin (txd) to go to 0 when + * closing the last filehandle, NASTY!. + * Added break generation, not tested though! + * Use SA_SHIRQ when request_irq() for ser2 and ser3 (shared with) par0 and par1. + * You can't use them at the same time (yet..), but you can hopefully switch + * between ser2/par0, ser3/par1 with the same kernel config. + * Replaced some magic constants with defines + * + * + */ + +static char *serial_version = "$Revision: 1.6 $"; + +#include <linux/config.h> +#include <linux/version.h> + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/timer.h> +#include <linux/interrupt.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/major.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/mm.h> +#if (LINUX_VERSION_CODE >= 131343) +#include <linux/init.h> +#endif +#if (LINUX_VERSION_CODE >= 131336) +#include <asm/uaccess.h> +#endif +#include <linux/kernel.h> + +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/bitops.h> +#include <asm/delay.h> + +#include <asm/svinto.h> + +/* non-arch dependant serial structures are in linux/serial.h */ +#include <linux/serial.h> +/* while we keep our own stuff (struct e100_serial) in a local .h file */ +#include "serial.h" + +/* + * All of the compatibilty code so we can compile serial.c against + * older kernels is hidden in serial_compat.h + */ +#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ +#include "serial_compat.h" +#endif + +static DECLARE_TASK_QUEUE(tq_serial); + +struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +/* serial subtype definitions */ +#ifndef SERIAL_TYPE_NORMAL +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#endif + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +//#define SERIAL_DEBUG_INTR +//#define SERIAL_DEBUG_OPEN +//#define SERIAL_DEBUG_FLOW +//#define SERIAL_DEBUG_DATA +//#define SERIAL_DEBUG_THROTTLE +//#define SERIAL_DEBUG_IO /* Debug for Extra control and status pins */ +#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */ + +/* Enable this to use serial interrupts to handle when you + expect the first received event on the serial port to + be an error, break or similar. Used to be able to flash IRMA + from eLinux */ +//#define SERIAL_HANDLE_EARLY_ERRORS + + +#ifndef CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS +/* Default number of timer ticks before flushing rx fifo + * When using "little data, low latency applications: use 0 + * When using "much data applications (PPP)" use ~5 + */ +#define CONFIG_ETRAX100_SERIAL_RX_TIMEOUT_TICKS 5 +#endif + +#define MAX_FLUSH_TIME 8 + +#define _INLINE_ inline + +static void change_speed(struct e100_serial *info); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count); + +#define DEF_BAUD 0x99 /* 115.2 kbit/s */ +#define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST ) +#define DEF_RX 0x20 /* or SERIAL_CTRL_W >> 8 */ +/* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */ +#define DEF_TX 0x80 /* or SERIAL_CTRL_B */ + +/* offsets from R_SERIALx_CTRL */ + +#define REG_DATA 0 +#define REG_TR_DATA 0 +#define REG_STATUS 1 +#define REG_TR_CTRL 1 +#define REG_REC_CTRL 2 +#define REG_BAUD 3 +#define REG_XOFF 4 /* this is a 32 bit register */ + +/* this is the data for the four serial ports in the etrax100 */ +/* DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */ +/* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */ + +static struct e100_serial rs_table[] = { + { DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */ + R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD, + R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, + R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD, + R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, + STD_FLAGS, DEF_RX, DEF_TX, 2 }, /* ttyS0 */ +#ifndef CONFIG_SVINTO_SIM + { DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */ + R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD, + R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, + R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD, + R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, + STD_FLAGS, DEF_RX, DEF_TX, 3 }, /* ttyS1 */ + + { DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4, /* uses DMA 2 and 3 */ + R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD, + R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, + R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD, + R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, + STD_FLAGS, DEF_RX, DEF_TX, 0 }, /* ttyS2 */ + + { DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8, /* uses DMA 4 and 5 */ + R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD, + R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, + R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD, + R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, + STD_FLAGS, DEF_RX, DEF_TX, 1 } /* ttyS3 */ +#endif +}; + + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct e100_serial)) + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + + +/* RS-485 */ +#if defined(CONFIG_RS485) +#if defined(CONFIG_RS485_ON_PA) +static int rs485_pa_bit = CONFIG_RS485_ON_PA_BIT; +#endif +#endif + + +/* For now we assume that all bits are on the same port for each serial port */ + +/* Dummy shadow variables */ +static unsigned char dummy_ser0 = 0x00; +static unsigned char dummy_ser1 = 0x00; +static unsigned char dummy_ser2 = 0x00; +static unsigned char dummy_ser3 = 0x00; + +static unsigned char dummy_dir_ser0 = 0x00; +static unsigned char dummy_dir_ser1 = 0x00; +static unsigned char dummy_dir_ser2 = 0x00; +static unsigned char dummy_dir_ser3 = 0x00; + +/* Info needed for each ports extra control/status signals. + We only supports that all pins uses same register for each port */ +struct control_pins +{ + volatile unsigned char *port; + volatile unsigned char *shadow; + volatile unsigned char *dir_shadow; + + unsigned char dtr_bit; + unsigned char ri_bit; + unsigned char dsr_bit; + unsigned char cd_bit; +}; + +static const struct control_pins e100_modem_pins[NR_PORTS] = +{ +/* Ser 0 */ + { +#if defined(CONFIG_ETRAX100_SER0_DTR_RI_DSR_CD_ON_PB) + R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow, + CONFIG_ETRAX100_SER0_DTR_ON_PB_BIT, + CONFIG_ETRAX100_SER0_RI_ON_PB_BIT, + CONFIG_ETRAX100_SER0_DSR_ON_PB_BIT, + CONFIG_ETRAX100_SER0_CD_ON_PB_BIT +#else + &dummy_ser0, &dummy_ser0, &dummy_dir_ser0, 0, 1, 2, 3 +#endif + }, +/* Ser 1 */ + { +#if defined(CONFIG_ETRAX100_SER1_DTR_RI_DSR_CD_ON_PB) + R_PORT_PB_DATA, &port_pb_data_shadow, &port_pb_dir_shadow, + CONFIG_ETRAX100_SER1_DTR_ON_PB_BIT, + CONFIG_ETRAX100_SER1_RI_ON_PB_BIT, + CONFIG_ETRAX100_SER1_DSR_ON_PB_BIT, + CONFIG_ETRAX100_SER1_CD_ON_PB_BIT +#else + &dummy_ser1, &dummy_ser1, &dummy_dir_ser1, 0, 1, 2, 3 +#endif + }, +/* Ser 2 */ + { +#if defined(CONFIG_ETRAX100_SER2_DTR_RI_DSR_CD_ON_PA) + R_PORT_PA_DATA, &port_pa_data_shadow, &port_pa_dir_shadow, + CONFIG_ETRAX100_SER2_DTR_ON_PA_BIT, + CONFIG_ETRAX100_SER2_RI_ON_PA_BIT, + CONFIG_ETRAX100_SER2_DSR_ON_PA_BIT, + CONFIG_ETRAX100_SER2_CD_ON_PA_BIT +#else + &dummy_ser2, &dummy_ser2, &dummy_dir_ser2, 0, 1, 2, 3 +#endif + }, +/* Ser 3 */ + { + &dummy_ser3, &dummy_ser3, &dummy_dir_ser3, 0, 1, 2, 3 + } +}; + +#if defined(CONFIG_RS485) && defined(CONFIG_RS485_ON_PA) +unsigned char rs485_pa_port = CONFIG_RS485_ON_PA_BIT; +#endif + +#define E100_RTS_MASK 0x20 +#define E100_CTS_MASK 0x40 + + +/* All serial port signals are active low: + * active = 0 -> 3.3V to RS-232 driver -> -12V on RS-232 level + * inactive = 1 -> 0V to RS-232 driver -> +12V on RS-232 level + * + * These macros returns the pin value: 0=0V, >=1 = 3.3V on ETRAX chip + */ + +/* Output */ +#define E100_RTS_GET(info) ((info)->rx_ctrl & E100_RTS_MASK) +/* Input */ +#define E100_CTS_GET(info) ((info)->port[REG_STATUS] & E100_CTS_MASK) + +/* These are typically PA or PB and 0 means 0V, 1 means 3.3V */ +/* Is an output */ +#define E100_DTR_GET(info) ((*e100_modem_pins[(info)->line].shadow) & (1 << e100_modem_pins[(info)->line].dtr_bit)) + +/* Normally inputs */ +#define E100_RI_GET(info) ((*e100_modem_pins[(info)->line].port) & (1 << e100_modem_pins[(info)->line].ri_bit)) +#define E100_CD_GET(info) ((*e100_modem_pins[(info)->line].port) & (1 << e100_modem_pins[(info)->line].cd_bit)) + +/* Input */ +#define E100_DSR_GET(info) ((*e100_modem_pins[(info)->line].port) & (1 << e100_modem_pins[(info)->line].dsr_bit)) + + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the memcpy_fromfs blocks while swapping in a page, + * and some other program tries to do a serial write at the same time. + * Since the lock will only come under contention when the system is + * swapping and available memory is low, it makes sense to share one + * buffer across all the serial ports, since it significantly saves + * memory if large numbers of serial ports are open. + */ +static unsigned char *tmp_buf; +#ifdef DECLARE_MUTEX +static DECLARE_MUTEX(tmp_buf_sem); +#else +static struct semaphore tmp_buf_sem = MUTEX; +#endif + +#ifdef CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST +#define TIMER1_IRQ_NBR 3 + +/* clock select 10 for timer 1 gives 230400 Hz */ +#define FASTTIMER_SELECT (10) +/* we use a source of 230400 Hz and a divider of 15 => 15360 Hz */ +#define FASTTIMER_DIV (15) + +/* fast flush timer stuff */ +static int fast_timer_started = 0; +static unsigned long int fast_timer_ints = 0; + +static void _INLINE_ start_flush_timer(void) +{ + if (fast_timer_started) + return; + + *R_TIMER_CTRL = r_timer_ctrl_shadow = + (r_timer_ctrl_shadow & + ~IO_MASK(R_TIMER_CTRL, timerdiv1) & + ~IO_MASK(R_TIMER_CTRL, tm1) & + ~IO_MASK(R_TIMER_CTRL, clksel1)) | + IO_FIELD(R_TIMER_CTRL, timerdiv1, FASTTIMER_DIV) | + IO_STATE(R_TIMER_CTRL, tm1, stop_ld) | + IO_FIELD(R_TIMER_CTRL, clksel1, FASTTIMER_SELECT); + + *R_TIMER_CTRL = r_timer_ctrl_shadow = + (r_timer_ctrl_shadow & ~IO_MASK(R_TIMER_CTRL, tm1)) | + IO_STATE(R_TIMER_CTRL, tm1, run); + + /* enable timer1 irq */ + + *R_IRQ_MASK0_SET = IO_STATE(R_IRQ_MASK0_SET, timer1, set); + fast_timer_started = 1; +} +#endif /* CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST */ + +/* + * This function maps from the Bxxxx defines in asm/termbits.h into real + * baud rates. + */ + +static int +cflag_to_baud(unsigned int cflag) +{ + static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, + 4800, 9600, 19200, 38400 }; + + static int ext_baud_table[] = { + 0, 57600, 115200, 230400, 460800, 921600, 1843200, 6250000 }; + + if(cflag & CBAUDEX) + return ext_baud_table[(cflag & CBAUD) & ~CBAUDEX]; + else + return baud_table[cflag & CBAUD]; +} + +/* and this maps to an etrax100 hardware baud constant */ + +static unsigned char +cflag_to_etrax_baud(unsigned int cflag) +{ + char retval; + + static char baud_table[] = { + -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, -1, 3, 4, 5, 6, 7 }; + + static char ext_baud_table[] = { + -1, 8, 9, 10, 11, 12, 13, 14 }; + + if(cflag & CBAUDEX) + retval = ext_baud_table[(cflag & CBAUD) & ~CBAUDEX]; + else + retval = baud_table[cflag & CBAUD]; + + if(retval < 0) { + printk("serdriver tried setting invalid baud rate, flags %x.\n", cflag); + retval = 5; /* choose default 9600 instead */ + } + + return retval | (retval << 4); /* choose same for both TX and RX */ +} + + +/* Various static support functions */ + +/* Functions to set or clear DTR/RTS on the requested line */ +/* It is complicated by the fact that RTS is a serial port register, while + * DTR might not be implemented in the HW at all, and if it is, it can be on + * any general port. + */ + +static inline void +e100_dtr(struct e100_serial *info, int set) +{ +#ifndef CONFIG_SVINTO_SIM + unsigned char mask = ( 1 << e100_modem_pins[info->line].dtr_bit); +#ifdef SERIAL_DEBUG_IO + printk("ser%i dtr %i mask: 0x%02X\n", info->line, set, mask); + printk("ser%i shadow before 0x%02X get: %i\n", + info->line, *e100_modem_pins[info->line].shadow, + E100_DTR_GET(info)); +#endif + /* DTR is active low */ + { + unsigned long flags; + save_flags(flags); + cli(); + *e100_modem_pins[info->line].shadow &= ~mask; + *e100_modem_pins[info->line].shadow |= (set ? 0 : mask); + *e100_modem_pins[info->line].port = *e100_modem_pins[info->line].shadow; + restore_flags(flags); + } + +#if 0 + REG_SHADOW_SET(e100_modem_pins[info->line].port, + *e100_modem_pins[info->line].shadow, + e100_modem_pins[info->line].dtr_bit, !set); +#endif +#ifdef SERIAL_DEBUG_IO + printk("ser%i shadow after 0x%02X get: %i\n", + info->line, *e100_modem_pins[info->line].shadow, + E100_DTR_GET(info)); +#endif +#endif +} + +/* set = 0 means 3.3V on the pin, bitvalue: 0=active, 1=inactive + * 0=0V , 1=3.3V + */ +static inline void +e100_rts(struct e100_serial *info, int set) +{ +#ifndef CONFIG_SVINTO_SIM +#ifdef SERIAL_DEBUG_IO + printk("ser%i rts %i\n", info->line, set); +#endif + info->rx_ctrl &= ~E100_RTS_MASK; + info->rx_ctrl |= (set ? 0 : E100_RTS_MASK); /* RTS is active low */ + info->port[REG_REC_CTRL] = info->rx_ctrl; +#endif +} + +/* If this behaves as a modem, RI and CD is an output */ +static inline void +e100_ri_out(struct e100_serial *info, int set) +{ +#ifndef CONFIG_SVINTO_SIM + /* RI is active low */ + { + unsigned char mask = ( 1 << e100_modem_pins[info->line].ri_bit); + unsigned long flags; + save_flags(flags); + cli(); + *e100_modem_pins[info->line].shadow &= ~mask; + *e100_modem_pins[info->line].shadow |= (set ? 0 : mask); + *e100_modem_pins[info->line].port = *e100_modem_pins[info->line].shadow; + restore_flags(flags); + } +#if 0 + REG_SHADOW_SET(e100_modem_pins[info->line].port, + *e100_modem_pins[info->line].shadow, + e100_modem_pins[info->line].ri_bit, !set); +#endif +#endif +} +static inline void +e100_cd_out(struct e100_serial *info, int set) +{ +#ifndef CONFIG_SVINTO_SIM + /* CD is active low */ + { + unsigned char mask = ( 1 << e100_modem_pins[info->line].cd_bit); + unsigned long flags; + save_flags(flags); + cli(); + *e100_modem_pins[info->line].shadow &= ~mask; + *e100_modem_pins[info->line].shadow |= (set ? 0 : mask); + *e100_modem_pins[info->line].port = *e100_modem_pins[info->line].shadow; + restore_flags(flags); + } +#if 0 + REG_SHADOW_SET(e100_modem_pins[info->line].port, + *e100_modem_pins[info->line].shadow, + e100_modem_pins[info->line].cd_bit, !set); +#endif +#endif +} + +static inline void +e100_disable_rx(struct e100_serial *info) +{ +#ifndef CONFIG_SVINTO_SIM + /* disable the receiver */ + info->port[REG_REC_CTRL] = (info->rx_ctrl &= ~0x40); +#endif +} + +static inline void +e100_enable_rx(struct e100_serial *info) +{ +#ifndef CONFIG_SVINTO_SIM + /* enable the receiver */ + info->port[REG_REC_CTRL] = (info->rx_ctrl |= 0x40); +#endif +} + +/* the rx DMA uses both the dma_descr and the dma_eop interrupts */ + +static inline void +e100_disable_rxdma_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("rxdma_irq(%d): 0\n",info->line); +#endif + *R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3); +} + +static inline void +e100_enable_rxdma_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("rxdma_irq(%d): 1\n",info->line); +#endif + *R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3); +} + +/* the tx DMA uses only dma_descr interrupt */ + +static inline void +e100_disable_txdma_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("txdma_irq(%d): 0\n",info->line); +#endif + *R_IRQ_MASK2_CLR = info->irq; +} + +static inline void +e100_enable_txdma_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("txdma_irq(%d): 1\n",info->line); +#endif + *R_IRQ_MASK2_SET = info->irq; +} + +#ifdef SERIAL_HANDLE_EARLY_ERRORS +/* in order to detect and fix errors on the first byte + we have to use the serial interrupts as well. */ + +static inline void +e100_disable_serial_data_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("ser_irq(%d): 0\n",info->line); +#endif + *R_IRQ_MASK1_CLR = (1U << (8+2*info->line)); +} + +static inline void +e100_enable_serial_data_irq(struct e100_serial *info) +{ +#ifdef SERIAL_DEBUG_INTR + printk("ser_irq(%d): 1\n",info->line); + printk("**** %d = %d\n", + (8+2*info->line), + (1U << (8+2*info->line))); +#endif + *R_IRQ_MASK1_SET = (1U << (8+2*info->line)); +} +#endif + +#if defined(CONFIG_RS485) +/* Enable RS-485 mode on selected port. This is UGLY. */ +static int +e100_enable_rs485(struct tty_struct *tty,struct rs485_control *r) +{ + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + +#if defined(CONFIG_RS485_ON_PA) + *R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit); +#endif + + info->rs485.rts_on_send = 0x01 & r->rts_on_send; + info->rs485.rts_after_sent = 0x01 & r->rts_after_sent; + info->rs485.delay_rts_before_send = r->delay_rts_before_send; + info->rs485.enabled = r->enabled; + + return 0; +} + +/* Enable RS-485 mode on selected port. This is UGLY. */ +static int +e100_write_rs485(struct tty_struct *tty,struct rs485_write *r) +{ + int stop_delay; + int total, i; + int max_j, delay_ms, bits; + tcflag_t cflags; + int size = (*r).outc_size; + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + struct wait_queue wait = { current, NULL }; + + /* If we are in RS-485 mode, we need to toggle RTS and disable + * the receiver before initiating a DMA transfer + */ + e100_rts(info, info->rs485.rts_on_send); +#if defined(CONFIG_RS485_DISABLE_RECEIVER) + e100_disable_rx(info); + e100_disable_rxdma_irq(info); +#endif + + if (info->rs485.delay_rts_before_send > 0){ + current->timeout = jiffies + (info->rs485.delay_rts_before_send * HZ)/1000; + current->state = TASK_INTERRUPTIBLE; + schedule(); + current->timeout = 0; + } + total = rs_write(tty, 1, (*r).outc, (*r).outc_size); + + /* If we are in RS-485 mode the following things has to be done: + * wait until DMA is ready + * wait on transmit shift register + * wait to toggle RTS + * enable the receiver + */ + + /* wait on transmit shift register */ + /* All is sent, check if we should wait more before toggling rts */ + + /* calc. number of bits / data byte */ + cflags = info->tty->termios->c_cflag; + /* databits + startbit and 1 stopbit */ + if((cflags & CSIZE) == CS7) + bits = 9; + else + bits = 10; + + if(cflags & CSTOPB) /* 2 stopbits ? */ + bits++; + + if(cflags & PARENB) /* parity bit ? */ + bits++; + + /* calc timeout */ + delay_ms = ((bits * size * 1000) / info->baud) + 1; + max_j = jiffies + (delay_ms * HZ)/1000 + 10; + + while (jiffies < max_j ) { + if (info->port[REG_STATUS] & 0x20) { + for( i=0 ; i<100; i++ ) {}; + if (info->port[REG_STATUS] & 0x20) { + /* ~25 for loops per usec */ + stop_delay = 25 * (1000000 / info->baud); + if(cflags & CSTOPB) + stop_delay *= 2; + for( i=0 ; i<stop_delay; i++ ) {}; + break; + } + } + } + + e100_rts(info, info->rs485.rts_after_sent); + +#if defined(CONFIG_RS485_DISABLE_RECEIVER) + e100_enable_rx(info); + e100_enable_rxdma_irq(info); +#endif + + return total; +} +#endif + +/* + * ------------------------------------------------------------ + * rs_stop() and rs_start() + * + * This routines are called before setting or resetting tty->stopped. + * They enable or disable transmitter interrupts, as necessary. + * ------------------------------------------------------------ + */ + +/* FIXME - when are these used and what is the purpose ? + * In rs_stop we probably just can block the transmit DMA ready irq + * and in rs_start we re-enable it (and then the old one will come). + */ + +static void +rs_stop(struct tty_struct *tty) +{ +} + +static void +rs_start(struct tty_struct *tty) +{ +} + +/* + * ---------------------------------------------------------------------- + * + * Here starts the interrupt handling routines. All of the following + * subroutines are declared as inline and are folded into + * rs_interrupt(). They were separated out for readability's sake. + * + * Note: rs_interrupt() is a "fast" interrupt, which means that it + * runs with interrupts turned off. People who may want to modify + * rs_interrupt() should try to keep the interrupt handler as fast as + * possible. After you are done making modifications, it is not a bad + * idea to do: + * + * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c + * + * and look at the resulting assemble code in serial.s. + * + * - Ted Ts'o (tytso@mit.edu), 7-Mar-93 + * ----------------------------------------------------------------------- + */ + +/* + * This routine is used by the interrupt handler to schedule + * processing in the software interrupt portion of the driver. + */ +static _INLINE_ void +rs_sched_event(struct e100_serial *info, + int event) +{ + info->event |= 1 << event; + queue_task(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +/* The output DMA channel is free - use it to send as many chars as possible + * NOTES: + * We don't pay attention to info->x_char, which means if the TTY wants to + * use XON/XOFF it will set info->x_char but we won't send any X char! + * + * To implement this, we'd just start a DMA send of 1 byte pointing at a + * buffer containing the X char, and skip updating xmit. We'd also have to + * check if the last sent char was the X char when we enter this function + * the next time, to avoid updating xmit with the sent X value. + */ + +static void +transmit_chars(struct e100_serial *info) +{ + unsigned int c, sentl; + struct etrax_dma_descr *descr; + +#ifdef CONFIG_SVINTO_SIM + /* This will output too little if tail is not 0 always since + * we don't reloop to send the other part. Anyway this SHOULD be a + * no-op - transmit_chars would never really be called during sim + * since rs_write does not write into the xmit buffer then. + */ + if(info->xmit.tail) + printk("Error in serial.c:transmit_chars(), tail!=0\n"); + if(info->xmit.head != info->xmit.tail) { + SIMCOUT(info->xmit.buf + info->xmit.tail, + CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE)); + info->xmit.head = info->xmit.tail; /* move back head */ + info->tr_running = 0; + } + return; +#endif + /* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */ + *info->oclrintradr = 3; + +#ifdef SERIAL_DEBUG_INTR + if(info->line == SERIAL_DEBUG_LINE) + printk("tc\n"); +#endif + if(!info->tr_running) { + /* weirdo... we shouldn't get here! */ + printk("Achtung: transmit_chars with !tr_running\n"); + return; + } + + descr = &info->tr_descr; + + /* first get the amount of bytes sent during the last DMA transfer, + and update xmit accordingly */ + + /* if the stop bit was not set, all data has been sent */ + if(!(descr->status & d_stop)) { + sentl = descr->sw_len; + } else + /* otherwise we find the amount of data sent here */ + sentl = descr->hw_len; + + /* update stats */ + info->icount.tx += sentl; + + /* update xmit buffer */ + info->xmit.tail = (info->xmit.tail + sentl) & (SERIAL_XMIT_SIZE - 1); + + /* if there is only a few chars left in the buf, wake up the blocked + write if any */ + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < WAKEUP_CHARS) + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + + /* find out the largest amount of consecutive bytes we want to send now */ + + c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); + + if(c <= 0) { + /* our job here is done, don't schedule any new DMA transfer */ + info->tr_running = 0; + +#if defined(CONFIG_RS485) + /* Check if we should toggle RTS now */ + if (info->rs485.enabled) + { + /* Make sure fifo is empty */ + int in_fifo = 0 ; + do{ + in_fifo = (*info->ostatusadr) & 0x007F ; + } while (in_fifo > 0) ; + /* Any way to really check transmitter empty? (TEMT) */ + /* Control RTS to set to RX mode */ + e100_rts(info, info->rs485.rts_after_sent); +#if defined(CONFIG_RS485_DISABLE_RECEIVER) + e100_enable_rx(info); + e100_enable_rxdma_irq(info); +#endif + } +#endif /* RS485 */ + + return; + } + + /* ok we can schedule a dma send of c chars starting at info->xmit.tail */ + /* set up the descriptor correctly for output */ + + descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */ + descr->sw_len = c; + descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail); + descr->status = 0; + + *info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */ + *info->ocmdadr = 1; /* dma command start -> R_DMAx_CMD */ + + /* DMA is now running (hopefully) */ + +} + +static void +start_transmit(struct e100_serial *info) +{ +#if 0 + if(info->line == SERIAL_DEBUG_LINE) + printk("x\n"); +#endif + + info->tr_descr.sw_len = 0; + info->tr_descr.hw_len = 0; + info->tr_descr.status = 0; + info->tr_running = 1; + + transmit_chars(info); +} + + +static _INLINE_ void +receive_chars(struct e100_serial *info) +{ + struct tty_struct *tty; + unsigned char rstat; + unsigned int recvl; + struct etrax_dma_descr *descr; + +#ifdef CONFIG_SVINTO_SIM + /* No receive in the simulator. Will probably be when the rest of + * the serial interface works, and this piece will just be removed. + */ + return; +#endif + + tty = info->tty; + + /* acknowledge both a dma_descr and dma_eop irq in R_DMAx_CLRINTR */ + + *info->iclrintradr = 3; + + if(!tty) /* something wrong... */ + return; + + descr = &info->rec_descr; + + /* find out how many bytes were read */ + + /* if the eop bit was not set, all data has been received */ + if(!(descr->status & d_eop)) { + recvl = descr->sw_len; + } else { + /* otherwise we find the amount of data received here */ + recvl = descr->hw_len; + } + if(recvl) { + unsigned char *buf; + struct async_icount *icount; + + icount = &info->icount; + + /* update stats */ + icount->rx += recvl; + + /* read the status register so we can detect errors */ + rstat = info->port[REG_STATUS]; + + if(rstat & 0xe) { + /* if we got an error, we must reset it by reading the + * data_in field + */ + (void)info->port[REG_DATA]; + } + + /* we only ever write errors into the first byte in the flip + * flag buffer, so we dont have to clear it all every time + */ + + if(rstat & 0x04) { + icount->parity++; + *tty->flip.flag_buf_ptr = TTY_PARITY; + } else if(rstat & 0x08) { + icount->overrun++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + } else if(rstat & 0x02) { + icount->frame++; + *tty->flip.flag_buf_ptr = TTY_FRAME; + } else + *tty->flip.flag_buf_ptr = 0; + + /* use the flip buffer next in turn to restart DMA into */ + + if (tty->flip.buf_num) { + buf = tty->flip.char_buf; + } else { + buf = tty->flip.char_buf + TTY_FLIPBUF_SIZE; + } + + if(buf == phys_to_virt(descr->buf)) { + printk("ttyS%d flip-buffer overrun!\n", info->line); + icount->overrun++; + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + /* restart old buffer */ + } else { + descr->buf = virt_to_phys(buf); + + /* schedule or push a flip of the buffer */ + + info->tty->flip.count = recvl; + +#if (LINUX_VERSION_CODE > 131394) /* 2.1.66 */ + /* this includes a check for low-latency */ + tty_flip_buffer_push(tty); +#else + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +#endif + } + } + + /* restart the receiving dma */ + + descr->sw_len = TTY_FLIPBUF_SIZE; + descr->ctrl = d_int | d_eol | d_eop; + descr->hw_len = 0; + descr->status = 0; + + *info->ifirstadr = virt_to_phys(descr); + *info->icmdadr = 1; /* start */ + +#ifdef SERIAL_HANDLE_EARLY_ERRORS + e100_enable_serial_data_irq(info); +#endif + /* input dma should be running now */ +} + +static void +start_receive(struct e100_serial *info) +{ + struct etrax_dma_descr *descr; + +#ifdef CONFIG_SVINTO_SIM + /* No receive in the simulator. Will probably be when the rest of + * the serial interface works, and this piece will just be removed. + */ + return; +#endif + + /* reset the input dma channel to be sure it works */ + + *info->icmdadr = 4; + while((*info->icmdadr & 7) == 4); + + descr = &info->rec_descr; + + /* start the receiving dma into the flip buffer */ + + descr->ctrl = d_int | d_eol | d_eop; + descr->sw_len = TTY_FLIPBUF_SIZE; + descr->buf = virt_to_phys(info->tty->flip.char_buf_ptr); + descr->hw_len = 0; + descr->status = 0; + + info->tty->flip.count = 0; + + *info->ifirstadr = virt_to_phys(descr); + *info->icmdadr = 1; /* start */ +} + + +static _INLINE_ void +status_handle(struct e100_serial *info, unsigned short status) +{ +} + +/* the bits in the MASK2 register are laid out like this: + DMAI_EOP DMAI_DESCR DMAO_EOP DMAO_DESCR + where I is the input channel and O is the output channel for the port. + info->irq is the bit number for the DMAO_DESCR so to check the others we + shift info->irq to the left. +*/ + +/* dma output channel interrupt handler + this interrupt is called from DMA2(ser2), DMA4(ser3), DMA6(ser0) or + DMA8(ser1) when they have finished a descriptor with the intr flag set. +*/ + +static void +tr_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct e100_serial *info; + unsigned long ireg; + int i; + +#ifdef CONFIG_SVINTO_SIM + /* No receive in the simulator. Will probably be when the rest of + * the serial interface works, and this piece will just be removed. + */ + { + const char *s = "What? tr_interrupt in simulator??\n"; + SIMCOUT(s,strlen(s)); + } + return; +#endif + + /* find out the line that caused this irq and get it from rs_table */ + + ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */ + + for(i = 0; i < NR_PORTS; i++) { + info = rs_table + i; + /* check for dma_descr (dont need to check for dma_eop in output dma for serial */ + if(ireg & info->irq) { + /* we can send a new dma bunch. make it so. */ + transmit_chars(info); + } + + /* FIXME: here we should really check for a change in the + status lines and if so call status_handle(info) */ + } +} + +/* dma input channel interrupt handler */ + +static void +rec_interrupt(int irq, void *dev_id, struct pt_regs * regs) +{ + struct e100_serial *info; + unsigned long ireg; + int i; + +#ifdef CONFIG_SVINTO_SIM + /* No receive in the simulator. Will probably be when the rest of + * the serial interface works, and this piece will just be removed. + */ + { + const char *s = "What? rec_interrupt in simulator??\n"; + SIMCOUT(s,strlen(s)); + } + return; +#endif + + /* find out the line that caused this irq and get it from rs_table */ + + ireg = *R_IRQ_MASK2_RD; /* get the active irq bits for the dma channels */ + + for(i = 0; i < NR_PORTS; i++) { + info = rs_table + i; + /* check for both dma_eop and dma_descr for the input dma channel */ + if(ireg & ((info->irq << 2) | (info->irq << 3))) { + /* we have received something */ + receive_chars(info); + } + + /* FIXME: here we should really check for a change in the + status lines and if so call status_handle(info) */ + } +} + +/* dma fifo/buffer timeout handler + forces an end-of-packet for the dma input channel if no chars + have been received for CONFIG_ETRAX100_RX_TIMEOUT_TICKS/100 s. + If CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST is configured then this + handler is instead run at 15360 Hz. +*/ + +#ifndef CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST +static int timeout_divider = 0; +#endif + +static struct timer_list flush_timer; + +static void +timed_flush_handler(void) +{ + struct e100_serial *info; + int i; + unsigned int magic; + +#ifdef CONFIG_SVINTO_SIM + return; +#endif + + for(i = 0; i < NR_PORTS; i++) { + info = rs_table + i; + if(!(info->flags & ASYNC_INITIALIZED)) + continue; + + /* istatusadr (bit 6-0) hold number of bytes in fifo + * ihwswadr (bit 31-16) holds number of bytes in dma buffer + * ihwswadr (bit 15-0) specifies size of dma buffer + */ + + magic = (*info->istatusadr & 0x3f); + magic += ((*info->ihwswadr&0xffff ) - (*info->ihwswadr >> 16)); + + /* if magic is equal to fifo_magic (magic in previous + * timeout_interrupt) then no new data has arrived since last + * interrupt and we'll force eop to flush fifo+dma buffers + */ + + if(magic != info->fifo_magic) { + info->fifo_magic = magic; + info->fifo_didmagic = 0; + } else { + /* hit the timeout, force an EOP for the input + * dma channel if we haven't already + */ + if(!info->fifo_didmagic && magic) { + info->fifo_didmagic = 1; + info->fifo_magic = 0; + *R_SET_EOP = 1U << info->iseteop; + } + } + } + + /* restart flush timer */ + + mod_timer(&flush_timer, jiffies + MAX_FLUSH_TIME); +} + + +#ifdef SERIAL_HANDLE_EARLY_ERRORS + +/* If there is an error (ie break) when the DMA is running and + * there are no bytes in the fifo the DMA is stopped and we get no + * eop interrupt. Thus we have to monitor the first bytes on a DMA + * transfer, and if it is without error we can turn the serial + * interrupts off. + */ + +static void +ser_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct e100_serial *info; + int i; + unsigned char rstat; + + for(i = 0; i < NR_PORTS; i++) { + + info = rs_table + i; + rstat = info->port[REG_STATUS]; + + if(*R_IRQ_MASK1_RD & (1U << (8+2*info->line))) { /* This line caused the irq */ +#ifdef SERIAL_DEBUG_INTR + printk("Interrupt from serport %d\n", i); +#endif + if(rstat & 0x0e) { + /* FIXME: This is weird, but if this delay is + * not present then irmaflash does not work... + */ + udelay(2300); + + /* if we got an error, we must reset it by + * reading the data_in field + */ + (void)info->port[REG_DATA]; + + PROCSTAT(early_errors_cnt[info->line]++); + + /* restart the DMA */ + *info->icmdadr = 3; + } + else { /* it was a valid byte, now let the dma do the rest */ +#ifdef SERIAL_DEBUG_INTR + printk("** OK, disabling ser_interupts\n"); +#endif + e100_disable_serial_data_irq(info); + } + } + } +} +#endif + +/* + * ------------------------------------------------------------------- + * Here ends the serial interrupt routines. + * ------------------------------------------------------------------- + */ + +/* + * This routine is used to handle the "bottom half" processing for the + * serial driver, known also the "software interrupt" processing. + * This processing is done at the kernel interrupt level, after the + * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This + * is where time-consuming activities which can not be done in the + * interrupt driver proper are done; the interrupt driver schedules + * them using rs_sched_event(), and they get done here. + */ +static void +do_serial_bh(void) +{ + run_task_queue(&tq_serial); +} + +static void +do_softint(void *private_) +{ + struct e100_serial *info = (struct e100_serial *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + wake_up_interruptible(&tty->write_wait); + } +} + +/* + * This routine is called from the scheduler tqueue when the interrupt + * routine has signalled that a hangup has occurred. The path of + * hangup processing is: + * + * serial interrupt routine -> (scheduler tqueue) -> + * do_serial_hangup() -> tty->hangup() -> rs_hangup() + * + */ +static void +do_serial_hangup(void *private_) +{ + struct e100_serial *info = (struct e100_serial *) private_; + struct tty_struct *tty; + + tty = info->tty; + if (!tty) + return; + + tty_hangup(tty); +} + +static int +startup(struct e100_serial * info) +{ + unsigned long flags; + unsigned long page; + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + /* if it was already initialized, skip this */ + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + restore_flags(flags); + return -EBUSY; + } + + if (info->xmit.buf) + free_page(page); + else + info->xmit.buf = (unsigned char *) page; + +#ifdef SERIAL_DEBUG_OPEN + printk("starting up ttyS%d (xmit_buf 0x%x)...\n", info->line, info->xmit_buf); +#endif + + if(info->tty) { + + /* clear the tty flip flag buffer since we will not + * be using it (we only use the first byte..) + */ + + memset(info->tty->flip.flag_buf, 0, TTY_FLIPBUF_SIZE * 2); + } + + save_flags(flags); + cli(); + +#ifdef CONFIG_SVINTO_SIM + /* Bits and pieces collected from below. Better to have them + in one ifdef:ed clause than to mix in a lot of ifdefs, + right? */ + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + info->xmit.head = info->xmit.tail = 0; + + /* No real action in the simulator, but may set info important + to ioctl. */ + change_speed(info); +#else + + /* + * Clear the FIFO buffers and disable them + * (they will be reenabled in change_speed()) + */ + + /* + * Reset the DMA channels and make sure their interrupts are cleared + */ + + *info->icmdadr = 4; /* reset command */ + *info->ocmdadr = 4; /* reset command */ + + while((*info->icmdadr & 7) == 4); /* wait until reset cycle is complete */ + while((*info->ocmdadr & 7) == 4); + + *info->iclrintradr = 3; /* make sure the irqs are cleared */ + *info->oclrintradr = 3; + + if (info->tty) + clear_bit(TTY_IO_ERROR, &info->tty->flags); + + info->xmit.head = info->xmit.tail = 0; + + /* + * and set the speed and other flags of the serial port + * this will start the rx/tx as well + */ +#ifdef SERIAL_HANDLE_EARLY_ERRORS + e100_enable_serial_data_irq(info); +#endif + change_speed(info); + + /* dummy read to reset any serial errors */ + + (void)info->port[REG_DATA]; + + /* enable the interrupts */ + + e100_enable_txdma_irq(info); + e100_enable_rxdma_irq(info); + + info->tr_running = 0; /* to be sure we dont lock up the transmitter */ + + /* setup the dma input descriptor and start dma */ + + start_receive(info); + + /* for safety, make sure the descriptors last result is 0 bytes written */ + + info->tr_descr.sw_len = 0; + info->tr_descr.hw_len = 0; + info->tr_descr.status = 0; + + /* enable RTS/DTR last */ + + e100_rts(info, 1); + e100_dtr(info, 1); + +#endif /* CONFIG_SVINTO_SIM */ + + info->flags |= ASYNC_INITIALIZED; + + restore_flags(flags); + return 0; +} + +/* + * This routine will shutdown a serial port; interrupts are disabled, and + * DTR is dropped if the hangup on close termio flag is on. + */ +static void +shutdown(struct e100_serial * info) +{ + unsigned long flags; + +#ifndef CONFIG_SVINTO_SIM + /* shut down the transmitter and receiver */ + + e100_disable_rx(info); + info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40); + + e100_disable_rxdma_irq(info); + e100_disable_txdma_irq(info); + + info->tr_running = 0; + + /* reset both dma channels */ + + *info->icmdadr = 4; + *info->ocmdadr = 4; + +#endif /* CONFIG_SVINTO_SIM */ + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....\n", info->line, + info->irq); +#endif + + save_flags(flags); + cli(); /* Disable interrupts */ + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { + /* hang up DTR and RTS if HUPCL is enabled */ + e100_dtr(info, 0); + e100_rts(info, 0); /* could check CRTSCTS before doing this */ + } + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + + +/* change baud rate and other assorted parameters */ + +static void +change_speed(struct e100_serial *info) +{ + unsigned int cflag; + + /* first some safety checks */ + + if(!info->tty || !info->tty->termios) + return; + if (!info->port) + return; + + cflag = info->tty->termios->c_cflag; + + /* possibly, the tx/rx should be disabled first to do this safely */ + + /* change baud-rate and write it to the hardware */ + + info->baud = cflag_to_baud(cflag); + +#ifndef CONFIG_SVINTO_SIM + info->port[REG_BAUD] = cflag_to_etrax_baud(cflag); + /* start with default settings and then fill in changes */ + + info->rx_ctrl &= ~(0x07); /* 8 bit, no/even parity */ + info->tx_ctrl &= ~(0x37); /* 8 bit, no/even parity, 1 stop bit, no cts */ + + if ((cflag & CSIZE) == CS7) { + /* set 7 bit mode */ + info->tx_ctrl |= 0x01; + info->rx_ctrl |= 0x01; + } + + if (cflag & CSTOPB) { + /* set 2 stop bit mode */ + info->tx_ctrl |= 0x10; + } + + if (cflag & PARENB) { + /* enable parity */ + info->tx_ctrl |= 0x02; + info->rx_ctrl |= 0x02; + } + + if (cflag & PARODD) { + /* set odd parity */ + info->tx_ctrl |= 0x04; + info->rx_ctrl |= 0x04; + } + + if (cflag & CRTSCTS) { + /* enable automatic CTS handling */ + info->tx_ctrl |= 0x20; + } + + /* make sure the tx and rx are enabled */ + + info->tx_ctrl |= 0x40; + info->rx_ctrl |= 0x40; + + /* actually write the control regs to the hardware */ + + info->port[REG_TR_CTRL] = info->tx_ctrl; + info->port[REG_REC_CTRL] = info->rx_ctrl; + *((unsigned long *)&info->port[REG_XOFF]) = 0; + +#endif /* CONFIG_SVINTO_SIM */ +} + +/* start transmitting chars NOW */ + +static void +rs_flush_chars(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + if (info->tr_running + || info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + +#ifdef SERIAL_DEBUG_FLOW + printk("rs_flush_chars\n"); +#endif + + /* this protection might not exactly be necessary here */ + + save_flags(flags); + cli(); + start_transmit(info); + restore_flags(flags); +} + +static int +rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + /* first some sanity checks */ + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + +#ifdef SERIAL_DEBUG_DATA + if(info->line == SERIAL_DEBUG_LINE) + printk("rs_write (%d), status %d\n", + count, info->port[REG_STATUS]); +#endif + +#ifdef CONFIG_SVINTO_SIM + /* Really simple. The output is here and now. */ + SIMCOUT(buf, count); + return; +#endif + save_flags(flags); + + /* the cli/restore_flags pairs below are needed because the + * DMA interrupt handler moves the info->xmit values. the memcpy + * needs to be in the critical region unfortunately, because we + * need to read xmit values, memcpy, write xmit values in one + * atomic operation... this could perhaps be avoided by more clever + * design. + */ + if(from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + restore_flags(flags); + buf += c; + count -= c; + ret += c; + } + up(&tmp_buf_sem); + } else { + cli(); + while(1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + + if (count < c) + c = count; + if (c <= 0) + break; + + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = (info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + + /* enable transmitter if not running, unless the tty is stopped + * this does not need IRQ protection since if tr_running == 0 + * the IRQ's are not running anyway for this port. + */ + + if(info->xmit.head != info->xmit.tail + && !tty->stopped && + !tty->hw_stopped && + !info->tr_running) { + start_transmit(info); + } + + return ret; +} + +/* how much space is available in the xmit buffer? */ + +static int +rs_write_room(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +/* How many chars are in the xmit buffer? + * This does not include any chars in the transmitter FIFO. + * Use wait_until_sent for waiting for FIFO drain. + */ + +static int +rs_chars_in_buffer(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +/* discard everything in the xmit buffer */ + +static void +rs_flush_buffer(struct tty_struct *tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + save_flags(flags); + cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + + wake_up_interruptible(&tty->write_wait); + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + * + * Since we don't bother to check for info->x_char in transmit_chars yet, + * we don't really implement this function yet. + */ +static void rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + printk("serial.c:rs_send_xchar not implemented!\n"); + + info->x_char = ch; + if (ch) { + /* Make sure transmit interrupts are on */ + /* TODO. */ + } +} + +/* + * ------------------------------------------------------------ + * rs_throttle() + * + * This routine is called by the upper-layer tty layer to signal that + * incoming characters should be throttled. + * ------------------------------------------------------------ + */ +static void +rs_throttle(struct tty_struct * tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("throttle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (I_IXOFF(tty)) + info->x_char = STOP_CHAR(tty); + + /* Turn off RTS line (do this atomic) should here be an else ?? */ + + save_flags(flags); + cli(); + e100_rts(info, 0); + restore_flags(flags); +} + +static void +rs_unthrottle(struct tty_struct * tty) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + unsigned long flags; +#ifdef SERIAL_DEBUG_THROTTLE + char buf[64]; + + printk("unthrottle %s: %d....\n", _tty_name(tty, buf), + tty->ldisc.chars_in_buffer(tty)); +#endif + + if (I_IXOFF(tty)) { + if (info->x_char) + info->x_char = 0; + else + info->x_char = START_CHAR(tty); + } + + /* Assert RTS line (do this atomic) */ + + save_flags(flags); + cli(); + e100_rts(info, 1); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ + +static int +get_serial_info(struct e100_serial * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + + /* this is all probably wrong, there are a lot of fields + * here that we don't have in e100_serial and maybe we + * should set them to something else than 0. + */ + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = info->type; + tmp.line = info->line; + tmp.port = (int)info->port; + tmp.irq = info->irq; + tmp.flags = info->flags; + tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int +set_serial_info(struct e100_serial * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct e100_serial old_info; + int retval = 0; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + + old_info = *info; + + if(!capable(CAP_SYS_ADMIN)) { + if((new_serial.type != info->type) || + (new_serial.close_delay != info->close_delay) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (info->flags & ~ASYNC_USR_MASK))) + return -EPERM; + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + goto check_and_exit; + } + + if (info->count > 1) + return -EBUSY; + + /* + * OK, past this point, all the error checking has been done. + * At this point, we start making changes..... + */ + + info->flags = ((info->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->type = new_serial.type; + info->close_delay = new_serial.close_delay; + info->closing_wait = new_serial.closing_wait; +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + + check_and_exit: + if(info->flags & ASYNC_INITIALIZED) { + change_speed(info); + } else + retval = startup(info); + return retval; +} + +/* + * get_lsr_info - get line status register info + * + * Purpose: Let user call ioctl() to get info when the UART physically + * is emptied. On bus types like RS485, the transmitter must + * release the bus after transmitting. This must be done when + * the transmit shift register is empty, not be done when the + * transmit holding register is empty. This functionality + * allows an RS485 driver to be written in user space. + */ +static int +get_lsr_info(struct e100_serial * info, unsigned int *value) +{ + unsigned int result; + +#ifdef CONFIG_SVINTO_SIM + /* Always open. */ + result = TIOCSER_TEMT; +#else + if (*info->ostatusadr & 0x007F) /* something in fifo */ + result = 0; + else + result = TIOCSER_TEMT; +#endif + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +#ifdef SERIAL_DEBUG_IO +struct state_str +{ + int state; + const char *str; + +}; + +const struct state_str control_state_str[]={ + {TIOCM_DTR, "DTR" }, + {TIOCM_RTS, "RTS"}, + {TIOCM_ST, "ST?" }, + {TIOCM_SR, "SR?" }, + {TIOCM_CTS, "CTS" }, + {TIOCM_CD, "CD" }, + {TIOCM_RI, "RI" }, + {TIOCM_DSR, "DSR" }, + {0, NULL } +}; + +char *get_control_state_str(int MLines, char *s) +{ + int i = 0; + s[0]='\0'; + while (control_state_str[i].str != NULL) { + if (MLines & control_state_str[i].state) { + if (s[0] != '\0') { + strcat(s, ", "); + } + strcat(s, control_state_str[i].str); + } + i++; + } + return s; +} +#endif + +static int +get_modem_info(struct e100_serial * info, unsigned int *value) +{ + unsigned int result; + /* Polarity isn't verified */ +#if 0 /*def SERIAL_DEBUG_IO */ + + printk("get_modem_info: RTS: %i DTR: %i CD: %i RI: %i DSR: %i CTS: %i\n", + E100_RTS_GET(info), + E100_DTR_GET(info), + E100_CD_GET(info), + E100_RI_GET(info), + E100_DSR_GET(info), + E100_CTS_GET(info)); +#endif + result = + (!E100_RTS_GET(info) ? TIOCM_RTS : 0) + | (!E100_DTR_GET(info) ? TIOCM_DTR : 0) + | (!E100_CD_GET(info) ? TIOCM_CAR : 0) + | (!E100_RI_GET(info) ? TIOCM_RNG : 0) + | (!E100_DSR_GET(info) ? TIOCM_DSR : 0) + | (!E100_CTS_GET(info) ? TIOCM_CTS : 0); + +#ifdef SERIAL_DEBUG_IO + printk("e100ser: modem state: %i 0x%08X\n", result, result); + { + char s[100]; + + get_control_state_str(result, s); + printk("state: %s\n", s); + } +#endif + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + + +static int +set_modem_info(struct e100_serial * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) { + e100_rts(info, 1); + } + if (arg & TIOCM_DTR) { + e100_dtr(info, 1); + } + /* Handle FEMALE behaviour */ + if (arg & TIOCM_RI) { + e100_ri_out(info, 1); + } + if (arg & TIOCM_CD) { + e100_cd_out(info, 1); + } + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) { + e100_rts(info, 0); + } + if (arg & TIOCM_DTR) { + e100_dtr(info, 0); + } + /* Handle FEMALE behaviour */ + if (arg & TIOCM_RI) { + e100_ri_out(info, 0); + } + if (arg & TIOCM_CD) { + e100_cd_out(info, 0); + } + break; + case TIOCMSET: + e100_rts(info, arg & TIOCM_RTS); + e100_dtr(info, arg & TIOCM_DTR); + /* Handle FEMALE behaviour */ + e100_ri_out(info, arg & TIOCM_RI); + e100_cd_out(info, arg & TIOCM_CD); + break; + default: + return -EINVAL; + } + return 0; +} + +/* + * This routine sends a break character out the serial port. + */ +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +static void +send_break(struct e100_serial * info, int duration) +{ + unsigned long flags; + + if (!info->port) + return; + + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + + save_flags(flags); + cli(); + + /* Go to manual mode and set the txd pin to 0 */ + + info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */ + info->port[REG_TR_CTRL] = info->tx_ctrl; + + /* wait for "duration" jiffies */ + + schedule(); + + info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */ + info->port[REG_TR_CTRL] = info->tx_ctrl; + + /* the DMA gets awfully confused if we toggle the tranceiver like this + * so we need to reset it + */ + *info->ocmdadr = 4; + + restore_flags(flags); +} +#else +static void +rs_break(struct tty_struct *tty, int break_state) +{ + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + if (!info->port) + return; + + save_flags(flags); + cli(); + if (break_state == -1) { + /* Go to manual mode and set the txd pin to 0 */ + info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */ + } else { + info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */ + } + info->port[REG_TR_CTRL] = info->tx_ctrl; + restore_flags(flags); +} +#endif + +static int +rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + int error; + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + int retval; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && + (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ + case TCSBRK: /* SVID version: non-zero arg --> no break */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); /* 1/4 second */ + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: /* support for POSIX tcsendbreak() */ + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCGSOFTCAR: + error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long)); + if (error) + return error; + put_fs_long(C_CLOCAL(tty) ? 1 : 0, + (unsigned long *) arg); + return 0; + case TIOCSSOFTCAR: + arg = get_fs_long((unsigned long *) arg); + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (arg ? CLOCAL : 0)); + return 0; +#endif + case TIOCMGET: + return get_modem_info(info, (unsigned int *) arg); + case TIOCMBIS: + case TIOCMBIC: + case TIOCMSET: + return set_modem_info(info, cmd, (unsigned int *) arg); + case TIOCGSERIAL: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERGETLSR: /* Get line status register */ + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct e100_serial *) arg, + info, sizeof(struct e100_serial))) + return -EFAULT; + return 0; + +#if defined(CONFIG_RS485) + case TIOCSERSETRS485: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct rs485_control)); + + if (error) + return error; + + return e100_enable_rs485(tty, (struct rs485_control *) arg); + + case TIOCSERWRRS485: + error = verify_area(VERIFY_WRITE, (void *) arg, + sizeof(struct rs485_write)); + + if (error) + return error; + + return e100_write_rs485(tty, (struct rs485_write *) arg); +#endif + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static void +rs_set_termios(struct tty_struct *tty, struct termios *old_termios) +{ + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + + change_speed(info); + + if ((old_termios->c_cflag & CRTSCTS) && + !(tty->termios->c_cflag & CRTSCTS)) { + tty->hw_stopped = 0; + rs_start(tty); + } + +} + +/* + * ------------------------------------------------------------ + * rs_close() + * + * This routine is called when the serial port gets closed. First, we + * wait for the last remaining data to be sent. Then, we unlink its + * S structure from the interrupt chain if necessary, and we free + * that IRQ if nothing is left in the chain. + * ------------------------------------------------------------ + */ +static void +rs_close(struct tty_struct *tty, struct file * filp) +{ + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + unsigned long flags; + + if (!info) + return; + + /* interrupts are disabled for this entire function */ + + save_flags(flags); + cli(); + + if (tty_hung_up_p(filp)) { + restore_flags(flags); + return; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("[%d] rs_close ttyS%d, count = %d\n", current->pid, + info->line, info->count); +#endif + if ((tty->count == 1) && (info->count != 1)) { + /* + * Uh, oh. tty->count is 1, which means that the tty + * structure will be freed. Info->count should always + * be one in these conditions. If it's greater than + * one, we've got real problems, since it means the + * serial port won't be shutdown. + */ + printk("rs_close: bad serial port count; tty->count is 1, " + "info->count is %d\n", info->count); + info->count = 1; + } + if (--info->count < 0) { + printk("rs_close: bad serial port count for ttyS%d: %d\n", + info->line, info->count); + info->count = 0; + } + if (info->count) { + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + /* + * Save the termios structure, since this port may have + * separate termios for callout and dialin. + */ + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->callout_termios = *tty->termios; + /* + * Now we wait for the transmit buffer to clear; and we notify + * the line discipline to only process XON/XOFF characters. + */ + tty->closing = 1; + if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) + tty_wait_until_sent(tty, info->closing_wait); + /* + * At this point we stop accepting input. To do this, we + * disable the serial receiver and the DMA receive interrupt. + */ +#ifdef SERIAL_HANDLE_EARLY_ERRORS + e100_disable_serial_data_irq(info); +#endif + +#ifndef CONFIG_SVINTO_SIM + e100_disable_rx(info); + e100_disable_rxdma_irq(info); + + if (info->flags & ASYNC_INITIALIZED) { + /* + * Before we drop DTR, make sure the UART transmitter + * has completely drained; this is especially + * important as we have a transmit FIFO! + */ + rs_wait_until_sent(tty, HZ); + } +#endif + + shutdown(info); + if (tty->driver.flush_buffer) + tty->driver.flush_buffer(tty); + if (tty->ldisc.flush_buffer) + tty->ldisc.flush_buffer(tty); + tty->closing = 0; + info->event = 0; + info->tty = 0; + if (info->blocked_open) { + if (info->close_delay) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->close_delay); + } + wake_up_interruptible(&info->open_wait); + } + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| + ASYNC_CLOSING); + wake_up_interruptible(&info->close_wait); + restore_flags(flags); + + /* port closed */ + +#if defined(CONFIG_RS485) + if (info->rs485.enabled) { + info->rs485.enabled = 0; +#if defined(CONFIG_RS485_ON_PA) + *R_PORT_PA_DATA = port_pa_data_shadow &= ~(1 << rs485_pa_bit); +#endif + } +#endif +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + unsigned long orig_jiffies; + struct e100_serial *info = (struct e100_serial *)tty->driver_data; + + /* + * Check R_DMA_CHx_STATUS bit 0-6=number of available bytes in FIFO + * R_DMA_CHx_HWSW bit 31-16=nbr of bytes left in DMA buffer (0=64k) + */ + orig_jiffies = jiffies; + while(info->xmit.head != info->xmit.tail || /* More in send queue */ + (*info->ostatusadr & 0x007f)) { /* more in FIFO */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + set_current_state(TASK_RUNNING); +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +void +rs_hangup(struct tty_struct *tty) +{ + struct e100_serial * info = (struct e100_serial *)tty->driver_data; + + rs_flush_buffer(tty); + shutdown(info); + info->event = 0; + info->count = 0; + info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); + info->tty = 0; + wake_up_interruptible(&info->open_wait); +} + +/* + * ------------------------------------------------------------ + * rs_open() and friends + * ------------------------------------------------------------ + */ +static int +block_til_ready(struct tty_struct *tty, struct file * filp, + struct e100_serial *info) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int retval; + int do_clocal = 0, extra_count = 0; + + /* + * If the device is in the middle of being closed, then block + * until it's done, and then try again. + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + return -EAGAIN; + else + return -ERESTARTSYS; +#else + return -EAGAIN; +#endif + } + + /* + * If this is a callout device, then just make sure the normal + * device isn't being used. + */ + if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { + if (info->flags & ASYNC_NORMAL_ACTIVE) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_SESSION_LOCKOUT) && + (info->session != current->session)) + return -EBUSY; + if ((info->flags & ASYNC_CALLOUT_ACTIVE) && + (info->flags & ASYNC_PGRP_LOCKOUT) && + (info->pgrp != current->pgrp)) + return -EBUSY; + info->flags |= ASYNC_CALLOUT_ACTIVE; + return 0; + } + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if ((filp->f_flags & O_NONBLOCK) || + (tty->flags & (1 << TTY_IO_ERROR))) { + if (info->flags & ASYNC_CALLOUT_ACTIVE) + return -EBUSY; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; + } + + if (info->flags & ASYNC_CALLOUT_ACTIVE) { + if (info->normal_termios.c_cflag & CLOCAL) + do_clocal = 1; + } else { + if (tty->termios->c_cflag & CLOCAL) + do_clocal = 1; + } + + /* + * Block waiting for the carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, info->count is dropped by one, so that + * rs_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + retval = 0; + add_wait_queue(&info->open_wait, &wait); +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready before block: ttyS%d, count = %d\n", + info->line, info->count); +#endif + save_flags(flags); + cli(); + if (!tty_hung_up_p(filp)) { + extra_count++; + info->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); + cli(); + if (!(info->flags & ASYNC_CALLOUT_ACTIVE)) { + /* assert RTS and DTR */ + e100_rts(info, 1); + e100_dtr(info, 1); + } + restore_flags(flags); + set_current_state(TASK_INTERRUPTIBLE); + if (tty_hung_up_p(filp) || + !(info->flags & ASYNC_INITIALIZED)) { +#ifdef SERIAL_DO_RESTART + if (info->flags & ASYNC_HUP_NOTIFY) + retval = -EAGAIN; + else + retval = -ERESTARTSYS; +#else + retval = -EAGAIN; +#endif + break; + } + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + !(info->flags & ASYNC_CLOSING) && do_clocal) + /* && (do_clocal || DCD_IS_ASSERTED) */ + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttyS%d, count = %d\n", + info->line, info->count); +#endif + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + info->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttyS%d, count = %d\n", + info->line, info->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +/* + * This routine is called whenever a serial port is opened. + * It performs the serial-specific initialization for the tty structure. + */ +static int +rs_open(struct tty_struct *tty, struct file * filp) +{ + struct e100_serial *info; + int retval, line; + unsigned long page; + + /* find which port we want to open */ + + line = MINOR(tty->device) - tty->driver.minor_start; + + if (line < 0 || line >= NR_PORTS) + return -ENODEV; + + /* dont allow opening ports that are not enabled in the HW config */ + +#ifndef CONFIG_ETRAX100_SERIAL_PORT2 + if (line == 2) + return -ENODEV; +#endif +#ifndef CONFIG_ETRAX100_SERIAL_PORT3 + if (line == 3) + return -ENODEV; +#endif + + /* find the corresponding e100_serial struct in the table */ + + info = rs_table + line; + +#ifdef SERIAL_DEBUG_OPEN + printk("[%d] rs_open %s%d, count = %d\n", current->pid, + tty->driver.name, info->line, + info->count); +#endif + + info->count++; + tty->driver_data = info; + info->tty = tty; + +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + return -ENOMEM; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + /* + * If the port is the middle of closing, bail out now + */ + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + + /* + * Start up the serial port + */ + + retval = startup(info); + if (retval) + return retval; + + retval = block_til_ready(tty, filp, info); + if (retval) { +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open returning after block_til_ready with %d\n", + retval); +#endif + return retval; + } + + if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->normal_termios; + else + *tty->termios = info->callout_termios; + change_speed(info); + } + + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttyS%d successful...\n", info->line); +#endif + return 0; +} + +/* + * /proc fs routines.... + */ + +static inline int line_info(char *buf, struct e100_serial *info) +{ + char stat_buf[30], control, status; + int ret; + unsigned long flags; + + ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d", + info->line, info->port, info->irq); + + if (!info->port || (info->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (E100_RTS_GET(info)) + strcat(stat_buf, "|RTS"); + if (E100_CTS_GET(info)) + strcat(stat_buf, "|CTS"); + if (E100_DTR_GET(info)) + strcat(stat_buf, "|DTR"); + if (E100_DSR_GET(info)) + strcat(stat_buf, "|DSR"); + if (E100_CD_GET(info)) + strcat(stat_buf, "|CD"); + if (E100_RI_GET(info)) + strcat(stat_buf, "|RI"); + + ret += sprintf(buf+ret, " baud:%d", info->baud); + + ret += sprintf(buf+ret, " tx:%d rx:%d", + info->icount.tx, info->icount.rx); + + if (info->icount.frame) + ret += sprintf(buf+ret, " fe:%d", info->icount.frame); + + if (info->icount.parity) + ret += sprintf(buf+ret, " pe:%d", info->icount.parity); + + if (info->icount.brk) + ret += sprintf(buf+ret, " brk:%d", info->icount.brk); + + if (info->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", info->icount.overrun); + + /* + * Last thing is the RS-232 status lines + */ + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + +int rs_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s\n", + serial_version); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + l = line_info(page + len, &rs_table[i]); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* Finally, routines used to initialize the serial driver. */ + +static void +show_serial_version(void) +{ + printk("ETRAX 100LX serial-driver %s, (c) 2000 Axis Communications AB\r\n", + serial_version); +} + +/* rs_init inits the driver at boot (using the module_init chain) */ + +static int __init +rs_init(void) +{ + int i; + struct e100_serial *info; + + show_serial_version(); + + init_bh(SERIAL_BH, do_serial_bh); + + /* Setup the timed flush handler system */ + + init_timer(&flush_timer); + flush_timer.function = timed_flush_handler; + mod_timer(&flush_timer, jiffies + MAX_FLUSH_TIME); + + /* Initialize the tty_driver structure */ + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x20100) + serial_driver.driver_name = "serial"; +#endif + serial_driver.name = "ttyS"; + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64; + serial_driver.num = NR_PORTS; /* etrax100 has 4 serial ports */ + serial_driver.type = TTY_DRIVER_TYPE_SERIAL; + serial_driver.subtype = SERIAL_TYPE_NORMAL; + serial_driver.init_termios = tty_std_termios; + serial_driver.init_termios.c_cflag = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */ + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + serial_driver.refcount = &serial_refcount; + serial_driver.table = serial_table; + serial_driver.termios = serial_termios; + serial_driver.termios_locked = serial_termios_locked; + + serial_driver.open = rs_open; + serial_driver.close = rs_close; + serial_driver.write = rs_write; + /* should we have an rs_put_char as well here ? */ + serial_driver.flush_chars = rs_flush_chars; + serial_driver.write_room = rs_write_room; + serial_driver.chars_in_buffer = rs_chars_in_buffer; + serial_driver.flush_buffer = rs_flush_buffer; + serial_driver.ioctl = rs_ioctl; + serial_driver.throttle = rs_throttle; + serial_driver.unthrottle = rs_unthrottle; + serial_driver.set_termios = rs_set_termios; + serial_driver.stop = rs_stop; + serial_driver.start = rs_start; + serial_driver.hangup = rs_hangup; +#if (LINUX_VERSION_CODE >= 131394) /* Linux 2.1.66 */ + serial_driver.break_ctl = rs_break; +#endif +#if (LINUX_VERSION_CODE >= 131343) + serial_driver.send_xchar = rs_send_xchar; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; +#endif + + /* + * The callout device is just like normal device except for + * major number and the subtype code. + */ + callout_driver = serial_driver; + callout_driver.name = "cua"; + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; +#if (LINUX_VERSION_CODE >= 131343) + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; +#endif + + if (tty_register_driver(&serial_driver)) + panic("Couldn't register serial driver\n"); + if (tty_register_driver(&callout_driver)) + panic("Couldn't register callout driver\n"); + + /* do some initializing for the separate ports */ + + for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) { + info->line = i; + info->tty = 0; + info->type = PORT_ETRAX100; + info->tr_running = 0; + info->fifo_magic = 0; + info->fifo_didmagic = 0; + info->flags = 0; + info->close_delay = 5*HZ/10; + info->closing_wait = 30*HZ; + info->x_char = 0; + info->event = 0; + info->count = 0; + info->blocked_open = 0; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->callout_termios = callout_driver.init_termios; + info->normal_termios = serial_driver.init_termios; + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + info->xmit.buf = 0; + info->xmit.tail = info->xmit.head = 0; + + printk(KERN_INFO "%s%d at 0x%x is a builtin UART with DMA\n", + serial_driver.name, info->line, (unsigned int)info->port); + } + +#ifndef CONFIG_SVINTO_SIM + /* Not needed in simulator. May only complicate stuff. */ + /* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */ + if(request_irq(22, tr_interrupt, SA_INTERRUPT, "serial 0 dma tr", NULL)) + panic("irq22"); + if(request_irq(23, rec_interrupt, SA_INTERRUPT, "serial 0 dma rec", NULL)) + panic("irq23"); +#ifdef SERIAL_HANDLE_EARLY_ERRORS + if(request_irq(8, ser_interrupt, SA_INTERRUPT, "serial ", NULL)) + panic("irq8"); +#endif + if(request_irq(24, tr_interrupt, SA_INTERRUPT, "serial 1 dma tr", NULL)) + panic("irq24"); + if(request_irq(25, rec_interrupt, SA_INTERRUPT, "serial 1 dma rec", NULL)) + panic("irq25"); +#ifdef CONFIG_ETRAX100_SERIAL_PORT2 + /* DMA Shared with par0 (and SCSI0 and ATA) */ + if(request_irq(18, tr_interrupt, SA_SHIRQ, "serial 2 dma tr", NULL)) + panic("irq18"); + if(request_irq(19, rec_interrupt, SA_SHIRQ, "serial 2 dma rec", NULL)) + panic("irq19"); +#endif +#ifdef CONFIG_ETRAX100_SERIAL_PORT3 + /* DMA Shared with par1 (and SCSI1 and Extern DMA 0) */ + if(request_irq(20, tr_interrupt, SA_SHIRQ, "serial 3 dma tr", NULL)) + panic("irq20"); + if(request_irq(21, rec_interrupt, SA_SHIRQ, "serial 3 dma rec", NULL)) + panic("irq21"); +#endif +#ifdef CONFIG_ETRAX100_SERIAL_FLUSH_DMA_FAST + /* TODO: a timeout_interrupt needs to be written that calls timeout_handler */ + if(request_irq(TIMER1_IRQ_NBR, timeout_interrupt, SA_SHIRQ, + "fast serial dma timeout", NULL)) { + printk("err: timer1 irq\n"); + } +#endif +#endif /* CONFIG_SVINTO_SIM */ + + return 0; +} + +/* this makes sure that rs_init is called during kernel boot */ + +module_init(rs_init); + +/* + * register_serial and unregister_serial allows for serial ports to be + * configured at run-time, to support PCMCIA modems. + */ +int +register_serial(struct serial_struct *req) +{ + return -1; +} + +void unregister_serial(int line) +{ +} diff --git a/arch/cris/drivers/serial.h b/arch/cris/drivers/serial.h new file mode 100644 index 000000000..650c5e9b4 --- /dev/null +++ b/arch/cris/drivers/serial.h @@ -0,0 +1,106 @@ +/* + * serial.h: Arch-dep definitions for the Etrax100 serial driver. + * + * Copyright (C) 1998, 1999, 2000 Axis Communications AB + */ + +#ifndef _ETRAX100_SERIAL_H +#define _ETRAX100_SERIAL_H + +#include <linux/config.h> +#include <linux/circ_buf.h> +#include <asm/termios.h> + +/* Software state per channel */ + +#ifdef __KERNEL__ +/* + * This is our internal structure for each serial port's state. + * + * Many fields are paralleled by the structure used by the serial_struct + * structure. + * + * For definitions of the flags field, see tty.h + */ + +struct e100_serial { + int baud; + volatile unsigned char * port; /* R_SERIALx_CTRL */ + unsigned long irq; /* bitnr in R_IRQ_MASK2 for dmaX_descr */ + + volatile char *oclrintradr; /* adr to R_DMA_CHx_CLR_INTR, output */ + volatile unsigned long *ofirstadr; /* adr to R_DMA_CHx_FIRST, output */ + volatile char *ocmdadr; /* adr to R_DMA_CHx_CMD, output */ + const volatile unsigned short *ostatusadr; /* adr to R_DMA_CHx_STATUS, output */ + volatile unsigned long *ohwswadr; /* adr to R_DMA_CHx_HWSW, output */ + + volatile char *iclrintradr; /* adr to R_DMA_CHx_CLR_INTR, input */ + volatile unsigned long *ifirstadr; /* adr to R_DMA_CHx_FIRST, input */ + volatile char *icmdadr; /* adr to R_DMA_CHx_CMD, input */ + const volatile unsigned short *istatusadr; /* adr to R_DMA_CHx_STATUS, input */ + volatile unsigned long *ihwswadr; /* adr to R_DMA_CHx_HWSW, input */ + + int flags; /* defined in tty.h */ + + unsigned char rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */ + unsigned char tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */ + unsigned char iseteop; /* bit number for R_SET_EOP for the input dma */ +/* end of fields defined in rs_table[] in .c-file */ + unsigned char fifo_didmagic; /* a fifo eop has been forced */ + + struct etrax_dma_descr tr_descr, rec_descr; + + int fifo_magic; /* fifo amount - bytes left in dma buffer */ + + volatile int tr_running; /* 1 if output is running */ + + struct tty_struct *tty; + int read_status_mask; + int ignore_status_mask; + int x_char; /* xon/xoff character */ + int close_delay; + unsigned short closing_wait; + unsigned short closing_wait2; + unsigned long event; + unsigned long last_active; + int line; + int type; /* PORT_ETRAX100 */ + int count; /* # of fd on device */ + int blocked_open; /* # of blocked opens */ + long session; /* Session of opening process */ + long pgrp; /* pgrp of opening process */ + struct circ_buf xmit; + + struct tq_struct tqueue; + struct async_icount icount; /* error-statistics etc.*/ + struct termios normal_termios; + struct termios callout_termios; +#ifdef DECLARE_WAITQUEUE + wait_queue_head_t open_wait; + wait_queue_head_t close_wait; +#else + struct wait_queue *open_wait; + struct wait_queue *close_wait; +#endif + +#ifdef CONFIG_RS485 + struct rs485_control rs485; /* RS-485 support */ +#endif +}; + +/* this PORT is not in the standard serial.h. it's not actually used for + * anything since we only have one type of async serial-port anyway in this + * system. + */ + +#define PORT_ETRAX100 1 + +/* + * Events are used to schedule things to happen at timer-interrupt + * time, instead of at rs interrupt time. + */ +#define RS_EVENT_WRITE_WAKEUP 0 + +#endif /* __KERNEL__ */ + +#endif /* !(_ETRAX100_SERIAL_H) */ diff --git a/arch/cris/kernel/Makefile b/arch/cris/kernel/Makefile new file mode 100644 index 000000000..0681e4807 --- /dev/null +++ b/arch/cris/kernel/Makefile @@ -0,0 +1,25 @@ +# $Id: Makefile,v 1.3 2001/01/10 21:11:07 bjornw Exp $ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) $(AFLAGS) -traditional -c $< -o $*.o + +all: kernel.o head.o + +O_TARGET := kernel.o +obj-y := process.o signal.o entry.o traps.o irq.o \ + ptrace.o setup.o time.o sys_cris.o shadows.o \ + debugport.o semaphore.o + +obj-$(CONFIG_KGDB) += kgdb.o + +clean: + +include $(TOPDIR)/Rules.make diff --git a/arch/cris/kernel/debugport.c b/arch/cris/kernel/debugport.c new file mode 100644 index 000000000..a2b29be95 --- /dev/null +++ b/arch/cris/kernel/debugport.c @@ -0,0 +1,242 @@ +/* Serialport functions for debugging + * + * Copyright (c) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen + * + * Exports: + * console_print_etrax(char *buf) + * int getDebugChar() + * putDebugChar(int) + * enableDebugIRQ() + * init_etrax_debug() + * + * $Log: debugport.c,v $ + * Revision 1.4 2000/10/06 12:37:26 bjornw + * Use physical addresses when talking to DMA + * + * + */ + +#include <linux/config.h> +#include <linux/console.h> +#include <linux/init.h> +#include <linux/major.h> + +#include <asm/system.h> +#include <asm/svinto.h> +#include <asm/io.h> /* Get SIMCOUT. */ + +/* Which serial-port is our debug port ? */ + +#if defined(CONFIG_DEBUG_PORT0) || defined(CONFIG_DEBUG_PORT_NULL) +#define DEBUG_PORT_IDX 0 +#define DEBUG_OCMD R_DMA_CH6_CMD +#define DEBUG_FIRST R_DMA_CH6_FIRST +#define DEBUG_OCLRINT R_DMA_CH6_CLR_INTR +#define DEBUG_STATUS R_DMA_CH6_STATUS +#define DEBUG_READ R_SERIAL0_READ +#define DEBUG_WRITE R_SERIAL0_TR_DATA +#define DEBUG_TR_CTRL R_SERIAL0_TR_CTRL +#define DEBUG_REC_CTRL R_SERIAL0_REC_CTRL +#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser0_data, set) +#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma6_descr, clr) +#endif + +#ifdef CONFIG_DEBUG_PORT1 +#define DEBUG_PORT_IDX 1 +#define DEBUG_OCMD R_DMA_CH8_CMD +#define DEBUG_FIRST R_DMA_CH8_FIRST +#define DEBUG_OCLRINT R_DMA_CH8_CLR_INTR +#define DEBUG_STATUS R_DMA_CH8_STATUS +#define DEBUG_READ R_SERIAL1_READ +#define DEBUG_WRITE R_SERIAL1_TR_DATA +#define DEBUG_TR_CTRL R_SERIAL1_TR_CTRL +#define DEBUG_REC_CTRL R_SERIAL1_REC_CTRL +#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser1_data, set) +#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma8_descr, clr) +#endif + +#ifdef CONFIG_DEBUG_PORT2 +#define DEBUG_PORT_IDX 2 +#define DEBUG_OCMD R_DMA_CH2_CMD +#define DEBUG_FIRST R_DMA_CH2_FIRST +#define DEBUG_OCLRINT R_DMA_CH2_CLR_INTR +#define DEBUG_STATUS R_DMA_CH2_STATUS +#define DEBUG_READ R_SERIAL2_READ +#define DEBUG_WRITE R_SERIAL2_TR_DATA +#define DEBUG_TR_CTRL R_SERIAL2_TR_CTRL +#define DEBUG_REC_CTRL R_SERIAL2_REC_CTRL +#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser2_data, set) +#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma2_descr, clr) +#endif + +#ifdef CONFIG_DEBUG_PORT3 +#define DEBUG_PORT_IDX 3 +#define DEBUG_OCMD R_DMA_CH4_CMD +#define DEBUG_FIRST R_DMA_CH4_FIRST +#define DEBUG_OCLRINT R_DMA_CH4_CLR_INTR +#define DEBUG_STATUS R_DMA_CH4_STATUS +#define DEBUG_READ R_SERIAL3_READ +#define DEBUG_WRITE R_SERIAL3_TR_DATA +#define DEBUG_TR_CTRL R_SERIAL3_TR_CTRL +#define DEBUG_REC_CTRL R_SERIAL3_REC_CTRL +#define DEBUG_IRQ IO_STATE(R_IRQ_MASK1_SET, ser3_data, set) +#define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma4_descr, clr) +#endif + +/* Write a string of count length to the console (debug port) using DMA, polled + * for completion. Interrupts are disabled during the whole process. Some + * caution needs to be taken to not interfere with ttyS business on this port. + */ + +static void +console_write(struct console *co, const char *buf, unsigned int len) +{ + static struct etrax_dma_descr descr; + unsigned long flags; + int in_progress; + +#ifdef CONFIG_DEBUG_PORT_NULL + /* no debug printout at all */ + return; +#endif + +#ifdef CONFIG_SVINTO_SIM + /* no use to simulate the serial debug output */ + SIMCOUT(buf,len); + return; +#endif + + save_flags(flags); + cli(); + +#ifdef CONFIG_KGDB + /* kgdb needs to output debug info using the gdb protocol */ + putDebugString(buf, len); + restore_flags(flags); + return; +#endif + + /* make sure the transmitter is enabled. + * NOTE: this overrides any setting done in ttySx, to 8N1, no auto-CTS. + * in the future, move the tr/rec_ctrl shadows from etrax100ser.c to + * shadows.c and use it here as well... + */ + + *DEBUG_TR_CTRL = 0x40; + + /* if the tty has some ongoing business, remember it */ + + in_progress = *DEBUG_OCMD & 7; + + if(in_progress) { + /* wait until the output dma channel is ready */ + + while(*DEBUG_OCMD & 7) /* nothing */ ; + } + + descr.ctrl = d_eol; + descr.sw_len = len; + descr.buf = __pa(buf); + + *DEBUG_FIRST = __pa(&descr); /* write to R_DMAx_FIRST */ + *DEBUG_OCMD = 1; /* dma command start -> R_DMAx_CMD */ + + /* wait until the output dma channel is ready again */ + + while(*DEBUG_OCMD & 7) /* nothing */; + + /* clear pending interrupts so we don't get a surprise below */ + + if(in_progress) + *DEBUG_OCLRINT = 2; /* only clear EOP, leave DESCR for the tty */ + else + *DEBUG_OCLRINT = 3; /* clear both EOP and DESCR */ + + while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */ + + restore_flags(flags); +} + +/* legacy function */ + +void +console_print_etrax(const char *buf) +{ + console_write(NULL, buf, strlen(buf)); +} + +/* Use polling to get a single character FROM the debug port */ + +int +getDebugChar(void) +{ + unsigned long readval; + + do { + readval = *DEBUG_READ; + } while(!(readval & IO_MASK(R_SERIAL0_READ, data_avail))); + + return (readval & IO_MASK(R_SERIAL0_READ, data_in)); +} + +/* Use polling to put a single character to the debug port */ + +void +putDebugChar(int val) +{ + while(!(*DEBUG_READ & IO_MASK(R_SERIAL0_READ, tr_ready))) ; +; + *DEBUG_WRITE = val; +} + +/* Enable irq for receiving chars on the debug port, used by kgdb */ + +void +enableDebugIRQ(void) +{ + *R_IRQ_MASK1_SET = DEBUG_IRQ; + /* use R_VECT_MASK directly, since we really bypass Linux normal + * IRQ handling in kgdb anyway, we don't need to use enable_irq + */ + *R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set); + + *DEBUG_REC_CTRL = IO_STATE(R_SERIAL0_REC_CTRL, rec_enable, enable); +} + +static kdev_t +console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +static int __init +console_setup(struct console *co, char *options) +{ + return 0; +} + +static struct console sercons = { + "ttyS", + console_write, + NULL, + console_device, + NULL, + NULL, + console_setup, + CON_PRINTBUFFER, + DEBUG_PORT_IDX, + 0, + NULL +}; + +/* + * Register console (for printk's etc) + */ + +void __init +init_etrax_debug(void) +{ + register_console(&sercons); +} diff --git a/arch/cris/kernel/entry.S b/arch/cris/kernel/entry.S new file mode 100644 index 000000000..1af62eb2c --- /dev/null +++ b/arch/cris/kernel/entry.S @@ -0,0 +1,738 @@ +/* $Id: entry.S,v 1.11 2001/01/10 21:13:29 bjornw Exp $ + * + * linux/arch/cris/entry.S + * + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * $Log: entry.S,v $ + * Revision 1.11 2001/01/10 21:13:29 bjornw + * SYMBOL_NAME is defined incorrectly for the compiler options we currently use + * + * Revision 1.10 2000/12/18 23:47:56 bjornw + * * Added syscall trace support (ptrace), completely untested of course + * * Removed redundant check for NULL entries in syscall_table + * + * Revision 1.9 2000/11/21 16:40:51 bjornw + * * New frame type used when an SBFS frame needs to be popped without + * actually restarting the instruction + * * Enable interrupts in signal_return (they did so in x86, I hope it's a good + * idea) + * + * Revision 1.8 2000/11/17 16:53:35 bjornw + * Added detection of frame-type in Rexit, so that mmu_bus_fault can + * use ret_from_intr in the return-path to check for signals (like SEGV) + * and other foul things that might have occured during the fault. + * + * Revision 1.7 2000/10/06 15:04:28 bjornw + * Include mof in register savings + * + * Revision 1.6 2000/09/12 16:02:44 bjornw + * Linux-2.4.0-test7 derived updates + * + * Revision 1.5 2000/08/17 15:35:15 bjornw + * 2.4.0-test6 changed local_irq_count and friends API + * + * Revision 1.4 2000/08/02 13:59:30 bjornw + * Removed olduname and uname from the syscall list + * + * Revision 1.3 2000/07/31 13:32:58 bjornw + * * Export ret_from_intr + * * _resume updated (prev/last tjohejsan) + * * timer_interrupt obsolete + * * SIGSEGV detection in mmu_bus_fault temporarily disabled + * + * + */ + +/* + * entry.S contains the system-call and fault low-level handling routines. + * + * NOTE: This code handles signal-recognition, which happens every time + * after a timer-interrupt and after each system call. + * + * Stack layout in 'ret_from_system_call': + * ptrace needs to have all regs on the stack. + * if the order here is changed, it needs to be + * updated in fork.c:copy_process, signal.c:do_signal, + * ptrace.c and ptrace.h + * + */ + +#include <linux/linkage.h> +#include <linux/sys.h> + + ;; functions exported from this file + + .globl _system_call + .globl _ret_from_intr + .globl _ret_from_sys_call + .globl _resume + .globl _multiple_interrupt + .globl _hwbreakpoint + .globl _IRQ1_interrupt + .globl _timer_interrupt + .globl _timer_shortcut + .globl _spurious_interrupt + .globl _hw_bp_trigs + .globl _mmu_bus_fault + + .globl _sys_call_table + + ;; syscall error codes + +LENOSYS = 38 + + ;; offsets into the task_struct (found at sp aligned to THREAD_SIZE, 8192) + ;; linux/sched.h + +LTASK_SIGPENDING = 8 +LTASK_NEEDRESCHED = 20 +LTASK_PTRACE = 24 + + ;; some pt_regs offsets (from ptrace.h) + +LORIG_R10 = 4 +LR13 = 8 +LR12 = 12 +LR11 = 16 +LR10 = 20 +LR1 = 56 +LR0 = 60 +LDCCR = 68 +LSRP = 72 +LIRP = 76 + + ;; below are various parts of system_call which are not in the fast-path + + ;; handle software irqs + +handle_softirq: + push r9 + jsr _do_softirq ; call the C routine for softirq handling + pop r9 + + ;; fall-through + +_ret_from_intr: + ;; check for resched only if we're going back to user-mode + + move ccr, r0 + btstq 8, r0 ; U-flag + bpl Rexit ; go back directly + nop + ba ret_with_reschedule ; go back but check schedule and signals first + nop + +reschedule: + ;; keep r9 intact + push r9 + jsr _schedule + pop r9 + ba _ret_from_sys_call + nop + + ;; return but call do_signal first +signal_return: + ei ; we can get here from an interrupt + move.d r9,r10 ; do_signals syscall/irq param + moveq 0,r11 ; oldset param - 0 in this case + move.d sp,r12 ; another argument to do_signal (the regs param) + jsr _do_signal ; arch/cris/kernel/signal.c + ba Rexit + nop + + ;; The system_call is called by a BREAK instruction, which works like + ;; an interrupt call but it stores the return PC in BRP instead of IRP. + ;; Since we dont really want to have two epilogues (one for system calls + ;; and one for interrupts) we push the contents of BRP instead of IRP in the + ;; system call prologue, to make it look like an ordinary interrupt on the + ;; stackframe. + ;; + ;; Since we can't have system calls inside interrupts, it should not matter + ;; that we don't stack IRP. + ;; + ;; In r1 we have the wanted syscall number. Arguments come in r10,r11,r12,r13,r0 + ;; + ;; This function looks on the _surface_ like spaghetti programming, but it's + ;; really designed so that the fast-path does not force cache-loading of non-used + ;; instructions. Only the non-common cases cause the outlined code to run.. + +_system_call: + ;; stack-frame similar to the irq heads, which is reversed in ret_from_sys_call + push brp ; this is normally push irp + push srp + push dccr + push mof + subq 14*4,sp ; make room for r0-r13 + movem r13,[sp] ; push r0-r13 + push r10 ; push orig_r10 + clear.d [sp=sp-4] ; frametype == 0, normal stackframe + + move.d r10,r2 ; save for later + + movs.w -LENOSYS,r10 + move.d r10,[sp+LR10] ; put the default return value in r10 in the frame + + move.d sp,r10 + jsr _set_esp0 ; save top of frame (clobbers r9...) + + ;; check if this process is syscall-traced + + move.d sp, r10 + and.d -8192, r10 ; THREAD_SIZE == 8192 + move.d [r10+LTASK_PTRACE],r10 + btstq 2, r10 ; PT_TRACESYS + bmi tracesys + nop + + ;; check for sanity in the requested syscall number + + cmpu.w NR_syscalls,r1 + bcc _ret_from_sys_call + lslq 2,r1 ; multiply by 4, in the delay slot + + ;; read the system call vector into r1 + + move.d [r1+_sys_call_table],r1 + + ;; the parameter carrying registers r11, r12 and 13 are intact - restore r10. + ;; the fifth parameter (if any) was in r0, and we need to put it on the stack + + push r0 + move.d r2,r10 + + jsr r1 ; actually call the corresponding system call + addq 4,sp ; pop the r0 parameter + move.d r10,[sp+LR10] ; save the return value + + moveq 1,r9 ; "parameter" to ret_from_sys_call to show it was a sys call + + ;; fall through into ret_from_sys_call to return + +_ret_from_sys_call: + ;; r9 is a parameter - if 1, we came from a syscall, if 0, from an irq + + ;; check if any bottom halves need service + + move.d [_irq_stat],r0 ; softirq_active + and.d [_irq_stat+4],r0 ; softirq_mask + bne handle_softirq + nop + +ret_with_reschedule: + ;; first get the current task-struct pointer (see top for defs) + + move.d sp, r0 + and.d -8192, r0 ; THREAD_SIZE == 8192 + + ;; see if we want to reschedule into another process + + test.d [r0+LTASK_NEEDRESCHED] + bne reschedule + nop + + ;; see if we need to run signal checks (important that r9 is intact here) + + test.d [r0+LTASK_SIGPENDING] + bne signal_return + nop + +Rexit: + ;; this epilogue MUST match the prologues in multiple_interrupt, irq.h and ptregs.h + pop r10 ; frametype + bne RBFexit ; was not CRIS_FRAME_NORMAL, handle otherwise + addq 4,sp ; skip orig_r10, in delayslot + movem [sp+],r13 ; registers r0-r13 + pop mof ; multiply overflow register + pop dccr ; condition codes + pop srp ; subroutine return pointer + jmpu [sp+] ; return by popping irp and jumping there + ;; jmpu takes the U-flag into account to see if we return to + ;; user-mode or kernel mode. + +RBFexit: + cmpq 2, r10 ; was it CRIS_FRAME_FIXUP ? + beq 2f + movem [sp+],r13 ; registers r0-r13, in delay slot + pop mof ; multiply overflow register + pop dccr ; condition codes + pop srp ; subroutine return pointer + rbf [sp+] ; return by popping the CPU status + +2: pop mof ; multiply overflow register + pop dccr ; condition codes + pop srp ; subroutine return pointer + ;; now we have a 4-word SBFS frame which we do not want to restore + ;; using RBF since we have made a fixup. instead we would like to + ;; just get the PC value to restart it with, and skip the rest of + ;; the frame. + pop irp ; fixup location will be here + pop p8 ; null pop + pop p8 ; null pop + reti ; return to IRP, taking U-flag into account + pop p8 ; null pop in delayslot + + +tracesys: + ;; this first invocation of syscall_trace _requires_ that + ;; LR10 in the frame contains -LENOSYS (as is set in the beginning + ;; of system_call + + jsr _syscall_trace + + ;; now we should more or less do the same things as in the system_call + ;; but since our argument regs got clobbered during syscall_trace and + ;; because syscall_trace might want to alter them, we need to reload them + ;; from the stack-frame as we use them. + + ;; check for sanity in the requested syscall number + + move.d [sp+LR1], r1 + movs.w -LENOSYS, r10 + cmpu.w NR_syscalls,r1 + bcc 1f + lslq 2,r1 ; multiply by 4, in the delay slot + + ;; read the system call vector entry into r1 + + move.d [r1+_sys_call_table],r1 + + ;; restore r10, r11, r12, r13 and r0 into the needed registers + + move.d [sp+LORIG_R10], r10 ; LR10 is already filled with -LENOSYS + move.d [sp+LR11], r11 + move.d [sp+LR12], r12 + move.d [sp+LR13], r13 + move.d [sp+LR0], r0 + + ;; the fifth parameter needs to be put on the stack for the system + ;; call to find it + + push r0 + jsr r1 ; actually call the system-call + addq 4,sp ; pop the r0 parameter + +1: move.d r10,[sp+LR10] ; save the return value + + ;; second call of syscall_trace, to let it grab the results + + jsr _syscall_trace + + moveq 1,r9 ; "parameter" to ret_from_sys_call to show it was a sys call + ba _ret_from_sys_call + nop + + ;; from asm/processor.h, the thread_struct + +LTHREAD_KSP = 0 +LTHREAD_USP = 4 +LTHREAD_ESP0 = 8 +LTHREAD_DCCR = 12 + + ;; _resume performs the actual task-switching, by switching stack pointers + ;; input arguments: r10 = prev, r11 = next, r12 = thread offset in task struct + ;; returns old current in r10 + ;; + ;; TODO: see the i386 version. The switch_to which calls resume in our version + ;; could really be an inline asm of this. + +_resume: + push srp ; we keep the old/new PC on the stack + add.d r12, r10 ; r10 = current tasks tss + move dccr, [r10+LTHREAD_DCCR] ; save irq enable state + di + + move usp, [r10+LTHREAD_USP] ; save user-mode stackpointer + + subq 10*4, sp + movem r9, [sp] ; save non-scratch registers + + move.d sp, [r10+LTHREAD_KSP] ; save the kernel stack pointer for the old task + move.d sp, r10 ; return last running task in r10 + and.d -8192, r10 ; get task ptr from stackpointer + add.d r12, r11 ; find the new tasks tss + move.d [r11+LTHREAD_KSP], sp ; switch into the new stackframe by restoring kernel sp + + movem [sp+], r9 ; restore non-scratch registers + + move [r11+LTHREAD_USP], usp ; restore user-mode stackpointer + + move [r11+LTHREAD_DCCR], dccr ; restore irq enable status + jump [sp+] ; restore PC + + ;; This is the MMU bus fault handler. + ;; It needs to stack the CPU status and overall is different + ;; from the other interrupt handlers. + +_mmu_bus_fault: + sbfs [sp=sp-16] ; push the internal CPU status + ;; the first longword in the sbfs frame was the interrupted PC + ;; which fits nicely with the "IRP" slot in pt_regs normally used to + ;; contain the return address. used by Oops to print kernel errors.. + push srp ; make a stackframe similar to pt_regs + push dccr + push mof + di + subq 14*4, sp + movem r13, [sp] + push r10 ; dummy orig_r10 + moveq 1, r10 + push r10 ; frametype == 1, BUSFAULT frame type + + moveq 0, r9 ; busfault is equivalent to an irq + + move.d sp, r10 ; pt_regs argument to handle_mmu_bus_fault + + jsr _handle_mmu_bus_fault ; in arch/cris/mm/fault.c + + ;; now we need to return through the normal path, we cannot just + ;; do the RBFexit since we might have killed off the running + ;; process due to a SEGV, scheduled due to a page blocking or + ;; whatever. + + ba _ret_from_intr + nop + + ;; special handlers for breakpoint and NMI +#if 0 +_hwbreakpoint: + push dccr + di + push r10 + push r11 + push r12 + push r13 + clearf b + move brp,r11 + move.d [_hw_bp_msg],r10 + jsr _printk + setf b + pop r13 + pop r12 + pop r11 + pop r10 + pop dccr + retb + nop +#else +_hwbreakpoint: + push dccr + di +#if 1 + push r10 + push r11 + move.d [_hw_bp_trig_ptr],r10 + move.d [r10],r11 + cmp.d 42,r11 + beq nobp + nop + move brp,r11 + move.d r11,[r10+] + move.d r10,[_hw_bp_trig_ptr] +nobp: pop r11 + pop r10 +#endif + pop dccr + retb + nop +#endif + +_IRQ1_interrupt: +_spurious_interrupt: + di + move.b 4,r0 + move.b r0,[0xb0000030] +basse2: ba basse2 + nop + + ;; this handles the case when multiple interrupts arrive at the same time + ;; we jump to the first set interrupt bit in a priority fashion + ;; the hardware will call the unserved interrupts after the handler finishes + +_multiple_interrupt: + ;; this prologue MUST match the one in irq.h and the struct in ptregs.h!!! + push irp + push srp + push dccr + push mof + di + subq 14*4,sp + movem r13,[sp] + push r10 ; push orig_r10 + clear.d [sp=sp-4] ; frametype == 0, normal frame + + move.d _irq_shortcuts + 8,r1 + moveq 2,r2 ; first bit we care about is the timer0 irq + move.d [0xb00000d8],r0 ; read the irq bits that triggered the multiple irq +multloop: + btst r2,r0 ; check for the irq given by bit r2 + bmi do_shortcut ; actually do the shortcut + nop + addq 1,r2 ; next vector bit - remember this is in the delay slot! + addq 4,r1 ; next vector + cmpq 26,r2 + bne multloop ; process all irq's up to and including number 25 + nop + + ;; strange, we didn't get any set vector bits.. oh well, just return + + ba Rexit + nop + +do_shortcut: + test.d [r1] + beq Rexit + nop + jump [r1] ; jump to the irq handlers shortcut + + + .data + +_hw_bp_trigs: + .space 64*4 +_hw_bp_trig_ptr: + .dword _hw_bp_trigs + +/* linux/linkage.h got it wrong for this compiler currently */ + +#undef SYMBOL_NAME +#define SYMBOL_NAME(X) _/**/X + +_sys_call_table: + .long SYMBOL_NAME(sys_ni_syscall) /* 0 - old "setup()" system call*/ + .long SYMBOL_NAME(sys_exit) + .long SYMBOL_NAME(sys_fork) + .long SYMBOL_NAME(sys_read) + .long SYMBOL_NAME(sys_write) + .long SYMBOL_NAME(sys_open) /* 5 */ + .long SYMBOL_NAME(sys_close) + .long SYMBOL_NAME(sys_waitpid) + .long SYMBOL_NAME(sys_creat) + .long SYMBOL_NAME(sys_link) + .long SYMBOL_NAME(sys_unlink) /* 10 */ + .long SYMBOL_NAME(sys_execve) + .long SYMBOL_NAME(sys_chdir) + .long SYMBOL_NAME(sys_time) + .long SYMBOL_NAME(sys_mknod) + .long SYMBOL_NAME(sys_chmod) /* 15 */ + .long SYMBOL_NAME(sys_lchown16) + .long SYMBOL_NAME(sys_ni_syscall) /* old break syscall holder */ + .long SYMBOL_NAME(sys_stat) + .long SYMBOL_NAME(sys_lseek) + .long SYMBOL_NAME(sys_getpid) /* 20 */ + .long SYMBOL_NAME(sys_mount) + .long SYMBOL_NAME(sys_oldumount) + .long SYMBOL_NAME(sys_setuid16) + .long SYMBOL_NAME(sys_getuid16) + .long SYMBOL_NAME(sys_stime) /* 25 */ + .long SYMBOL_NAME(sys_ptrace) + .long SYMBOL_NAME(sys_alarm) + .long SYMBOL_NAME(sys_fstat) + .long SYMBOL_NAME(sys_pause) + .long SYMBOL_NAME(sys_utime) /* 30 */ + .long SYMBOL_NAME(sys_ni_syscall) /* old stty syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old gtty syscall holder */ + .long SYMBOL_NAME(sys_access) + .long SYMBOL_NAME(sys_nice) + .long SYMBOL_NAME(sys_ni_syscall) /* 35 old ftime syscall holder */ + .long SYMBOL_NAME(sys_sync) + .long SYMBOL_NAME(sys_kill) + .long SYMBOL_NAME(sys_rename) + .long SYMBOL_NAME(sys_mkdir) + .long SYMBOL_NAME(sys_rmdir) /* 40 */ + .long SYMBOL_NAME(sys_dup) + .long SYMBOL_NAME(sys_pipe) + .long SYMBOL_NAME(sys_times) + .long SYMBOL_NAME(sys_ni_syscall) /* old prof syscall holder */ + .long SYMBOL_NAME(sys_brk) /* 45 */ + .long SYMBOL_NAME(sys_setgid16) + .long SYMBOL_NAME(sys_getgid16) + .long SYMBOL_NAME(sys_signal) + .long SYMBOL_NAME(sys_geteuid16) + .long SYMBOL_NAME(sys_getegid16) /* 50 */ + .long SYMBOL_NAME(sys_acct) + .long SYMBOL_NAME(sys_umount) /* recycled never used phys() */ + .long SYMBOL_NAME(sys_ni_syscall) /* old lock syscall holder */ + .long SYMBOL_NAME(sys_ioctl) + .long SYMBOL_NAME(sys_fcntl) /* 55 */ + .long SYMBOL_NAME(sys_ni_syscall) /* old mpx syscall holder */ + .long SYMBOL_NAME(sys_setpgid) + .long SYMBOL_NAME(sys_ni_syscall) /* old ulimit syscall holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* old sys_olduname holder */ + .long SYMBOL_NAME(sys_umask) /* 60 */ + .long SYMBOL_NAME(sys_chroot) + .long SYMBOL_NAME(sys_ustat) + .long SYMBOL_NAME(sys_dup2) + .long SYMBOL_NAME(sys_getppid) + .long SYMBOL_NAME(sys_getpgrp) /* 65 */ + .long SYMBOL_NAME(sys_setsid) + .long SYMBOL_NAME(sys_sigaction) + .long SYMBOL_NAME(sys_sgetmask) + .long SYMBOL_NAME(sys_ssetmask) + .long SYMBOL_NAME(sys_setreuid16) /* 70 */ + .long SYMBOL_NAME(sys_setregid16) + .long SYMBOL_NAME(sys_sigsuspend) + .long SYMBOL_NAME(sys_sigpending) + .long SYMBOL_NAME(sys_sethostname) + .long SYMBOL_NAME(sys_setrlimit) /* 75 */ + .long SYMBOL_NAME(sys_old_getrlimit) + .long SYMBOL_NAME(sys_getrusage) + .long SYMBOL_NAME(sys_gettimeofday) + .long SYMBOL_NAME(sys_settimeofday) + .long SYMBOL_NAME(sys_getgroups16) /* 80 */ + .long SYMBOL_NAME(sys_setgroups16) + .long SYMBOL_NAME(sys_select) /* was old_select in Linux/E100 */ + .long SYMBOL_NAME(sys_symlink) + .long SYMBOL_NAME(sys_lstat) + .long SYMBOL_NAME(sys_readlink) /* 85 */ + .long SYMBOL_NAME(sys_uselib) + .long SYMBOL_NAME(sys_swapon) + .long SYMBOL_NAME(sys_reboot) + .long SYMBOL_NAME(old_readdir) + .long SYMBOL_NAME(old_mmap) /* 90 */ + .long SYMBOL_NAME(sys_munmap) + .long SYMBOL_NAME(sys_truncate) + .long SYMBOL_NAME(sys_ftruncate) + .long SYMBOL_NAME(sys_fchmod) + .long SYMBOL_NAME(sys_fchown16) /* 95 */ + .long SYMBOL_NAME(sys_getpriority) + .long SYMBOL_NAME(sys_setpriority) + .long SYMBOL_NAME(sys_ni_syscall) /* old profil syscall holder */ + .long SYMBOL_NAME(sys_statfs) + .long SYMBOL_NAME(sys_fstatfs) /* 100 */ + .long SYMBOL_NAME(sys_ni_syscall) /* sys_ioperm in i386 */ + .long SYMBOL_NAME(sys_socketcall) + .long SYMBOL_NAME(sys_syslog) + .long SYMBOL_NAME(sys_setitimer) + .long SYMBOL_NAME(sys_getitimer) /* 105 */ + .long SYMBOL_NAME(sys_newstat) + .long SYMBOL_NAME(sys_newlstat) + .long SYMBOL_NAME(sys_newfstat) + .long SYMBOL_NAME(sys_ni_syscall) /* old sys_uname holder */ + .long SYMBOL_NAME(sys_ni_syscall) /* sys_iopl in i386 */ + .long SYMBOL_NAME(sys_vhangup) + .long SYMBOL_NAME(sys_ni_syscall) /* old "idle" system call */ + .long SYMBOL_NAME(sys_ni_syscall) /* vm86old in i386 */ + .long SYMBOL_NAME(sys_wait4) + .long SYMBOL_NAME(sys_swapoff) /* 115 */ + .long SYMBOL_NAME(sys_sysinfo) + .long SYMBOL_NAME(sys_ipc) + .long SYMBOL_NAME(sys_fsync) + .long SYMBOL_NAME(sys_sigreturn) + .long SYMBOL_NAME(sys_clone) /* 120 */ + .long SYMBOL_NAME(sys_setdomainname) + .long SYMBOL_NAME(sys_newuname) + .long SYMBOL_NAME(sys_ni_syscall) /* TODO sys_modify_ldt - do something ?*/ + .long SYMBOL_NAME(sys_adjtimex) + .long SYMBOL_NAME(sys_mprotect) /* 125 */ + .long SYMBOL_NAME(sys_sigprocmask) + .long SYMBOL_NAME(sys_create_module) + .long SYMBOL_NAME(sys_init_module) + .long SYMBOL_NAME(sys_delete_module) + .long SYMBOL_NAME(sys_get_kernel_syms) /* 130 */ + .long SYMBOL_NAME(sys_quotactl) + .long SYMBOL_NAME(sys_getpgid) + .long SYMBOL_NAME(sys_fchdir) + .long SYMBOL_NAME(sys_bdflush) + .long SYMBOL_NAME(sys_sysfs) /* 135 */ + .long SYMBOL_NAME(sys_personality) + .long SYMBOL_NAME(sys_ni_syscall) /* for afs_syscall */ + .long SYMBOL_NAME(sys_setfsuid16) + .long SYMBOL_NAME(sys_setfsgid16) + .long SYMBOL_NAME(sys_llseek) /* 140 */ + .long SYMBOL_NAME(sys_getdents) + .long SYMBOL_NAME(sys_select) + .long SYMBOL_NAME(sys_flock) + .long SYMBOL_NAME(sys_msync) + .long SYMBOL_NAME(sys_readv) /* 145 */ + .long SYMBOL_NAME(sys_writev) + .long SYMBOL_NAME(sys_getsid) + .long SYMBOL_NAME(sys_fdatasync) + .long SYMBOL_NAME(sys_sysctl) + .long SYMBOL_NAME(sys_mlock) /* 150 */ + .long SYMBOL_NAME(sys_munlock) + .long SYMBOL_NAME(sys_mlockall) + .long SYMBOL_NAME(sys_munlockall) + .long SYMBOL_NAME(sys_sched_setparam) + .long SYMBOL_NAME(sys_sched_getparam) /* 155 */ + .long SYMBOL_NAME(sys_sched_setscheduler) + .long SYMBOL_NAME(sys_sched_getscheduler) + .long SYMBOL_NAME(sys_sched_yield) + .long SYMBOL_NAME(sys_sched_get_priority_max) + .long SYMBOL_NAME(sys_sched_get_priority_min) /* 160 */ + .long SYMBOL_NAME(sys_sched_rr_get_interval) + .long SYMBOL_NAME(sys_nanosleep) + .long SYMBOL_NAME(sys_mremap) + .long SYMBOL_NAME(sys_setresuid16) + .long SYMBOL_NAME(sys_getresuid16) /* 165 */ + .long SYMBOL_NAME(sys_ni_syscall) /* sys_vm86 */ + .long SYMBOL_NAME(sys_query_module) + .long SYMBOL_NAME(sys_poll) + .long SYMBOL_NAME(sys_nfsservctl) + .long SYMBOL_NAME(sys_setresgid16) /* 170 */ + .long SYMBOL_NAME(sys_getresgid16) + .long SYMBOL_NAME(sys_prctl) + .long SYMBOL_NAME(sys_rt_sigreturn) + .long SYMBOL_NAME(sys_rt_sigaction) + .long SYMBOL_NAME(sys_rt_sigprocmask) /* 175 */ + .long SYMBOL_NAME(sys_rt_sigpending) + .long SYMBOL_NAME(sys_rt_sigtimedwait) + .long SYMBOL_NAME(sys_rt_sigqueueinfo) + .long SYMBOL_NAME(sys_rt_sigsuspend) + .long SYMBOL_NAME(sys_pread) /* 180 */ + .long SYMBOL_NAME(sys_pwrite) + .long SYMBOL_NAME(sys_chown16) + .long SYMBOL_NAME(sys_getcwd) + .long SYMBOL_NAME(sys_capget) + .long SYMBOL_NAME(sys_capset) /* 185 */ + .long SYMBOL_NAME(sys_sigaltstack) + .long SYMBOL_NAME(sys_sendfile) + .long SYMBOL_NAME(sys_ni_syscall) /* streams1 */ + .long SYMBOL_NAME(sys_ni_syscall) /* streams2 */ + .long SYMBOL_NAME(sys_vfork) /* 190 */ + .long SYMBOL_NAME(sys_getrlimit) + .long SYMBOL_NAME(sys_mmap2) + .long SYMBOL_NAME(sys_truncate64) + .long SYMBOL_NAME(sys_ftruncate64) + .long SYMBOL_NAME(sys_stat64) /* 195 */ + .long SYMBOL_NAME(sys_lstat64) + .long SYMBOL_NAME(sys_fstat64) + .long SYMBOL_NAME(sys_lchown) + .long SYMBOL_NAME(sys_getuid) + .long SYMBOL_NAME(sys_getgid) /* 200 */ + .long SYMBOL_NAME(sys_geteuid) + .long SYMBOL_NAME(sys_getegid) + .long SYMBOL_NAME(sys_setreuid) + .long SYMBOL_NAME(sys_setregid) + .long SYMBOL_NAME(sys_getgroups) /* 205 */ + .long SYMBOL_NAME(sys_setgroups) + .long SYMBOL_NAME(sys_fchown) + .long SYMBOL_NAME(sys_setresuid) + .long SYMBOL_NAME(sys_getresuid) + .long SYMBOL_NAME(sys_setresgid) /* 210 */ + .long SYMBOL_NAME(sys_getresgid) + .long SYMBOL_NAME(sys_chown) + .long SYMBOL_NAME(sys_setuid) + .long SYMBOL_NAME(sys_setgid) + .long SYMBOL_NAME(sys_setfsuid) /* 215 */ + .long SYMBOL_NAME(sys_setfsgid) + .long SYMBOL_NAME(sys_pivot_root) + .long SYMBOL_NAME(sys_mincore) + .long SYMBOL_NAME(sys_madvise) + .long SYMBOL_NAME(sys_getdents64) /* 220 */ + + /* + * NOTE!! This doesn't have to be exact - we just have + * to make sure we have _enough_ of the "sys_ni_syscall" + * entries. Don't panic if you notice that this hasn't + * been shrunk every time we add a new system call. + */ + + ;; TODO: this needs to actually generate sys_ni_syscall entires + ;; since we now have removed the check for NULL entries in this + ;; table in system_call! + + .space (NR_syscalls-220)*4 + diff --git a/arch/cris/kernel/head.S b/arch/cris/kernel/head.S new file mode 100644 index 000000000..b436aa843 --- /dev/null +++ b/arch/cris/kernel/head.S @@ -0,0 +1,519 @@ + ;; $Id: head.S,v 1.11 2001/01/16 16:31:38 bjornw Exp $ + ;; + ;; Head of the kernel - alter with care + ;; + ;; Copyright (C) 2000, 2001 Axis Communications AB + ;; + ;; Authors: Bjorn Wesen (bjornw@axis.com) + ;; + ;; $Log: head.S,v $ + ;; Revision 1.11 2001/01/16 16:31:38 bjornw + ;; * Changed name and semantics of running_from_flash to romfs_in_flash, + ;; set by head.S to indicate to setup.c whether there is a cramfs image + ;; after the kernels BSS or not. Should work for all three boot-cases + ;; (DRAM with cramfs in DRAM, DRAM with cramfs in flash (compressed boot), + ;; and flash with cramfs in flash) + ;; + ;; Revision 1.10 2001/01/16 14:12:21 bjornw + ;; * Check for cramfs start passed in r9 from the decompressor, if all other + ;; cramfs options fail (if we boot from DRAM but don't find a cramfs image + ;; after the kernel in DRAM, it is probably still in the flash) + ;; * Check magic in cramfs detection when booting from flash directly + ;; + ;; Revision 1.9 2001/01/15 17:17:02 bjornw + ;; * Corrected the code that detects the cramfs lengths + ;; * Added a comment saying that the above does not work due to other + ;; reasons.. + ;; + ;; Revision 1.8 2001/01/15 16:27:51 jonashg + ;; Made boot after flashing work. + ;; * end destination is __vmlinux_end in RAM. + ;; * _romfs_start moved because of virtual memory. + ;; + ;; Revision 1.7 2000/11/21 13:55:29 bjornw + ;; Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type + ;; + ;; Revision 1.6 2000/10/06 12:36:55 bjornw + ;; Forgot swapper_pg_dir when changing memory map.. + ;; + ;; Revision 1.5 2000/10/04 16:49:30 bjornw + ;; * Fixed memory mapping in LX + ;; * Check for cramfs instead of romfs + ;; + ;; + +#include <linux/config.h> +#define ASSEMBLER_MACROS_ONLY +#include <asm/sv_addr_ag.h> + +#define CRAMFS_MAGIC 0x28cd3d45 + + ;; exported symbols + + .globl _etrax_irv + .globl _romfs_start + .globl _romfs_length + .globl _romfs_in_flash + .globl _swapper_pg_dir + + .text + + ;; This is the entry point of the kernel. We are in supervisor mode. + ;; 0x00000000 if Flash, 0x40004000 if DRAM + ;; since etrax actually starts at address 2 when booting from flash, we + ;; put a nop (2 bytes) here first so we dont accidentally skip the di + ;; + ;; NOTICE! The register r9 is used as a parameter carrying register from + ;; the decompressor (if the kernel was compressed). It should not be + ;; used in the code below until it is read. + + nop + di + + ;; First setup the kseg_c mapping from where the kernel is linked + ;; to 0x40000000 (where the actual DRAM resides) otherwise + ;; we cannot do very much! See arch/cris/README.mm + ;; + ;; Notice that since we're potentially running at 0x00 or 0x40 right now, + ;; we will get a fault as soon as we enable the MMU if we dont + ;; temporarily map those segments linearily. + ;; + ;; Due to a bug in Etrax-100 LX version 1 we need to map the memory + ;; slightly different. We also let the simulator get this mapping for now. + +#ifdef CONFIG_CRIS_LOW_MAP + move.d 0x0800b000, r0 ; kseg mappings + move.d r0, [R_MMU_KBASE_HI] + + move.d 0x04040000, r0 ; temporary map of 0x40->0x40 and 0x00->0x00 + move.d r0, [R_MMU_KBASE_LO] + + move.d 0x80074871, r0 ; mmu enable, segs e,b,6,5,4,0 segment mapped + move.d r0, [R_MMU_CONFIG] +#else + move.d 0x0804b000, r0 ; kseg mappings + move.d r0, [R_MMU_KBASE_HI] + + move.d 0x00040000, r0 ; temporary map of 0x40->0x40 and 0x00->0x00 + move.d r0, [R_MMU_KBASE_LO] + + move.d 0x8007d811, r0 ; mmu enable, segs f,e,c,b,4,0 segment mapped + move.d r0, [R_MMU_CONFIG] +#endif + + ;; Now we need to sort out the segments and their locations in RAM or + ;; Flash. The image in the Flash (or in DRAM) consists of 3 pieces: + ;; 1) kernel text, 2) kernel data, 3) ROM filesystem image + ;; But the linker has linked the kernel to expect this layout in + ;; DRAM memory: + ;; 1) kernel text, 2) kernel data, 3) kernel BSS + ;; (the location of the ROM filesystem is determined by the krom driver) + ;; If we boot this from Flash, we want to keep the ROM filesystem in + ;; the flash, we want to copy the text and need to copy the data to DRAM. + ;; But if we boot from DRAM, we need to move the ROMFS image + ;; from its position after kernel data, to after kernel BSS, BEFORE the + ;; kernel starts using the BSS area (since its "overlayed" with the ROMFS) + ;; + ;; In both cases, we start in un-cached mode, and need to jump into a + ;; cached PC after we're done fiddling around with the segments. + ;; + ;; arch/etrax100/etrax100.ld sets some symbols that define the start + ;; and end of each segment. + + ;; Check if we start from DRAM or FLASH by testing PC + + move.d pc,r0 + and.d 0x7fffffff,r0 ; get rid of the non-cache bit + cmp.d 0x10000,r0 ; arbitrary... just something above this code + bcs inflash + nop + + jump inram ; enter cached ram + +inflash: + +#ifndef CONFIG_SVINTO_SIM + + ;; We need to setup the bus registers before we start using the DRAM + + move.d DEF_R_WAITSTATES, r0 + move.d r0, [R_WAITSTATES] + + move.d DEF_R_BUS_CONFIG, r0 + move.d r0, [R_BUS_CONFIG] + + move.d DEF_R_DRAM_CONFIG, r0 + move.d r0, [R_DRAM_CONFIG] + + move.d DEF_R_DRAM_TIMING, r0 + move.d r0, [R_DRAM_TIMING] + +#endif + ;; Copy text+data to DRAM + ;; This is fragile - the calculation of r4 as the image size depends + ;; on that the labels below actually are the first and last positions + ;; in the linker-script. + ;; + ;; Then the locating of the cramfs image depends on the aforementioned + ;; image being located in the flash at 0. This is most often not true, + ;; thus the following does not work (normally there is a rescue-block + ;; between the physical start of the flash and the flash-image start, + ;; and when run with compression, the kernel is actually unpacked to + ;; DRAM and we never get here in the first place :)) + + moveq 0, r0 ; source + move.d _text_start, r1 ; destination + move.d __vmlinux_end, r2 ; end destination + move.d r2, r4 + sub.d r1, r4 ; r4=__vmlinux_end in flash, used below +1: move.w [r0+], r3 + move.w r3, [r1+] + cmp.d r2, r1 + bcs 1b + nop + + ;; We keep the cramfs in the flash. + ;; There might be none, but that does not matter because + ;; we don't do anything than read some bytes here. + + moveq 0, r0 + move.d r0, [_romfs_length] ; default if there is no cramfs + + move.d [r4], r0 ; cramfs_super.magic + cmp.d CRAMFS_MAGIC, r0 + bne 1f + nop + move.d [r4 + 4], r0 ; cramfs_super.size + move.d r0, [_romfs_length] +#ifdef CONFIG_CRIS_LOW_MAP + add.d 0x50000000, r4 ; add flash start in virtual memory (cached) +#else + add.d 0xf0000000, r4 ; add flash start in virtual memory (cached) +#endif + move.d r4, [_romfs_start] +1: + moveq 1, r0 + move.d r0, [_romfs_in_flash] + + jump start_it ; enter code, cached this time + +inram: + ;; Move the ROM fs to after BSS end. This assumes that the cramfs + ;; second longword contains the length of the cramfs + + moveq 0, r0 + move.d r0, [_romfs_length] ; default if there is no cramfs + + ;; First check if there is a cramfs (magic value) + ;; Notice that we check for cramfs magic value - which is + ;; the "rom fs" we'll possibly use in 2.4 if not JFFS (which does + ;; not need this mechanism anyway) + + move.d __vmlinux_end, r0 ; the image will be after the vmlinux end address + move.d [r0], r1 ; cramfs assumes same endian on host/target + cmp.d CRAMFS_MAGIC, r1; magic value in cramfs superblock + bne no_romfs_in_ram + nop + + ;; Ok. What is its size ? + + move.d [r0 + 4], r2 ; cramfs_super.size (again, no need to swapwb) + + ;; We want to copy it to the end of the BSS + + move.d _end, r1 + + ;; Remember values so cramfs and setup can find this info + + move.d r1, [_romfs_start] ; new romfs location + move.d r2, [_romfs_length] + + ;; We need to copy it backwards, since they can be overlapping + + add.d r2, r0 + add.d r2, r1 + + ;; Go ahead. Make my loop. + + lsrq 1, r2 ; size is in bytes, we copy words + +1: move.w [r0=r0-2],r3 + move.w r3,[r1=r1-2] + subq 1, r2 + bne 1b + nop + + ;; Dont worry that the BSS is tainted. It will be cleared later. + + moveq 0, r0 + move.d r0, [_romfs_in_flash] + + jump start_it ; better skip the additional cramfs check below + +no_romfs_in_ram: + + ;; We have still one other possibility at this point - the kernel + ;; could have been unpacked to DRAM by the loader, but the cramfs + ;; image was still in the Flash directly after the compressed kernel + ;; image. The loader passes the address of the byte succeeding the + ;; last compressed byte in the flash in the register r9 when starting + ;; the kernel. Check if r9 points to a decent cramfs image! + ;; (Notice that if this is not booted from the loader, r9 will be + ;; garbage but we do sanity checks on it, the chance that it points + ;; to a cramfs magic is small.. ) + + cmp.d 0x0ffffff8, r9 + bcc 1f ; r9 points outside the flash area + nop + move.d [r9], r0 ; cramfs_super.magic + cmp.d CRAMFS_MAGIC, r0 + bne 1f + nop + move.d [r9+4], r0 ; cramfs_super.length + move.d r0, [_romfs_length] +#ifdef CONFIG_CRIS_LOW_MAP + add.d 0x50000000, r9 ; add flash start in virtual memory (cached) +#else + add.d 0xf0000000, r9 ; add flash start in virtual memory (cached) +#endif + move.d r9, [_romfs_start] + + moveq 1, r0 + move.d r0, [_romfs_in_flash] +1: + + jump start_it ; enter code, cached this time + +start_it: + ;; the kernel stack is overlayed with the task structure for each + ;; task. thus the initial kernel stack is in the same page as the + ;; init_task (but starts in the top of the page, size 8192) + move.d _init_task_union + 8192,sp + move.d _ibr_start,r0 ; this symbol is set by the linker script + move r0,ibr + move.d r0,[_etrax_irv] ; set the interrupt base register and pointer + + ;; Clear BSS region, from _bss_start to _end + + move.d __bss_start, r0 + move.d _end, r1 +1: clear.d [r0+] + cmp.d r1, r0 + bcs 1b + nop + +#ifdef CONFIG_BLK_DEV_ETRAXIDE + ;; disable ATA before enabling it in genconfig below + moveq 0,r0 + move.d r0,[R_ATA_CTRL_DATA] + move.d r0,[R_ATA_TRANSFER_CNT] + move.d r0,[R_ATA_CONFIG] +#if 0 + move.d R_PORT_G_DATA,r1 + move.d r0,[r1]; assert ATA bus-reset + nop + nop + nop + nop + nop + nop + move.d 0x08000000,r0 + move.d r0,[r1] +#endif +#endif + +#ifdef CONFIG_JULIETTE + ;; configure external DMA channel 0 before enabling it in genconfig + + moveq 0,r0 + move.d r0,[R_EXT_DMA_0_ADDR] + move.d 0x860000,r0 ; cnt enable, word size, output, stop, size 0 + move.d r0,[R_EXT_DMA_0_CMD] + + ;; reset dma4 and wait for completion + + moveq 4,r0 + move.b r0,[R_DMA_CH4_CMD] +w4u: move.b [R_DMA_CH4_CMD],r0 + and.b 7,r0 + cmp.b 4,r0 + beq w4u + nop + + ;; reset dma5 and wait for completion + + moveq 4,r0 + move.b r0,[R_DMA_CH5_CMD] +w5u: move.b [R_DMA_CH5_CMD],r0 + and.b 7,r0 + cmp.b 4,r0 + beq w5u + nop +#endif + + ;; Etrax product HW genconfig setup + + moveq 0,r0 +#if !defined(CONFIG_KGDB) && !defined(CONFIG_DMA_MEMCPY) + or.d 0x140000,r0 ; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA +#endif +#if !defined(CONFIG_KGDB) || !defined(CONFIG_DEBUG_PORT1) + or.d 0xc00000,r0 ; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA +#endif +#ifdef CONFIG_DMA_MEMCPY + or.d 0x003c0000,r0 ; 6/7 memory-memory DMA +#endif +#ifdef CONFIG_ETRAX100_SERIAL_PORT2 + or.d 0x2808,r0 ; DMA channels 2 and 3 to serport 2, port 2 enabled +#endif +#ifdef CONFIG_ETRAX100_SERIAL_PORT3 + or.d 0x28100,r0 ; DMA channels 4 and 5 to serport 3, port 3 enabled +#endif +#if defined(CONFIG_ETRAX100_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE) + or.w 0x4,r0 ; parport 0 enabled using DMA 2/3 +#endif +#if defined(CONFIG_ETRAX100_PARALLEL_PORT1) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE) + or.w 0x80,r0 ; parport 1 enabled using DMA 4/5 +#endif +#ifdef CONFIG_BLK_DEV_ETRAXIDE + or.d 0x3c02,r0 ; DMA channels 2 and 3 to ATA, ATA enabled +#endif +#ifdef CONFIG_JULIETTE + or.d 0x3c000,r0 ; DMA channels 4 and 5 to EXTDMA0, for Juliette +#ifndef CONFIG_BLK_DEV_ETRAXIDE + or.d 0x41,r0 ; HACK for now! To make G27 connected for the RTC +#endif +#endif + move.d r0,[_genconfig_shadow] ; init a shadow register of R_GEN_CONFIG + +#ifndef CONFIG_SVINTO_SIM + move.d r0,[R_GEN_CONFIG] + +#if 0 + moveq 4,r0 + move.b r0,[R_DMA_CH6_CMD] ; reset (ser0 dma out) + move.b r0,[R_DMA_CH7_CMD] ; reset (ser0 dma in) +w61: move.b [R_DMA_CH6_CMD],r0 ; wait for reset cycle to finish + and.b 7,r0 + cmp.b 4,r0 + beq w61 + nop +w71: move.b [R_DMA_CH7_CMD],r0 ; wait for reset cycle to finish + and.b 7,r0 + cmp.b 4,r0 + beq w71 + nop +#endif + + moveq 4,r0 + move.b r0,[R_DMA_CH8_CMD] ; reset (ser1 dma out) + move.b r0,[R_DMA_CH9_CMD] ; reset (ser1 dma in) +w81: move.b [R_DMA_CH8_CMD],r0 ; wait for reset cycle to finish + and.b 7,r0 + cmp.b 4,r0 + beq w81 + nop +w91: move.b [R_DMA_CH9_CMD],r0 ; wait for reset cycle to finish + and.b 7,r0 + cmp.b 4,r0 + beq w91 + nop + + ;; setup port PA and PB default initial directions and data + ;; including their shadow registers + + move.b DEF_R_PORT_PA_DIR,r0 + move.b r0,[_port_pa_dir_shadow] + move.b r0,[R_PORT_PA_DIR] + move.b DEF_R_PORT_PA_DATA,r0 + move.b r0,[_port_pa_data_shadow] + move.b r0,[R_PORT_PA_DATA] + + move.b DEF_R_PORT_PB_CONFIG,r0 + move.b r0,[_port_pb_config_shadow] + move.b r0,[R_PORT_PB_CONFIG] + move.b DEF_R_PORT_PB_DIR,r0 + move.b r0,[_port_pb_dir_shadow] + move.b r0,[R_PORT_PB_DIR] + move.b DEF_R_PORT_PB_DATA,r0 + move.b r0,[_port_pb_data_shadow] + move.b r0,[R_PORT_PB_DATA] + + moveq 0,r0 + move.d r0,[_port_g_data_shadow] + move.d r0,[R_PORT_G_DATA] + + ;; setup the serial port 0 at 115200 baud for debug purposes + + moveq 0,r0 + move.d r0,[R_SERIAL0_XOFF] + + move.b 0x99,r0 + move.b r0,[R_SERIAL0_BAUD] ; 115.2kbaud for both transmit and receive + + move.b 0x40,r0 ; rec enable + move.b r0,[R_SERIAL0_REC_CTRL] + + move.b 0x40,r0 ; tr enable + move.b r0,[R_SERIAL0_TR_CTRL] + + ;; setup the serial port 1 at 115200 baud for debug purposes + + moveq 0,r0 + move.d r0,[R_SERIAL1_XOFF] + + move.b 0x99,r0 + move.b r0,[R_SERIAL1_BAUD] ; 115.2kbaud for both transmit and receive + + move.b 0x40,r0 ; rec enable + move.b r0,[R_SERIAL1_REC_CTRL] + + move.b 0x40,r0 ; tr enable + move.b r0,[R_SERIAL1_TR_CTRL] + +#ifdef CONFIG_ETRAX_90000000_LEDS + ;; clear LED's on Stallone and Olga boards + moveq -1,r0 + move.d r0,[_port_90000000_shadow] + move.d r0,[0x90000000] +#endif + +#ifdef CONFIG_ETRAX100_SERIAL_PORT3 + ;; setup the serial port 3 at 115200 baud for debug purposes + + moveq 0,r0 + move.d r0,[R_SERIAL3_XOFF] + + move.b 0x99,r0 + move.b r0,[R_SERIAL3_BAUD] ; 115.2kbaud for both transmit and receive + + move.b 0x40,r0 ; rec enable + move.b r0,[R_SERIAL3_REC_CTRL] + + move.b 0x40,r0 ; tr enable + move.b r0,[R_SERIAL3_TR_CTRL] +#endif + +#endif /* CONFIG_SVINTO_SIM */ + + jump _start_kernel ; jump into the C-function _start_kernel in init/main.c + + + .data +_etrax_irv: + .dword 0 +_romfs_start: + .dword 0 +_romfs_length: + .dword 0 +_romfs_in_flash: + .dword 0 + + ;; put some special pages at the beginning of the kernel aligned + ;; to page boundaries - the kernel cannot start until after this + +#ifdef CONFIG_CRIS_LOW_MAP +_swapper_pg_dir = 0x60002000 +#else +_swapper_pg_dir = 0xc0002000 +#endif diff --git a/arch/cris/kernel/hexify.c b/arch/cris/kernel/hexify.c new file mode 100644 index 000000000..daa331fec --- /dev/null +++ b/arch/cris/kernel/hexify.c @@ -0,0 +1,31 @@ +#include <stdio.h> + + +void main() +{ + int c; + int comma=0; + int count=0; + while((c=getchar())!=EOF) + { + unsigned char x=c; + if(comma) + printf(","); + else + comma=1; + if(count==8) + { + count=0; + printf("\n"); + } + if(count==0) + printf("\t"); + printf("0x%02X",c); + count++; + } + if(count) + printf("\n"); + exit(0); +} + + diff --git a/arch/cris/kernel/irq.c b/arch/cris/kernel/irq.c new file mode 100644 index 000000000..e55f94d20 --- /dev/null +++ b/arch/cris/kernel/irq.c @@ -0,0 +1,467 @@ +/* $Id: irq.c,v 1.5 2000/08/17 15:35:15 bjornw Exp $ + * + * linux/arch/cris/kernel/irq.c + * + * Copyright (c) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * This file contains the code used by various IRQ handling routines: + * asking for different IRQ's should be done through these routines + * instead of just grabbing them. Thus setups with different IRQ numbers + * shouldn't result in any weird surprises, and installing new handlers + * should be easier. + * + * Notice Linux/CRIS: these routines do not care about SMP + * + */ + +/* + * IRQ's are in fact implemented a bit like signal handlers for the kernel. + * Naturally it's not a 1:1 relation, but there are similarities. + */ + +#include <linux/config.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/malloc.h> +#include <linux/random.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> + +#include <asm/svinto.h> + +char *hw_bp_msg = "BP 0x%x\n"; + +static inline void +mask_irq(unsigned int irq_nr) +{ + *R_VECT_MASK_CLR = 1 << irq_nr; +} + +static inline void +unmask_irq(unsigned int irq_nr) +{ + *R_VECT_MASK_SET = 1 << irq_nr; +} + +void +disable_irq(unsigned int irq_nr) +{ + unsigned long flags; + + save_flags(flags); + cli(); + mask_irq(irq_nr); + restore_flags(flags); +} + +void +enable_irq(unsigned int irq_nr) +{ + unsigned long flags; + save_flags(flags); + cli(); + unmask_irq(irq_nr); + restore_flags(flags); +} + +unsigned long +probe_irq_on() +{ + return 0; +} + +int +probe_irq_off(unsigned long x) +{ + return 0; +} + +irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */ + +/* don't use set_int_vector, it bypasses the linux interrupt handlers. it is + * global just so that the kernel gdb can use it. + */ + +void +set_int_vector(int n, irqvectptr addr, irqvectptr saddr) +{ + /* remember the shortcut entry point, after the prologue */ + + irq_shortcuts[n] = saddr; + + etrax_irv->v[n + 0x20] = (irqvectptr)addr; +} + +/* the breakpoint vector is obviously not made just like the normal irq handlers + * but needs to contain _code_ to jump to addr. + * + * the BREAK n instruction jumps to IBR + n * 8 + */ + +void +set_break_vector(int n, irqvectptr addr) +{ + unsigned short *jinstr = (unsigned short *)&etrax_irv->v[n*2]; + unsigned long *jaddr = (unsigned long *)(jinstr + 1); + + /* if you don't know what this does, do not touch it! */ + + *jinstr = 0x0d3f; + *jaddr = (unsigned long)addr; + + /* 00000026 <clrlop+1a> 3f0d82000000 jump 0x82 */ +} + + +/* + * This builds up the IRQ handler stubs using some ugly macros in irq.h + * + * These macros create the low-level assembly IRQ routines that do all + * the operations that are needed. They are also written to be fast - and to + * disable interrupts as little as humanly possible. + * + */ + +/* IRQ0 and 1 are special traps */ +void hwbreakpoint(void); +void IRQ1_interrupt(void); +BUILD_IRQ(2, 0x04) /* the timer interrupt */ +BUILD_IRQ(3, 0x08) +BUILD_IRQ(4, 0x10) +BUILD_IRQ(5, 0x20) +BUILD_IRQ(6, 0x40) +BUILD_IRQ(7, 0x80) +BUILD_IRQ(8, 0x100) +BUILD_IRQ(9, 0x200) +BUILD_IRQ(10, 0x400) +BUILD_IRQ(11, 0x800) +BUILD_IRQ(12, 0x1000) +BUILD_IRQ(13, 0x2000) +void mmu_bus_fault(void); /* IRQ 14 is the bus fault interrupt */ +void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */ +BUILD_IRQ(16, 0x10000) +BUILD_IRQ(17, 0x20000) +BUILD_IRQ(18, 0x40000) +BUILD_IRQ(19, 0x80000) +BUILD_IRQ(20, 0x100000) +BUILD_IRQ(21, 0x200000) +BUILD_IRQ(22, 0x400000) +BUILD_IRQ(23, 0x800000) +BUILD_IRQ(24, 0x1000000) +BUILD_IRQ(25, 0x2000000) + +/* + * Pointers to the low-level handlers + */ + +static void (*interrupt[NR_IRQS])(void) = { + NULL, NULL, IRQ2_interrupt, IRQ3_interrupt, + IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt, + IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt, + IRQ12_interrupt, IRQ13_interrupt, NULL, NULL, + IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt, + IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt, + IRQ24_interrupt, IRQ25_interrupt +}; + +static void (*sinterrupt[NR_IRQS])(void) = { + NULL, NULL, sIRQ2_interrupt, sIRQ3_interrupt, + sIRQ4_interrupt, sIRQ5_interrupt, sIRQ6_interrupt, sIRQ7_interrupt, + sIRQ8_interrupt, sIRQ9_interrupt, sIRQ10_interrupt, sIRQ11_interrupt, + sIRQ12_interrupt, sIRQ13_interrupt, NULL, NULL, + sIRQ16_interrupt, sIRQ17_interrupt, sIRQ18_interrupt, sIRQ19_interrupt, + sIRQ20_interrupt, sIRQ21_interrupt, sIRQ22_interrupt, sIRQ23_interrupt, + sIRQ24_interrupt, sIRQ25_interrupt +}; + +static void (*bad_interrupt[NR_IRQS])(void) = { + NULL, NULL, + NULL, bad_IRQ3_interrupt, + bad_IRQ4_interrupt, bad_IRQ5_interrupt, + bad_IRQ6_interrupt, bad_IRQ7_interrupt, + bad_IRQ8_interrupt, bad_IRQ9_interrupt, + bad_IRQ10_interrupt, bad_IRQ11_interrupt, + bad_IRQ12_interrupt, bad_IRQ13_interrupt, + NULL, NULL, + bad_IRQ16_interrupt, bad_IRQ17_interrupt, + bad_IRQ18_interrupt, bad_IRQ19_interrupt, + bad_IRQ20_interrupt, bad_IRQ21_interrupt, + bad_IRQ22_interrupt, bad_IRQ23_interrupt, + bad_IRQ24_interrupt, bad_IRQ25_interrupt +}; + +/* + * Initial irq handlers. + */ + +static struct irqaction *irq_action[NR_IRQS] = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL +}; + +int get_irq_list(char *buf) +{ + int i, len = 0; + struct irqaction * action; + + for (i = 0; i < NR_IRQS; i++) { + action = irq_action[i]; + if (!action) + continue; + len += sprintf(buf+len, "%2d: %10u %c %s", + i, kstat.irqs[0][i], + (action->flags & SA_INTERRUPT) ? '+' : ' ', + action->name); + for (action = action->next; action; action = action->next) { + len += sprintf(buf+len, ",%s %s", + (action->flags & SA_INTERRUPT) ? " +" : "", + action->name); + } + len += sprintf(buf+len, "\n"); + } + return len; +} + +/* called by the assembler IRQ entry functions defined in irq.h + * to dispatch the interrupts to registred handlers + * interrupts are disabled upon entry - depending on if the + * interrupt was registred with SA_INTERRUPT or not, interrupts + * are re-enabled or not. + */ + +asmlinkage void do_IRQ(int irq, struct pt_regs * regs) +{ + struct irqaction *action; + int do_random, cpu; + + cpu = smp_processor_id(); + irq_enter(cpu); + kstat.irqs[cpu][irq]++; + + action = *(irq + irq_action); + if (action) { + if (!(action->flags & SA_INTERRUPT)) + __sti(); + action = *(irq + irq_action); + do_random = 0; + do { + do_random |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + if (do_random & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + } + irq_exit(cpu); + + if (softirq_active(cpu) & softirq_mask(cpu)) + do_softirq(); + + /* unmasking and bottom half handling is done magically for us. */ +} + +/* this function links in a handler into the chain of handlers for the + given irq, and if the irq has never been registred, the appropriate + handler is entered into the interrupt vector +*/ + +int setup_etrax_irq(int irq, struct irqaction * new) +{ + int shared = 0; + struct irqaction *old, **p; + unsigned long flags; + + p = irq_action + irq; + if ((old = *p) != NULL) { + /* Can't share interrupts unless both agree to */ + if (!(old->flags & new->flags & SA_SHIRQ)) + return -EBUSY; + + /* Can't share interrupts unless both are same type */ + if ((old->flags ^ new->flags) & SA_INTERRUPT) + return -EBUSY; + + /* add new interrupt at end of irq queue */ + do { + p = &old->next; + old = *p; + } while (old); + shared = 1; + } + + if (new->flags & SA_SAMPLE_RANDOM) + rand_initialize_irq(irq); + + save_flags(flags); + cli(); + *p = new; + + if (!shared) { + /* if the irq wasn't registred before, enter it into the vector table + and unmask it physically + */ + set_int_vector(irq, interrupt[irq], sinterrupt[irq]); + unmask_irq(irq); + } + + restore_flags(flags); + return 0; +} + +/* this function is called by a driver to register an irq handler + Valid flags: + SA_INTERRUPT -> it's a fast interrupt, handler called with irq disabled and + no signal checking etc is performed upon exit + SA_SHIRQ -> the interrupt can be shared between different handlers, the handler + is required to check if the irq was "aimed" at it explicitely + SA_RANDOM -> the interrupt will add to the random generators entropy +*/ + +int request_irq(unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + int retval; + struct irqaction * action; + + /* interrupts 0 and 1 are hardware breakpoint and NMI and we can't support + these yet. interrupt 15 is the multiple irq, it's special. */ + + if(irq < 2 || irq == 15 || irq >= NR_IRQS) + return -EINVAL; + + if(!handler) + return -EINVAL; + + /* allocate and fill in a handler structure and setup the irq */ + + action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = irqflags; + action->mask = 0; + action->name = devname; + action->next = NULL; + action->dev_id = dev_id; + + retval = setup_etrax_irq(irq, action); + + if (retval) + kfree(action); + return retval; +} + +void free_irq(unsigned int irq, void *dev_id) +{ + struct irqaction * action, **p; + unsigned long flags; + + if (irq >= NR_IRQS) { + printk("Trying to free IRQ%d\n",irq); + return; + } + for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) { + if (action->dev_id != dev_id) + continue; + + /* Found it - now free it */ + save_flags(flags); + cli(); + *p = action->next; + if (!irq[irq_action]) { + mask_irq(irq); + set_int_vector(irq, bad_interrupt[irq], 0); + } + restore_flags(flags); + kfree(action); + return; + } + printk("Trying to free free IRQ%d\n",irq); +} + +void weird_irq(void) +{ + __asm__("di"); + printk("weird irq\n"); + while(1); +} + +/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and + setting the irq vector table to point to bad_interrupt ptrs. +*/ + +void system_call(void); /* from entry.S */ + +void init_IRQ(void) +{ + int i; + + /* clear all interrupt masks */ + +#ifndef CONFIG_SVINTO_SIM + *R_IRQ_MASK0_CLR = 0xffffffff; + *R_IRQ_MASK1_CLR = 0xffffffff; + *R_IRQ_MASK2_CLR = 0xffffffff; +#endif + + *R_VECT_MASK_CLR = 0xffffffff; + + /* clear the shortcut entry points */ + + for(i = 0; i < NR_IRQS; i++) + irq_shortcuts[i] = NULL; + + for (i = 0; i < 256; i++) + etrax_irv->v[i] = weird_irq; + + /* set all etrax irq's to the bad handlers */ + for (i = 2; i < NR_IRQS; i++) + set_int_vector(i, bad_interrupt[i], 0); + + /* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */ + + set_int_vector(15, multiple_interrupt, 0); + + /* 0 and 1 which are special breakpoint/NMI traps */ + + set_int_vector(0, hwbreakpoint, 0); + set_int_vector(1, IRQ1_interrupt, 0); + + /* and irq 14 which is the mmu bus fault handler */ + + set_int_vector(14, mmu_bus_fault, 0); + + /* setup the system-call trap, which is reached by BREAK 13 */ + + set_break_vector(13, system_call); + +#ifdef CONFIG_KGDB + /* setup kgdb if its enabled, and break into the debugger */ + + kgdb_init(); + + breakpoint(); +#endif + +} diff --git a/arch/cris/kernel/kgdb.c b/arch/cris/kernel/kgdb.c new file mode 100644 index 000000000..98d427b05 --- /dev/null +++ b/arch/cris/kernel/kgdb.c @@ -0,0 +1,1540 @@ +/*!************************************************************************** +*! +*! FILE NAME : kgdb.c +*! +*! DESCRIPTION: Implementation of the gdb stub with respect to ETRAX 100. +*! It is a mix of arch/m68k/kernel/kgdb.c and cris_stub.c. +*! +*!--------------------------------------------------------------------------- +*! HISTORY +*! +*! DATE NAME CHANGES +*! ---- ---- ------- +*! Apr 26 1999 Hendrik Ruijter Initial version. +*! May 6 1999 Hendrik Ruijter Removed call to strlen in libc and removed +*! struct assignment as it generates calls to +*! memcpy in libc. +*! Jun 17 1999 Hendrik Ruijter Added gdb 4.18 support. 'X', 'qC' and 'qL'. +*! Jul 21 1999 Bjorn Wesen eLinux port +*! +*! $Log: kgdb.c,v $ +*! Revision 1.2 2001/01/12 14:22:25 orjanf +*! Updated kernel debugging support to work with ETRAX 100LX. +*! +*! Revision 1.1 2000/07/10 16:25:21 bjornw +*! Initial revision +*! +*! Revision 1.1.1.1 1999/12/03 14:57:31 bjornw +*! * Initial version of arch/cris, the latest CRIS architecture with an MMU. +*! Mostly copied from arch/etrax100 with appropriate renames of files. +*! The mm/ subdir is copied from arch/i386. +*! This does not compile yet at all. +*! +*! +*! Revision 1.4 1999/07/22 17:25:25 bjornw +*! Dont wait for + in putpacket if we havent hit the initial breakpoint yet. Added a kgdb_init function which sets up the break and irq vectors. +*! +*! Revision 1.3 1999/07/21 19:51:18 bjornw +*! Check if the interrupting char is a ctrl-C, ignore otherwise. +*! +*! Revision 1.2 1999/07/21 18:09:39 bjornw +*! Ported to eLinux architecture, and added some kgdb documentation. +*! +*! +*!--------------------------------------------------------------------------- +*! +*! $Id: kgdb.c,v 1.2 2001/01/12 14:22:25 orjanf Exp $ +*! +*! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN +*! +*!**************************************************************************/ +/* @(#) cris_stub.c 1.3 06/17/99 */ + +/* + * kgdb usage notes: + * ----------------- + * + * If you select CONFIG_KGDB in the configuration, the kernel will be built + * with different gcc flags: "-g" is added to get debug infos, and + * "-fomit-frame-pointer" is omitted to make debugging easier. Since the + * resulting kernel will be quite big (approx. > 7 MB), it will be stripped + * before compresion. Such a kernel will behave just as usually, except if + * given a "debug=<device>" command line option. (Only serial devices are + * allowed for <device>, i.e. no printers or the like; possible values are + * machine depedend and are the same as for the usual debug device, the one + * for logging kernel messages.) If that option is given and the device can be + * initialized, the kernel will connect to the remote gdb in trap_init(). The + * serial parameters are fixed to 8N1 and 115200 bps, for easyness of + * implementation. + * + * To start a debugging session, start that gdb with the debugging kernel + * image (the one with the symbols, vmlinux.debug) named on the command line. + * This file will be used by gdb to get symbol and debugging infos about the + * kernel. Next, select remote debug mode by + * target remote <device> + * where <device> is the name of the serial device over which the debugged + * machine is connected. Maybe you have to adjust the baud rate by + * set remotebaud <rate> + * or also other parameters with stty: + * shell stty ... </dev/... + * If the kernel to debug has already booted, it waited for gdb and now + * connects, and you'll see a breakpoint being reported. If the kernel isn't + * running yet, start it now. The order of gdb and the kernel doesn't matter. + * Another thing worth knowing about in the getting-started phase is how to + * debug the remote protocol itself. This is activated with + * set remotedebug 1 + * gdb will then print out each packet sent or received. You'll also get some + * messages about the gdb stub on the console of the debugged machine. + * + * If all that works, you can use lots of the usual debugging techniques on + * the kernel, e.g. inspecting and changing variables/memory, setting + * breakpoints, single stepping and so on. It's also possible to interrupt the + * debugged kernel by pressing C-c in gdb. Have fun! :-) + * + * The gdb stub is entered (and thus the remote gdb gets control) in the + * following situations: + * + * - If breakpoint() is called. This is just after kgdb initialization, or if + * a breakpoint() call has been put somewhere into the kernel source. + * (Breakpoints can of course also be set the usual way in gdb.) + * In eLinux, we call breakpoint() in init/main.c after IRQ initialization. + * + * - If there is a kernel exception, i.e. bad_super_trap() or die_if_kernel() + * are entered. All the CPU exceptions are mapped to (more or less..., see + * the hard_trap_info array below) appropriate signal, which are reported + * to gdb. die_if_kernel() is usually called after some kind of access + * error and thus is reported as SIGSEGV. + * + * - When panic() is called. This is reported as SIGABRT. + * + * - If C-c is received over the serial line, which is treated as + * SIGINT. + * + * Of course, all these signals are just faked for gdb, since there is no + * signal concept as such for the kernel. It also isn't possible --obviously-- + * to set signal handlers from inside gdb, or restart the kernel with a + * signal. + * + * Current limitations: + * + * - While the kernel is stopped, interrupts are disabled for safety reasons + * (i.e., variables not changing magically or the like). But this also + * means that the clock isn't running anymore, and that interrupts from the + * hardware may get lost/not be served in time. This can cause some device + * errors... + * + * - When single-stepping, only one instruction of the current thread is + * executed, but interrupts are allowed for that time and will be serviced + * if pending. Be prepared for that. + * + * - All debugging happens in kernel virtual address space. There's no way to + * access physical memory not mapped in kernel space, or to access user + * space. A way to work around this is using get_user_long & Co. in gdb + * expressions, but only for the current process. + * + * - Interrupting the kernel only works if interrupts are currently allowed, + * and the interrupt of the serial line isn't blocked by some other means + * (IPL too high, disabled, ...) + * + * - The gdb stub is currently not reentrant, i.e. errors that happen therein + * (e.g. accesing invalid memory) may not be caught correctly. This could + * be removed in future by introducing a stack of struct registers. + * + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to kgdb_init() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * bBB..BB Set baud rate to BB..BB OK or BNN, then sets + * baud rate + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + + +#include <linux/string.h> +#include <linux/signal.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/linkage.h> + +#include <asm/setup.h> +#include <asm/ptrace.h> + +#include <asm/svinto.h> +#include <asm/irq.h> + +static int kgdb_started = 0; + +/********************************* Register image ****************************/ +/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's + Reference", p. 1-1, with the additional register definitions of the + ETRAX 100LX in cris-opc.h. + There are 16 general 32-bit registers, R0-R15, where R14 is the stack + pointer, SP, and R15 is the program counter, PC. + There are 16 special registers, P0-P15, where three of the unimplemented + registers, P0, P4 and P8, are reserved as zero-registers. A read from + any of these registers returns zero and a write has no effect. */ + +typedef +struct register_image +{ + /* Offset */ + unsigned int r0; /* 0x00 */ + unsigned int r1; /* 0x04 */ + unsigned int r2; /* 0x08 */ + unsigned int r3; /* 0x0C */ + unsigned int r4; /* 0x10 */ + unsigned int r5; /* 0x14 */ + unsigned int r6; /* 0x18 */ + unsigned int r7; /* 0x1C */ + unsigned int r8; /* 0x20 Frame pointer */ + unsigned int r9; /* 0x24 */ + unsigned int r10; /* 0x28 */ + unsigned int r11; /* 0x2C */ + unsigned int r12; /* 0x30 */ + unsigned int r13; /* 0x34 */ + unsigned int sp; /* 0x38 Stack pointer */ + unsigned int pc; /* 0x3C Program counter */ + + unsigned char p0; /* 0x40 8-bit zero-register */ + unsigned char vr; /* 0x41 Version register */ + + unsigned short p4; /* 0x42 16-bit zero-register */ + unsigned short ccr; /* 0x44 Condition code register */ + + unsigned int mof; /* 0x46 Multiply overflow register */ + + unsigned int p8; /* 0x4A 32-bit zero-register */ + unsigned int ibr; /* 0x4E Interrupt base register */ + unsigned int irp; /* 0x52 Interrupt return pointer */ + unsigned int srp; /* 0x56 Subroutine return pointer */ + unsigned int bar; /* 0x5A Breakpoint address register */ + unsigned int dccr; /* 0x5E Double condition code register */ + unsigned int brp; /* 0x62 Breakpoint return pointer (pc in caller) */ + unsigned int usp; /* 0x66 User mode stack pointer */ +} registers; + +/************** Prototypes for local library functions ***********************/ + +/* Copy of strcpy from libc. */ +static char *gdb_cris_strcpy (char *s1, const char *s2); + +/* Copy of strlen from libc. */ +static int gdb_cris_strlen (const char *s); + +/* Copy of memchr from libc. */ +static void *gdb_cris_memchr (const void *s, int c, int n); + +/* Copy of strtol from libc. Does only support base 16. */ +static int gdb_cris_strtol (const char *s, char **endptr, int base); + +/********************** Prototypes for local functions. **********************/ +/* Copy the content of a register image into another. The size n is + the size of the register image. Due to struct assignment generation of + memcpy in libc. */ +static void copy_registers (registers *dptr, registers *sptr, int n); + +/* Copy the stored registers from the stack. Put the register contents + of thread thread_id in the struct reg. */ +static void copy_registers_from_stack (int thread_id, registers *reg); + +/* Copy the registers to the stack. Put the register contents of thread + thread_id from struct reg to the stack. */ +static void copy_registers_to_stack (int thread_id, registers *reg); + +/* Write a value to a specified register regno in the register image + of the current thread. */ +static int write_register (int regno, char *val); + +/* Write a value to a specified register in the stack of a thread other + than the current thread. */ +static write_stack_register (int thread_id, int regno, char *valptr); + +/* Read a value from a specified register in the register image. Returns the + status of the read operation. The register value is returned in valptr. */ +static int read_register (char regno, unsigned int *valptr); + +/* Serial port, reads one character. ETRAX 100 specific. from debugport.c */ +int getDebugChar (void); + +/* Serial port, writes one character. ETRAX 100 specific. from debugport.c */ +void putDebugChar (int val); + +void enableDebugIRQ (void); + +/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte, + represented by int x. */ +static char highhex (int x); + +/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte, + represented by int x. */ +static char lowhex (int x); + +/* Returns the integer equivalent of a hexadecimal character. */ +static int hex (char ch); + +/* Convert the memory, pointed to by mem into hexadecimal representation. + Put the result in buf, and return a pointer to the last character + in buf (null). */ +static char *mem2hex (char *buf, unsigned char *mem, int count); + +/* Convert the array, in hexadecimal representation, pointed to by buf into + binary representation. Put the result in mem, and return a pointer to + the character after the last byte written. */ +static unsigned char *hex2mem (unsigned char *mem, char *buf, int count); + +/* Put the content of the array, in binary representation, pointed to by buf + into memory pointed to by mem, and return a pointer to + the character after the last byte written. */ +static unsigned char *bin2mem (unsigned char *mem, unsigned char *buf, int count); + +/* Await the sequence $<data>#<checksum> and store <data> in the array buffer + returned. */ +static void getpacket (char *buffer); + +/* Send $<data>#<checksum> from the <data> in the array buffer. */ +static void putpacket (char *buffer); + +/* Build and send a response packet in order to inform the host the + stub is stopped. */ +static void stub_is_stopped (int sigval); + +/* All expected commands are sent from remote.c. Send a response according + to the description in remote.c. */ +static void handle_exception (int sigval); + +/* Performs a complete re-start from scratch. ETRAX specific. */ +static void kill_restart (void); + +/******************** Prototypes for global functions. ***********************/ + +/* The string str is prepended with the GDB printout token and sent. */ +void putDebugString (const unsigned char *str, int length); /* used by etrax100ser.c */ + +/* The hook for both static (compiled) and dynamic breakpoints set by GDB. + ETRAX 100 specific. */ +void handle_breakpoint (void); /* used by irq.c */ + +/* The hook for an interrupt generated by GDB. ETRAX 100 specific. */ +void handle_interrupt (void); /* used by irq.c */ + +/* A static breakpoint to be used at startup. */ +void breakpoint (void); /* called by init/main.c */ + +/* From osys_int.c, executing_task contains the number of the current + executing task in osys. Does not know of object-oriented threads. */ +extern unsigned char executing_task; + +/* The number of characters used for a 64 bit thread identifier. */ +#define HEXCHARS_IN_THREAD_ID 16 + +/* Avoid warning as the internal_stack is not used in the C-code. */ +#define USEDVAR(name) { if (name) { ; } } +#define USEDFUN(name) { void (*pf)(void) = (void *)name; USEDVAR(pf) } + +/********************************** Packet I/O ******************************/ +/* BUFMAX defines the maximum number of characters in + inbound/outbound buffers */ +#define BUFMAX 512 + +/* Run-length encoding maximum length. Send 64 at most. */ +#define RUNLENMAX 64 + +/* Definition of all valid hexadecimal characters */ +static const char hexchars[] = "0123456789abcdef"; + +/* The inbound/outbound buffers used in packet I/O */ +static char remcomInBuffer[BUFMAX]; +static char remcomOutBuffer[BUFMAX]; + +/* Error and warning messages. */ +enum error_type +{ + SUCCESS, E01, E02, E03, E04, E05, E06, E07 +}; +static char *error_message[] = +{ + "", + "E01 Set current or general thread - H[c,g] - internal error.", + "E02 Change register content - P - cannot change read-only register.", + "E03 Thread is not alive.", /* T, not used. */ + "E04 The command is not supported - [s,C,S,!,R,d,r] - internal error.", + "E05 Change register content - P - the register is not implemented..", + "E06 Change memory content - M - internal error.", + "E07 Change register content - P - the register is not stored on the stack" +}; +/********************************* Register image ****************************/ +/* Use the order of registers as defined in "AXIS ETRAX CRIS Programmer's + Reference", p. 1-1, with the additional register definitions of the + ETRAX 100LX in cris-opc.h. + There are 16 general 32-bit registers, R0-R15, where R14 is the stack + pointer, SP, and R15 is the program counter, PC. + There are 16 special registers, P0-P15, where three of the unimplemented + registers, P0, P4 and P8, are reserved as zero-registers. A read from + any of these registers returns zero and a write has no effect. */ +enum register_name +{ + R0, R1, R2, R3, + R4, R5, R6, R7, + R8, R9, R10, R11, + R12, R13, SP, PC, + P0, VR, P2, P3, + P4, CCR, P6, MOF, + P8, IBR, IRP, SRP, + BAR, DCCR, BRP, USP +}; + +/* The register sizes of the registers in register_name. An unimplemented register + is designated by size 0 in this array. */ +static int register_size[] = +{ + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 4, 4, 4, 4, + 1, 1, 0, 0, + 2, 2, 0, 4, + 4, 4, 4, 4, + 4, 4, 4, 4 +}; + +/* Contains the register image of the executing thread in the assembler + part of the code in order to avoid horrible addressing modes. */ +static registers reg; + +/* FIXME: Should this be used? Delete otherwise. */ +/* Contains the assumed consistency state of the register image. Uses the + enum error_type for state information. */ +static int consistency_status = SUCCESS; + +/********************************** Handle exceptions ************************/ +/* The variable reg contains the register image associated with the + current_thread_c variable. It is a complete register image created at + entry. The reg_g contains a register image of a task where the general + registers are taken from the stack and all special registers are taken + from the executing task. It is associated with current_thread_g and used + in order to provide access mainly for 'g', 'G' and 'P'. +*/ + +/* Need two task id pointers in order to handle Hct and Hgt commands. */ +static int current_thread_c = 0; +static int current_thread_g = 0; + +/* Need two register images in order to handle Hct and Hgt commands. The + variable reg_g is in addition to reg above. */ +static registers reg_g; + +/********************************** Breakpoint *******************************/ +/* Use an internal stack in the breakpoint and interrupt response routines */ +#define INTERNAL_STACK_SIZE 1024 +static char internal_stack[INTERNAL_STACK_SIZE]; + +/* Due to the breakpoint return pointer, a state variable is needed to keep + track of whether it is a static (compiled) or dynamic (gdb-invoked) + breakpoint to be handled. A static breakpoint uses the content of register + BRP as it is whereas a dynamic breakpoint requires subtraction with 2 + in order to execute the instruction. The first breakpoint is static. */ +static unsigned char is_dyn_brkp = 0; + +/********************************* String library ****************************/ +/* Single-step over library functions creates trap loops. */ + +/* Copy char s2[] to s1[]. */ +static char* +gdb_cris_strcpy (char *s1, const char *s2) +{ + char *s = s1; + + for (s = s1; (*s++ = *s2++) != '\0'; ) + ; + return (s1); +} + +/* Find length of s[]. */ +static int +gdb_cris_strlen (const char *s) +{ + const char *sc; + + for (sc = s; *sc != '\0'; sc++) + ; + return (sc - s); +} + +/* Find first occurrence of c in s[n]. */ +static void* +gdb_cris_memchr (const void *s, int c, int n) +{ + const unsigned char uc = c; + const unsigned char *su; + + for (su = s; 0 < n; ++su, --n) + if (*su == uc) + return ((void *)su); + return (NULL); +} +/******************************* Standard library ****************************/ +/* Single-step over library functions creates trap loops. */ +/* Convert string to long. */ +static int +gdb_cris_strtol (const char *s, char **endptr, int base) +{ + char *s1; + char *sd; + int x = 0; + + for (s1 = (char*)s; (sd = gdb_cris_memchr(hexchars, *s1, base)) != NULL; ++s1) + x = x * base + (sd - hexchars); + + if (endptr) + { + /* Unconverted suffix is stored in endptr unless endptr is NULL. */ + *endptr = s1; + } + + return x; +} + +int +double_this(int x) +{ + return 2 * x; +} + +/********************************* Register image ****************************/ +/* Copy the content of a register image into another. The size n is + the size of the register image. Due to struct assignment generation of + memcpy in libc. */ +static void +copy_registers (registers *dptr, registers *sptr, int n) +{ + unsigned char *dreg; + unsigned char *sreg; + + for (dreg = (unsigned char*)dptr, sreg = (unsigned char*)sptr; n > 0; n--) + *dreg++ = *sreg++; +} + +#ifdef PROCESS_SUPPORT +/* Copy the stored registers from the stack. Put the register contents + of thread thread_id in the struct reg. */ +static void +copy_registers_from_stack (int thread_id, registers *regptr) +{ + int j; + stack_registers *s = (stack_registers *)stack_list[thread_id]; + unsigned int *d = (unsigned int *)regptr; + + for (j = 13; j >= 0; j--) + *d++ = s->r[j]; + regptr->sp = (unsigned int)stack_list[thread_id]; + regptr->pc = s->pc; + regptr->dccr = s->dccr; + regptr->srp = s->srp; +} + +/* Copy the registers to the stack. Put the register contents of thread + thread_id from struct reg to the stack. */ +static void +copy_registers_to_stack (int thread_id, registers *regptr) +{ + int i; + stack_registers *d = (stack_registers *)stack_list[thread_id]; + unsigned int *s = (unsigned int *)regptr; + + for (i = 0; i < 14; i++) { + d->r[i] = *s++; + } + d->pc = regptr->pc; + d->dccr = regptr->dccr; + d->srp = regptr->srp; +} +#endif + +/* Write a value to a specified register in the register image of the current + thread. Returns status code SUCCESS, E02 or E05. */ +static int +write_register (int regno, char *val) +{ + int status = SUCCESS; + registers *current_reg = ® + + if (regno >= R0 && regno <= PC) { + /* 32-bit register with simple offset. */ + hex2mem ((unsigned char *)current_reg + regno * sizeof(unsigned int), + val, sizeof(unsigned int)); + } + else if (regno == P0 || regno == VR || regno == P4 || regno == P8) { + /* Do not support read-only registers. */ + status = E02; + } + else if (regno == CCR) { + /* 16 bit register with complex offset. (P4 is read-only, P6 is not implemented, + and P7 (MOF) is 32 bits in ETRAX 100LX. */ + hex2mem ((unsigned char *)&(current_reg->ccr) + (regno-CCR) * sizeof(unsigned short), + val, sizeof(unsigned short)); + } + else if (regno >= MOF && regno <= USP) { + /* 32 bit register with complex offset. (P8 has been taken care of.) */ + hex2mem ((unsigned char *)&(current_reg->ibr) + (regno-IBR) * sizeof(unsigned int), + val, sizeof(unsigned int)); + } + else { + /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ + status = E05; + } + return status; +} + +#ifdef PROCESS_SUPPORT +/* Write a value to a specified register in the stack of a thread other + than the current thread. Returns status code SUCCESS or E07. */ +static int +write_stack_register (int thread_id, int regno, char *valptr) +{ + int status = SUCCESS; + stack_registers *d = (stack_registers *)stack_list[thread_id]; + unsigned int val; + + hex2mem ((unsigned char *)&val, valptr, sizeof(unsigned int)); + if (regno >= R0 && regno < SP) { + d->r[regno] = val; + } + else if (regno == SP) { + stack_list[thread_id] = val; + } + else if (regno == PC) { + d->pc = val; + } + else if (regno == SRP) { + d->srp = val; + } + else if (regno == DCCR) { + d->dccr = val; + } + else { + /* Do not support registers in the current thread. */ + status = E07; + } + return status; +} +#endif + +/* Read a value from a specified register in the register image. Returns the + value in the register or -1 for non-implemented registers. + Should check consistency_status after a call which may be E05 after changes + in the implementation. */ +static int +read_register (char regno, unsigned int *valptr) +{ + registers *current_reg = ® + + if (regno >= R0 && regno <= PC) { + /* 32-bit register with simple offset. */ + *valptr = *(unsigned int *)((char *)current_reg + regno * sizeof(unsigned int)); + return SUCCESS; + } + else if (regno == P0 || regno == VR) { + /* 8 bit register with complex offset. */ + *valptr = (unsigned int)(*(unsigned char *) + ((char *)&(current_reg->p0) + (regno-P0) * sizeof(char))); + return SUCCESS; + } + else if (regno == P4 || regno == CCR) { + /* 16 bit register with complex offset. */ + *valptr = (unsigned int)(*(unsigned short *) + ((char *)&(current_reg->p4) + (regno-P4) * sizeof(unsigned short))); + return SUCCESS; + } + else if (regno >= MOF && regno <= USP) { + /* 32 bit register with complex offset. */ + *valptr = *(unsigned int *)((char *)&(current_reg->p8) + + (regno-P8) * sizeof(unsigned int)); + return SUCCESS; + } + else { + /* Do not support nonexisting or unimplemented registers (P2, P3, and P6). */ + consistency_status = E05; + return E05; + } +} + +/********************************** Packet I/O ******************************/ +/* Returns the character equivalent of a nibble, bit 7, 6, 5, and 4 of a byte, + represented by int x. */ +static inline char +highhex(int x) +{ + return hexchars[(x >> 4) & 0xf]; +} + +/* Returns the character equivalent of a nibble, bit 3, 2, 1, and 0 of a byte, + represented by int x. */ +static inline char +lowhex(int x) +{ + return hexchars[x & 0xf]; +} + +/* Returns the integer equivalent of a hexadecimal character. */ +static int +hex (char ch) +{ + if ((ch >= 'a') && (ch <= 'f')) + return (ch - 'a' + 10); + if ((ch >= '0') && (ch <= '9')) + return (ch - '0'); + if ((ch >= 'A') && (ch <= 'F')) + return (ch - 'A' + 10); + return (-1); +} + +/* Convert the memory, pointed to by mem into hexadecimal representation. + Put the result in buf, and return a pointer to the last character + in buf (null). */ + +static int do_printk = 0; + +static char * +mem2hex(char *buf, unsigned char *mem, int count) +{ + int i; + int ch; + + if (mem == NULL) { + /* Bogus read from m0. FIXME: What constitutes a valid address? */ + for (i = 0; i < count; i++) { + *buf++ = '0'; + *buf++ = '0'; + } + } else { + /* Valid mem address. */ + for (i = 0; i < count; i++) { + ch = *mem++; + *buf++ = highhex (ch); + *buf++ = lowhex (ch); + } + } + + /* Terminate properly. */ + *buf = '\0'; + return (buf); +} + +/* Convert the array, in hexadecimal representation, pointed to by buf into + binary representation. Put the result in mem, and return a pointer to + the character after the last byte written. */ +static unsigned char* +hex2mem (unsigned char *mem, char *buf, int count) +{ + int i; + unsigned char ch; + for (i = 0; i < count; i++) { + ch = hex (*buf++) << 4; + ch = ch + hex (*buf++); + *mem++ = ch; + } + return (mem); +} + +/* Put the content of the array, in binary representation, pointed to by buf + into memory pointed to by mem, and return a pointer to the character after + the last byte written. + Gdb will escape $, #, and the escape char (0x7d). */ +static unsigned char* +bin2mem (unsigned char *mem, unsigned char *buf, int count) +{ + int i; + unsigned char *next; + for (i = 0; i < count; i++) { + /* Check for any escaped characters. Be paranoid and + only unescape chars that should be escaped. */ + if (*buf == 0x7d) { + next = buf + 1; + if (*next == 0x3 || *next == 0x4 || *next == 0x5D) /* #, $, ESC */ + { + buf++; + *buf += 0x20; + } + } + *mem++ = *buf++; + } + return (mem); +} + +/* Await the sequence $<data>#<checksum> and store <data> in the array buffer + returned. */ +static void +getpacket (char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + char ch; + do { + while ((ch = getDebugChar ()) != '$') + /* Wait for the start character $ and ignore all other characters */; + checksum = 0; + xmitcsum = -1; + count = 0; + /* Read until a # or the end of the buffer is reached */ + while (count < BUFMAX) { + ch = getDebugChar (); + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + buffer[count] = '\0'; + + if (ch == '#') { + xmitcsum = hex (getDebugChar ()) << 4; + xmitcsum += hex (getDebugChar ()); + if (checksum != xmitcsum) { + /* Wrong checksum */ + putDebugChar ('-'); + } + else { + /* Correct checksum */ + putDebugChar ('+'); + /* If sequence characters are received, reply with them */ + if (buffer[2] == ':') { + putDebugChar (buffer[0]); + putDebugChar (buffer[1]); + /* Remove the sequence characters from the buffer */ + count = gdb_cris_strlen (buffer); + for (i = 3; i <= count; i++) + buffer[i - 3] = buffer[i]; + } + } + } + } while (checksum != xmitcsum); +} + +/* Send $<data>#<checksum> from the <data> in the array buffer. */ + +static void +putpacket(char *buffer) +{ + int checksum; + int runlen; + int encode; + + do { + char *src = buffer; + putDebugChar ('$'); + checksum = 0; + while (*src) { + /* Do run length encoding */ + putDebugChar (*src); + checksum += *src; + runlen = 0; + while (runlen < RUNLENMAX && *src == src[runlen]) { + runlen++; + } + if (runlen > 3) { + /* Got a useful amount */ + putDebugChar ('*'); + checksum += '*'; + encode = runlen + ' ' - 4; + putDebugChar (encode); + checksum += encode; + src += runlen; + } + else { + src++; + } + } + putDebugChar ('#'); + putDebugChar (highhex (checksum)); + putDebugChar (lowhex (checksum)); + } while(kgdb_started && (getDebugChar() != '+')); +} + +/* The string str is prepended with the GDB printout token and sent. Required + in traditional implementations. */ +void +putDebugString (const unsigned char *str, int length) +{ + remcomOutBuffer[0] = 'O'; + mem2hex(&remcomOutBuffer[1], (unsigned char *)str, length); + putpacket(remcomOutBuffer); +} + +/********************************** Handle exceptions ************************/ +/* Build and send a response packet in order to inform the host the + stub is stopped. TAAn...:r...;n...:r...;n...:r...; + AA = signal number + n... = register number (hex) + r... = register contents + n... = `thread' + r... = thread process ID. This is a hex integer. + n... = other string not starting with valid hex digit. + gdb should ignore this n,r pair and go on to the next. + This way we can extend the protocol. */ +static void +stub_is_stopped(int sigval) +{ + char *ptr = remcomOutBuffer; + int regno; + + unsigned int reg_cont; + int status; + + /* Send trap type (converted to signal) */ + + *ptr++ = 'T'; + *ptr++ = highhex (sigval); + *ptr++ = lowhex (sigval); + + /* Send register contents. We probably only need to send the + * PC, frame pointer and stack pointer here. Other registers will be + * explicitely asked for. But for now, send all. + */ + + for (regno = R0; regno <= USP; regno++) { + /* Store n...:r...; for the registers in the buffer. */ + + status = read_register (regno, ®_cont); + + if (status == SUCCESS) { + + *ptr++ = highhex (regno); + *ptr++ = lowhex (regno); + *ptr++ = ':'; + + ptr = mem2hex(ptr, (unsigned char *)®_cont, + register_size[regno]); + *ptr++ = ';'; + } + + } + +#ifdef PROCESS_SUPPORT + /* Store the registers of the executing thread. Assume that both step, + continue, and register content requests are with respect to this + thread. The executing task is from the operating system scheduler. */ + + current_thread_c = executing_task; + current_thread_g = executing_task; + + /* A struct assignment translates into a libc memcpy call. Avoid + all libc functions in order to prevent recursive break points. */ + copy_registers (®_g, ®, sizeof(registers)); + + /* Store thread:r...; with the executing task TID. */ + gdb_cris_strcpy (&remcomOutBuffer[pos], "thread:"); + pos += gdb_cris_strlen ("thread:"); + remcomOutBuffer[pos++] = highhex (executing_task); + remcomOutBuffer[pos++] = lowhex (executing_task); + gdb_cris_strcpy (&remcomOutBuffer[pos], ";"); +#endif + + /* null-terminate and send it off */ + + *ptr = 0; + + putpacket (remcomOutBuffer); +} + +/* All expected commands are sent from remote.c. Send a response according + to the description in remote.c. */ +static void +handle_exception (int sigval) +{ + /* Avoid warning of not used. */ + + USEDFUN(handle_exception); + USEDVAR(internal_stack[0]); + + /* Send response. */ + + stub_is_stopped (sigval); + + for (;;) { + remcomOutBuffer[0] = '\0'; + getpacket (remcomInBuffer); + switch (remcomInBuffer[0]) { + case 'g': + /* Read registers: g + Success: Each byte of register data is described by two hex digits. + Registers are in the internal order for GDB, and the bytes + in a register are in the same order the machine uses. + Failure: void. */ + + { +#ifdef PROCESS_SUPPORT + /* Use the special register content in the executing thread. */ + copy_registers (®_g, ®, sizeof(registers)); + /* Replace the content available on the stack. */ + if (current_thread_g != executing_task) { + copy_registers_from_stack (current_thread_g, ®_g); + } + mem2hex ((unsigned char *)remcomOutBuffer, (unsigned char *)®_g, sizeof(registers)); +#else + mem2hex(remcomOutBuffer, (char *)®, sizeof(registers)); +#endif + } + break; + + case 'G': + /* Write registers. GXX..XX + Each byte of register data is described by two hex digits. + Success: OK + Failure: void. */ +#ifdef PROCESS_SUPPORT + hex2mem ((unsigned char *)®_g, &remcomInBuffer[1], sizeof(registers)); + if (current_thread_g == executing_task) { + copy_registers (®, ®_g, sizeof(registers)); + } + else { + copy_registers_to_stack(current_thread_g, ®_g); + } +#else + hex2mem((char *)®, &remcomInBuffer[1], sizeof(registers)); +#endif + gdb_cris_strcpy (remcomOutBuffer, "OK"); + break; + + case 'P': + /* Write register. Pn...=r... + Write register n..., hex value without 0x, with value r..., + which contains a hex value without 0x and two hex digits + for each byte in the register (target byte order). P1f=11223344 means + set register 31 to 44332211. + Success: OK + Failure: E02, E05 */ + { + char *suffix; + int regno = gdb_cris_strtol (&remcomInBuffer[1], &suffix, 16); + int status; +#ifdef PROCESS_SUPPORT + if (current_thread_g =! executing_task) + status = write_stack_register (current_thread_g, regno, suffix+1); + else +#endif + status = write_register (regno, suffix+1); + + switch (status) { + case E02: + /* Do not support read-only registers. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E02]); + break; + case E05: + /* Do not support non-existing registers. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E05]); + break; + case E07: + /* Do not support non-existing registers on the stack. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E07]); + break; + default: + /* Valid register number. */ + gdb_cris_strcpy (remcomOutBuffer, "OK"); + break; + } + } + break; + + case 'm': + /* Read from memory. mAA..AA,LLLL + AA..AA is the address and LLLL is the length. + Success: XX..XX is the memory content. Can be fewer bytes than + requested if only part of the data may be read. m6000120a,6c means + retrieve 108 byte from base address 6000120a. + Failure: void. */ + { + char *suffix; + unsigned char *addr = (unsigned char *)gdb_cris_strtol(&remcomInBuffer[1], + &suffix, 16); int length = gdb_cris_strtol(suffix+1, 0, 16); + + mem2hex(remcomOutBuffer, addr, length); + } + break; + + case 'X': + /* Write to memory. XAA..AA,LLLL:XX..XX + AA..AA is the start address, LLLL is the number of bytes, and + XX..XX is the binary data. + Success: OK + Failure: void. */ + case 'M': + /* Write to memory. MAA..AA,LLLL:XX..XX + AA..AA is the start address, LLLL is the number of bytes, and + XX..XX is the hexadecimal data. + Success: OK + Failure: void. */ + { + char *lenptr; + char *dataptr; + unsigned char *addr = (unsigned char *)gdb_cris_strtol(&remcomInBuffer[1], + &lenptr, 16); + int length = gdb_cris_strtol(lenptr+1, &dataptr, 16); + if (*lenptr == ',' && *dataptr == ':') { + if (remcomInBuffer[0] == 'M') { + hex2mem(addr, dataptr + 1, length); + } + else /* X */ { + bin2mem(addr, dataptr + 1, length); + } + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + else { + gdb_cris_strcpy (remcomOutBuffer, error_message[E06]); + } + } + break; + + case 'c': + /* Continue execution. cAA..AA + AA..AA is the address where execution is resumed. If AA..AA is + omitted, resume at the present address. + Success: return to the executing thread. + Failure: will never know. */ + if (remcomInBuffer[1] != '\0') { + reg.pc = gdb_cris_strtol (&remcomInBuffer[1], 0, 16); + } + enableDebugIRQ(); + return; + + case 's': + /* Step. sAA..AA + AA..AA is the address where execution is resumed. If AA..AA is + omitted, resume at the present address. Success: return to the + executing thread. Failure: will never know. + + Should never be invoked. The single-step is implemented on + the host side. If ever invoked, it is an internal error E04. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E04]); + putpacket (remcomOutBuffer); + return; + + case '?': + /* The last signal which caused a stop. ? + Success: SAA, where AA is the signal number. + Failure: void. */ + remcomOutBuffer[0] = 'S'; + remcomOutBuffer[1] = highhex (sigval); + remcomOutBuffer[2] = lowhex (sigval); + remcomOutBuffer[3] = 0; + break; + + case 'D': + /* Detach from host. D + Success: OK, and return to the executing thread. + Failure: will never know */ + putpacket ("OK"); + return; + + case 'k': + case 'r': + /* kill request or reset request. + Success: restart of target. + Failure: will never know. */ + kill_restart (); + break; + + case 'C': + case 'S': + case '!': + case 'R': + case 'd': + /* Continue with signal sig. Csig;AA..AA + Step with signal sig. Ssig;AA..AA + Use the extended remote protocol. ! + Restart the target system. R0 + Toggle debug flag. d + Search backwards. tAA:PP,MM + Not supported: E04 */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E04]); + break; +#ifdef PROCESS_SUPPORT + + case 'T': + /* Thread alive. TXX + Is thread XX alive? + Success: OK, thread XX is alive. + Failure: E03, thread XX is dead. */ + { + int thread_id = (int)gdb_cris_strtol (&remcomInBuffer[1], 0, 16); + /* Cannot tell whether it is alive or not. */ + if (thread_id >= 0 && thread_id < number_of_tasks) + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + break; + + case 'H': + /* Set thread for subsequent operations: Hct + c = 'c' for thread used in step and continue; + t can be -1 for all threads. + c = 'g' for thread used in other operations. + t = 0 means pick any thread. + Success: OK + Failure: E01 */ + { + int thread_id = gdb_cris_strtol (&remcomInBuffer[2], 0, 16); + if (remcomInBuffer[1] == 'c') { + /* c = 'c' for thread used in step and continue */ + /* Do not change current_thread_c here. It would create a mess in + the scheduler. */ + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + else if (remcomInBuffer[1] == 'g') { + /* c = 'g' for thread used in other operations. + t = 0 means pick any thread. Impossible since the scheduler does + not allow that. */ + if (thread_id >= 0 && thread_id < number_of_tasks) { + current_thread_g = thread_id; + gdb_cris_strcpy (remcomOutBuffer, "OK"); + } + else { + /* Not expected - send an error message. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E01]); + } + } + else { + /* Not expected - send an error message. */ + gdb_cris_strcpy (remcomOutBuffer, error_message[E01]); + } + } + break; + + case 'q': + case 'Q': + /* Query of general interest. qXXXX + Set general value XXXX. QXXXX=yyyy */ + { + int pos; + int nextpos; + int thread_id; + + switch (remcomInBuffer[1]) { + case 'C': + /* Identify the remote current thread. */ + gdb_cris_strcpy (&remcomOutBuffer[0], "QC"); + remcomOutBuffer[2] = highhex (current_thread_c); + remcomOutBuffer[3] = lowhex (current_thread_c); + remcomOutBuffer[4] = '\0'; + break; + case 'L': + gdb_cris_strcpy (&remcomOutBuffer[0], "QM"); + /* Reply with number of threads. */ + if (os_is_started()) { + remcomOutBuffer[2] = highhex (number_of_tasks); + remcomOutBuffer[3] = lowhex (number_of_tasks); + } + else { + remcomOutBuffer[2] = highhex (0); + remcomOutBuffer[3] = lowhex (1); + } + /* Done with the reply. */ + remcomOutBuffer[4] = lowhex (1); + pos = 5; + /* Expects the argument thread id. */ + for (; pos < (5 + HEXCHARS_IN_THREAD_ID); pos++) + remcomOutBuffer[pos] = remcomInBuffer[pos]; + /* Reply with the thread identifiers. */ + if (os_is_started()) { + /* Store the thread identifiers of all tasks. */ + for (thread_id = 0; thread_id < number_of_tasks; thread_id++) { + nextpos = pos + HEXCHARS_IN_THREAD_ID - 1; + for (; pos < nextpos; pos ++) + remcomOutBuffer[pos] = lowhex (0); + remcomOutBuffer[pos++] = lowhex (thread_id); + } + } + else { + /* Store the thread identifier of the boot task. */ + nextpos = pos + HEXCHARS_IN_THREAD_ID - 1; + for (; pos < nextpos; pos ++) + remcomOutBuffer[pos] = lowhex (0); + remcomOutBuffer[pos++] = lowhex (current_thread_c); + } + remcomOutBuffer[pos] = '\0'; + break; + default: + /* Not supported: "" */ + /* Request information about section offsets: qOffsets. */ + remcomOutBuffer[0] = 0; + break; + } + } + break; +#endif /* PROCESS_SUPPORT */ + + default: + /* The stub should ignore other request and send an empty + response ($#<checksum>). This way we can extend the protocol and GDB + can tell whether the stub it is talking to uses the old or the new. */ + remcomOutBuffer[0] = 0; + break; + } + putpacket(remcomOutBuffer); + } +} + +/* The jump is to the address 0x00000002. Performs a complete re-start + from scratch. */ +static void +kill_restart () +{ + __asm__ volatile ("jump 2"); +} + +/********************************** Breakpoint *******************************/ +/* The hook for both a static (compiled) and a dynamic breakpoint set by GDB. + An internal stack is used by the stub. The register image of the caller is + stored in the structure register_image. + Interactive communication with the host is handled by handle_exception and + finally the register image is restored. */ + +void kgdb_handle_breakpoint(void); + +asm (" + .global _kgdb_handle_breakpoint +_kgdb_handle_breakpoint: +;; +;; Response to the break-instruction +;; +;; Create a register image of the caller +;; + move dccr,[_reg+0x5E] ; Save the flags in DCCR before disable interrupts + di ; Disable interrupts + move.d r0,[_reg] ; Save R0 + move.d r1,[_reg+0x04] ; Save R1 + move.d r2,[_reg+0x08] ; Save R2 + move.d r3,[_reg+0x0C] ; Save R3 + move.d r4,[_reg+0x10] ; Save R4 + move.d r5,[_reg+0x14] ; Save R5 + move.d r6,[_reg+0x18] ; Save R6 + move.d r7,[_reg+0x1C] ; Save R7 + move.d r8,[_reg+0x20] ; Save R8 + move.d r9,[_reg+0x24] ; Save R9 + move.d r10,[_reg+0x28] ; Save R10 + move.d r11,[_reg+0x2C] ; Save R11 + move.d r12,[_reg+0x30] ; Save R12 + move.d r13,[_reg+0x34] ; Save R13 + move.d sp,[_reg+0x38] ; Save SP (R14) +;; Due to the old assembler-versions BRP might not be recognized + .word 0xE670 ; move brp,r0 + subq 2,r0 ; Set to address of previous instruction. + move.d r0,[_reg+0x3c] ; Save the address in PC (R15) + clear.b [_reg+0x40] ; Clear P0 + move vr,[_reg+0x41] ; Save special register P1 + clear.w [_reg+0x42] ; Clear P4 + move ccr,[_reg+0x44] ; Save special register CCR + move mof,[_reg+0x46] ; P7 + clear.d [_reg+0x4A] ; Clear P8 + move ibr,[_reg+0x4E] ; P9, + move irp,[_reg+0x52] ; P10, + move srp,[_reg+0x56] ; P11, + move dtp0,[_reg+0x5A] ; P12, register BAR, assembler might not know BAR + ; P13, register DCCR already saved +;; Due to the old assembler-versions BRP might not be recognized + .word 0xE670 ; move brp,r0 +;; Static (compiled) breakpoints must return to the next instruction in order +;; to avoid infinite loops. Dynamic (gdb-invoked) must restore the instruction +;; in order to execute it when execution is continued. + test.b [_is_dyn_brkp] ; Is this a dynamic breakpoint? + beq is_static ; No, a static breakpoint + nop + subq 2,r0 ; rerun the instruction the break replaced +is_static: + moveq 1,r1 + move.b r1,[_is_dyn_brkp] ; Set the state variable to dynamic breakpoint + move.d r0,[_reg+0x62] ; Save the return address in BRP + move usp,[_reg+0x66] ; USP +;; +;; Handle the communication +;; + move.d _internal_stack+1020,sp ; Use the internal stack which grows upward + moveq 5,r10 ; SIGTRAP + jsr _handle_exception ; Interactive routine +;; +;; Return to the caller +;; + move.d [_reg],r0 ; Restore R0 + move.d [_reg+0x04],r1 ; Restore R1 + move.d [_reg+0x08],r2 ; Restore R2 + move.d [_reg+0x0C],r3 ; Restore R3 + move.d [_reg+0x10],r4 ; Restore R4 + move.d [_reg+0x14],r5 ; Restore R5 + move.d [_reg+0x18],r6 ; Restore R6 + move.d [_reg+0x1C],r7 ; Restore R7 + move.d [_reg+0x20],r8 ; Restore R8 + move.d [_reg+0x24],r9 ; Restore R9 + move.d [_reg+0x28],r10 ; Restore R10 + move.d [_reg+0x2C],r11 ; Restore R11 + move.d [_reg+0x30],r12 ; Restore R12 + move.d [_reg+0x34],r13 ; Restore R13 +;; +;; FIXME: Which registers should be restored? +;; + move.d [_reg+0x38],sp ; Restore SP (R14) + move [_reg+0x56],srp ; Restore the subroutine return pointer. + move [_reg+0x5E],dccr ; Restore DCCR + move [_reg+0x66],usp ; Restore USP + jump [_reg+0x62] ; A jump to the content in register BRP works. + nop ; +"); + +/* The hook for an interrupt generated by GDB. An internal stack is used + by the stub. The register image of the caller is stored in the structure + register_image. Interactive communication with the host is handled by + handle_exception and finally the register image is restored. Due to the + old assembler which does not recognise the break instruction and the + breakpoint return pointer hex-code is used. */ + +void kgdb_handle_serial(void); + +asm (" + .global _kgdb_handle_serial +_kgdb_handle_serial: +;; +;; Response to a serial interrupt +;; + + move dccr,[_reg+0x5E] ; Save the flags in DCCR + di ; Disable interrupts + move.d r0,[_reg] ; Save R0 + move.d r1,[_reg+0x04] ; Save R1 + move.d r2,[_reg+0x08] ; Save R2 + move.d r3,[_reg+0x0C] ; Save R3 + move.d r4,[_reg+0x10] ; Save R4 + move.d r5,[_reg+0x14] ; Save R5 + move.d r6,[_reg+0x18] ; Save R6 + move.d r7,[_reg+0x1C] ; Save R7 + move.d r8,[_reg+0x20] ; Save R8 + move.d r9,[_reg+0x24] ; Save R9 + move.d r10,[_reg+0x28] ; Save R10 + move.d r11,[_reg+0x2C] ; Save R11 + move.d r12,[_reg+0x30] ; Save R12 + move.d r13,[_reg+0x34] ; Save R13 + move.d sp,[_reg+0x38] ; Save SP (R14) + move irp,[_reg+0x3c] ; Save the address in PC (R15) + clear.b [_reg+0x40] ; Clear P0 + move vr,[_reg+0x41] ; Save special register P1, + clear.w [_reg+0x42] ; Clear P4 + move ccr,[_reg+0x44] ; Save special register CCR + move mof,[_reg+0x46] ; P7 + clear.d [_reg+0x4A] ; Clear P8 + move ibr,[_reg+0x4E] ; P9, + move irp,[_reg+0x52] ; P10, + move srp,[_reg+0x56] ; P11, + move dtp0,[_reg+0x5A] ; P12, register BAR, assembler might not know BAR + ; P13, register DCCR already saved +;; Due to the old assembler-versions BRP might not be recognized + .word 0xE670 ; move brp,r0 + move.d r0,[_reg+0x62] ; Save the return address in BRP + move usp,[_reg+0x66] ; USP + +;; get the serial character (from debugport.c) and check if its a ctrl-c + + jsr _getDebugChar + cmp.b 3, r10 + bne goback + nop + +;; +;; Handle the communication +;; + move.d _internal_stack+1020,sp ; Use the internal stack + moveq 2,r10 ; SIGINT + jsr _handle_exception ; Interactive routine + +goback: +;; +;; Return to the caller +;; + move.d [_reg],r0 ; Restore R0 + move.d [_reg+0x04],r1 ; Restore R1 + move.d [_reg+0x08],r2 ; Restore R2 + move.d [_reg+0x0C],r3 ; Restore R3 + move.d [_reg+0x10],r4 ; Restore R4 + move.d [_reg+0x14],r5 ; Restore R5 + move.d [_reg+0x18],r6 ; Restore R6 + move.d [_reg+0x1C],r7 ; Restore R7 + move.d [_reg+0x20],r8 ; Restore R8 + move.d [_reg+0x24],r9 ; Restore R9 + move.d [_reg+0x28],r10 ; Restore R10 + move.d [_reg+0x2C],r11 ; Restore R11 + move.d [_reg+0x30],r12 ; Restore R12 + move.d [_reg+0x34],r13 ; Restore R13 +;; +;; FIXME: Which registers should be restored? +;; + move.d [_reg+0x38],sp ; Restore SP (R14) + move [_reg+0x56],srp ; Restore the subroutine return pointer. + move [_reg+0x5E],dccr ; Restore DCCR + move [_reg+0x66],usp ; Restore USP + reti ; Return from the interrupt routine + nop +"); + +/* Use this static breakpoint in the start-up only. */ + +void +breakpoint(void) +{ + kgdb_started = 1; + is_dyn_brkp = 0; /* This is a static, not a dynamic breakpoint. */ + __asm__ volatile ("break 8"); /* Jump to handle_breakpoint. */ +} + +/* initialize kgdb. doesn't break into the debugger, but sets up irq and ports */ + +void +kgdb_init(void) +{ + /* could initialize debug port as well but it's done in head.S already... */ + + set_break_vector(8, kgdb_handle_breakpoint); + set_int_vector(8, kgdb_handle_serial, 0); + + enableDebugIRQ(); +} + +/****************************** End of file **********************************/ diff --git a/arch/cris/kernel/ksyms.c b/arch/cris/kernel/ksyms.c new file mode 100644 index 000000000..fa48796dc --- /dev/null +++ b/arch/cris/kernel/ksyms.c @@ -0,0 +1,2 @@ +/* no kernel support yet */ + diff --git a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c new file mode 100644 index 000000000..901449879 --- /dev/null +++ b/arch/cris/kernel/process.c @@ -0,0 +1,327 @@ +/* $Id: process.c,v 1.8 2000/09/13 14:34:13 bjornw Exp $ + * + * linux/arch/cris/kernel/process.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#define __KERNEL_SYSCALLS__ +#include <stdarg.h> + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/io.h> +#include <linux/smp.h> + +//#define DEBUG + +/* + * Initial task structure. Make this a per-architecture thing, + * because different architectures tend to have different + * alignment requirements and potentially different initial + * setup. + */ + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task 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 task_union init_task_union + __attribute__((__section__(".data.init_task"))) = + { INIT_TASK(init_task_union.task) }; + +static int hlt_counter=0; + +/* in a system call, set_esp0 is called to remember the stack frame, therefore + in the implementation of syscalls we can use that value to access the stack + frame and saved registers. +*/ + +#define currentregs ((struct pt_regs *)current->thread.esp0) + +asmlinkage void set_esp0(unsigned long ssp) +{ + current->thread.esp0 = ssp; +} + +void disable_hlt(void) +{ + hlt_counter++; +} + +void enable_hlt(void) +{ + hlt_counter--; +} + +int cpu_idle(void *unused) +{ + while(1) { + current->counter = -100; + schedule(); + } +} + +/* if the watchdog is enabled, we can simply disable interrupts and go + * into an eternal loop, and the watchdog will reset the CPU after 0.1s + */ + +void hard_reset_now (void) +{ + printk("*** HARD RESET ***\n"); + cli(); + while(1) /* waiting for RETRIBUTION! */ ; +} + +void machine_restart(void) +{ + hard_reset_now(); +} + +/* can't do much here... */ + +void machine_halt(void) +{ +} + +void machine_power_off(void) +{ +} + +/* + * This is the mechanism for creating a new kernel thread. + * + * NOTE! Only a kernel-only process(ie the swapper or direct descendants + * who haven't done an "execve()") should use this: it will work within + * a system call from a "real" process, but the process memory space will + * not be free'd until both the parent and the child have exited. + */ + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + register long __a __asm__ ("r10"); + + __asm__ __volatile__ + ("movu.w %1,r1\n\t" /* r1 contains syscall number, to sys_clone */ + "clear.d r10\n\t" /* r10 is argument 1 to clone */ + "move.d %2,r11\n\t" /* r11 is argument 2 to clone, the flags */ + "break 13\n\t" /* call sys_clone, this will fork */ + "test.d r10\n\t" /* parent or child? child returns 0 here. */ + "bne 1f\n\t" /* jump if parent */ + "nop\n\t" /* delay slot */ + "move.d %4,r10\n\t" /* set argument to function to call */ + "jsr %5\n\t" /* call specified function */ + "movu.w %3,r1\n\t" /* r1 is sys_exit syscall number */ + "moveq -1,r10\n\t" /* Give a really bad exit-value */ + "break 13\n\t" /* call sys_exit, killing the child */ + "1:\n\t" + : "=r" (__a) + : "g" (__NR_clone), "r" (flags | CLONE_VM), "g" (__NR_exit), + "r" (arg), "r" (fn) + : "r10", "r11", "r1"); + + return __a; +} + + + +void flush_thread(void) +{ +} + +asmlinkage void ret_from_sys_call(void); + +/* setup the child's kernel stack with a pt_regs and switch_stack on it. + * it will be un-nested during _resume and _ret_from_sys_call when the + * new thread is scheduled. + * + * also setup the thread switching structure which is used to keep + * thread-specific data during _resumes. + * + */ + +int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, + unsigned long unused, + struct task_struct *p, struct pt_regs *regs) +{ + struct pt_regs * childregs; + struct switch_stack *swstack; + + /* put the pt_regs structure at the end of the new kernel stack page and fix it up + * remember that the task_struct doubles as the kernel stack for the task + */ + + childregs = ((struct pt_regs *) ((unsigned long)p + THREAD_SIZE)) - 1; + + *childregs = *regs; /* struct copy of pt_regs */ + + childregs->r10 = 0; /* child returns 0 after a fork/clone */ + + /* put the switch stack right below the pt_regs */ + + swstack = ((struct switch_stack *)childregs) - 1; + + swstack->r9 = 0; /* parameter to ret_from_sys_call, 0 == dont restart the syscall */ + + /* we want to return into ret_from_sys_call after the _resume */ + + swstack->return_ip = (unsigned long) ret_from_sys_call; + + /* fix the user-mode stackpointer */ + + p->thread.usp = usp; + + /* and the kernel-mode one */ + + p->thread.ksp = (unsigned long) swstack; + + /* esp0 keeps the pt_regs stacked structure pointer */ + + p->thread.esp0 = (unsigned long) childregs; + +#ifdef DEBUG + printk("kern_stack_page 0x%x, used stack %d, thread.usp 0x%x, usp 0x%x\n", + current->kernel_stack_page, usedstack, p->thread.usp, usp); +#endif + return 0; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + int i; +#if 0 +/* changed the size calculations - should hopefully work better. lbt */ + dump->magic = CMAGIC; + dump->start_code = 0; + dump->start_stack = regs->esp & ~(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; + for (i = 0; i < 8; i++) + dump->u_debugreg[i] = current->debugreg[i]; + + if (dump->start_stack < TASK_SIZE) + dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT; + + dump->regs = *regs; + + dump->u_fpvalid = dump_fpu (regs, &dump->i387); +#endif +} + +asmlinkage int sys_fork(void) +{ + return do_fork(SIGCHLD, rdusp(), currentregs, 0); +} + +/* if newusp is 0, we just grab the old usp */ + +asmlinkage int sys_clone(unsigned long newusp, unsigned long flags) +{ + if (!newusp) + newusp = rdusp(); + return do_fork(flags, newusp, currentregs, 0); +} + +/* vfork is a system call in i386 because of register-pressure - maybe + * we can remove it and handle it in libc but we put it here until then. + */ + +asmlinkage int sys_vfork(void) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), currentregs, 0); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(const char *fname, char **argv, char **envp) +{ + int error; + char *filename; + + filename = getname(fname); + error = PTR_ERR(filename); + + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, argv, envp, currentregs); + putname(filename); + out: + return error; +} + +/* + * These bracket the sleeping functions.. + */ + +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ +#if 0 + /* YURGH. TODO. */ + + unsigned long ebp, esp, eip; + unsigned long stack_page; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + stack_page = (unsigned long)p; + esp = p->thread.esp; + if (!stack_page || esp < stack_page || esp > 8188+stack_page) + return 0; + /* include/asm-i386/system.h:switch_to() pushes ebp last. */ + ebp = *(unsigned long *) esp; + do { + if (ebp < stack_page || ebp > 8184+stack_page) + return 0; + eip = *(unsigned long *) (ebp+4); + if (eip < first_sched || eip >= last_sched) + return eip; + ebp = *(unsigned long *) ebp; + } while (count++ < 16); +#endif + return 0; +} +#undef last_sched +#undef first_sched diff --git a/arch/cris/kernel/ptrace.c b/arch/cris/kernel/ptrace.c new file mode 100644 index 000000000..384952211 --- /dev/null +++ b/arch/cris/kernel/ptrace.c @@ -0,0 +1,340 @@ +/* + * linux/arch/cris/kernel/ptrace.c + * + * Parts taken from the m68k port. + * + * Copyright (c) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen + * + * $Log: ptrace.c,v $ + * Revision 1.3 2000/12/18 23:45:25 bjornw + * Linux/CRIS first version + * + * + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> + +#include <asm/uaccess.h> +#include <asm/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 DCCR the user has access to. */ +/* 1 = access 0 = no access */ +#define DCCR_MASK 0x0000001f /* XNZVC */ + +/* + * Get contents of register REGNO in task TASK. + */ +static inline long get_reg(struct task_struct *task, unsigned int regno) +{ + /* USP is a special case, it's not in the pt_regs struct but + * in the tasks thread struct + */ + + if (regno == PT_USP) + return task->thread.usp; + else if (regno <= PT_MAX) + return ((unsigned long *)(task->thread.esp0))[regno]; + else + return 0; +} + +/* + * Write contents of register REGNO in task TASK. + */ +static inline int put_reg(struct task_struct *task, unsigned int regno, + unsigned long data) +{ + unsigned long *addr; + + if (regno == PT_USP) + task->thread.usp = data; + else if (regno <= PT_MAX) + ((unsigned long *)(task->thread.esp0))[regno] = data; + else + return -1; + return 0; +} + +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) { + if (child == current) + goto out_tsk; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid) || + (!cap_issubset(child->cap_permitted, current->cap_permitted)) || + (current->gid != child->gid)) && !capable(CAP_SYS_PTRACE)) + goto out_tsk; + /* the same process cannot be attached many times */ + if (child->ptrace & PT_PTRACED) + goto out_tsk; + child->ptrace |= PT_PTRACED; + + write_lock_irq(&tasklist_lock); + if (child->p_pptr != current) { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irq(&tasklist_lock); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out_tsk; + } + ret = -ESRCH; + if (!(child->ptrace & PT_PTRACED)) + goto out_tsk; + if (child->state != TASK_STOPPED) { + if (request != PTRACE_KILL) + goto out_tsk; + } + if (child->p_pptr != current) + 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)) + break; + + tmp = 0; /* Default return condition */ + ret = -EIO; + if (addr < sizeof(struct pt_regs)) { + tmp = get_reg(child, addr >> 2); + 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)) + break; + + if (addr < sizeof(struct pt_regs)) { + addr >>= 2; + + if (addr == PT_DCCR) { + /* don't allow the tracing process to change stuff like + * interrupt enable, kernel/user bit, dma enables etc. + */ + data &= DCCR_MASK; + data |= get_reg(child, PT_DCCR) & ~DCCR_MASK; + } + if (put_reg(child, addr, data)) + break; + 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) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + /* TODO: make sure any pending breakpoint is killed */ + 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->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + break; + } + + case PTRACE_SINGLESTEP: { /* set the trap flag. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + + /* TODO: set some clever breakpoint mechanism... */ + + 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. */ + long tmp; + + ret = -EIO; + if ((unsigned long) data > _NSIG) + break; + child->ptrace &= ~(PT_PTRACED | PT_TRACESYS); + child->exit_code = data; + write_lock_irq(&tasklist_lock); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irq(&tasklist_lock); + /* TODO: make sure any pending breakpoint is killed */ + wake_up_process(child); + ret = 0; + break; + } + + case PTRACE_GETREGS: { /* Get all gp regs from the child. */ + int i; + unsigned long tmp; + for (i = 0; i <= PT_MAX; i++) { + tmp = get_reg(child, i); + 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 <= PT_MAX; i++) { + if (get_user(tmp, (unsigned long *) data)) { + ret = -EFAULT; + break; + } + if (i == PT_DCCR) { + tmp &= DCCR_MASK; + tmp |= get_reg(child, PT_DCCR) & ~DCCR_MASK; + } + put_reg(child, i, tmp); + data += sizeof(long); + } + ret = 0; + break; + } + + default: + ret = -EIO; + break; + } +out_tsk: + free_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +asmlinkage void syscall_trace(void) +{ + if ((current->ptrace & (PT_PTRACED | PT_TRACESYS)) != + (PT_PTRACED | PT_TRACESYS)) + return; + /* TODO: make a way to distinguish between a syscall stop and SIGTRAP + * delivery like in the i386 port ? + */ + current->exit_code = SIGTRAP; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + /* + * 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/cris/kernel/semaphore.c b/arch/cris/kernel/semaphore.c new file mode 100644 index 000000000..5a9478f03 --- /dev/null +++ b/arch/cris/kernel/semaphore.c @@ -0,0 +1,238 @@ +/* + * Generic semaphore code. Buyer beware. Do your own + * specific changes in <asm/semaphore-helper.h> + */ + +#include <linux/sched.h> +#include <asm/semaphore-helper.h> + +/* + * 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_VAR \ + struct task_struct *tsk = current; \ + wait_queue_t wait; \ + init_waitqueue_entry(&wait, tsk); + +#define DOWN_HEAD(task_state) \ + \ + \ + tsk->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) \ + tsk->state = (task_state); \ + } \ + tsk->state = TASK_RUNNING; \ + remove_wait_queue(&sem->wait, &wait); + +void __down(struct semaphore * sem) +{ + DOWN_VAR + DOWN_HEAD(TASK_UNINTERRUPTIBLE) + if (waking_non_zero(sem)) + break; + schedule(); + DOWN_TAIL(TASK_UNINTERRUPTIBLE) +} + +int __down_interruptible(struct semaphore * sem) +{ + int ret = 0; + DOWN_VAR + DOWN_HEAD(TASK_INTERRUPTIBLE) + + ret = waking_non_zero_interruptible(sem, tsk); + 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); +} + +/* + * RW Semaphores + */ +void +__down_read(struct rw_semaphore *sem, int count) +{ + DOWN_VAR; + + retry_down: + if (count < 0) { + /* Wait for the lock to become unbiased. Readers + are non-exclusive. */ + + /* This takes care of granting the lock. */ + up_read(sem); + + add_wait_queue(&sem->wait, &wait); + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (atomic_read(&sem->count) >= 0) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + mb(); + count = atomic_dec_return(&sem->count); + if (count <= 0) + goto retry_down; + } else { + add_wait_queue(&sem->wait, &wait); + + while (1) { + if (test_and_clear_bit(0, &sem->granted)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if ((sem->granted & 1) == 0) + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + } +} + +void +__down_write(struct rw_semaphore *sem, int count) +{ + DOWN_VAR; + + retry_down: + if (count + RW_LOCK_BIAS < 0) { + up_write(sem); + + add_wait_queue_exclusive(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (atomic_read(&sem->count) >= RW_LOCK_BIAS) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + + mb(); + count = atomic_sub_return(RW_LOCK_BIAS, &sem->count); + if (count != 0) + goto retry_down; + } else { + /* Put ourselves at the end of the list. */ + add_wait_queue_exclusive(&sem->write_bias_wait, &wait); + + while (1) { + if (test_and_clear_bit(1, &sem->granted)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if ((sem->granted & 2) == 0) + schedule(); + } + + remove_wait_queue(&sem->write_bias_wait, &wait); + tsk->state = TASK_RUNNING; + + /* If the lock is currently unbiased, awaken the sleepers. + FIXME: This wakes up the readers early in a bit of a + stampede -> bad! */ + if (atomic_read(&sem->count) >= 0) + wake_up(&sem->wait); + } +} + +void +__rwsem_wake(struct rw_semaphore *sem, unsigned long readers) +{ + if (readers) { + if (test_and_set_bit(0, &sem->granted)) + BUG(); + wake_up(&sem->wait); + } else { + if (test_and_set_bit(1, &sem->granted)) + BUG(); + wake_up(&sem->write_bias_wait); + } +} diff --git a/arch/cris/kernel/setup.c b/arch/cris/kernel/setup.c new file mode 100644 index 000000000..ff7b3e6ac --- /dev/null +++ b/arch/cris/kernel/setup.c @@ -0,0 +1,264 @@ +/* $Id: setup.c,v 1.8 2001/01/16 16:31:38 bjornw Exp $ + * + * linux/arch/cris/kernel/setup.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (c) 2000 Axis Communications AB + */ + +/* + * This file handles the architecture-dependent parts of initialization + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/config.h> +#include <linux/init.h> +#include <linux/bootmem.h> + +#include <asm/segment.h> +#include <asm/system.h> +#include <asm/smp.h> +#include <asm/types.h> +#include <asm/svinto.h> + +/* + * Setup options + */ +struct drive_info_struct { char dummy[32]; } drive_info; +struct screen_info screen_info; + +unsigned char aux_device_present; + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */ +extern int rd_image_start; /* starting block # of image */ +#endif + +extern int root_mountflags; +extern char _etext, _edata, _end; + +#define COMMAND_LINE_SIZE 256 + +static char command_line[COMMAND_LINE_SIZE] = { 0, }; + char saved_command_line[COMMAND_LINE_SIZE]; + +extern const unsigned long text_start, edata; /* set by the linker script */ + +extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* from head.S */ + +/* This mainly sets up the memory area, and can be really confusing. + * + * The physical DRAM is virtually mapped into dram_start to dram_end + * (usually c0000000 to c0000000 + DRAM size). The physical address is + * given by the macro __pa(). + * + * In this DRAM, the kernel code and data is loaded, in the beginning. + * It really starts at c00a0000 to make room for some special pages - + * the start address is text_start. The kernel data ends at _end. After + * this the ROM filesystem is appended (if there is any). + * + * Between this address and dram_end, we have RAM pages usable to the + * boot code and the system. + * + */ + +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size; + unsigned long start_pfn, max_pfn; + unsigned long memory_start; + extern void console_print_etrax(const char *b); + +#if (defined(CONFIG_CHR_DEV_FLASH) || defined(CONFIG_BLK_DEV_FLASH)) + /* TODO: move this into flash_init I think */ + flash_probe(); +#endif + + /* register an initial console printing routine for printk's */ + + init_etrax_debug(); + + /* we should really poll for DRAM size! */ + + high_memory = &dram_end; + + if(romfs_in_flash || !romfs_length) { + /* if we have the romfs in flash, or if there is no rom filesystem, + * our free area starts directly after the BSS + */ + memory_start = (unsigned long) &_end; + } else { + /* otherwise the free area starts after the ROM filesystem */ + printk("ROM fs in RAM, size %d bytes\n", romfs_length); + memory_start = romfs_start + romfs_length; + } + + /* process 1's initial memory region is the kernel code/data */ + + init_mm.start_code = (unsigned long) &text_start; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + +#define PFN_UP(x) (((x) + PAGE_SIZE-1) >> PAGE_SHIFT) +#define PFN_DOWN(x) ((x) >> PAGE_SHIFT) +#define PFN_PHYS(x) ((x) << PAGE_SHIFT) + + /* min_low_pfn points to the start of DRAM, start_pfn points + * to the first DRAM pages after the kernel, and max_low_pfn + * to the end of DRAM. + */ + + /* + * partially used pages are not usable - thus + * we are rounding upwards: + */ + + start_pfn = PFN_UP(memory_start); /* usually c0000000 + kernel + romfs */ + max_pfn = PFN_DOWN((unsigned long)high_memory); /* usually c0000000 + dram size */ + + /* + * Initialize the boot-time allocator (start, end) + * + * We give it access to all our DRAM, but we could as well just have + * given it a small slice. No point in doing that though, unless we + * have non-contiguous memory and want the boot-stuff to be in, say, + * the smallest area. + * + * It will put a bitmap of the allocated pages in the beginning + * of the range we give it, but it won't mark the bitmaps pages + * as reserved. We have to do that ourselves below. + * + * We need to use init_bootmem_node instead of init_bootmem + * because our map starts at a quite high address (min_low_pfn). + */ + + max_low_pfn = max_pfn; + min_low_pfn = PAGE_OFFSET >> PAGE_SHIFT; + + bootmap_size = init_bootmem_node(NODE_DATA(0), start_pfn, + min_low_pfn, + max_low_pfn); + + /* And free all memory not belonging to the kernel (addr, size) */ + + free_bootmem(PFN_PHYS(start_pfn), PFN_PHYS(max_pfn - start_pfn)); + + /* + * Reserve the bootmem bitmap itself as well. We do this in two + * steps (first step was init_bootmem()) because this catches + * the (very unlikely) case of us accidentally initializing the + * bootmem allocator with an invalid RAM area. + * + * Arguments are start, size + */ + + reserve_bootmem(PFN_PHYS(start_pfn), bootmap_size); + + /* paging_init() sets up the MMU and marks all pages as reserved */ + + paging_init(); + + /* we dont use a command line yet, so just let it be an empty string */ + + *cmdline_p = command_line; + strcpy(command_line, "root=/dev/rom"); /* use the appended romdisk as root */ + + /* give credit for the CRIS port */ + + printk("Linux/CRIS port on ETRAX 100LX (c) 2000 Axis Communications AB\n"); + +} + +#ifdef CONFIG_PROC_FS +#define HAS_FPU 0x0001 +#define HAS_MMU 0x0002 +#define HAS_ETHERNET100 0x0004 +#define HAS_TOKENRING 0x0008 +#define HAS_SCSI 0x0010 +#define HAS_ATA 0x0020 +#define HAS_USB 0x0040 +#define HAS_IRQ_BUG 0x0080 + +static struct cpu_info { + char *model; + unsigned short cache; + unsigned short flags; +} cpu_info[] = { + { "ETRAX 1", 0, 0 }, + { "ETRAX 2", 0, 0 }, /* Don't say it HAS_TOKENRING - there are + lethal bugs in that chip that + prevents T-R from ever working. + Never go there, and never lead anyone + into believing it can work. BTW: + Anyone working on a T-R network + driver? :-) :-) :-) :-/ */ + { "ETRAX 3", 0, HAS_TOKENRING }, + { "ETRAX 4", 0, HAS_TOKENRING | HAS_SCSI }, + { "Unknown", 0, 0 }, + { "Unknown", 0, 0 }, + { "Unknown", 0, 0 }, + { "Simulator", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA }, + { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_IRQ_BUG }, + { "ETRAX 100", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA }, + { "ETRAX 100LX", 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB | HAS_MMU }, + { "Unknown", 0, 0 }, +}; + +/* + * BUFFER is PAGE_SIZE bytes long. + */ +int get_cpuinfo(char *buffer) +{ + int revision; +#ifndef CONFIG_SVINTO_SIM + unsigned char tmp; + + __asm__ volatile ("move vr,%0" : "=rm" (tmp)); + revision = tmp; +#else + /* Fake a revision for the simulator */ + revision = 7; +#endif + + return sprintf(buffer, + "cpu\t\t: CRIS\n" + "cpu revision\t: %d\n" + "cpu model\t: %s\n" + "cache size\t: %d kB\n" + "fpu\t\t: %s\n" + "mmu\t\t: %s\n" + "ethernet\t: %s Mbps\n" + "token ring\t: %s\n" + "scsi\t\t: %s\n" + "ata\t\t: %s\n" + "usb\t\t: %s\n" + "bogomips\t: %lu.%02lu\n", + + revision, + cpu_info[revision].model, + cpu_info[revision].cache, + cpu_info[revision].flags & HAS_FPU ? "yes" : "no", + cpu_info[revision].flags & HAS_MMU ? "yes" : "no", + cpu_info[revision].flags & HAS_ETHERNET100 ? "10/100" : "10", + cpu_info[revision].flags & HAS_TOKENRING ? "4/16 Mbps" : "no", + cpu_info[revision].flags & HAS_SCSI ? "yes" : "no", + cpu_info[revision].flags & HAS_ATA ? "yes" : "no", + cpu_info[revision].flags & HAS_USB ? "yes" : "no", + (loops_per_jiffy * HZ + 500) / 100000, + ((loops_per_jiffy * HZ + 500) / 1000) % 100); +} +#endif /* CONFIG_PROC_FS */ diff --git a/arch/cris/kernel/shadows.c b/arch/cris/kernel/shadows.c new file mode 100644 index 000000000..0a6449f4c --- /dev/null +++ b/arch/cris/kernel/shadows.c @@ -0,0 +1,20 @@ +/* $Id: shadows.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ + * + * Various Etrax shadow registers. Defines for these are in include/asm-etrax100/io.h + */ + +#include <linux/config.h> + +unsigned long genconfig_shadow = 42; +unsigned long port_g_data_shadow = 42; +unsigned char port_pa_dir_shadow = 42; +unsigned char port_pa_data_shadow = 42; +unsigned char port_pb_i2c_shadow = 42; +unsigned char port_pb_config_shadow = 42; +unsigned char port_pb_dir_shadow = 42; +unsigned char port_pb_data_shadow = 42; +unsigned long r_timer_ctrl_shadow = 42; + +#ifdef CONFIG_ETRAX_90000000_LEDS +unsigned long port_90000000_shadow = 42; +#endif diff --git a/arch/cris/kernel/signal.c b/arch/cris/kernel/signal.c new file mode 100644 index 000000000..8aab31a45 --- /dev/null +++ b/arch/cris/kernel/signal.c @@ -0,0 +1,667 @@ +/* + * linux/arch/cris/kernel/signal.c + * + * Based on arch/i386/kernel/signal.c by + * Copyright (C) 1991, 1992 Linus Torvalds + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson * + * + * Ideas also taken from arch/arm. + * + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> + +#include <asm/processor.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* a syscall in Linux/CRIS is a break 13 instruction which is 2 bytes */ +/* manipulate regs so that upon return, it will be re-executed */ + +#define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->irp -= 2; + +int sys_wait4(pid_t pid, unsigned long *stat_addr, + int options, unsigned long *ru); + +int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +int +sys_sigsuspend(old_sigset_t mask) +{ + struct pt_regs * regs = (struct pt_regs *)current_regs(); + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->r10 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(0, &saveset, regs)) + return -EINTR; + } +} + +int +sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize) +{ + struct pt_regs * regs = (struct pt_regs *)current_regs(); + sigset_t saveset, newset; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&newset, unewset, sizeof(newset))) + return -EFAULT; + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + regs->r10 = -EINTR; + while (1) { + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (do_signal(0, &saveset, regs)) + return -EINTR; + } +} + +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 (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +int +sys_sigaltstack(const stack_t *uss, stack_t *uoss) +{ + return do_sigaltstack(uss, uoss, rdusp()); +} + + +/* + * Do a signal return; undo the signal stack. + */ + +struct sigframe { + struct sigcontext sc; + unsigned long extramask[_NSIG_WORDS-1]; + unsigned char retcode[8]; /* trampoline code */ +}; + +struct rt_sigframe { + struct siginfo *pinfo; + void *puc; + struct siginfo info; + struct ucontext uc; + unsigned char retcode[8]; /* trampoline code */ +}; + + +static int +restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc) +{ + unsigned int err = 0; + unsigned long old_usp; + + /* restore the regs from &sc->regs (same as sc, since regs is first) + * (sc is already checked for VERIFY_READ since the sigframe was + * checked in sys_sigreturn previously) + */ + + if (__copy_from_user(regs, sc, sizeof(struct pt_regs))) + goto badframe; + + /* make sure the U-flag is set so user-mode cannot fool us */ + + regs->dccr |= 1 << 8; + + /* restore the old USP as it was before we stacked the sc etc. + * (we cannot just pop the sigcontext since we aligned the sp and + * stuff after pushing it) + */ + + err |= __get_user(old_usp, &sc->usp); + + wrusp(old_usp); + + /* TODO: the other ports use regs->orig_XX to disable syscall checks + * after this completes, but we don't use that mechanism. maybe we can + * use it now ? + */ + + return err; + +badframe: + return 1; +} + +asmlinkage int sys_sigreturn(void) +{ + struct pt_regs *regs = (struct pt_regs *)current_regs(); + struct sigframe *frame = (struct sigframe *)rdusp(); + sigset_t set; + + /* + * Since we stacked the signal on a dword boundary, + * then frame should be dword aligned here. If it's + * not, then the user is trying to mess with us. + */ + if (((long)frame) & 3) + goto badframe; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__get_user(set.sig[0], &frame->sc.oldmask) + || (_NSIG_WORDS > 1 + && __copy_from_user(&set.sig[1], &frame->extramask, + sizeof(frame->extramask)))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(regs, &frame->sc)) + goto badframe; + + /* TODO: SIGTRAP when single-stepping as in arm ? */ + + return regs->r10; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage int sys_rt_sigreturn(void) +{ + struct pt_regs *regs = (struct pt_regs *)current_regs(); + struct rt_sigframe *frame = (struct rt_sigframe *)rdusp(); + sigset_t set; + stack_t st; + + /* + * Since we stacked the signal on a dword boundary, + * then frame should be dword aligned here. If it's + * not, then the user is trying to mess with us. + */ + if (((long)frame) & 3) + goto badframe; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + if (restore_sigcontext(regs, &frame->uc.uc_mcontext)) + goto badframe; + + if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, rdusp()); + + return regs->r10; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + +static int +setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, unsigned long mask) +{ + int err = 0; + unsigned long usp = rdusp(); + + /* copy the regs. they are first in sc so we can use sc directly */ + + err |= __copy_to_user(sc, regs, sizeof(struct pt_regs)); + + /* then some other stuff */ + + err |= __put_user(mask, &sc->oldmask); + + err |= __put_user(usp, &sc->usp); + + return err; +} + +/* figure out where we want to put the new signal frame - usually on the stack */ + +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long sp = rdusp(); + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + /* make sure the frame is dword-aligned */ + + sp &= ~3; + + return (void *)(sp - frame_size); +} + +/* grab and setup a signal frame. + * + * basically we stack a lot of state info, and arrange for the + * user-mode program to return to the kernel using either a + * trampoline which performs the syscall sigreturn, or a provided + * user-mode trampoline. + */ + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + struct sigframe *frame; + unsigned long return_ip; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= setup_sigcontext(&frame->sc, regs, set->sig[0]); + if (err) + goto give_sigsegv; + + if (_NSIG_WORDS > 1) { + err |= __copy_to_user(frame->extramask, &set->sig[1], + sizeof(frame->extramask)); + } + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + return_ip = (unsigned long)ka->sa.sa_restorer; + } else { + /* trampoline - the desired return ip is the retcode itself */ + return_ip = (unsigned long)&frame->retcode; + /* This is movu.w __NR_sigreturn, r1; break 13; */ + /* TODO: check byteorder */ + err |= __put_user(0x1c5f, (short *)(frame->retcode+0)); + err |= __put_user(__NR_sigreturn, (short *)(frame->retcode+2)); + err |= __put_user(0xe93d, (short *)(frame->retcode+4)); + } + + if (err) + goto give_sigsegv; + + /* Set up registers for signal handler */ + + regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */ + regs->srp = return_ip; /* what we enter LATER */ + + /* actually move the usp to reflect the stacked frame */ + + wrusp((unsigned long)frame); + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +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; + unsigned long return_ip; + int err = 0; + + frame = get_sigframe(ka, regs, sizeof(*frame)); + + if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame))) + goto give_sigsegv; + + err |= __put_user(&frame->info, &frame->pinfo); + err |= __put_user(&frame->uc, &frame->puc); + err |= copy_siginfo_to_user(&frame->info, info); + if (err) + goto give_sigsegv; + + /* Clear all the bits of the ucontext we don't use. */ + err |= __clear_user(&frame->uc, offsetof(struct ucontext, uc_mcontext)); + + err |= setup_sigcontext(&frame->uc.uc_mcontext, regs, set->sig[0]); + + err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); + + if (err) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + return_ip = (unsigned long)ka->sa.sa_restorer; + } else { + /* trampoline - the desired return ip is the retcode itself */ + return_ip = (unsigned long)&frame->retcode; + /* This is movu.w __NR_sigreturn, r1; break 13; */ + /* TODO: check byteorder */ + err |= __put_user(0x1c5f, (short *)(frame->retcode+0)); + err |= __put_user(__NR_sigreturn, (short *)(frame->retcode+2)); + err |= __put_user(0xe93d, (short *)(frame->retcode+4)); + } + + if (err) + goto give_sigsegv; + + /* TODO what is the current->exec_domain stuff and invmap ? */ + + /* Set up registers for signal handler */ + + regs->irp = (unsigned long) ka->sa.sa_handler; /* what we enter NOW */ + regs->srp = return_ip; /* what we enter LATER */ + + /* actually move the usp to reflect the stacked frame */ + + wrusp((unsigned long)frame); + + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +/* + * OK, we're invoking a handler + */ + +static inline void +handle_signal(int canrestart, unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +{ + /* Are we from a system call? */ + if (canrestart) { + /* If so, check system call restarting.. */ + switch (regs->r10) { + case -ERESTARTNOHAND: + /* ERESTARTNOHAND means that the syscall should only be + restarted if there was no handler for the signal, and since + we only get here if there is a handler, we dont restart */ + regs->r10 = -EINTR; + break; + + case -ERESTARTSYS: + /* ERESTARTSYS means to restart the syscall if there is no + handler or the handler was registered with SA_RESTART */ + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->r10 = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + /* ERESTARTNOINTR means that the syscall should be called again + after the signal handler returns. */ + RESTART_CRIS_SYS(regs); + } + } + + /* 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->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } +} + +/* + * 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. + */ +int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs) +{ + siginfo_t info; + struct k_sigaction *ka; + + /* + * 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; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + current->state = TASK_STOPPED; + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + current->state = TASK_STOPPED; + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + case SIGBUS: case SIGSYS: case SIGXCPU: case SIGXFSZ: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + lock_kernel(); + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Whee! Actually deliver the signal. */ + handle_signal(canrestart, signr, ka, &info, oldset, regs); + return 1; + } + + /* Did we come from a system call? */ + if (canrestart) { + /* Restart the system call - no handlers present */ + if (regs->r10 == -ERESTARTNOHAND || + regs->r10 == -ERESTARTSYS || + regs->r10 == -ERESTARTNOINTR) { + RESTART_CRIS_SYS(regs); + } + } + return 0; +} diff --git a/arch/cris/kernel/sys_cris.c b/arch/cris/kernel/sys_cris.c new file mode 100644 index 000000000..cfcb097f9 --- /dev/null +++ b/arch/cris/kernel/sys_cris.c @@ -0,0 +1,201 @@ +/* $Id: sys_cris.c,v 1.3 2000/08/02 13:59:02 bjornw Exp $ + * + * linux/arch/cris/kernel/sys_etrax.c + * + * This file contains various random system calls that + * have a non-standard calling sequence on some platforms. + * Since we don't have to do any backwards compatibility, our + * versions are done in the most "normal" way possible. + * + */ + +#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/mman.h> +#include <linux/file.h> + +#include <asm/uaccess.h> +#include <asm/ipc.h> +#include <asm/segment.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; + + lock_kernel(); + error = do_pipe(fd); + unlock_kernel(); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +/* sys_mmap used to take a ptr to a buffer instead containing the args + * but we support syscalls with 6 arguments now + */ + +asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t offset) +{ + struct file * file = NULL; + int ret = -EBADF; + + lock_kernel(); + if (!(flags & MAP_ANONYMOUS)) { + if (!(file = fget(fd))) + goto out; + } + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + down(¤t->mm->mmap_sem); + ret = do_mmap(file, addr, len, prot, flags, offset); + up(¤t->mm->mmap_sem); + if (file) + fput(file); + out: + unlock_kernel(); + return ret; +} + +/* 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(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +asmlinkage unsigned long old_mmap(unsigned long addr, size_t len, int prot, + int flags, int fd, off_t offset) +{ + return do_mmap2(addr, len, prot, flags, fd, offset >> PAGE_SHIFT); +} + +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); +} + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. (same as arch/i386) + */ + +asmlinkage int sys_ipc (uint call, int first, int second, + int third, void *ptr, long fifth) +{ + int version, ret; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + case MSGRCV: + switch (version) { + case 0: { + struct ipc_kludge tmp; + if (!ptr) + return -EINVAL; + + if (copy_from_user(&tmp, + (struct ipc_kludge *) ptr, + sizeof (tmp))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, second, + tmp.msgtyp, third); + } + default: + return sys_msgrcv (first, + (struct msgbuf *) ptr, + second, fifth, third); + } + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + + case SHMAT: + switch (version) { + default: { + ulong raddr; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong *) third); + } + case 1: /* iBCS2 emulator entry point */ + if (!segment_eq(get_fs(), get_ds())) + return -EINVAL; + return sys_shmat (first, (char *) ptr, second, (ulong *) third); + } + case SHMDT: + return sys_shmdt ((char *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); + default: + return -EINVAL; + } +} + +/* apparently this is legacy - if we don't need this in Linux/CRIS we can remove it. */ + +asmlinkage int sys_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c new file mode 100644 index 000000000..d46ed47fa --- /dev/null +++ b/arch/cris/kernel/time.c @@ -0,0 +1,453 @@ +/* $Id: time.c,v 1.4 2000/10/17 14:44:58 bjornw Exp $ + * + * linux/arch/cris/kernel/time.c + * + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + * Copyright (C) 1999, 2000 Axis Communications AB + * + * 1994-07-02 Alan Modra + * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime + * 1995-03-26 Markus Kuhn + * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 + * precision CMOS clock update + * 1996-05-03 Ingo Molnar + * fixed time warps in do_[slow|fast]_gettimeoffset() + * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 + * "A Kernel Model for Precision Timekeeping" by Dave Mills + * + * Linux/CRIS specific code: + * + * Authors: Bjorn Wesen + * + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/delay.h> + +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/delay.h> +#include <asm/rtc.h> + +#include <linux/timex.h> +#include <linux/config.h> + +#include <asm/svinto.h> + +static int have_rtc; /* used to remember if we have an RTC or not */ + +/* define this if you need to use print_timestamp */ +/* it will make jiffies at 96 hz instead of 100 hz though */ +#undef USE_CASCADE_TIMERS + +extern int setup_etrax_irq(int, struct irqaction *); + +#define TICK_SIZE tick + +static unsigned long do_slow_gettimeoffset(void) +{ + unsigned long count; + + static unsigned long count_p = LATCH; /* for the first call after boot */ + static unsigned long jiffies_p = 0; + + /* + * cache volatile jiffies temporarily; we have IRQs turned off. + */ + unsigned long jiffies_t; + + /* The timer interrupt comes from Etrax timer 0. In order to get + * better precision, we check the current value. It might have + * underflowed already though. + */ + +#ifndef CONFIG_SVINTO_SIM + /* Not available in the xsim simulator. */ + count = *R_TIMER0_DATA; +#else + count = 0; +#endif + + jiffies_t = jiffies; + + /* + * avoiding timer inconsistencies (they are rare, but they happen)... + * there are three kinds of problems that must be avoided here: + * 1. the timer counter underflows + * 2. hardware problem with the timer, not giving us continuous time, + * the counter does small "jumps" upwards on some Pentium systems, + * thus causes time warps + * 3. we are after the timer interrupt, but the bottom half handler + * hasn't executed yet. + */ + if( jiffies_t == jiffies_p ) { + if( count > count_p ) { + } + } else + jiffies_p = jiffies_t; + + count_p = count; + + count = ((LATCH-1) - count) * TICK_SIZE; + count = (count + LATCH/2) / LATCH; + + return count; +} + +static unsigned long (*do_gettimeoffset)(void) = do_slow_gettimeoffset; + +/* + * This version of gettimeofday has near microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + + save_flags(flags); + cli(); + *tv = xtime; + tv->tv_usec += do_gettimeoffset(); + if (tv->tv_usec >= 1000000) { + tv->tv_usec -= 1000000; + tv->tv_sec++; + } + restore_flags(flags); +} + +void do_settimeofday(struct timeval *tv) +{ + cli(); + /* 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! + */ + tv->tv_usec -= do_gettimeoffset(); + + if (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_state = TIME_ERROR; /* p. 24, (a) */ + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + sti(); +} + + +/* + * BUG: This routine does not handle hour overflow properly; it just + * sets the minutes. Usually you'll only notice that after reboot! + */ + +static int set_rtc_mmss(unsigned long nowtime) +{ + int retval = 0; + int real_seconds, real_minutes, cmos_minutes; + unsigned char save_control, save_freq_select; + + printk("set_rtc_mmss(%d)\n", nowtime); + + if(!have_rtc) + return 0; + + cmos_minutes = CMOS_READ(RTC_MINUTES); + BCD_TO_BIN(cmos_minutes); + + /* + * since we're only adjusting minutes and seconds, + * don't interfere with hour overflow. This avoids + * messing with unknown time zones but requires your + * RTC not to be off by more than 15 minutes + */ + real_seconds = nowtime % 60; + real_minutes = nowtime / 60; + if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) + real_minutes += 30; /* correct for half hour time zone */ + real_minutes %= 60; + + if (abs(real_minutes - cmos_minutes) < 30) { + BIN_TO_BCD(real_seconds); + BIN_TO_BCD(real_minutes); + CMOS_WRITE(real_seconds,RTC_SECONDS); + CMOS_WRITE(real_minutes,RTC_MINUTES); + } else { + printk(KERN_WARNING + "set_rtc_mmss: can't update from %d to %d\n", + cmos_minutes, real_minutes); + retval = -1; + } + + return retval; +} + +/* Except from the Etrax100 HSDD about the built-in watchdog: + * + * 3.10.4 Watchdog timer + + * When the watchdog timer is started, it generates an NMI if the watchdog + * isn't restarted or stopped within 0.1 s. If it still isn't restarted or + * stopped after an additional 3.3 ms, the watchdog resets the chip. + * The watchdog timer is stopped after reset. The watchdog timer is controlled + * by the R_WATCHDOG register. The R_WATCHDOG register contains an enable bit + * and a 3-bit key value. The effect of writing to the R_WATCHDOG register is + * described in the table below: + * + * Watchdog Value written: + * state: To enable: To key: Operation: + * -------- ---------- ------- ---------- + * stopped 0 X No effect. + * stopped 1 key_val Start watchdog with key = key_val. + * started 0 ~key Stop watchdog + * started 1 ~key Restart watchdog with key = ~key. + * started X new_key_val Change key to new_key_val. + * + * Note: '~' is the bitwise NOT operator. + * + */ + +/* right now, starting the watchdog is the same as resetting it */ +#define start_watchdog reset_watchdog + +static int watchdog_key = 0; /* arbitrary number */ + +/* number of pages to consider "out of memory". it is normal that the memory + * is used though, so put this really low. + */ + +#define WATCHDOG_MIN_FREE_PAGES 8 + +extern int nr_free_pages; + +static inline void +reset_watchdog(void) +{ +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) + /* only keep watchdog happy as long as we have memory left! */ + if(nr_free_pages > WATCHDOG_MIN_FREE_PAGES) { + /* reset the watchdog with the inverse of the old key */ + watchdog_key ^= 0x7; /* invert key, which is 3 bits */ + *R_WATCHDOG = IO_FIELD(R_WATCHDOG, key, watchdog_key) | + IO_STATE(R_WATCHDOG, enable, start); + } +#endif +} + +/* last time the cmos clock got updated */ +static long last_rtc_update = 0; + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ + +//static unsigned short myjiff; /* used by our debug routine print_timestamp */ + +static inline void +timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* acknowledge the timer irq */ + +#ifdef USE_CASCADE_TIMERS + *R_TIMER_CTRL = + IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | + IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | + IO_STATE( R_TIMER_CTRL, i1, clr) | + IO_STATE( R_TIMER_CTRL, tm1, run) | + IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | + IO_STATE( R_TIMER_CTRL, i0, clr) | + IO_STATE( R_TIMER_CTRL, tm0, run) | + IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); +#else + *R_TIMER_CTRL = r_timer_ctrl_shadow | + IO_STATE(R_TIMER_CTRL, i0, clr); +#endif + + /* reset watchdog otherwise it resets us! */ + + reset_watchdog(); + + /* call the real timer interrupt handler */ + + do_timer(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_usec > 500000 - (tick >> 1) && + xtime.tv_usec < 500000 + (tick >> 1)) + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + last_rtc_update = xtime.tv_sec - 600; + +} + +#if 0 +/* some old debug code for testing the microsecond timing of packets */ +static unsigned int lastjiff; + +void print_timestamp(const char *s) +{ + unsigned long flags; + unsigned int newjiff; + save_flags(flags); + cli(); + newjiff = (myjiff << 16) | (unsigned short)(-*R_TIMER01_DATA); + printk("%s: %x (%x)\n", s, newjiff, newjiff - lastjiff); + lastjiff = newjiff; + restore_flags(flags); +} +#endif + +/* grab the time from the RTC chip */ + +unsigned long +get_cmos_time(void) +{ + unsigned int year, mon, day, hour, min, sec; + int i; + + sec = CMOS_READ(RTC_SECONDS); + min = CMOS_READ(RTC_MINUTES); + hour = CMOS_READ(RTC_HOURS); + day = CMOS_READ(RTC_DAY_OF_MONTH); + mon = CMOS_READ(RTC_MONTH); + year = CMOS_READ(RTC_YEAR); + + printk("rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n", + sec, min, hour, day, mon, year); + + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hour); + BCD_TO_BIN(day); + BCD_TO_BIN(mon); + BCD_TO_BIN(year); + + if ((year += 1900) < 1970) + year += 100; + + return mktime(year, mon, day, hour, min, sec); +} + +/* update xtime from the CMOS settings. used when /dev/rtc gets a SET_TIME. + * TODO: this doesn't reset the fancy NTP phase stuff as do_settimeofday does. + */ + +void +update_xtime_from_cmos(void) +{ + if(have_rtc) { + xtime.tv_sec = get_cmos_time(); + xtime.tv_usec = 0; + } +} + +/* timer is SA_SHIRQ so drivers can add stuff to the timer irq chain + * it needs to be SA_INTERRUPT to make the jiffies update work properly + */ + +static struct irqaction irq2 = { timer_interrupt, SA_SHIRQ | SA_INTERRUPT, + 0, "timer", NULL, NULL}; + +void __init +time_init(void) +{ + /* probe for the RTC and read it if it exists */ + + if(RTC_INIT() < 0) { + /* no RTC, start at 1980 */ + xtime.tv_sec = 0; + xtime.tv_usec = 0; + have_rtc = 0; + } else { + /* get the current time */ + have_rtc = 1; + update_xtime_from_cmos(); + } + + /* Setup the etrax timers + * Base frequency is 19200 hz, divider 192 -> 100 hz as Linux wants + * In normal mode, we use timer0, so timer1 is free. In cascade + * mode (which we sometimes use for debugging) both timers are used. + * Remember that linux/timex.h contains #defines that rely on the + * timer settings below (hz and divide factor) !!! + */ + +#ifdef USE_CASCADE_TIMERS + *R_TIMER_CTRL = + IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | + IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | + IO_STATE( R_TIMER_CTRL, i1, nop) | + IO_STATE( R_TIMER_CTRL, tm1, stop_ld) | + IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | + IO_STATE( R_TIMER_CTRL, i0, nop) | + IO_STATE( R_TIMER_CTRL, tm0, stop_ld) | + IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); + + *R_TIMER_CTRL = r_timer_ctrl_shadow = + IO_FIELD( R_TIMER_CTRL, timerdiv1, 0) | + IO_FIELD( R_TIMER_CTRL, timerdiv0, 0) | + IO_STATE( R_TIMER_CTRL, i1, nop) | + IO_STATE( R_TIMER_CTRL, tm1, run) | + IO_STATE( R_TIMER_CTRL, clksel1, cascade0) | + IO_STATE( R_TIMER_CTRL, i0, nop) | + IO_STATE( R_TIMER_CTRL, tm0, run) | + IO_STATE( R_TIMER_CTRL, clksel0, c6250kHz); +#else + *R_TIMER_CTRL = + IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) | + IO_FIELD(R_TIMER_CTRL, timerdiv0, 192) | + IO_STATE(R_TIMER_CTRL, i1, nop) | + IO_STATE(R_TIMER_CTRL, tm1, stop_ld) | + IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) | + IO_STATE(R_TIMER_CTRL, i0, nop) | + IO_STATE(R_TIMER_CTRL, tm0, stop_ld) | + IO_STATE(R_TIMER_CTRL, clksel0, c19k2Hz); + + *R_TIMER_CTRL = r_timer_ctrl_shadow = + IO_FIELD(R_TIMER_CTRL, timerdiv1, 192) | + IO_FIELD(R_TIMER_CTRL, timerdiv0, 192) | + IO_STATE(R_TIMER_CTRL, i1, nop) | + IO_STATE(R_TIMER_CTRL, tm1, run) | + IO_STATE(R_TIMER_CTRL, clksel1, c19k2Hz) | + IO_STATE(R_TIMER_CTRL, i0, nop) | + IO_STATE(R_TIMER_CTRL, tm0, run) | + IO_STATE(R_TIMER_CTRL, clksel0, c19k2Hz); +#endif + + *R_IRQ_MASK0_SET = + IO_STATE(R_IRQ_MASK0_SET, timer0, set); /* unmask the timer irq */ + + /* now actually register the timer irq handler that calls timer_interrupt() */ + + setup_etrax_irq(2, &irq2); /* irq 2 is the timer0 irq in etrax */ + + /* enable watchdog if we should use one */ + +#if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM) + printk("Enabling watchdog...\n"); + start_watchdog(); +#endif + +} diff --git a/arch/cris/kernel/traps.c b/arch/cris/kernel/traps.c new file mode 100644 index 000000000..9994487d4 --- /dev/null +++ b/arch/cris/kernel/traps.c @@ -0,0 +1,167 @@ +/* $Id: traps.c,v 1.3 2000/10/04 16:50:06 bjornw Exp $ + * + * linux/arch/cris/traps.c + * + * Etrax100 does not have any hardware traps, only IRQ's, which we setup + * in irq.c instead. Here we just define the die_if_kernel Oops'er. + * + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen + * + */ + +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/io.h> +#include <asm/pgtable.h> + +int kstack_depth_to_print = 24; + +/* + * These constants are for searching for possible module text + * segments. MODULE_RANGE is a guess of how much space is likely + * to be vmalloced. + */ + +#define MODULE_RANGE (8*1024*1024) + +void show_stack(unsigned long *sp) +{ + unsigned long *stack, addr, module_start, module_end; + int i; + extern char _stext, _etext; + + // debugging aid: "show_stack(NULL);" prints the + // back trace for this cpu. + + if(sp == NULL) + sp = (unsigned long*)rdsp(); + + stack = sp; + + for(i = 0; i < kstack_depth_to_print; i++) { + if (((long) stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i % 8) == 0)) + printk("\n "); + printk("%08lx ", *stack++); + } + + printk("\nCall Trace: "); + stack = sp; + i = 1; + module_start = VMALLOC_START; + module_end = VMALLOC_END; + while (((long) stack & (THREAD_SIZE-1)) != 0) { + 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) &_stext) && + (addr <= (unsigned long) &_etext)) || + ((addr >= module_start) && (addr <= module_end))) { + if (i && ((i % 8) == 0)) + printk("\n "); + printk("[<%08lx>] ", addr); + i++; + } + } +} + +#if 0 +/* displays a short stack trace */ + +int show_stack() +{ + unsigned long *sp = (unsigned long *)rdusp(); + int i; + printk("Stack dump [0x%08lx]:\n", (unsigned long)sp); + for(i = 0; i < 16; i++) + printk("sp + %d: 0x%08lx\n", i*4, sp[i]); + return 0; +} +#endif + +void show_registers(struct pt_regs * regs) +{ + unsigned long usp = rdusp(); + + printk("IRP: %08lx SRP: %08lx CCR: %08lx USP: %08lx\n", + regs->irp, regs->srp, regs->dccr, usp ); + printk(" r0: %08lx r1: %08lx r2: %08lx r3: %08lx\n", + regs->r0, regs->r1, regs->r2, regs->r3); + printk(" r4: %08lx r5: %08lx r6: %08lx r7: %08lx\n", + regs->r4, regs->r5, regs->r6, regs->r7); + printk(" r8: %08lx r9: %08lx r10: %08lx r11: %08lx\n", + regs->r8, regs->r9, regs->r10, regs->r11); + printk("r12: %08lx r13: %08lx oR10: %08lx\n", + regs->r12, regs->r13, regs->orig_r10); + printk("Process %s (pid: %d, stackpage=%08lx)\n", + current->comm, current->pid, (unsigned long)current); + + // TODO, fix in_kernel detection + +#if 0 + /* + * When in-kernel, we also print out the stack and code at the + * time of the fault.. + */ + if (1) { + + printk("\nStack: "); + show_stack((unsigned long*)usp); + + printk("\nCode: "); + if(regs->irp < PAGE_OFFSET) + goto bad; + + for(i = 0; i < 20; i++) + { + unsigned char c; + if(__get_user(c, &((unsigned char*)regs->irp)[i])) { +bad: + printk(" Bad IP value."); + break; + } + printk("%02x ", c); + } + } + printk("\n"); +#endif +} + + + +void die_if_kernel(const char * str, struct pt_regs * regs, long err) +{ + if(user_mode(regs)) + return; + + printk("%s: %04lx\n", str, err & 0xffff); + + show_registers(regs); + show_stack(NULL); /* print backtrace for kernel stack on this CPU */ + + do_exit(SIGSEGV); +} + +void __init trap_init(void) +{ + + +} diff --git a/arch/cris/lib/Makefile b/arch/cris/lib/Makefile new file mode 100644 index 000000000..6ede712e3 --- /dev/null +++ b/arch/cris/lib/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for Etrax-specific library files.. +# + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o + +L_TARGET = lib.a +obj-y = checksum.o checksumcopy.o string.o usercopy.o memset.o + +include $(TOPDIR)/Rules.make diff --git a/arch/cris/lib/checksum.S b/arch/cris/lib/checksum.S new file mode 100644 index 000000000..4ee0daa0c --- /dev/null +++ b/arch/cris/lib/checksum.S @@ -0,0 +1,113 @@ + ;; $Id: checksum.S,v 1.1 2000/07/10 16:25:21 bjornw Exp $ + ;; A fast checksum routine using movem + ;; Copyright (c) 1998 Bjorn Wesen/Axis Communications AB + + ;; csum_partial(const unsigned char * buff, int len, unsigned int sum) + + .globl _csum_partial +_csum_partial: + + ;; check for breakeven length between movem and normal word looping versions + + cmpu.w 80,r11 + bcs no_movem + nop + + ;; need to save the registers we use below in the movem loop + ;; this overhead is why we have a check above for breakeven length + + subq 9*4,sp + movem r8,[sp] + + ;; do a movem checksum + + ;; r10 - src + ;; r11 - length + ;; r12 - checksum + + subq 10*4,r11 ; update length for the first loop + +mloop: movem [r10+],r9 ; read 10 longwords + + ;; perform dword checksumming on the 10 longwords + + add.d r0,r12 + ax + add.d r1,r12 + ax + add.d r2,r12 + ax + add.d r3,r12 + ax + add.d r4,r12 + ax + add.d r5,r12 + ax + add.d r6,r12 + ax + add.d r7,r12 + ax + add.d r8,r12 + ax + add.d r9,r12 + + ;; fold the carry into the checksum, to avoid having to loop the carry + ;; back into the top + + ax + addq 0,r12 + ax ; do it again, since we might have generated a carry + addq 0,r12 + + subq 10*4,r11 + bge mloop + nop + + addq 10*4,r11 ; compensate for last loop underflowing length + + ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below + + moveq -1,r1 ; put 0xffff in r1, faster than move.d 0xffff,r1 + lsrq 16,r1 + + move.d r12,r0 + lsrq 16,r0 ; r0 = checksum >> 16 + and.d r1,r12 ; checksum = checksum & 0xffff + add.d r0,r12 ; checksum += r0 + move.d r12,r0 ; do the same again, maybe we got a carry last add + lsrq 16,r0 + and.d r1,r12 + add.d r0,r12 + + movem [sp+],r8 ; restore regs + +no_movem: + cmpq 2,r11 + blt no_words + nop + + ;; checksum the rest of the words + + subq 2,r11 + +wloop: subq 2,r11 + bge wloop + addu.w [r10+],r12 + + addq 2,r11 + +no_words: + ;; see if we have one odd byte more + cmpq 1,r11 + beq do_byte + nop + ret + move.d r12, r10 + +do_byte: + ;; copy and checksum the last byte + addu.b [r10],r12 + ret + move.d r12, r10 + +
\ No newline at end of file diff --git a/arch/cris/lib/checksumcopy.S b/arch/cris/lib/checksumcopy.S new file mode 100644 index 000000000..eae9c7ace --- /dev/null +++ b/arch/cris/lib/checksumcopy.S @@ -0,0 +1,120 @@ + ;; $Id: checksumcopy.S,v 1.2 2000/08/08 16:57:31 bjornw Exp $ + ;; A fast checksum+copy routine using movem + ;; Copyright (c) 1998, 2000 Axis Communications AB + ;; + ;; Authors: Bjorn Wesen + ;; + ;; csum_partial_copy_nocheck(const char *src, char *dst, + ;; int len, unsigned int sum) + + .globl _csum_partial_copy_nocheck +_csum_partial_copy_nocheck: + + ;; check for breakeven length between movem and normal word looping versions + + cmpu.w 80,r12 + bcs no_movem + nop + + ;; need to save the registers we use below in the movem loop + ;; this overhead is why we have a check above for breakeven length + + subq 9*4,sp + movem r8,[sp] + + ;; do a movem copy and checksum + + ;; r10 - src + ;; r11 - dst + ;; r12 - length + ;; r13 - checksum + + subq 10*4,r12 ; update length for the first loop + +mloop: movem [r10+],r9 ; read 10 longwords + movem r9,[r11+] ; write 10 longwords + + ;; perform dword checksumming on the 10 longwords + + add.d r0,r13 + ax + add.d r1,r13 + ax + add.d r2,r13 + ax + add.d r3,r13 + ax + add.d r4,r13 + ax + add.d r5,r13 + ax + add.d r6,r13 + ax + add.d r7,r13 + ax + add.d r8,r13 + ax + add.d r9,r13 + + ;; fold the carry into the checksum, to avoid having to loop the carry + ;; back into the top + + ax + addq 0,r13 + + subq 10*4,r12 + bge mloop + nop + + addq 10*4,r12 ; compensate for last loop underflowing length + + ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below + + moveq -1,r1 ; put 0xffff in r1, faster than move.d 0xffff,r1 + lsrq 16,r1 + + move.d r13,r0 + lsrq 16,r0 ; r0 = checksum >> 16 + and.d r1,r13 ; checksum = checksum & 0xffff + add.d r0,r13 ; checksum += r0 + move.d r13,r0 ; do the same again, maybe we got a carry last add + lsrq 16,r0 + and.d r1,r13 + add.d r0,r13 + + movem [sp+],r8 ; restore regs + +no_movem: + cmpq 2,r12 + blt no_words + nop + + ;; copy and checksum the rest of the words + + subq 2,r12 + +wloop: move.w [r10+],r9 + addu.w r9,r13 + subq 2,r12 + bge wloop + move.w r9,[r11+] + + addq 2,r12 + +no_words: + ;; see if we have one odd byte more + cmpq 1,r12 + beq do_byte + nop + ret + move.d r13, r10 + +do_byte: + ;; copy and checksum the last byte + move.b [r10],r9 + addu.b r9,r13 + move.b r9,[r11] + ret + move.d r13, r10 + +
\ No newline at end of file diff --git a/arch/cris/lib/dmacopy.c b/arch/cris/lib/dmacopy.c new file mode 100644 index 000000000..318577a2d --- /dev/null +++ b/arch/cris/lib/dmacopy.c @@ -0,0 +1,43 @@ +/* $Id: dmacopy.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ + * + * memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax + */ + +#include <asm/svinto.h> +#include <asm/io.h> + +#define D(x) + +void *dma_memcpy(void *pdst, + const void *psrc, + unsigned int pn) +{ + static etrax_dma_descr indma, outdma; + + D(printk("dma_memcpy %d bytes... ", pn)); + +#if 0 + *R_GEN_CONFIG = genconfig_shadow = + (genconfig_shadow & ~0x3c0000) | + IO_STATE(R_GEN_CONFIG, dma6, intdma7) | + IO_STATE(R_GEN_CONFIG, dma7, intdma6); +#endif + indma.sw_len = outdma.sw_len = pn; + indma.ctrl = d_eol | d_eop; + outdma.ctrl = d_eol; + indma.buf = psrc; + outdma.buf = pdst; + + *R_DMA_CH6_FIRST = &indma; + *R_DMA_CH7_FIRST = &outdma; + *R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, start); + *R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, start); + + while(*R_DMA_CH7_CMD == 1) /* wait for completion */ ; + + D(printk("done\n")); + +} + + + diff --git a/arch/cris/lib/memset.c b/arch/cris/lib/memset.c new file mode 100644 index 000000000..2f9f3fe37 --- /dev/null +++ b/arch/cris/lib/memset.c @@ -0,0 +1,245 @@ +/*#************************************************************************#*/ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# FUNCTION NAME: memset() */ +/*# */ +/*# PARAMETERS: void* dst; Destination address. */ +/*# int c; Value of byte to write. */ +/*# int len; Number of bytes to write. */ +/*# */ +/*# RETURNS: dst. */ +/*# */ +/*# DESCRIPTION: Sets the memory dst of length len bytes to c, as standard. */ +/*# Framework taken from memcpy. This routine is */ +/*# very sensitive to compiler changes in register allocation. */ +/*# Should really be rewritten to avoid this problem. */ +/*# */ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# HISTORY */ +/*# */ +/*# DATE NAME CHANGES */ +/*# ---- ---- ------- */ +/*# 990713 HP Tired of watching this function (or */ +/*# really, the nonoptimized generic */ +/*# implementation) take up 90% of simulator */ +/*# output. Measurements needed. */ +/*# */ +/*#-------------------------------------------------------------------------*/ + +/* No, there's no macro saying 12*4, since it is "hard" to get it into + the asm in a good way. Thus better to expose the problem everywhere. + */ + +/* Assuming 1 cycle per dword written or read (ok, not really true), and + one per instruction, then 43+3*(n/48-1) <= 24+24*(n/48-1) + so n >= 45.7; n >= 0.9; we win on the first full 48-byte block to set. */ + +#define ZERO_BLOCK_SIZE (1*12*4) + +void *memset(void *pdst, + int c, + unsigned int plen) +{ + /* Ok. Now we want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. */ + + register char *return_dst __asm__ ("r10") = pdst; + register int n __asm__ ("r12") = plen; + register int lc __asm__ ("r11") = c; + + /* Most apps use memset sanely. Only those memsetting about 3..4 + bytes or less get penalized compared to the generic implementation + - and that's not really sane use. */ + + /* Ugh. This is fragile at best. Check with newer GCC releases, if + they compile cascaded "x |= x << 8" sanely! */ + __asm__("movu.b %0,r13\n\tlslq 8,r13\n\tmove.b %0,r13\n\tmove.d r13,%0\n\tlslq 16,r13\n\tor.d r13,%0" + : "=r" (lc) : "0" (lc) : "r13"); + + { + register char *dst __asm__ ("r13") = pdst; + + /* This is NONPORTABLE, but since this whole routine is */ + /* grossly nonportable that doesn't matter. */ + + if (((unsigned long) pdst & 3) != 0 + /* Oops! n=0 must be a legal call, regardless of alignment. */ + && n >= 3) + { + if ((unsigned long)dst & 1) + { + *dst = (char) lc; + n--; + dst++; + } + + if ((unsigned long)dst & 2) + { + *(short *)dst = lc; + n -= 2; + dst += 2; + } + } + + /* Now the fun part. For the threshold value of this, check the equation + above. */ + /* Decide which copying method to use. */ + if (n >= ZERO_BLOCK_SIZE) + { + /* For large copies we use 'movem' */ + + /* It is not optimal to tell the compiler about clobbering any + registers; that will move the saving/restoring of those registers + to the function prologue/epilogue, and make non-movem sizes + suboptimal. + + This method is not foolproof; it assumes that the "asm reg" + declarations at the beginning of the function really are used + here (beware: they may be moved to temporary registers). + This way, we do not have to save/move the registers around into + temporaries; we can safely use them straight away. + + If you want to check that the allocation was right; then + check the equalities in the first comment. It should say + "r13=r13, r12=r12, r11=r11" */ + __asm__ volatile (" + ;; Check that the following is true (same register names on + ;; both sides of equal sign, as in r8=r8): + ;; %0=r13, %1=r12, %4=r11 + ;; + ;; Save the registers we'll clobber in the movem process + ;; on the stack. Don't mention them to gcc, it will only be + ;; upset. + subq 11*4,sp + movem r10,[sp] + + move.d r11,r0 + move.d r11,r1 + move.d r11,r2 + move.d r11,r3 + move.d r11,r4 + move.d r11,r5 + move.d r11,r6 + move.d r11,r7 + move.d r11,r8 + move.d r11,r9 + move.d r11,r10 + + ;; Now we've got this: + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 12*4,r12 +0: + subq 12*4,r12 + bge 0b + movem r11,[r13+] + + addq 12*4,r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [sp+],r10" + + /* Outputs */ : "=r" (dst), "=r" (n) + /* Inputs */ : "0" (dst), "1" (n), "r" (lc)); + + } + + /* Either we directly starts copying, using dword copying + in a loop, or we copy as much as possible with 'movem' + and then the last block (<44 bytes) is copied here. + This will work since 'movem' will have updated src,dst,n. */ + + while ( n >= 16 ) + { + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + n -= 16; + } + + /* A switch() is definitely the fastest although it takes a LOT of code. + * Particularly if you inline code this. + */ + switch (n) + { + case 0: + break; + case 1: + *(char*)dst = (char) lc; + break; + case 2: + *(short*)dst = (short) lc; + break; + case 3: + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + case 4: + *((long*)dst)++ = lc; + break; + case 5: + *((long*)dst)++ = lc; + *(char*)dst = (char) lc; + break; + case 6: + *((long*)dst)++ = lc; + *(short*)dst = (short) lc; + break; + case 7: + *((long*)dst)++ = lc; + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + case 8: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + break; + case 9: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(char*)dst = (char) lc; + break; + case 10: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(short*)dst = (short) lc; + break; + case 11: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + case 12: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + break; + case 13: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(char*)dst = (char) lc; + break; + case 14: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *(short*)dst = (short) lc; + break; + case 15: + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((long*)dst)++ = lc; + *((short*)dst)++ = (short) lc; + *(char*)dst = (char) lc; + break; + } + } + + return return_dst; /* destination pointer. */ +} /* memset() */ diff --git a/arch/cris/lib/old_checksum.c b/arch/cris/lib/old_checksum.c new file mode 100644 index 000000000..6035a48ae --- /dev/null +++ b/arch/cris/lib/old_checksum.c @@ -0,0 +1,127 @@ +/* $Id: old_checksum.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ + * + * INET An implementation of the TCP/IP protocol suite for the LINUX + * operating system. INET is implemented using the BSD Socket + * interface as the means of communication with the user level. + * + * IP/TCP/UDP checksumming routines + * + * Authors: Jorge Cwik, <jorge@laser.satlink.net> + * Arnt Gulbrandsen, <agulbra@nvg.unit.no> + * Tom May, <ftom@netcom.com> + * Lots of code moved from tcp.c and ip.c; see those files + * for more names. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <net/checksum.h> + +#undef PROFILE_CHECKSUM + +#ifdef PROFILE_CHECKSUM +/* these are just for profiling the checksum code with an oscillioscope.. uh */ +#if 0 +#define BITOFF *((unsigned char *)0xb0000030) = 0xff +#define BITON *((unsigned char *)0xb0000030) = 0x0 +#endif +#include <asm/io.h> +#define CBITON LED_ACTIVE_SET(1) +#define CBITOFF LED_ACTIVE_SET(0) +#define BITOFF +#define BITON +#else +#define BITOFF +#define BITON +#define CBITOFF +#define CBITON +#endif + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ + +#include <asm/delay.h> + +unsigned int csum_partial(const unsigned char * buff, int len, unsigned int sum) +{ + /* + * Experiments with ethernet and slip connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. + */ + const unsigned char *endMarker = buff + len; + const unsigned char *marker = endMarker - (len % 16); +#if 0 + if((int)buff & 0x3) + printk("unaligned buff %p\n", buff); + __delay(900); /* extra delay of 90 us to test performance hit */ +#endif + BITON; + while (buff < marker) { + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + sum += *((unsigned short *)buff)++; + } + marker = endMarker - (len % 2); + while(buff < marker) { + sum += *((unsigned short *)buff)++; + } + if(endMarker - buff > 0) { + sum += *buff; /* add extra byte seperately */ + } + BITOFF; + return(sum); +} + +#if 0 + +/* + * copy while checksumming, otherwise like csum_partial + */ + +unsigned int csum_partial_copy(const unsigned char *src, unsigned char *dst, + int len, unsigned int sum) +{ + const unsigned char *endMarker; + const unsigned char *marker; + printk("csum_partial_copy len %d.\n", len); +#if 0 + if((int)src & 0x3) + printk("unaligned src %p\n", src); + if((int)dst & 0x3) + printk("unaligned dst %p\n", dst); + __delay(1800); /* extra delay of 90 us to test performance hit */ +#endif + endMarker = src + len; + marker = endMarker - (len % 16); + CBITON; + while(src < marker) { + sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++); + sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++); + sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++); + sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++); + sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++); + sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++); + sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++); + sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++); + } + marker = endMarker - (len % 2); + while(src < marker) { + sum += (*((unsigned short *)dst)++ = *((unsigned short *)src)++); + } + if(endMarker - src > 0) { + sum += (*dst = *src); /* add extra byte seperately */ + } + CBITOFF; + return(sum); +} + +#endif diff --git a/arch/cris/lib/string.c b/arch/cris/lib/string.c new file mode 100644 index 000000000..6218cad56 --- /dev/null +++ b/arch/cris/lib/string.c @@ -0,0 +1,223 @@ +/*#************************************************************************#*/ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# FUNCTION NAME: memcpy() */ +/*# */ +/*# PARAMETERS: void* dst; Destination address. */ +/*# void* src; Source address. */ +/*# int len; Number of bytes to copy. */ +/*# */ +/*# RETURNS: dst. */ +/*# */ +/*# DESCRIPTION: Copies len bytes of memory from src to dst. No guarantees */ +/*# about copying of overlapping memory areas. This routine is */ +/*# very sensitive to compiler changes in register allocation. */ +/*# Should really be rewritten to avoid this problem. */ +/*# */ +/*#-------------------------------------------------------------------------*/ +/*# */ +/*# HISTORY */ +/*# */ +/*# DATE NAME CHANGES */ +/*# ---- ---- ------- */ +/*# 941007 Kenny R Creation */ +/*# 941011 Kenny R Lots of optimizations and inlining. */ +/*# 941129 Ulf A Adapted for use in libc. */ +/*# 950216 HP N==0 forgotten if non-aligned src/dst. */ +/*# Added some optimizations. */ +/*# 001025 HP Make src and dst char *. Align dst to */ +/*# dword, not just word-if-both-src-and-dst- */ +/*# are-misaligned. */ +/*# */ +/*#-------------------------------------------------------------------------*/ + +void *memcpy(void *pdst, + const void *psrc, + unsigned int pn) +{ + /* Ok. Now we want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register void *return_dst __asm__ ("r10") = pdst; + register char *dst __asm__ ("r13") = pdst; + register const char *src __asm__ ("r11") = psrc; + register int n __asm__ ("r12") = pn; + + + /* When src is aligned but not dst, this makes a few extra needless + cycles. I believe it would take as many to check that the + re-alignment was unnecessary. */ + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes; so we + don't have to check further for overflows. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + n--; + *(char*)dst = *(char*)src; + src++; + dst++; + } + + if ((unsigned long) dst & 2) + { + n -= 2; + *(short*)dst = *(short*)src; + src += 2; + dst += 2; + } + } + + /* Decide which copying method to use. */ + if (n >= 44*2) /* Break even between movem and + move16 is at 38.7*2, but modulo 44. */ + { + /* For large copies we use 'movem' */ + + /* It is not optimal to tell the compiler about clobbering any + registers; that will move the saving/restoring of those registers + to the function prologue/epilogue, and make non-movem sizes + suboptimal. + + This method is not foolproof; it assumes that the "asm reg" + declarations at the beginning of the function really are used + here (beware: they may be moved to temporary registers). + This way, we do not have to save/move the registers around into + temporaries; we can safely use them straight away. + + If you want to check that the allocation was right; then + check the equalities in the first comment. It should say + "r13=r13, r11=r11, r12=r12" */ + __asm__ volatile (" + ;; Check that the following is true (same register names on + ;; both sides of equal sign, as in r8=r8): + ;; %0=r13, %1=r11, %2=r12 + ;; + ;; Save the registers we'll use in the movem process + ;; on the stack. + subq 11*4,sp + movem r10,[sp] + + ;; Now we've got this: + ;; r11 - src + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 44,r12 +0: + movem [r11+],r10 + subq 44,r12 + bge 0b + movem r10,[r13+] + + addq 44,r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [sp+],r10" + + /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n) + /* Inputs */ : "0" (dst), "1" (src), "2" (n)); + + } + + /* Either we directly starts copying, using dword copying + in a loop, or we copy as much as possible with 'movem' + and then the last block (<44 bytes) is copied here. + This will work since 'movem' will have updated src,dst,n. */ + + while ( n >= 16 ) + { + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + n -= 16; + } + + /* A switch() is definitely the fastest although it takes a LOT of code. + * Particularly if you inline code this. + */ + switch (n) + { + case 0: + break; + case 1: + *(char*)dst = *(char*)src; + break; + case 2: + *(short*)dst = *(short*)src; + break; + case 3: + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + case 4: + *((long*)dst)++ = *((long*)src)++; + break; + case 5: + *((long*)dst)++ = *((long*)src)++; + *(char*)dst = *(char*)src; + break; + case 6: + *((long*)dst)++ = *((long*)src)++; + *(short*)dst = *(short*)src; + break; + case 7: + *((long*)dst)++ = *((long*)src)++; + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + case 8: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + break; + case 9: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(char*)dst = *(char*)src; + break; + case 10: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(short*)dst = *(short*)src; + break; + case 11: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + case 12: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + break; + case 13: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(char*)dst = *(char*)src; + break; + case 14: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *(short*)dst = *(short*)src; + break; + case 15: + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((long*)dst)++ = *((long*)src)++; + *((short*)dst)++ = *((short*)src)++; + *(char*)dst = *(char*)src; + break; + } + + return return_dst; /* destination pointer. */ +} /* memcpy() */ diff --git a/arch/cris/lib/usercopy.c b/arch/cris/lib/usercopy.c new file mode 100644 index 000000000..17eebf2ee --- /dev/null +++ b/arch/cris/lib/usercopy.c @@ -0,0 +1,501 @@ +/* + * User address space access functions. + * The non-inlined parts of asm-cris/uaccess.h are here. + * + * Copyright (C) 2000, Axis Communications AB. + * + * Written by Hans-Peter Nilsson. + * Pieces used from memcpy, originally by Kenny Ranerup long time ago. + */ + +#include <asm/uaccess.h> + +/* Asm:s have been tweaked (within the domain of correctness) to give + satisfactory results for "gcc version 2.96 20000427 (experimental)". + + Check regularly... + + Note that the PC saved at a bus-fault is the address *after* the + faulting instruction, which means the branch-target for instructions in + delay-slots for taken branches. Note also that the postincrement in + the instruction is performed regardless of bus-fault; the register is + seen updated in fault handlers. + + Oh, and on the code formatting issue, to whomever feels like "fixing + it" to Conformity: I'm too "lazy", but why don't you go ahead and "fix" + string.c too. I just don't think too many people will hack this file + for the code format to be an issue. */ + + +/* Copy to userspace. This is based on the memcpy used for + kernel-to-kernel copying; see "string.c". */ + +unsigned long +__copy_user (void *pdst, const void *psrc, unsigned long pn) +{ + /* We want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + FIXME: Comment for old gcc version. Check. + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register char *dst __asm__ ("r13") = pdst; + register const char *src __asm__ ("r11") = psrc; + register int n __asm__ ("r12") = pn; + register int retn __asm__ ("r10") = 0; + + + /* When src is aligned but not dst, this makes a few extra needless + cycles. I believe it would take as many to check that the + re-alignment was unnecessary. */ + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes; so we + don't have to check further for overflows. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + __asm_copy_to_user_1 (dst, src, retn); + n--; + } + + if ((unsigned long) dst & 2) + { + __asm_copy_to_user_2 (dst, src, retn); + n -= 2; + } + } + + /* Decide which copying method to use. */ + if (n >= 44*2) /* Break even between movem and + move16 is at 38.7*2, but modulo 44. */ + { + /* For large copies we use 'movem'. */ + + /* It is not optimal to tell the compiler about clobbering any + registers; that will move the saving/restoring of those registers + to the function prologue/epilogue, and make non-movem sizes + suboptimal. + + This method is not foolproof; it assumes that the "asm reg" + declarations at the beginning of the function really are used + here (beware: they may be moved to temporary registers). + This way, we do not have to save/move the registers around into + temporaries; we can safely use them straight away. + + If you want to check that the allocation was right; then + check the equalities in the first comment. It should say + "r13=r13, r11=r11, r12=r12". */ + __asm__ volatile (" + ;; Check that the following is true (same register names on + ;; both sides of equal sign, as in r8=r8): + ;; %0=r13, %1=r11, %2=r12 %3=r10 + ;; + ;; Save the registers we'll use in the movem process + ;; on the stack. + subq 11*4,sp + movem r10,[sp] + + ;; Now we've got this: + ;; r11 - src + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 44,r12 + +; Since the noted PC of a faulting instruction in a delay-slot of a taken +; branch, is that of the branch target, we actually point at the from-movem +; for this case. There is no ambiguity here; if there was a fault in that +; instruction (meaning a kernel oops), the faulted PC would be the address +; after *that* movem. + +0: + movem [r11+],r10 + subq 44,r12 + bge 0b + movem r10,[r13+] +1: + addq 44,r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [sp+],r10 +2: + .section .fixup,\"ax\" + +; To provide a correct count in r10 of bytes that failed to be copied, +; we jump back into the loop if the loop-branch was taken. There is no +; performance penalty for sany use; the program will segfault soon enough. + +3: + move.d [sp],r10 + addq 44,r10 + move.d r10,[sp] + jump 0b +4: + movem [sp+],r10 + addq 44,r10 + addq 44,r12 + jump 2b + + .previous + .section __ex_table,\"a\" + .dword 0b,3b + .dword 1b,4b + .previous" + + /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn) + /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn)); + + } + + /* Either we directly start copying, using dword copying in a loop, or + we copy as much as possible with 'movem' and then the last block (<44 + bytes) is copied here. This will work since 'movem' will have + updated SRC, DST and N. */ + + while (n >= 16) + { + __asm_copy_to_user_16 (dst, src, retn); + n -= 16; + } + + /* Having a separate by-four loops cuts down on cache footprint. + FIXME: Test with and without; increasing switch to be 0..15. */ + while (n >= 4) + { + __asm_copy_to_user_4 (dst, src, retn); + n -= 4; + } + + switch (n) + { + case 0: + break; + case 1: + __asm_copy_to_user_1 (dst, src, retn); + break; + case 2: + __asm_copy_to_user_2 (dst, src, retn); + break; + case 3: + __asm_copy_to_user_3 (dst, src, retn); + break; + } + + return retn; +} + +/* Copy from user to kernel, zeroing the bytes that were inaccessible in + userland. */ + +unsigned long +__copy_user_zeroing (void *pdst, const void *psrc, unsigned long pn) +{ + /* We want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + FIXME: Comment for old gcc version. Check. + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register char *dst __asm__ ("r13") = pdst; + register const char *src __asm__ ("r11") = psrc; + register int n __asm__ ("r12") = pn; + register int retn __asm__ ("r10") = 0; + + /* When src is aligned but not dst, this makes a few extra needless + cycles. I believe it would take as many to check that the + re-alignment was unnecessary. */ + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes; so we + don't have to check further for overflows. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + __asm_copy_from_user_1 (dst, src, retn); + n--; + } + + if ((unsigned long) dst & 2) + { + __asm_copy_from_user_2 (dst, src, retn); + n -= 2; + } + } + + /* Decide which copying method to use. */ + if (n >= 44*2) /* Break even between movem and + move16 is at 38.7*2, but modulo 44. */ + { + /* For large copies we use 'movem' */ + + /* It is not optimal to tell the compiler about clobbering any + registers; that will move the saving/restoring of those registers + to the function prologue/epilogue, and make non-movem sizes + suboptimal. + + This method is not foolproof; it assumes that the "asm reg" + declarations at the beginning of the function really are used + here (beware: they may be moved to temporary registers). + This way, we do not have to save/move the registers around into + temporaries; we can safely use them straight away. + + If you want to check that the allocation was right; then + check the equalities in the first comment. It should say + "r13=r13, r11=r11, r12=r12" */ + __asm__ volatile (" + ;; Check that the following is true (same register names on + ;; both sides of equal sign, as in r8=r8): + ;; %0=r13, %1=r11, %2=r12 %3=r10 + ;; + ;; Save the registers we'll use in the movem process + ;; on the stack. + subq 11*4,sp + movem r10,[sp] + + ;; Now we've got this: + ;; r11 - src + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 44,r12 +0: + movem [r11+],r10 +1: + subq 44,r12 + bge 0b + movem r10,[r13+] + + addq 44,r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [sp+],r10 + + .section .fixup,\"ax\" + +; To provide a correct count in r10 of bytes that failed to be copied, +; we jump back into the loop if the loop-branch was taken. +; There is no performance penalty; the program will segfault soon +; enough. + +3: + move.d [sp],r10 + addq 44,r10 + move.d r10,[sp] + clear.d r0 + clear.d r1 + clear.d r2 + clear.d r3 + clear.d r4 + clear.d r5 + clear.d r6 + clear.d r7 + clear.d r8 + clear.d r9 + clear.d r10 + jump 1b + + .previous + .section __ex_table,\"a\" + .dword 1b,3b + .previous" + + /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n), "=r" (retn) + /* Inputs */ : "0" (dst), "1" (src), "2" (n), "3" (retn)); + + } + + /* Either we directly start copying here, using dword copying in a loop, + or we copy as much as possible with 'movem' and then the last block + (<44 bytes) is copied here. This will work since 'movem' will have + updated src, dst and n. */ + + while (n >= 16) + { + __asm_copy_from_user_16 (dst, src, retn); + n -= 16; + } + + /* Having a separate by-four loops cuts down on cache footprint. + FIXME: Test with and without; increasing switch to be 0..15. */ + while (n >= 4) + { + __asm_copy_from_user_4 (dst, src, retn); + n -= 4; + } + + switch (n) + { + case 0: + break; + case 1: + __asm_copy_from_user_1 (dst, src, retn); + break; + case 2: + __asm_copy_from_user_2 (dst, src, retn); + break; + case 3: + __asm_copy_from_user_3 (dst, src, retn); + break; + } + + return retn; +} + +/* Zero userspace. */ + +unsigned long +__do_clear_user (void *pto, unsigned long pn) +{ + /* We want the parameters put in special registers. + Make sure the compiler is able to make something useful of this. + As it is now: r10 -> r13; r11 -> r11 (nop); r12 -> r12 (nop). + + FIXME: Comment for old gcc version. Check. + If gcc was allright, it really would need no temporaries, and no + stack space to save stuff on. */ + + register char *dst __asm__ ("r13") = pto; + register int n __asm__ ("r12") = pn; + register int retn __asm__ ("r10") = 0; + + + if (((unsigned long) dst & 3) != 0 + /* Don't align if we wouldn't copy more than a few bytes. */ + && n >= 3) + { + if ((unsigned long) dst & 1) + { + __asm_clear_1 (dst, retn); + n--; + } + + if ((unsigned long) dst & 2) + { + __asm_clear_2 (dst, retn); + n -= 2; + } + } + + /* Decide which copying method to use. + FIXME: This number is from the "ordinary" kernel memset. */ + if (n >= (1*48)) + { + /* For large clears we use 'movem' */ + + /* It is not optimal to tell the compiler about clobbering any + call-saved registers; that will move the saving/restoring of + those registers to the function prologue/epilogue, and make + non-movem sizes suboptimal. + + This method is not foolproof; it assumes that the "asm reg" + declarations at the beginning of the function really are used + here (beware: they may be moved to temporary registers). + This way, we do not have to save/move the registers around into + temporaries; we can safely use them straight away. + + If you want to check that the allocation was right; then + check the equalities in the first comment. It should say + something like "r13=r13, r11=r11, r12=r12". */ + __asm__ volatile (" + ;; Check that the following is true (same register names on + ;; both sides of equal sign, as in r8=r8): + ;; %0=r13, %1=r12 %2=r10 + ;; + ;; Save the registers we'll clobber in the movem process + ;; on the stack. Don't mention them to gcc, it will only be + ;; upset. + subq 11*4,sp + movem r10,[sp] + + clear.d r0 + clear.d r1 + clear.d r2 + clear.d r3 + clear.d r4 + clear.d r5 + clear.d r6 + clear.d r7 + clear.d r8 + clear.d r9 + clear.d r10 + clear.d r11 + + ;; Now we've got this: + ;; r13 - dst + ;; r12 - n + + ;; Update n for the first loop + subq 12*4,r12 +0: + subq 12*4,r12 + bge 0b + movem r11,[r13+] +1: + addq 12*4,r12 ;; compensate for last loop underflowing n + + ;; Restore registers from stack + movem [sp+],r10 +2: + .section .fixup,\"ax\" +3: + move.d [sp],r10 + addq 12*4,r10 + move.d r10,[sp] + clear.d r10 + jump 0b + +4: + movem [sp+],r10 + addq 12*4,r10 + addq 12*4,r12 + jump 2b + + .previous + .section __ex_table,\"a\" + .dword 0b,3b + .dword 1b,4b + .previous" + + /* Outputs */ : "=r" (dst), "=r" (n), "=r" (retn) + /* Inputs */ : "0" (dst), "1" (n), "2" (retn) + /* Clobber */ : "r11"); + } + + while (n >= 16) + { + __asm_clear_16 (dst, retn); + n -= 16; + } + + /* Having a separate by-four loops cuts down on cache footprint. + FIXME: Test with and without; increasing switch to be 0..15. */ + while (n >= 4) + { + __asm_clear_4 (dst, retn); + n -= 4; + } + + switch (n) + { + case 0: + break; + case 1: + __asm_clear_1 (dst, retn); + break; + case 2: + __asm_clear_2 (dst, retn); + break; + case 3: + __asm_clear_3 (dst, retn); + break; + } + + return retn; +} diff --git a/arch/cris/mm/Makefile b/arch/cris/mm/Makefile new file mode 100644 index 000000000..d1d21a7b7 --- /dev/null +++ b/arch/cris/mm/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for the linux cris-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := mm.o +obj-y := init.o fault.o tlb.o extable.o + +include $(TOPDIR)/Rules.make diff --git a/arch/cris/mm/extable.c b/arch/cris/mm/extable.c new file mode 100644 index 000000000..a4cf00f14 --- /dev/null +++ b/arch/cris/mm/extable.c @@ -0,0 +1,55 @@ +/* + * linux/arch/cris/mm/extable.c + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +extern const struct exception_table_entry _start___ex_table[]; +extern const struct exception_table_entry _stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + +#ifndef CONFIG_MODULES + /* There is only the kernel to search. */ + ret = search_one_table(_start___ex_table, _stop___ex_table-1, addr); + if (ret) return ret; +#else + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->ex_table_start == NULL) + continue; + ret = search_one_table(mp->ex_table_start, + mp->ex_table_end - 1, addr); + if (ret) return ret; + } +#endif + + return 0; +} diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c new file mode 100644 index 000000000..a4bb237b4 --- /dev/null +++ b/arch/cris/mm/fault.c @@ -0,0 +1,390 @@ +/* + * linux/arch/cris/mm/fault.c + * + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen + * + * $Log: fault.c,v $ + * Revision 1.8 2000/11/22 14:45:31 bjornw + * * 2.4.0-test10 removed the set_pgdir instantaneous kernel global mapping + * into all processes. Instead we fill in the missing PTE entries on demand. + * + * Revision 1.7 2000/11/21 16:39:09 bjornw + * fixup switches frametype + * + * Revision 1.6 2000/11/17 16:54:08 bjornw + * More detailed siginfo reporting + * + * + */ + +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/interrupt.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> +#include <asm/uaccess.h> +#include <asm/svinto.h> + +extern void die_if_kernel(const char *,struct pt_regs *,long); + +asmlinkage void do_invalid_op (struct pt_regs *, unsigned long); +asmlinkage void do_page_fault(unsigned long address, struct pt_regs *regs, + int error_code); + +/* debug of low-level TLB reload */ +#define D(x) +/* debug of higher-level faults */ +#define DPG(x) + +/* fast TLB-fill fault handler */ + +void +handle_mmu_bus_fault(struct pt_regs *regs) +{ + int cause, select; + int index; + int page_id; + int miss, we, acc, inv; + struct mm_struct *mm = current->active_mm; + pmd_t *pmd; + pte_t pte; + int errcode = 0; + unsigned long address; + + cause = *R_MMU_CAUSE; + select = *R_TLB_SELECT; + + address = cause & PAGE_MASK; /* get faulting address */ + + page_id = IO_EXTRACT(R_MMU_CAUSE, page_id, cause); + miss = IO_EXTRACT(R_MMU_CAUSE, miss_excp, cause); + we = IO_EXTRACT(R_MMU_CAUSE, we_excp, cause); + acc = IO_EXTRACT(R_MMU_CAUSE, acc_excp, cause); + inv = IO_EXTRACT(R_MMU_CAUSE, inv_excp, cause); + index = IO_EXTRACT(R_TLB_SELECT, index, select); + + D(printk("bus_fault from IRP 0x%x: addr 0x%x, miss %d, inv %d, we %d, acc %d, " + "idx %d pid %d\n", + regs->irp, address, miss, inv, we, acc, index, page_id)); + + /* for a miss, we need to reload the TLB entry */ + + if(miss) { + + /* see if the pte exists at all */ + + pmd = (pmd_t *)pgd_offset(mm, address); + if(pmd_none(*pmd)) + goto dofault; + if(pmd_bad(*pmd)) { + printk("bad pgdir entry 0x%x at 0x%x\n", *pmd, pmd); + pmd_clear(pmd); + return; + } + pte = *pte_offset(pmd, address); + if(!pte_present(pte)) + goto dofault; + + D(printk(" found pte %x pg %x ", pte_val(pte), pte_page(pte))); + D( + { + if(pte_val(pte) & _PAGE_SILENT_WRITE) + printk("Silent-W "); + if(pte_val(pte) & _PAGE_KERNEL) + printk("Kernel "); + if(pte_val(pte) & _PAGE_SILENT_READ) + printk("Silent-R "); + if(pte_val(pte) & _PAGE_GLOBAL) + printk("Global "); + if(pte_val(pte) & _PAGE_PRESENT) + printk("Present "); + if(pte_val(pte) & _PAGE_ACCESSED) + printk("Accessed "); + if(pte_val(pte) & _PAGE_MODIFIED) + printk("Modified "); + if(pte_val(pte) & _PAGE_READ) + printk("Readable "); + if(pte_val(pte) & _PAGE_WRITE) + printk("Writeable "); + printk("\n"); + }); + + /* load up the chosen TLB entry + * this assumes the pte format is the same as the TLB_LO layout. + * + * the write to R_TLB_LO also writes the vpn and page_id fields from + * R_MMU_CAUSE, which we in this case obviously want to keep + */ + + *R_TLB_LO = pte_val(pte); + + return; + } + + errcode = 0x01 | (we << 1); + + dofault: + /* leave it to the MM system fault handler below */ + D(printk("do_page_fault %p errcode %d\n", address, errcode)); + do_page_fault(address, regs, errcode); +} + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * Notice that the address we're given is aligned to the page the fault + * occured in, since we only get the PFN in R_MMU_CAUSE not the complete + * address. + * + * error_code: + * bit 0 == 0 means no page found, 1 means protection fault + * bit 1 == 0 means read, 1 means write + * + * If this routine detects a bad access, it returns 1, otherwise it + * returns 0. + */ + +asmlinkage void +do_page_fault(unsigned long address, struct pt_regs *regs, + int error_code) +{ + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct * vma; + int writeaccess; + int fault; + unsigned long fixup; + siginfo_t info; + + tsk = current; + + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. + * + * NOTE2: This is done so that, when updating the vmalloc + * mappings we don't have to walk all processes pgdirs and + * add the high mappings all at once. Instead we do it as they + * are used. + * + * TODO: On CRIS, we have a PTE Global bit which should be set in + * all the PTE's related to vmalloc in all processes - that means if + * we switch process and a vmalloc PTE is still in the TLB, it won't + * need to be reloaded. It's an optimization. + * + * Linux/CRIS's kernel is not page-mapped, so the comparision below + * should really be >= VMALLOC_START, however, kernel fixup errors + * will be handled more quickly by going through vmalloc_fault and then + * into bad_area_nosemaphore than falling through the find_vma user-mode + * tests. + */ + + if (address >= TASK_SIZE) + goto vmalloc_fault; + + mm = tsk->mm; + writeaccess = error_code & 2; + info.si_code = SEGV_MAPERR; + + /* + * If we're in an interrupt or have no user + * context, we must not take the fault.. + */ + + if (in_interrupt() || !mm) + goto no_context; + + down(&mm->mmap_sem); + vma = find_vma(mm, address); + if (!vma) + goto bad_area; + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (user_mode(regs)) { + /* + * accessing the stack below usp is always a bug. + * we get page-aligned addresses so we can only check + * if we're within a page from usp, but that might be + * enough to catch brutal errors at least. + */ + if (address + PAGE_SIZE < rdusp()) + goto bad_area; + } + if (expand_stack(vma, address)) + goto bad_area; + + /* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ + + good_area: + info.si_code = SEGV_ACCERR; + + /* first do some preliminary protection checks */ + + if (writeaccess) { + if (!(vma->vm_flags & VM_WRITE)) + goto bad_area; + } else { + if (!(vma->vm_flags & (VM_READ | VM_EXEC))) + goto bad_area; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + + switch (handle_mm_fault(mm, vma, address, writeaccess)) { + case 1: + tsk->min_flt++; + break; + case 2: + tsk->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; + } + + up(&mm->mmap_sem); + return; + + /* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ + + bad_area: + + up(&mm->mmap_sem); + + bad_area_nosemaphore: + DPG(show_registers(regs)); + + /* User mode accesses just cause a SIGSEGV */ + + if (user_mode(regs)) { + info.si_signo = SIGSEGV; + info.si_errno = 0; + /* info.si_code has been set above */ + info.si_addr = (void *)address; + force_sig_info(SIGSEGV, &info, tsk); + return; + } + + no_context: + + /* Are we prepared to handle this kernel fault? + * + * (The kernel has valid exception-points in the source + * when it acesses user-memory. When it fails in one + * of those points, we find it in a table and do a jump + * to some fixup code that loads an appropriate error + * code) + */ + + if ((fixup = search_exception_table(regs->irp)) != 0) { + regs->irp = fixup; + regs->frametype = CRIS_FRAME_FIXUP; + D(printk("doing fixup to 0x%x\n", fixup)); + return; + } + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + if ((unsigned long) (address) < PAGE_SIZE) + printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + else + printk(KERN_ALERT "Unable to handle kernel access"); + printk(" at virtual address %08lx\n",address); + + die_if_kernel("Oops", regs, error_code); + + do_exit(SIGKILL); + + /* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. + */ + + out_of_memory: + up(&mm->mmap_sem); + printk("VM: killing process %s\n", tsk->comm); + if(user_mode(regs)) + do_exit(SIGKILL); + goto no_context; + + do_sigbus: + up(&mm->mmap_sem); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + info.si_code = SIGBUS; + info.si_errno = 0; + info.si_code = BUS_ADRERR; + info.si_addr = (void *)address; + force_sig_info(SIGBUS, &info, tsk); + + /* Kernel mode? Handle exceptions or die */ + if (!user_mode(regs)) + goto no_context; + return; + +vmalloc_fault: + { + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. + */ + int offset = pgd_index(address); + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; + + pgd = tsk->active_mm->pgd + offset; + pgd_k = init_mm.pgd + offset; + + if (!pgd_present(*pgd)) { + if (!pgd_present(*pgd_k)) + goto bad_area_nosemaphore; + set_pgd(pgd, *pgd_k); + return; + } + + pmd = pmd_offset(pgd, address); + pmd_k = pmd_offset(pgd_k, address); + + if (pmd_present(*pmd) || !pmd_present(*pmd_k)) + goto bad_area_nosemaphore; + set_pmd(pmd, *pmd_k); + return; + } + +} diff --git a/arch/cris/mm/init.c b/arch/cris/mm/init.c new file mode 100644 index 000000000..3d0ceeffb --- /dev/null +++ b/arch/cris/mm/init.c @@ -0,0 +1,506 @@ +/* + * linux/arch/cris/mm/init.c + * + * Copyright (C) 1995 Linus Torvalds + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + * $Log: init.c,v $ + * Revision 1.15 2001/01/10 21:12:10 bjornw + * loops_per_sec -> loops_per_jiffy + * + * Revision 1.14 2000/11/22 16:23:20 bjornw + * Initialize totalhigh counters to 0 to make /proc/meminfo look nice. + * + * Revision 1.13 2000/11/21 16:37:51 bjornw + * Temporarily disable initmem freeing + * + * Revision 1.12 2000/11/21 13:55:07 bjornw + * Use CONFIG_CRIS_LOW_MAP for the low VM map instead of explicit CPU type + * + * Revision 1.11 2000/10/06 12:38:22 bjornw + * Cast empty_bad_page correctly (should really be of * type from the start.. + * + * Revision 1.10 2000/10/04 16:53:57 bjornw + * Fix memory-map due to LX features + * + * Revision 1.9 2000/09/13 15:47:49 bjornw + * Wrong count in reserved-pages loop + * + * Revision 1.8 2000/09/13 14:35:10 bjornw + * 2.4.0-test8 added a new arg to free_area_init_node + * + * Revision 1.7 2000/08/17 15:35:55 bjornw + * 2.4.0-test6 removed MAP_NR and inserted virt_to_page + * + * + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#include <linux/bootmem.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgalloc.h> +#include <asm/pgtable.h> +#include <asm/dma.h> +#include <asm/svinto.h> + +static unsigned long totalram_pages; + +struct pgtable_cache_struct quicklists; /* see asm/pgalloc.h */ + +const char bad_pmd_string[] = "Bad pmd in pte_alloc: %08lx\n"; + +extern void die_if_kernel(char *,struct pt_regs *,long); +extern void show_net_buffers(void); +extern void tlb_init(void); + +/* + * empty_bad_page is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving a inode + * unused etc.. + * + * the main point is that when a page table error occurs, we want to get + * out of the kernel safely before killing the process, so we need something + * to feed the MMU with when the fault occurs even if we don't have any + * real PTE's or page tables. + * + * empty_bad_page_table is the accompanying page-table: it is initialized + * to point to empty_bad_page writable-shared entries. + * + * empty_zero_page is a special page that is used for zero-initialized + * data and COW. + */ + +unsigned long empty_bad_page_table; +unsigned long empty_bad_page; +unsigned long empty_zero_page; + +pte_t * __bad_pagetable(void) +{ + /* somehow it is enough to just clear it and not fill it with + * bad page PTE's... + */ + memset((void *)empty_bad_page_table, 0, PAGE_SIZE); + + return (pte_t *) empty_bad_page_table; +} + +pte_t __bad_page(void) +{ + + /* clear the empty_bad_page page. this should perhaps be + * a more simple inlined loop like it is on the other + * architectures. + */ + + memset((void *)empty_bad_page, 0, PAGE_SIZE); + + return pte_mkdirty(__mk_pte((void *)empty_bad_page, PAGE_SHARED)); +} + +static pte_t * get_bad_pte_table(void) +{ + pte_t *empty_bad_pte_table = (pte_t *)empty_bad_page_table; + pte_t v; + int i; + + v = __bad_page(); + + for (i = 0; i < PAGE_SIZE/sizeof(pte_t); i++) + empty_bad_pte_table[i] = v; + + return empty_bad_pte_table; +} + +void __handle_bad_pmd(pmd_t *pmd) +{ + pmd_ERROR(*pmd); + pmd_set(pmd, get_bad_pte_table()); +} + +void __handle_bad_pmd_kernel(pmd_t *pmd) +{ + pmd_ERROR(*pmd); + pmd_set_kernel(pmd, get_bad_pte_table()); +} + +pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + clear_page(pte); + pmd_set_kernel(pmd, pte); + return pte + offset; + } + pmd_set_kernel(pmd, get_bad_pte_table()); + return NULL; + } + free_page((unsigned long)pte); + if (pmd_bad(*pmd)) { + __handle_bad_pmd_kernel(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + + pte = (pte_t *) __get_free_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + clear_page(pte); + pmd_set(pmd, pte); + return pte + offset; + } + pmd_set(pmd, get_bad_pte_table()); + return NULL; + } + free_page((unsigned long)pte); + if (pmd_bad(*pmd)) { + __handle_bad_pmd(pmd); + return NULL; + } + return (pte_t *) pmd_page(*pmd) + offset; +} + +#ifndef CONFIG_NO_PGT_CACHE +struct pgtable_cache_struct quicklists; + +/* trim the page-table cache if necessary */ + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + + if(pgtable_cache_size > high) { + do { + if(pgd_quicklist) { + free_pgd_slow(get_pgd_fast()); + freed++; + } + if(pmd_quicklist) { + free_pmd_slow(get_pmd_fast()); + freed++; + } + if(pte_quicklist) { + free_pte_slow(get_pte_fast()); + freed++; + } + } while(pgtable_cache_size > low); + } + return freed; +} +#else +int do_check_pgt_cache(int low, int high) +{ + return 0; +} +#endif + +void show_mem(void) +{ + int i,free = 0,total = 0,cached = 0, reserved = 0, nonshared = 0; + int shared = 0; + + printk("\nMem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (!page_count(mem_map+i)) + free++; + else if (page_count(mem_map+i) == 1) + nonshared++; + else + shared += page_count(mem_map+i) - 1; + } + printk("%d pages of RAM\n",total); + printk("%d free pages\n",free); + printk("%d reserved pages\n",reserved); + printk("%d pages nonshared\n",nonshared); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld pages in page table cache\n",pgtable_cache_size); + show_buffers(); +} + +/* + * The kernel is already mapped with a kernel segment at kseg_c so + * we don't need to map it with a page table. However head.S also + * temporarily mapped it at kseg_4 so we should set up the ksegs again, + * clear the TLB and do some other paging setup stuff. + */ + +void __init +paging_init(void) +{ + int i; + unsigned long zones_size[MAX_NR_ZONES]; + + printk("Setting up paging and the MMU.\n"); + + /* clear out the init_mm.pgd that will contain the kernel's mappings */ + + for(i = 0; i < PTRS_PER_PGD; i++) + swapper_pg_dir[i] = __pgd(0); + + /* initialise the TLB (tlb.c) */ + + tlb_init(); + + /* see README.mm for details on the KSEG setup */ + +#ifdef CONFIG_CRIS_LOW_MAP + + /* Etrax-100 LX version 1 has a bug so that we cannot map anything + * across the 0x80000000 boundary, so we need to shrink the user-virtual + * area to 0x50000000 instead of 0xb0000000 and map things slightly + * different. The unused areas are marked as paged so that we can catch + * freak kernel accesses there. + * + * The Juliette chip is mapped at 0xa so we pass that segment straight + * through. We cannot vremap it because the vmalloc area is below 0x8 + * and Juliette needs an uncached area above 0x8. + */ + + *R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, page ) | + IO_STATE(R_MMU_KSEG, seg_e, seg ) | /* uncached flash */ + IO_STATE(R_MMU_KSEG, seg_d, page ) | + IO_STATE(R_MMU_KSEG, seg_c, page ) | + IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */ + IO_STATE(R_MMU_KSEG, seg_a, seg ) | /* Juliette etc. */ + IO_STATE(R_MMU_KSEG, seg_9, page ) | + IO_STATE(R_MMU_KSEG, seg_8, page ) | + IO_STATE(R_MMU_KSEG, seg_7, page ) | /* kernel vmalloc area */ + IO_STATE(R_MMU_KSEG, seg_6, seg ) | /* kernel DRAM area */ + IO_STATE(R_MMU_KSEG, seg_5, seg ) | /* cached flash */ + IO_STATE(R_MMU_KSEG, seg_4, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_3, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_2, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_1, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_0, page ) ); /* user area */ + + *R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_e, 0x8 ) | + IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_c, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) | + IO_FIELD(R_MMU_KBASE_HI, base_a, 0xa ) | + IO_FIELD(R_MMU_KBASE_HI, base_9, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_8, 0x0 ) ); + + *R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_6, 0x4 ) | + IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) ); +#else + /* This code is for the hopefully corrected Etrax-100 LX version 2... */ + + *R_MMU_KSEG = ( IO_STATE(R_MMU_KSEG, seg_f, seg ) | /* cached flash */ + IO_STATE(R_MMU_KSEG, seg_e, seg ) | /* uncached flash */ + IO_STATE(R_MMU_KSEG, seg_d, page ) | /* vmalloc area */ + IO_STATE(R_MMU_KSEG, seg_c, seg ) | /* kernel area */ + IO_STATE(R_MMU_KSEG, seg_b, seg ) | /* kernel reg area */ + IO_STATE(R_MMU_KSEG, seg_a, page ) | /* user area */ + IO_STATE(R_MMU_KSEG, seg_9, page ) | + IO_STATE(R_MMU_KSEG, seg_8, page ) | + IO_STATE(R_MMU_KSEG, seg_7, page ) | + IO_STATE(R_MMU_KSEG, seg_6, page ) | + IO_STATE(R_MMU_KSEG, seg_5, page ) | + IO_STATE(R_MMU_KSEG, seg_4, page ) | + IO_STATE(R_MMU_KSEG, seg_3, page ) | + IO_STATE(R_MMU_KSEG, seg_2, page ) | + IO_STATE(R_MMU_KSEG, seg_1, page ) | + IO_STATE(R_MMU_KSEG, seg_0, page ) ); + + *R_MMU_KBASE_HI = ( IO_FIELD(R_MMU_KBASE_HI, base_f, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_e, 0x8 ) | + IO_FIELD(R_MMU_KBASE_HI, base_d, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_c, 0x4 ) | + IO_FIELD(R_MMU_KBASE_HI, base_b, 0xb ) | + IO_FIELD(R_MMU_KBASE_HI, base_a, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_9, 0x0 ) | + IO_FIELD(R_MMU_KBASE_HI, base_8, 0x0 ) ); + + *R_MMU_KBASE_LO = ( IO_FIELD(R_MMU_KBASE_LO, base_7, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_6, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_5, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_4, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_3, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_2, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_1, 0x0 ) | + IO_FIELD(R_MMU_KBASE_LO, base_0, 0x0 ) ); +#endif + + *R_MMU_CONTEXT = ( IO_FIELD(R_MMU_CONTEXT, page_id, 0 ) ); + + /* The MMU has been enabled ever since head.S but just to make + * it totally obvious we do it here as well. + */ + + *R_MMU_CTRL = ( IO_STATE(R_MMU_CTRL, inv_excp, enable ) | + IO_STATE(R_MMU_CTRL, acc_excp, enable ) | + IO_STATE(R_MMU_CTRL, we_excp, enable ) ); + + *R_MMU_ENABLE = IO_STATE(R_MMU_ENABLE, mmu_enable, enable); + + /* + * initialize the bad page table and bad page to point + * to a couple of allocated pages + */ + + empty_bad_page_table = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); + empty_bad_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); + empty_zero_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE); + memset((void *)empty_zero_page, 0, PAGE_SIZE); + + /* All pages are DMA'able in Etrax, so put all in the DMA'able zone */ + + zones_size[0] = ((unsigned long)high_memory - PAGE_OFFSET) >> PAGE_SHIFT; + + for (i = 1; i < MAX_NR_ZONES; i++) + zones_size[i] = 0; + + /* Use free_area_init_node instead of free_area_init, because the former + * is designed for systems where the DRAM starts at an address substantially + * higher than 0, like us (we start at PAGE_OFFSET). This saves space in the + * mem_map page array. + */ + + free_area_init_node(0, 0, 0, zones_size, PAGE_OFFSET, 0); +} + +extern unsigned long loops_per_jiffy; /* init/main.c */ +unsigned long loops_per_usec; + +extern char _stext, _edata, _etext; +extern char __init_begin, __init_end; + +void __init +mem_init(void) +{ + int codesize, reservedpages, datasize, initsize; + unsigned long tmp; + + if(!mem_map) + BUG(); + + /* max/min_low_pfn was set by setup.c + * now we just copy it to some other necessary places... + * + * high_memory was also set in setup.c + */ + + max_mapnr = num_physpages = max_low_pfn - min_low_pfn; + + /* this will put all memory onto the freelists */ + totalram_pages = free_all_bootmem(); + + reservedpages = 0; + for (tmp = 0; tmp < max_mapnr; tmp++) { + /* + * Only count reserved RAM pages + */ + if (PageReserved(mem_map + tmp)) + reservedpages++; + } + + codesize = (unsigned long) &_etext - (unsigned long) &_stext; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + + printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, " + "%dk init)\n" , + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + max_mapnr << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >> 10, + initsize >> 10 + ); + + /* HACK alert - calculate a loops_per_usec for asm/delay.h here + * since this is called just after calibrate_delay in init/main.c + * but before places which use udelay. cannot be in time.c since + * that is called _before_ calibrate_delay + */ + + loops_per_usec = (loops_per_jiffy * HZ) / 1000000; + + return; +} + +/* free the pages occupied by initialization code */ + +void free_initmem(void) +{ +#if 0 + /* currently this is a bad idea since the cramfs image is catted onto + * the vmlinux image, and the end of that image is not page-padded so + * part of the cramfs image will be freed here + */ + unsigned long addr; + + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + set_page_count(virt_to_page(addr), 1); + free_page(addr); + totalram_pages++; + } + printk ("Freeing unused kernel memory: %dk freed\n", + (&__init_end - &__init_begin) >> 10); +#endif +} + +void si_meminfo(struct sysinfo *val) +{ + int i; + + i = max_mapnr; + val->totalram = 0; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + while (i-- > 0) { + if (PageReserved(mem_map+i)) + continue; + val->totalram++; + if (!atomic_read(&mem_map[i].count)) + continue; + val->sharedram += atomic_read(&mem_map[i].count) - 1; + } + val->mem_unit = PAGE_SIZE; + val->totalhigh = 0; + val->freehigh = 0; +} diff --git a/arch/cris/mm/tlb.c b/arch/cris/mm/tlb.c new file mode 100644 index 000000000..ed74305ae --- /dev/null +++ b/arch/cris/mm/tlb.c @@ -0,0 +1,301 @@ +/* + * linux/arch/cris/mm/tlb.c + * + * Copyright (C) 2000 Axis Communications AB + * + * Authors: Bjorn Wesen (bjornw@axis.com) + * + */ + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/init.h> + +#include <asm/system.h> +#include <asm/segment.h> +#include <asm/pgtable.h> +#include <asm/svinto.h> + +#define D(x) + +/* CRIS in Etrax100LX TLB */ + +#define NUM_TLB_ENTRIES 64 +#define NUM_PAGEID 64 +#define INVALID_PAGEID 63 +#define NO_CONTEXT -1 + +/* The TLB can host up to 64 different mm contexts at the same time. + * The running context is R_MMU_CONTEXT, and each TLB entry contains a + * page_id that has to match to give a hit. In page_id_map, we keep track + * of which mm's we have assigned which page_id's, so that we know when + * to invalidate TLB entries. + * + * The last page_id is never running - it is used as an invalid page_id + * so we can make TLB entries that will never match. + */ + +struct mm_struct *page_id_map[NUM_PAGEID]; + +static int map_replace_ptr = 1; /* which page_id_map entry to replace next */ + +/* invalidate all TLB entries */ + +void +flush_tlb_all() +{ + int i; + + /* the vpn of i & 0xf is so we dont write similar TLB entries + * in the same 4-way entry group. details.. + */ + + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + *R_TLB_SELECT = ( IO_FIELD(R_TLB_SELECT, index, i) ); + + *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | + IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); + + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | + IO_STATE(R_TLB_LO, valid, no ) | + IO_STATE(R_TLB_LO, kernel,no ) | + IO_STATE(R_TLB_LO, we, no ) | + IO_FIELD(R_TLB_LO, pfn, 0 ) ); + } + D(printk("tlb: flushed all\n")); +} + +/* invalidate the selected mm context only */ + +void +flush_tlb_mm(struct mm_struct *mm) +{ + int i; + int page_id = mm->context; + + D(printk("tlb: flush mm context %d (%p)\n", page_id, mm)); + + if(page_id == NO_CONTEXT) + return; + + /* mark the TLB entries that match the page_id as invalid. + * here we could also check the _PAGE_GLOBAL bit and NOT flush + * global pages. is it worth the extra I/O ? + */ + + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); + if (IO_EXTRACT(R_TLB_HI, page_id, *R_TLB_HI) == page_id) { + *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | + IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); + + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | + IO_STATE(R_TLB_LO, valid, no ) | + IO_STATE(R_TLB_LO, kernel,no ) | + IO_STATE(R_TLB_LO, we, no ) | + IO_FIELD(R_TLB_LO, pfn, 0 ) ); + } + } +} + +/* invalidate a single page */ + +void +flush_tlb_page(struct vm_area_struct *vma, + unsigned long addr) +{ + struct mm_struct *mm = vma->vm_mm; + int page_id = mm->context; + int i; + + D(printk("tlb: flush page %p in context %d (%p)\n", addr, page_id, mm)); + + if(page_id == NO_CONTEXT) + return; + + addr &= PAGE_MASK; /* perhaps not necessary */ + + /* invalidate those TLB entries that match both the mm context + * and the virtual address requested + */ + + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + unsigned long tlb_hi; + *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); + tlb_hi = *R_TLB_HI; + if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id && + (tlb_hi & PAGE_MASK) == addr) { + *R_TLB_HI = IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | + addr; /* same addr as before works. */ + + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | + IO_STATE(R_TLB_LO, valid, no ) | + IO_STATE(R_TLB_LO, kernel,no ) | + IO_STATE(R_TLB_LO, we, no ) | + IO_FIELD(R_TLB_LO, pfn, 0 ) ); + } + } +} + +/* invalidate a page range */ + +void +flush_tlb_range(struct mm_struct *mm, + unsigned long start, + unsigned long end) +{ + int page_id = mm->context; + int i; + + D(printk("tlb: flush range %p<->%p in context %d (%p)\n", + start, end, page_id, mm)); + + if(page_id == NO_CONTEXT) + return; + + start &= PAGE_MASK; /* probably not necessary */ + end &= PAGE_MASK; /* dito */ + + /* invalidate those TLB entries that match both the mm context + * and the virtual address range + */ + + for(i = 0; i < NUM_TLB_ENTRIES; i++) { + unsigned long tlb_hi, vpn; + *R_TLB_SELECT = IO_FIELD(R_TLB_SELECT, index, i); + tlb_hi = *R_TLB_HI; + vpn = tlb_hi & PAGE_MASK; + if (IO_EXTRACT(R_TLB_HI, page_id, tlb_hi) == page_id && + vpn >= start && vpn < end) { + *R_TLB_HI = ( IO_FIELD(R_TLB_HI, page_id, INVALID_PAGEID ) | + IO_FIELD(R_TLB_HI, vpn, i & 0xf ) ); + + *R_TLB_LO = ( IO_STATE(R_TLB_LO, global,no ) | + IO_STATE(R_TLB_LO, valid, no ) | + IO_STATE(R_TLB_LO, kernel,no ) | + IO_STATE(R_TLB_LO, we, no ) | + IO_FIELD(R_TLB_LO, pfn, 0 ) ); + } + } +} + +/* + * Initialize the context related info for a new mm_struct + * instance. + */ + +int +init_new_context(struct task_struct *tsk, struct mm_struct *mm) +{ + mm->context = NO_CONTEXT; + return 0; +} + +/* the following functions are similar to those used in the PPC port */ + +static inline void +alloc_context(struct mm_struct *mm) +{ + struct mm_struct *old_mm; + + D(printk("tlb: alloc context %d (%p)\n", map_replace_ptr, mm)); + + /* did we replace an mm ? */ + + old_mm = page_id_map[map_replace_ptr]; + + if(old_mm) { + /* throw out any TLB entries belonging to the mm we replace + * in the map + */ + flush_tlb_mm(old_mm); + + old_mm->context = NO_CONTEXT; + } + + /* insert it into the page_id_map */ + + mm->context = map_replace_ptr; + page_id_map[map_replace_ptr] = mm; + + map_replace_ptr++; + + if(map_replace_ptr == INVALID_PAGEID) + map_replace_ptr = 0; /* wrap around */ + +} + +/* + * if needed, get a new MMU context for the mm. otherwise nothing is done. + */ + +void +get_mmu_context(struct mm_struct *mm) +{ + if(mm->context == NO_CONTEXT) + alloc_context(mm); +} + +/* called in schedule() just before actually doing the switch_to */ + +void +switch_mm(struct mm_struct *prev, struct mm_struct *next, + struct task_struct *tsk, int cpu) +{ + /* make sure we have a context */ + + get_mmu_context(next); + + /* switch context in the MMU */ + + D(printk("switching mmu_context to %d (%p)\n", next->context, next)); + + *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, page_id, next->context); +} + + +/* called by __exit_mm to destroy the used MMU context if any before + * destroying the mm itself. this is only called when the last user of the mm + * drops it. + * + * the only thing we really need to do here is mark the used PID slot + * as empty. + */ + +void +destroy_context(struct mm_struct *mm) +{ + if(mm->context != NO_CONTEXT) { + D(printk("destroy_context %d (%p)\n", mm->context, mm)); + flush_tlb_mm(mm); /* TODO this might be redundant ? */ + page_id_map[mm->context] = NULL; + /* mm->context = NO_CONTEXT; redundant.. mm will be freed */ + } +} + +/* called once during VM initialization, from init.c */ + +void __init +tlb_init(void) +{ + int i; + + /* clear the page_id map */ + + for(i = 0; i < 64; i++) + page_id_map[i] = NULL; + + /* invalidate the entire TLB */ + + flush_tlb_all(); + + /* the init_mm has context 0 from the boot */ + + page_id_map[0] = &init_mm; +} diff --git a/arch/i386/boot/compressed/misc.c b/arch/i386/boot/compressed/misc.c index abb382d8f..0baa87ea9 100644 --- a/arch/i386/boot/compressed/misc.c +++ b/arch/i386/boot/compressed/misc.c @@ -10,6 +10,7 @@ */ #include <linux/vmalloc.h> +#include <linux/tty.h> #include <asm/io.h> /* * gzip declarations diff --git a/arch/i386/defconfig b/arch/i386/defconfig index 05a2e3725..875a84bf9 100644 --- a/arch/i386/defconfig +++ b/arch/i386/defconfig @@ -162,6 +162,11 @@ CONFIG_IP_MULTICAST=y # CONFIG_BRIDGE is not set # +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# # Telephony Support # # CONFIG_PHONE is not set @@ -628,6 +633,8 @@ CONFIG_SOUND=y CONFIG_SOUND_ES1371=y # CONFIG_SOUND_ESSSOLO1 is not set # CONFIG_SOUND_MAESTRO is not set +# CONFIG_SOUND_MAESTRO3 is not set +# CONFIG_SOUND_ICH is not set # CONFIG_SOUND_SONICVIBES is not set # CONFIG_SOUND_TRIDENT is not set # CONFIG_SOUND_MSNDCLAS is not set diff --git a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c index 9703e3304..4e1ee2f83 100644 --- a/arch/i386/kernel/apm.c +++ b/arch/i386/kernel/apm.c @@ -176,7 +176,7 @@ #include <linux/stddef.h> #include <linux/timer.h> #include <linux/fcntl.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/stat.h> #include <linux/proc_fs.h> #include <linux/miscdevice.h> diff --git a/arch/i386/kernel/dmi_scan.c b/arch/i386/kernel/dmi_scan.c index 8d5a9efb3..4029a8d14 100644 --- a/arch/i386/kernel/dmi_scan.c +++ b/arch/i386/kernel/dmi_scan.c @@ -12,6 +12,8 @@ struct dmi_header u16 handle; }; +#define dmi_printk(x) + static char * __init dmi_string(struct dmi_header *dm, u8 s) { u8 *bp=(u8 *)dm; @@ -73,13 +75,13 @@ int __init dmi_iterate(void (*decode)(struct dmi_header *)) u16 len=buf[7]<<8|buf[6]; u32 base=buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]; - printk(KERN_INFO "DMI %d.%d present.\n", - buf[14]>>4, buf[14]&0x0F); - printk(KERN_INFO "%d structures occupying %d bytes.\n", + dmi_printk((KERN_INFO "DMI %d.%d present.\n", + buf[14]>>4, buf[14]&0x0F)); + dmi_printk((KERN_INFO "%d structures occupying %d bytes.\n", buf[13]<<8|buf[12], - buf[7]<<8|buf[6]); - printk(KERN_INFO "DMI table at 0x%08X.\n", - buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8]); + buf[7]<<8|buf[6])); + dmi_printk((KERN_INFO "DMI table at 0x%08X.\n", + buf[11]<<24|buf[10]<<16|buf[9]<<8|buf[8])); if(dmi_table(base,len, num, decode)==0) return 0; } @@ -106,11 +108,11 @@ static void __init dmi_decode(struct dmi_header *dm) if(*p && *p!=' ') { - printk("BIOS Vendor: %s\n", p); - printk("BIOS Version: %s\n", - dmi_string(dm, data[5])); - printk("BIOS Release: %s\n", - dmi_string(dm, data[8])); + dmi_printk(("BIOS Vendor: %s\n", p)); + dmi_printk(("BIOS Version: %s\n", + dmi_string(dm, data[5]))); + dmi_printk(("BIOS Release: %s\n", + dmi_string(dm, data[8]))); } /* @@ -144,13 +146,13 @@ static void __init dmi_decode(struct dmi_header *dm) if(*p && *p!=' ') { - printk("System Vendor: %s.\n",p); - printk("Product Name: %s.\n", - dmi_string(dm, data[5])); - printk("Version %s.\n", - dmi_string(dm, data[6])); - printk("Serial Number %s.\n", - dmi_string(dm, data[7])); + dmi_printk(("System Vendor: %s.\n",p)); + dmi_printk(("Product Name: %s.\n", + dmi_string(dm, data[5]))); + dmi_printk(("Version %s.\n", + dmi_string(dm, data[6]))); + dmi_printk(("Serial Number %s.\n", + dmi_string(dm, data[7]))); } break; case 2: @@ -158,17 +160,17 @@ static void __init dmi_decode(struct dmi_header *dm) if(*p && *p!=' ') { - printk("Board Vendor: %s.\n",p); - printk("Board Name: %s.\n", - dmi_string(dm, data[5])); - printk("Board Version: %s.\n", - dmi_string(dm, data[6])); + dmi_printk(("Board Vendor: %s.\n",p)); + dmi_printk(("Board Name: %s.\n", + dmi_string(dm, data[5]))); + dmi_printk(("Board Version: %s.\n", + dmi_string(dm, data[6]))); } break; case 3: p=dmi_string(dm,data[8]); if(*p && *p!=' ') - printk("Asset Tag: %s.\n", p); + dmi_printk(("Asset Tag: %s.\n", p)); break; } } diff --git a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S index c74b80135..400216f37 100644 --- a/arch/i386/kernel/head.S +++ b/arch/i386/kernel/head.S @@ -34,7 +34,7 @@ #define X86_HARD_MATH CPU_PARAMS+6 #define X86_CPUID CPU_PARAMS+8 #define X86_CAPABILITY CPU_PARAMS+12 -#define X86_VENDOR_ID CPU_PARAMS+16 +#define X86_VENDOR_ID CPU_PARAMS+28 /* * swapper_pg_dir is the main page directory, address 0x00101000 diff --git a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c index 071b3991a..20bc14fc6 100644 --- a/arch/i386/kernel/i386_ksyms.c +++ b/arch/i386/kernel/i386_ksyms.c @@ -13,6 +13,7 @@ #include <linux/apm_bios.h> #include <linux/kernel.h> #include <linux/string.h> +#include <linux/tty.h> #include <asm/semaphore.h> #include <asm/processor.h> diff --git a/arch/i386/kernel/i387.c b/arch/i386/kernel/i387.c index a3a526b4b..ba97ca4bc 100644 --- a/arch/i386/kernel/i387.c +++ b/arch/i386/kernel/i387.c @@ -216,7 +216,7 @@ void set_fpu_twd( struct task_struct *tsk, unsigned short twd ) void set_fpu_mxcsr( struct task_struct *tsk, unsigned short mxcsr ) { if ( cpu_has_xmm ) { - tsk->thread.i387.fxsave.mxcsr = mxcsr; + tsk->thread.i387.fxsave.mxcsr = (mxcsr & 0xffbf); } } @@ -354,6 +354,8 @@ static inline int restore_i387_fxsave( struct _fpstate *buf ) if ( __copy_from_user( &tsk->thread.i387.fxsave, &buf->_fxsr_env[0], sizeof(struct i387_fxsave_struct) ) ) return 1; + /* mxcsr bit 6 and 31-16 must be zero for security reasons */ + tsk->thread.i387.fxsave.mxcsr &= 0xffbf; return convert_fxsr_from_user( &tsk->thread.i387.fxsave, buf ); } diff --git a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c index 570614282..ce8bc8737 100644 --- a/arch/i386/kernel/i8259.c +++ b/arch/i386/kernel/i8259.c @@ -6,7 +6,7 @@ #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/smp_lock.h> #include <linux/init.h> diff --git a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c index b17f499b4..e7fe3957b 100644 --- a/arch/i386/kernel/io_apic.c +++ b/arch/i386/kernel/io_apic.c @@ -1509,15 +1509,16 @@ static inline void check_timer(void) * - IRQ2 is the cascade IRQ, and cannot be a io-apic IRQ. * Linux doesn't really care, as it's not actually used * for any interrupt handling anyway. - * - IRQ13 is the FPU error IRQ, and may be connected - * directly from the FPU to the old PIC. Linux doesn't - * really care, because Linux doesn't want to use IRQ13 - * anyway (exception 16 is the proper FPU error signal) + * - There used to be IRQ13 here as well, but all + * MPS-compliant must not use it for FPU coupling and we + * want to use exception 16 anyway. And there are + * systems who connect it to an I/O APIC for other uses. + * Thus we don't mark it special any longer. * * Additionally, something is definitely wrong with irq9 * on PIIX4 boards. */ -#define PIC_IRQS ((1<<2)|(1<<13)) +#define PIC_IRQS (1<<2) void __init setup_IO_APIC(void) { diff --git a/arch/i386/kernel/irq.c b/arch/i386/kernel/irq.c index cf6878787..2c935207a 100644 --- a/arch/i386/kernel/irq.c +++ b/arch/i386/kernel/irq.c @@ -25,7 +25,7 @@ #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/smp_lock.h> #include <linux/init.h> @@ -312,6 +312,7 @@ static inline void get_irqlock(int cpu) /* Uhhuh.. Somebody else got it. Wait.. */ do { do { + rep_nop(); } while (test_bit(0,&global_irq_lock)); } while (test_and_set_bit(0,&global_irq_lock)); } diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index f0a88c20e..0bc04116d 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -52,7 +52,7 @@ #include <linux/init.h> #include <linux/sched.h> #include <linux/module.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/miscdevice.h> #include <linux/devfs_fs_kernel.h> diff --git a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c index 60764e3cd..7d85ce240 100644 --- a/arch/i386/kernel/mtrr.c +++ b/arch/i386/kernel/mtrr.c @@ -241,7 +241,7 @@ #include <linux/kernel.h> #include <linux/wait.h> #include <linux/string.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/ioport.h> #include <linux/delay.h> #include <linux/fs.h> diff --git a/arch/i386/kernel/pci-irq.c b/arch/i386/kernel/pci-irq.c index 8281e3f82..bc1dd9c7e 100644 --- a/arch/i386/kernel/pci-irq.c +++ b/arch/i386/kernel/pci-irq.c @@ -9,7 +9,7 @@ #include <linux/kernel.h> #include <linux/pci.h> #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/irq.h> diff --git a/arch/i386/kernel/pci-pc.c b/arch/i386/kernel/pci-pc.c index 258e63627..d2a8afbbb 100644 --- a/arch/i386/kernel/pci-pc.c +++ b/arch/i386/kernel/pci-pc.c @@ -843,30 +843,40 @@ static void __init pci_fixup_i450gx(struct pci_dev *d) pcibios_last_bus = -1; } +/* + * ServerWorks host bridges -- Find and scan all secondary buses. + * Register 0x44 contains first, 0x45 last bus number routed there. + */ static void __init pci_fixup_serverworks(struct pci_dev *d) { - /* - * ServerWorks host bridges -- Find and scan all secondary buses. - * Register 0x44 contains first, 0x45 last bus number routed there. - */ - u8 busno; - pci_read_config_byte(d, 0x44, &busno); - printk("PCI: ServerWorks host bridge: secondary bus %02x\n", busno); - pci_scan_bus(busno, pci_root_ops, NULL); - pcibios_last_bus = -1; + u8 busno1, busno2; + + pci_read_config_byte(d, 0x44, &busno1); + pci_read_config_byte(d, 0x45, &busno2); + if (busno2 < busno1) + busno2 = busno1; + if (busno2 > pcibios_last_bus) { + pcibios_last_bus = busno2; + printk("PCI: ServerWorks host bridge: last bus %02x\n", pcibios_last_bus); + } } +/* + * Compaq host bridges -- Find and scan all secondary buses. + * This time registers 0xc8 and 0xc9. + */ static void __init pci_fixup_compaq(struct pci_dev *d) { - /* - * Compaq host bridges -- Find and scan all secondary buses. - * This time registers 0xc8 and 0xc9. - */ - u8 busno; - pci_read_config_byte(d, 0xc8, &busno); - printk("PCI: Compaq host bridge: secondary bus %02x\n", busno); - pci_scan_bus(busno, pci_root_ops, NULL); - pcibios_last_bus = -1; + u8 busno1, busno2; + + pci_read_config_byte(d, 0xc8, &busno1); + pci_read_config_byte(d, 0xc9, &busno2); + if (busno2 < busno1) + busno2 = busno1; + if (busno2 > pcibios_last_bus) { + pcibios_last_bus = busno2; + printk("PCI: Compaq host bridge: last bus %02x\n", busno2); + } } static void __init pci_fixup_umc_ide(struct pci_dev *d) @@ -924,6 +934,22 @@ static void __init pci_fixup_latency(struct pci_dev *d) pcibios_max_latency = 32; } +static void __init pci_fixup_vt8363(struct pci_dev *d) +{ + /* + * VIA VT8363 host bridge has broken feature 'PCI Master Read + * Caching'. It caches more than is good for it, sometimes + * serving the bus master with stale data. Some BIOSes enable + * it by default, so we disable it. + */ + u8 tmp; + pci_read_config_byte(d, 0x70, &tmp); + if(tmp & 4) { + printk("PCI: Bus master read caching disabled\n"); + pci_write_config_byte(d, 0x70, tmp & ~4); + } +} + struct pci_fixup pcibios_fixups[] = { { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX, pci_fixup_i450nx }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx }, @@ -936,6 +962,7 @@ struct pci_fixup pcibios_fixups[] = { { PCI_FIXUP_HEADER, PCI_ANY_ID, PCI_ANY_ID, pci_fixup_ide_bases }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, pci_fixup_latency }, { PCI_FIXUP_HEADER, PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5598, pci_fixup_latency }, + { PCI_FIXUP_HEADER, PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8363_0, pci_fixup_vt8363 }, { 0 } }; diff --git a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c index 35f11f20f..65f517966 100644 --- a/arch/i386/kernel/process.c +++ b/arch/i386/kernel/process.c @@ -23,7 +23,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/user.h> #include <linux/a.out.h> diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c index 2c3f61f7f..03eeabc2f 100644 --- a/arch/i386/kernel/setup.c +++ b/arch/i386/kernel/setup.c @@ -71,7 +71,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> @@ -1104,6 +1104,9 @@ static inline void do_cyrix_devid(unsigned char *dir0, unsigned char *dir1) /* * Cx86_dir0_msb is a HACK needed by check_cx686_cpuid/slop in bugs.h in * order to identify the Cyrix CPU model after we're out of setup.c + * + * Actually since bugs.h doesnt even reference this perhaps someone should + * fix the documentation ??? */ unsigned char Cx86_dir0_msb __initdata = 0; @@ -1129,6 +1132,8 @@ static char cyrix_model_mult2[] __initdata = "12233445"; * Reset the slow-loop (SLOP) bit on the 686(L) which is set by some old * BIOSes for compatability with DOS games. This makes the udelay loop * work correctly, and improves performance. + * + * FIXME: our newer udelay uses the tsc. We dont need to frob with SLOP */ extern void calibrate_delay(void) __init; @@ -1401,6 +1406,7 @@ static void __init init_centaur(struct cpuinfo_x86 *c) wrmsr (0x1107, lo, hi); set_bit(X86_FEATURE_CX8, &c->x86_capability); + set_bit(X86_FEATURE_3DNOW, &c->x86_capability); get_model_name(c); display_cacheinfo(c); @@ -1565,12 +1571,10 @@ static void __init init_intel(struct cpuinfo_x86 *c) case 4: if ( c->x86 > 6 && dl ) { /* P4 family */ - if ( dl ) { - /* L3 cache */ - cs = 128 << (dl-1); - l3 += cs; - break; - } + /* L3 cache */ + cs = 128 << (dl-1); + l3 += cs; + break; } /* else same as 8 - fall through */ case 8: @@ -1870,8 +1874,34 @@ static int __init id_and_try_enable_cpuid(struct cpuinfo_x86 *c) /* Detect Cyrix with disabled CPUID */ if ( c->x86 == 4 && test_cyrix_52div() ) { + unsigned char dir0, dir1; + strcpy(c->x86_vendor_id, "CyrixInstead"); c->x86_vendor = X86_VENDOR_CYRIX; + + /* Actually enable cpuid on the older cyrix */ + + /* Retrieve CPU revisions */ + + do_cyrix_devid(&dir0, &dir1); + + dir0>>=4; + + /* Check it is an affected model */ + + if (dir0 == 5 || dir0 == 3) + { + unsigned char ccr3, ccr4; + + printk(KERN_INFO "Enabling CPUID on Cyrix processor.\n"); + cli(); + ccr3 = getCx86(CX86_CCR3); + setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ + ccr4 = getCx86(CX86_CCR4); + setCx86(CX86_CCR4, ccr4 | 0x80); /* enable cpuid */ + setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ + sti(); + } } else /* Detect NexGen with old hypercode */ @@ -1914,7 +1944,6 @@ void __init identify_cpu(struct cpuinfo_x86 *c) (int *)&c->x86_vendor_id[4]); get_cpu_vendor(c); - /* Initialize the standard set of capabilities */ /* Note that the vendor-specific code below might override */ diff --git a/arch/i386/kernel/signal.c b/arch/i386/kernel/signal.c index 7df9c875c..7b096279f 100644 --- a/arch/i386/kernel/signal.c +++ b/arch/i386/kernel/signal.c @@ -18,6 +18,7 @@ #include <linux/ptrace.h> #include <linux/unistd.h> #include <linux/stddef.h> +#include <linux/tty.h> #include <asm/ucontext.h> #include <asm/uaccess.h> #include <asm/i387.h> diff --git a/arch/i386/kernel/smp.c b/arch/i386/kernel/smp.c index 6607edf8a..6d6463542 100644 --- a/arch/i386/kernel/smp.c +++ b/arch/i386/kernel/smp.c @@ -4,7 +4,7 @@ * (c) 1995 Alan Cox, Building #3 <alan@redhat.com> * (c) 1998-99, 2000 Ingo Molnar <mingo@redhat.com> * - * This code is released under the GNU public license version 2 or + * This code is released under the GNU General Public License version 2 or * later. */ diff --git a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c index aab31e839..a16965ac5 100644 --- a/arch/i386/kernel/smpboot.c +++ b/arch/i386/kernel/smpboot.c @@ -11,7 +11,7 @@ * Pentium Pro and Pentium-II/Xeon MP machines. * Original development of Linux SMP code supported by Caldera. * - * This code is released under the GNU public license version 2 or + * This code is released under the GNU General Public License version 2 or * later. * * Fixes @@ -389,6 +389,7 @@ void __init smp_callin(void) */ if (test_bit(cpuid, &cpu_callout_map)) break; + rep_nop(); } if (!time_before(jiffies, timeout)) { diff --git a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c index 7c02813af..6fa43476a 100644 --- a/arch/i386/kernel/traps.c +++ b/arch/i386/kernel/traps.c @@ -127,6 +127,11 @@ void show_trace(unsigned long * stack) printk("\n"); } +void show_trace_task(struct task_struct *tsk) +{ + show_trace(&tsk->thread.esp); +} + void show_stack(unsigned long * esp) { unsigned long *stack; diff --git a/arch/i386/kernel/visws_apic.c b/arch/i386/kernel/visws_apic.c index 017b8eb58..995d0cdcd 100644 --- a/arch/i386/kernel/visws_apic.c +++ b/arch/i386/kernel/visws_apic.c @@ -20,7 +20,7 @@ #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/smp.h> #include <linux/smp_lock.h> diff --git a/arch/i386/mm/extable.c b/arch/i386/mm/extable.c index ba34c0395..2eb7ca6aa 100644 --- a/arch/i386/mm/extable.c +++ b/arch/i386/mm/extable.c @@ -4,6 +4,7 @@ #include <linux/config.h> #include <linux/module.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> extern const struct exception_table_entry __start___ex_table[]; @@ -30,26 +31,32 @@ search_one_table(const struct exception_table_entry *first, return 0; } +extern spinlock_t modlist_lock; + unsigned long search_exception_table(unsigned long addr) { - unsigned long ret; - + unsigned long ret = 0; + unsigned long flags; + #ifndef CONFIG_MODULES /* There is only the kernel to search. */ ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); - if (ret) return ret; + return ret; #else /* The kernel is the last "module" -- no need to treat it special. */ struct module *mp; + + spin_lock_irqsave(&modlist_lock, flags); for (mp = module_list; mp != NULL; mp = mp->next) { if (mp->ex_table_start == NULL) continue; ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, addr); - if (ret) return ret; + if (ret) + break; } + spin_unlock_irqrestore(&modlist_lock, flags); + return ret; #endif - - return 0; } diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index e2a9ee9fe..ccbc5dce1 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c @@ -81,7 +81,7 @@ extern spinlock_t console_lock, timerlist_lock; /* * Unlock any spinlocks which will prevent us from getting the - * message out (timerlist_lock is aquired through the + * message out (timerlist_lock is acquired through the * console unblank code) */ void bust_spinlocks(void) diff --git a/arch/ia64/config.in b/arch/ia64/config.in index ae49891c5..45a0793a6 100644 --- a/arch/ia64/config.in +++ b/arch/ia64/config.in @@ -70,7 +70,7 @@ if [ "$CONFIG_IA64_SGI_SN1" = "y" ]; then if [ "$CONFIG_ITANIUM_BSTEP_SPECIFIC" = "y" ]; then bool ' Enable Itanium B0-step specific code' CONFIG_ITANIUM_B0_SPECIFIC fi - bool ' Enable SGI Medusa Simulator Support' CONFIG_IA64_SGI_SN1_SIM n + bool ' Enable SGI Medusa Simulator Support' CONFIG_IA64_SGI_SN1_SIM define_bool CONFIG_DEVFS_DEBUG y define_bool CONFIG_DEVFS_FS y define_bool CONFIG_IA64_BRL_EMU y @@ -79,8 +79,8 @@ if [ "$CONFIG_IA64_SGI_SN1" = "y" ]; then define_bool CONFIG_SGI_IOC3_ETH y define_bool CONFIG_PERCPU_IRQ y define_int CONFIG_CACHE_LINE_SHIFT 7 - bool ' Enable DISCONTIGMEM support' CONFIG_DISCONTIGMEM y - bool ' Enable NUMA support' CONFIG_NUMA y + bool ' Enable DISCONTIGMEM support' CONFIG_DISCONTIGMEM + bool ' Enable NUMA support' CONFIG_NUMA fi define_bool CONFIG_KCORE_ELF y # On IA-64, we always want an ELF /proc/kcore. diff --git a/arch/ia64/ia32/sys_ia32.c b/arch/ia64/ia32/sys_ia32.c index 25007aa43..555c6097e 100644 --- a/arch/ia64/ia32/sys_ia32.c +++ b/arch/ia64/ia32/sys_ia32.c @@ -31,7 +31,7 @@ #include <linux/msg.h> #include <linux/mm.h> #include <linux/shm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/uio.h> #include <linux/nfs_fs.h> #include <linux/smb_fs.h> diff --git a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c index ab8961a54..cf2cbbb45 100644 --- a/arch/ia64/kernel/irq.c +++ b/arch/ia64/kernel/irq.c @@ -25,7 +25,7 @@ #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/smp_lock.h> #include <linux/init.h> diff --git a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c index 155ee66b7..e4b8e8980 100644 --- a/arch/ia64/kernel/irq_ia64.c +++ b/arch/ia64/kernel/irq_ia64.c @@ -20,7 +20,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/kernel_stat.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/ptrace.h> #include <linux/random.h> /* for rand_initialize_irq() */ #include <linux/signal.h> diff --git a/arch/ia64/kernel/palinfo.c b/arch/ia64/kernel/palinfo.c index de71a2d22..a48ad1120 100644 --- a/arch/ia64/kernel/palinfo.c +++ b/arch/ia64/kernel/palinfo.c @@ -23,9 +23,6 @@ #include <linux/proc_fs.h> #include <linux/mm.h> #include <linux/module.h> -#if defined(MODVERSIONS) -#include <linux/modversions.h> -#endif #include <asm/pal.h> #include <asm/sal.h> diff --git a/arch/ia64/kernel/pci.c b/arch/ia64/kernel/pci.c index 37dbf811a..91e11dc51 100644 --- a/arch/ia64/kernel/pci.c +++ b/arch/ia64/kernel/pci.c @@ -10,7 +10,7 @@ #include <linux/pci.h> #include <linux/init.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/spinlock.h> diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c index 0b49bdcaa..71827cd69 100644 --- a/arch/ia64/kernel/ptrace.c +++ b/arch/ia64/kernel/ptrace.c @@ -447,7 +447,7 @@ ia64_poke (struct pt_regs *regs, struct task_struct *child, unsigned long addr, * When new_bsp is zero and force_loadrs_to_zero is 1 (non-zero), * loadrs is set to 0, and the bspstore value is set to the old bsp * value. This will cause the stacked registers (r32 and up) to be - * obtained entirely from the the child's memory space rather than + * obtained entirely from the child's memory space rather than * from the kernel. (This makes it easier to write code for * modifying the stacked registers in multi-threaded programs.) * diff --git a/arch/ia64/kernel/unwind.c b/arch/ia64/kernel/unwind.c index f5ae7e497..7642df0c6 100644 --- a/arch/ia64/kernel/unwind.c +++ b/arch/ia64/kernel/unwind.c @@ -5,7 +5,7 @@ /* * This file implements call frame unwind support for the Linux * kernel. Parsing and processing the unwind information is - * time-consuming, so this implementation translates the the unwind + * time-consuming, so this implementation translates the unwind * descriptors into unwind scripts. These scripts are very simple * (basically a sequence of assignments) and efficient to execute. * They are cached for later re-use. Each script is specific for a diff --git a/arch/ia64/lib/copy_user.S b/arch/ia64/lib/copy_user.S index cabbf6653..ec9e2bf0a 100644 --- a/arch/ia64/lib/copy_user.S +++ b/arch/ia64/lib/copy_user.S @@ -518,7 +518,7 @@ failure_in_pipe2: // Other key point: // - if you fail on the ld8 in the head, it means you went straight // to it, i.e. 8byte alignment within an unexisting page. - // Again this comes from the fact that if you crossed just for the the ld8 then + // Again this comes from the fact that if you crossed just for the ld8 then // you are 8byte aligned but also 16byte align, therefore you would // either go for the 16byte copy loop OR the ld8 in the tail part. // The combination ld1, ld2, ld4, ld8 where you fail on ld8 is impossible diff --git a/arch/ia64/sn/io/klconflib.c b/arch/ia64/sn/io/klconflib.c index 6fd745a0b..a156da166 100644 --- a/arch/ia64/sn/io/klconflib.c +++ b/arch/ia64/sn/io/klconflib.c @@ -511,7 +511,7 @@ find_gfxpipe(int pipenum) moduleid_t kgm, pkgm; int kgs, pkgs; -#if defined(DEBUG) && (defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1 || defined(CONFIG_IA64_GENERIC))) && defined(BRINGUP) +#if defined(DEBUG) && (defined(CONFIG_SGI_IP35) || defined(CONFIG_IA64_SGI_SN1) || defined(CONFIG_IA64_GENERIC)) && defined(BRINGUP) printf("find_gfxpipe(): PIPE: %s mod %M slot %d\n",lb?lb->brd_name:"!LBRD", lb->brd_module,lb->brd_slot); #endif diff --git a/arch/ia64/sn/io/klgraph_hack.c b/arch/ia64/sn/io/klgraph_hack.c index cc9d77871..71bf25bc4 100644 --- a/arch/ia64/sn/io/klgraph_hack.c +++ b/arch/ia64/sn/io/klgraph_hack.c @@ -139,7 +139,7 @@ klgraph_hack_init(void) uint64_t *tmp; volatile u32 *tmp32; -#ifdef 0 +#if 0 /* Preset some values */ /* Write IOERR clear to clear the CRAZY bit in the status */ tmp = (uint64_t *)0xc0000a0001c001f8; *tmp = (uint64_t)0xffffffff; diff --git a/arch/ia64/sn/io/l1.c b/arch/ia64/sn/io/l1.c index b8c5af674..6f4bf9f47 100644 --- a/arch/ia64/sn/io/l1.c +++ b/arch/ia64/sn/io/l1.c @@ -1462,7 +1462,7 @@ brl1_intrd( struct eframe_s *ep ) isr_reg = READ_L1_UART_REG(sc->nasid, REG_ISR); } - /* uart interrupts were blocked at bedrock when the the interrupt + /* uart interrupts were blocked at bedrock when the interrupt * was initially answered; reenable them now */ intr_unblock_bit( sc->intr_cpu, UART_INTR ); @@ -2865,7 +2865,7 @@ _elscuart_readc( l1sc_t *sc ) /* - * _elscuart_flush flushes queued output to the the L1. + * _elscuart_flush flushes queued output to the L1. * This routine blocks until the queue is flushed. */ int diff --git a/arch/ia64/sn/io/pcibr.c b/arch/ia64/sn/io/pcibr.c index e5279fefd..9c71b8569 100644 --- a/arch/ia64/sn/io/pcibr.c +++ b/arch/ia64/sn/io/pcibr.c @@ -3753,7 +3753,7 @@ pcibr_attach(devfs_handle_t xconn_vhdl) * completion interrupts will reach a CPU * after all DMA data has reached memory. * (Of course, there may be a few special - * drivers/controlers that explicitly manage + * drivers/controllers that explicitly manage * this ordering problem.) */ @@ -8609,7 +8609,7 @@ pcibr_dmard_error( BRIDGE_ERRUPPR_ADDRMASK) << 32))); /* - * need to ensure that the xtalk adress in ioe + * need to ensure that the xtalk address in ioe * maps to PCI error address read from bridge. * How to convert PCI address back to Xtalk address ? * (better idea: convert XTalk address to PCI address @@ -9108,7 +9108,7 @@ pcibr_device_flags_set(devfs_handle_t pconn_vhdl, #ifdef LITTLE_ENDIAN /* - * on sn-ia we need to twiddle the the addresses going out + * on sn-ia we need to twiddle the addresses going out * the pci bus because we use the unswizzled synergy space * (the alternative is to use the swizzled synergy space * and byte swap the data) diff --git a/arch/ia64/sn/io/xbow.c b/arch/ia64/sn/io/xbow.c index 904cf732c..5becaa918 100644 --- a/arch/ia64/sn/io/xbow.c +++ b/arch/ia64/sn/io/xbow.c @@ -314,7 +314,7 @@ xbow_attach(devfs_handle_t conn) /* * get the name of this xbow vertex and keep the info. - * This is needed during errors and interupts, but as + * This is needed during errors and interrupts, but as * long as we have it, we can use it elsewhere. */ s = dev_to_name(vhdl, devnm, MAXDEVNAME); diff --git a/arch/ia64/sn/io/xtalk.c b/arch/ia64/sn/io/xtalk.c index 22810d54c..fedcffe22 100644 --- a/arch/ia64/sn/io/xtalk.c +++ b/arch/ia64/sn/io/xtalk.c @@ -959,7 +959,7 @@ xwidget_register(xwidget_hwid_t hwid, /* widget's hardware ID */ widget_info->w_einfo = 0; /* * get the name of this xwidget vertex and keep the info. - * This is needed during errors and interupts, but as + * This is needed during errors and interrupts, but as * long as we have it, we can use it elsewhere. */ s = dev_to_name(widget,devnm,MAXDEVNAME); diff --git a/arch/m68k/atari/hades-pci.c b/arch/m68k/atari/hades-pci.c index 2c82e4a7b..6d8ec7333 100644 --- a/arch/m68k/atari/hades-pci.c +++ b/arch/m68k/atari/hades-pci.c @@ -17,7 +17,7 @@ #if defined(CONFIG_PCI) && defined(CONFIG_HADES) -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mm.h> #include <linux/pci.h> @@ -372,6 +372,8 @@ struct pci_bus_info * __init init_hades_pci(void) */ bus = kmalloc(sizeof(struct pci_bus_info), GFP_KERNEL); + if (!bus) + return NULL; memset(bus, 0, sizeof(struct pci_bus_info)); /* diff --git a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c index b4a797f2e..b7b7b9553 100644 --- a/arch/m68k/atari/stram.c +++ b/arch/m68k/atari/stram.c @@ -16,7 +16,7 @@ #include <linux/major.h> #include <linux/init.h> #include <linux/swap.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> #include <linux/shm.h> diff --git a/arch/m68k/bvme6000/rtc.c b/arch/m68k/bvme6000/rtc.c index 263cdaff7..23f9a9f1a 100644 --- a/arch/m68k/bvme6000/rtc.c +++ b/arch/m68k/bvme6000/rtc.c @@ -9,7 +9,7 @@ #include <linux/types.h> #include <linux/errno.h> #include <linux/miscdevice.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/ioport.h> #include <linux/fcntl.h> #include <linux/init.h> diff --git a/arch/m68k/ifpsp060/src/fplsp.S b/arch/m68k/ifpsp060/src/fplsp.S index fc4939b17..903e4d544 100644 --- a/arch/m68k/ifpsp060/src/fplsp.S +++ b/arch/m68k/ifpsp060/src/fplsp.S @@ -9963,7 +9963,7 @@ mns_tiny: # ALGORITHM *********************************************************** # # An underflow should occur as the result of transcendental # # emulation in the 060FPLSP. Create an underflow by using "fmul" # -# and two very small numbers of appropriate sign so the the operating # +# and two very small numbers of appropriate sign so the operating # # system can log the event. # # # ######################################################################### @@ -10020,7 +10020,7 @@ unf_pos: # ALGORITHM *********************************************************** # # An overflow should occur as the result of transcendental # # emulation in the 060FPLSP. Create an overflow by using "fmul" # -# and two very lareg numbers of appropriate sign so the the operating # +# and two very lareg numbers of appropriate sign so the operating # # system can log the event. # # For t_ovfl_sc() we take special care not to lose the INEX2 bit. # # # diff --git a/arch/m68k/ifpsp060/src/fpsp.S b/arch/m68k/ifpsp060/src/fpsp.S index 1099d5e53..400d556c1 100644 --- a/arch/m68k/ifpsp060/src/fpsp.S +++ b/arch/m68k/ifpsp060/src/fpsp.S @@ -2944,7 +2944,7 @@ iea_fmovm_trace: # The FPU is disabled and so we should really have taken the "Line # F Emulator" exception. So, here we create an 8-word stack frame # from our 4-word stack frame. This means we must calculate the length -# the the faulting instruction to get the "next PC". This is trivial for +# the faulting instruction to get the "next PC". This is trivial for # immediate operands but requires some extra work for fmovm dynamic # which can use most addressing modes. iea_disabled: diff --git a/arch/m68k/ifpsp060/src/isp.S b/arch/m68k/ifpsp060/src/isp.S index 0fa431ab9..f779e2c8f 100644 --- a/arch/m68k/ifpsp060/src/isp.S +++ b/arch/m68k/ifpsp060/src/isp.S @@ -2516,7 +2516,7 @@ _mul64: cmpi.b %d0, &0x7 # is src mode Dn or other? bgt.w mul64_memop # src is in memory -# multiplier operand in the the data register file. +# multiplier operand in the data register file. # must extract the register number and fetch the operand from the stack. mul64_regop: andi.w &0x7, %d0 # extract Dn diff --git a/arch/m68k/ifpsp060/src/pfpsp.S b/arch/m68k/ifpsp060/src/pfpsp.S index ec749785a..d175f7af0 100644 --- a/arch/m68k/ifpsp060/src/pfpsp.S +++ b/arch/m68k/ifpsp060/src/pfpsp.S @@ -2943,7 +2943,7 @@ iea_fmovm_trace: # The FPU is disabled and so we should really have taken the "Line # F Emulator" exception. So, here we create an 8-word stack frame # from our 4-word stack frame. This means we must calculate the length -# the the faulting instruction to get the "next PC". This is trivial for +# the faulting instruction to get the "next PC". This is trivial for # immediate operands but requires some extra work for fmovm dynamic # which can use most addressing modes. iea_disabled: diff --git a/arch/m68k/kernel/bios32.c b/arch/m68k/kernel/bios32.c index c3b5dacba..d9cf9d9b8 100644 --- a/arch/m68k/kernel/bios32.c +++ b/arch/m68k/kernel/bios32.c @@ -26,7 +26,7 @@ */ #include <linux/pci.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mm.h> #include <asm/io.h> diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 3e62e6fe1..fcb7b915c 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S @@ -140,7 +140,7 @@ SYMBOL_NAME_LABEL(ret_from_exception) bnes 2f | if so, skip resched, signals | only allow interrupts when we are really the last one on the | kernel stack, otherwise stack overflow can occur during - | heavy interupt load + | heavy interrupt load andw #ALLOWINT,%sr tstl %curptr@(TASK_NEEDRESCHED) jne SYMBOL_NAME(reschedule) diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index 6934e4168..7fd40aaa4 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c @@ -20,7 +20,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/reboot.h> diff --git a/arch/m68k/mac/misc.c b/arch/m68k/mac/misc.c index d6065716a..afc769d42 100644 --- a/arch/m68k/mac/misc.c +++ b/arch/m68k/mac/misc.c @@ -10,7 +10,7 @@ #include <linux/kernel.h> #include <linux/delay.h> #include <linux/sched.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/time.h> #include <linux/kd.h> #include <linux/mm.h> diff --git a/arch/m68k/mac/oss.c b/arch/m68k/mac/oss.c index 87d155ee0..f39171648 100644 --- a/arch/m68k/mac/oss.c +++ b/arch/m68k/mac/oss.c @@ -123,7 +123,7 @@ void oss_irq(int irq, void *dev_id, struct pt_regs *regs) /* * Nubus IRQ handler, OSS style * - * Unlike the VIA/RBV this is on its own autovector interupt level. + * Unlike the VIA/RBV this is on its own autovector interrupt level. */ void oss_nubus_irq(int irq, void *dev_id, struct pt_regs *regs) diff --git a/arch/m68k/math-emu/fp_cond.S b/arch/m68k/math-emu/fp_cond.S index d9981d6a8..db53cbe54 100644 --- a/arch/m68k/math-emu/fp_cond.S +++ b/arch/m68k/math-emu/fp_cond.S @@ -17,7 +17,7 @@ * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of - * the GNU Public License, in which case the provisions of the GPL are + * the GNU General Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) diff --git a/arch/m68k/math-emu/fp_decode.h b/arch/m68k/math-emu/fp_decode.h index 233269415..259785f34 100644 --- a/arch/m68k/math-emu/fp_decode.h +++ b/arch/m68k/math-emu/fp_decode.h @@ -17,7 +17,7 @@ * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of - * the GNU Public License, in which case the provisions of the GPL are + * the GNU General Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) diff --git a/arch/m68k/math-emu/fp_emu.h b/arch/m68k/math-emu/fp_emu.h index 1a4e16802..bf2d88b29 100644 --- a/arch/m68k/math-emu/fp_emu.h +++ b/arch/m68k/math-emu/fp_emu.h @@ -17,7 +17,7 @@ * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of - * the GNU Public License, in which case the provisions of the GPL are + * the GNU General Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) diff --git a/arch/m68k/math-emu/fp_entry.S b/arch/m68k/math-emu/fp_entry.S index 55088e8e8..d270b9cb0 100644 --- a/arch/m68k/math-emu/fp_entry.S +++ b/arch/m68k/math-emu/fp_entry.S @@ -17,7 +17,7 @@ * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of - * the GNU Public License, in which case the provisions of the GPL are + * the GNU General Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) diff --git a/arch/m68k/math-emu/fp_move.S b/arch/m68k/math-emu/fp_move.S index 45fb02bfe..71bdf83ba 100644 --- a/arch/m68k/math-emu/fp_move.S +++ b/arch/m68k/math-emu/fp_move.S @@ -17,7 +17,7 @@ * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of - * the GNU Public License, in which case the provisions of the GPL are + * the GNU General Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) diff --git a/arch/m68k/math-emu/fp_movem.S b/arch/m68k/math-emu/fp_movem.S index 01058b33b..8354d39e6 100644 --- a/arch/m68k/math-emu/fp_movem.S +++ b/arch/m68k/math-emu/fp_movem.S @@ -17,7 +17,7 @@ * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of - * the GNU Public License, in which case the provisions of the GPL are + * the GNU General Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) diff --git a/arch/m68k/math-emu/fp_scan.S b/arch/m68k/math-emu/fp_scan.S index 4f404914c..97b73c1de 100644 --- a/arch/m68k/math-emu/fp_scan.S +++ b/arch/m68k/math-emu/fp_scan.S @@ -17,7 +17,7 @@ * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of - * the GNU Public License, in which case the provisions of the GPL are + * the GNU General Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) diff --git a/arch/m68k/math-emu/fp_util.S b/arch/m68k/math-emu/fp_util.S index a909d813b..12e1e7863 100644 --- a/arch/m68k/math-emu/fp_util.S +++ b/arch/m68k/math-emu/fp_util.S @@ -17,7 +17,7 @@ * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of - * the GNU Public License, in which case the provisions of the GPL are + * the GNU General Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) diff --git a/arch/m68k/mm/kmap.c b/arch/m68k/mm/kmap.c index 55a455423..da23f36ec 100644 --- a/arch/m68k/mm/kmap.c +++ b/arch/m68k/mm/kmap.c @@ -12,7 +12,7 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/types.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <asm/setup.h> diff --git a/arch/m68k/mm/memory.c b/arch/m68k/mm/memory.c index a3c4f38c5..164c5cc72 100644 --- a/arch/m68k/mm/memory.c +++ b/arch/m68k/mm/memory.c @@ -9,7 +9,7 @@ #include <linux/kernel.h> #include <linux/string.h> #include <linux/types.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/pagemap.h> diff --git a/arch/m68k/mvme16x/rtc.c b/arch/m68k/mvme16x/rtc.c index 677621629..0c118ceb0 100644 --- a/arch/m68k/mvme16x/rtc.c +++ b/arch/m68k/mvme16x/rtc.c @@ -9,7 +9,7 @@ #include <linux/types.h> #include <linux/errno.h> #include <linux/miscdevice.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/ioport.h> #include <linux/fcntl.h> #include <linux/init.h> diff --git a/arch/mips/baget/irq.c b/arch/mips/baget/irq.c index 2fd91c9c9..3a7e9b177 100644 --- a/arch/mips/baget/irq.c +++ b/arch/mips/baget/irq.c @@ -14,7 +14,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/delay.h> diff --git a/arch/mips/baget/vacserial.c b/arch/mips/baget/vacserial.c index 12021daf4..61e459dfe 100644 --- a/arch/mips/baget/vacserial.c +++ b/arch/mips/baget/vacserial.c @@ -56,7 +56,7 @@ #include <linux/ptrace.h> #include <linux/ioport.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/delay.h> #ifdef CONFIG_SERIAL_CONSOLE diff --git a/arch/mips/dec/irq.c b/arch/mips/dec/irq.c index e57485d25..95bdc9ca2 100644 --- a/arch/mips/dec/irq.c +++ b/arch/mips/dec/irq.c @@ -13,7 +13,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <asm/bitops.h> diff --git a/arch/mips/defconfig b/arch/mips/defconfig index 9cea1dad3..1f78df82e 100644 --- a/arch/mips/defconfig +++ b/arch/mips/defconfig @@ -323,6 +323,7 @@ CONFIG_PARTITION_ADVANCED=y # CONFIG_MAC_PARTITION is not set CONFIG_MSDOS_PARTITION=y # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_SGI_PARTITION=y diff --git a/arch/mips/defconfig-decstation b/arch/mips/defconfig-decstation index 872744d32..6da63e12a 100644 --- a/arch/mips/defconfig-decstation +++ b/arch/mips/defconfig-decstation @@ -317,6 +317,7 @@ CONFIG_OSF_PARTITION=y # CONFIG_MAC_PARTITION is not set CONFIG_MSDOS_PARTITION=y # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set # CONFIG_SGI_PARTITION is not set diff --git a/arch/mips/defconfig-ip22 b/arch/mips/defconfig-ip22 index 9cea1dad3..1f78df82e 100644 --- a/arch/mips/defconfig-ip22 +++ b/arch/mips/defconfig-ip22 @@ -323,6 +323,7 @@ CONFIG_PARTITION_ADVANCED=y # CONFIG_MAC_PARTITION is not set CONFIG_MSDOS_PARTITION=y # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_SGI_PARTITION=y diff --git a/arch/mips/defconfig-rm200 b/arch/mips/defconfig-rm200 index a9c2aee2f..d5196eb35 100644 --- a/arch/mips/defconfig-rm200 +++ b/arch/mips/defconfig-rm200 @@ -340,6 +340,7 @@ CONFIG_PARTITION_ADVANCED=y # CONFIG_MAC_PARTITION is not set CONFIG_MSDOS_PARTITION=y # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_SGI_PARTITION=y diff --git a/arch/mips/galileo-boards/ev64120/irq.c b/arch/mips/galileo-boards/ev64120/irq.c index 44b0bf092..ff2d0c25f 100644 --- a/arch/mips/galileo-boards/ev64120/irq.c +++ b/arch/mips/galileo-boards/ev64120/irq.c @@ -41,7 +41,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/irq.h> #include <asm/bitops.h> diff --git a/arch/mips/galileo-boards/ev64120/pci_bios.c b/arch/mips/galileo-boards/ev64120/pci_bios.c index 75d2b4c8b..8500c4c0a 100644 --- a/arch/mips/galileo-boards/ev64120/pci_bios.c +++ b/arch/mips/galileo-boards/ev64120/pci_bios.c @@ -36,7 +36,7 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/version.h> #include <asm/pci.h> #include <asm/io.h> diff --git a/arch/mips/galileo-boards/ev96100/irq.c b/arch/mips/galileo-boards/ev96100/irq.c index 3cff158fa..7a7b7d9de 100644 --- a/arch/mips/galileo-boards/ev96100/irq.c +++ b/arch/mips/galileo-boards/ev96100/irq.c @@ -43,7 +43,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <asm/bitops.h> diff --git a/arch/mips/gt64120/common/irq.c b/arch/mips/gt64120/common/irq.c index c0fdcc44a..5192dfa1e 100644 --- a/arch/mips/gt64120/common/irq.c +++ b/arch/mips/gt64120/common/irq.c @@ -41,7 +41,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/irq.h> #include <asm/bitops.h> diff --git a/arch/mips/gt64120/common/pci.c b/arch/mips/gt64120/common/pci.c index d6b647e11..e74b86bd6 100644 --- a/arch/mips/gt64120/common/pci.c +++ b/arch/mips/gt64120/common/pci.c @@ -39,7 +39,7 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/kernel.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/version.h> #include <asm/pci.h> #include <asm/io.h> diff --git a/arch/mips/gt64120/momenco_ocelot/irq.c b/arch/mips/gt64120/momenco_ocelot/irq.c index 8d832aadb..407031e6a 100644 --- a/arch/mips/gt64120/momenco_ocelot/irq.c +++ b/arch/mips/gt64120/momenco_ocelot/irq.c @@ -37,7 +37,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/irq.h> #include <asm/bitops.h> diff --git a/arch/mips/kernel/irixelf.c b/arch/mips/kernel/irixelf.c index 5f63b0910..8daf876c1 100644 --- a/arch/mips/kernel/irixelf.c +++ b/arch/mips/kernel/irixelf.c @@ -24,7 +24,7 @@ #include <linux/file.h> #include <linux/fcntl.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/shm.h> #include <linux/personality.h> #include <linux/elfcore.h> diff --git a/arch/mips/kernel/irq.c b/arch/mips/kernel/irq.c index 39b3b8ce2..1b8da2b85 100644 --- a/arch/mips/kernel/irq.c +++ b/arch/mips/kernel/irq.c @@ -13,7 +13,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel_stat.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mm.h> #include <linux/random.h> #include <linux/sched.h> diff --git a/arch/mips/kernel/old-irq.c b/arch/mips/kernel/old-irq.c index 090da659a..a13948d32 100644 --- a/arch/mips/kernel/old-irq.c +++ b/arch/mips/kernel/old-irq.c @@ -22,7 +22,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <asm/bitops.h> diff --git a/arch/mips/kernel/process.c b/arch/mips/kernel/process.c index d3ef5124f..dd2810109 100644 --- a/arch/mips/kernel/process.c +++ b/arch/mips/kernel/process.c @@ -14,7 +14,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mman.h> #include <linux/sys.h> #include <linux/user.h> diff --git a/arch/mips/kernel/setup.c b/arch/mips/kernel/setup.c index 34b1691eb..8815cbb20 100644 --- a/arch/mips/kernel/setup.c +++ b/arch/mips/kernel/setup.c @@ -20,7 +20,7 @@ #include <linux/string.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/utsname.h> #include <linux/a.out.h> diff --git a/arch/mips/kernel/sysirix.c b/arch/mips/kernel/sysirix.c index 3577d89e9..ab051858e 100644 --- a/arch/mips/kernel/sysirix.c +++ b/arch/mips/kernel/sysirix.c @@ -10,7 +10,7 @@ #include <linux/pagemap.h> #include <linux/mm.h> #include <linux/mman.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/swap.h> #include <linux/errno.h> #include <linux/timex.h> diff --git a/arch/mips/mm/extable.c b/arch/mips/mm/extable.c index bc7cb7ab9..72d4c9040 100644 --- a/arch/mips/mm/extable.c +++ b/arch/mips/mm/extable.c @@ -3,6 +3,7 @@ */ #include <linux/config.h> #include <linux/module.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> extern const struct exception_table_entry __start___ex_table[]; @@ -29,26 +30,32 @@ search_one_table(const struct exception_table_entry *first, return 0; } +extern spinlock_t modlist_lock; + unsigned long search_exception_table(unsigned long addr) { - unsigned long ret; - + unsigned long ret = 0; + unsigned long flags; + #ifndef CONFIG_MODULES /* There is only the kernel to search. */ ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); - if (ret) return ret; + return ret; #else /* The kernel is the last "module" -- no need to treat it special. */ struct module *mp; + + spin_lock_irqsave(&modlist_lock, flags); for (mp = module_list; mp != NULL; mp = mp->next) { if (mp->ex_table_start == NULL) continue; ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, addr); - if (ret) return ret; + if (ret) + break; } + spin_unlock_irqrestore(&modlist_lock, flags); + return ret; #endif - - return 0; } diff --git a/arch/mips/sgi/kernel/indy_int.c b/arch/mips/sgi/kernel/indy_int.c index ab9168cb6..dca960944 100644 --- a/arch/mips/sgi/kernel/indy_int.c +++ b/arch/mips/sgi/kernel/indy_int.c @@ -18,7 +18,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/smp.h> #include <linux/smp_lock.h> diff --git a/arch/mips64/defconfig b/arch/mips64/defconfig index 8c2747bba..a15cc5785 100644 --- a/arch/mips64/defconfig +++ b/arch/mips64/defconfig @@ -121,6 +121,11 @@ CONFIG_IP_PNP=y # CONFIG_BRIDGE is not set # +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# # Telephony Support # # CONFIG_PHONE is not set @@ -422,6 +427,7 @@ CONFIG_PARTITION_ADVANCED=y # CONFIG_MAC_PARTITION is not set CONFIG_MSDOS_PARTITION=y # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_SGI_PARTITION=y diff --git a/arch/mips64/defconfig-ip22 b/arch/mips64/defconfig-ip22 index 43b521604..2a9aab549 100644 --- a/arch/mips64/defconfig-ip22 +++ b/arch/mips64/defconfig-ip22 @@ -111,6 +111,11 @@ CONFIG_IP_PNP_BOOTP=y # CONFIG_BRIDGE is not set # +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# # Telephony Support # # CONFIG_PHONE is not set diff --git a/arch/mips64/defconfig-ip27 b/arch/mips64/defconfig-ip27 index 8c2747bba..a15cc5785 100644 --- a/arch/mips64/defconfig-ip27 +++ b/arch/mips64/defconfig-ip27 @@ -121,6 +121,11 @@ CONFIG_IP_PNP=y # CONFIG_BRIDGE is not set # +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# # Telephony Support # # CONFIG_PHONE is not set @@ -422,6 +427,7 @@ CONFIG_PARTITION_ADVANCED=y # CONFIG_MAC_PARTITION is not set CONFIG_MSDOS_PARTITION=y # CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set # CONFIG_SOLARIS_X86_PARTITION is not set # CONFIG_UNIXWARE_DISKLABEL is not set CONFIG_SGI_PARTITION=y diff --git a/arch/mips64/kernel/process.c b/arch/mips64/kernel/process.c index b45f23a36..7060ce768 100644 --- a/arch/mips64/kernel/process.c +++ b/arch/mips64/kernel/process.c @@ -13,7 +13,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mman.h> #include <linux/sys.h> #include <linux/user.h> diff --git a/arch/mips64/kernel/setup.c b/arch/mips64/kernel/setup.c index 74511142b..7c996a336 100644 --- a/arch/mips64/kernel/setup.c +++ b/arch/mips64/kernel/setup.c @@ -18,7 +18,7 @@ #include <linux/string.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/utsname.h> #include <linux/a.out.h> diff --git a/arch/mips64/mm/extable.c b/arch/mips64/mm/extable.c index f9ae91ae0..af21d4ad1 100644 --- a/arch/mips64/mm/extable.c +++ b/arch/mips64/mm/extable.c @@ -9,6 +9,7 @@ */ #include <linux/config.h> #include <linux/module.h> +#include <linux/spinlock.h> #include <asm/uaccess.h> extern const struct exception_table_entry __start___ex_table[]; @@ -35,26 +36,32 @@ search_one_table(const struct exception_table_entry *first, return 0; } +extern spinlock_t modlist_lock; + unsigned long search_exception_table(unsigned long addr) { - unsigned long ret; - + unsigned long ret = 0; + unsigned long flags; + #ifndef CONFIG_MODULES /* There is only the kernel to search. */ ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); - if (ret) return ret; + return ret; #else /* The kernel is the last "module" -- no need to treat it special. */ struct module *mp; + + spin_lock_irqsave(&modlist_lock, flags); for (mp = module_list; mp != NULL; mp = mp->next) { if (mp->ex_table_start == NULL) continue; ret = search_one_table(mp->ex_table_start, mp->ex_table_end - 1, addr); - if (ret) return ret; + if (ret) + break; } + spin_unlock_irqrestore(&modlist_lock, flags); + return ret; #endif - - return 0; } diff --git a/arch/mips64/mm/fault.c b/arch/mips64/mm/fault.c index c8f4534d4..710b6b309 100644 --- a/arch/mips64/mm/fault.c +++ b/arch/mips64/mm/fault.c @@ -61,7 +61,7 @@ extern spinlock_t console_lock, timerlist_lock; /* * Unlock any spinlocks which will prevent us from getting the - * message out (timerlist_lock is aquired through the + * message out (timerlist_lock is acquired through the * console unblank code) */ void bust_spinlocks(void) diff --git a/arch/mips64/sgi-ip22/ip22-int.c b/arch/mips64/sgi-ip22/ip22-int.c index 70a5fdf3a..774b4fc33 100644 --- a/arch/mips64/sgi-ip22/ip22-int.c +++ b/arch/mips64/sgi-ip22/ip22-int.c @@ -17,7 +17,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/smp.h> #include <linux/smp_lock.h> diff --git a/arch/mips64/sgi-ip27/ip27-irq.c b/arch/mips64/sgi-ip27/ip27-irq.c index 446e3df41..651dc7b41 100644 --- a/arch/mips64/sgi-ip27/ip27-irq.c +++ b/arch/mips64/sgi-ip27/ip27-irq.c @@ -14,7 +14,7 @@ #include <linux/interrupt.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/smp_lock.h> #include <linux/kernel_stat.h> diff --git a/arch/parisc/kernel/ccio-dma.c b/arch/parisc/kernel/ccio-dma.c index c8f72c813..1df06d59d 100644 --- a/arch/parisc/kernel/ccio-dma.c +++ b/arch/parisc/kernel/ccio-dma.c @@ -36,7 +36,7 @@ #include <linux/init.h> #include <linux/mm.h> #include <linux/spinlock.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/string.h> #include <linux/pci.h> diff --git a/arch/parisc/kernel/iosapic.c b/arch/parisc/kernel/iosapic.c index 688cbf9bb..917fa76eb 100644 --- a/arch/parisc/kernel/iosapic.c +++ b/arch/parisc/kernel/iosapic.c @@ -162,7 +162,7 @@ #include <linux/spinlock.h> #include <linux/pci.h> /* pci cfg accessor functions */ #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/smp_lock.h> #include <linux/interrupt.h> /* irqaction */ #include <linux/irq.h> /* irq_region support */ diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c index aec36f7cc..b3914a1b1 100644 --- a/arch/parisc/kernel/irq.c +++ b/arch/parisc/kernel/irq.c @@ -35,7 +35,7 @@ #include <linux/types.h> #include <linux/ioport.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/interrupt.h> #include <linux/irq.h> diff --git a/arch/parisc/kernel/lba_pci.c b/arch/parisc/kernel/lba_pci.c index 6096d0153..490b4659b 100644 --- a/arch/parisc/kernel/lba_pci.c +++ b/arch/parisc/kernel/lba_pci.c @@ -36,7 +36,7 @@ #include <linux/init.h> /* for __init and __devinit */ #include <linux/pci.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/smp_lock.h> #include <asm/byteorder.h> diff --git a/arch/parisc/kernel/pci-dma.c b/arch/parisc/kernel/pci-dma.c index 88a9ddc77..f885584f5 100644 --- a/arch/parisc/kernel/pci-dma.c +++ b/arch/parisc/kernel/pci-dma.c @@ -26,7 +26,7 @@ #include <linux/pci.h> #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <asm/uaccess.h> diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c index a8d7a707e..d699f5dab 100644 --- a/arch/parisc/kernel/process.c +++ b/arch/parisc/kernel/process.c @@ -19,7 +19,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/interrupt.h> #include <linux/reboot.h> diff --git a/arch/parisc/kernel/sba_iommu.c b/arch/parisc/kernel/sba_iommu.c index b7b541478..701bf38cf 100644 --- a/arch/parisc/kernel/sba_iommu.c +++ b/arch/parisc/kernel/sba_iommu.c @@ -23,7 +23,7 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/spinlock.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/mm.h> diff --git a/arch/parisc/kernel/setup.c b/arch/parisc/kernel/setup.c index 16a35df30..a14b5ac62 100644 --- a/arch/parisc/kernel/setup.c +++ b/arch/parisc/kernel/setup.c @@ -28,7 +28,7 @@ #include <linux/errno.h> #include <linux/kernel.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/mm.h> #include <linux/ptrace.h> #include <linux/sched.h> diff --git a/arch/parisc/mm/kmap.c b/arch/parisc/mm/kmap.c index 686d540d9..4bd5dd4b4 100644 --- a/arch/parisc/mm/kmap.c +++ b/arch/parisc/mm/kmap.c @@ -7,7 +7,7 @@ #include <linux/string.h> #include <linux/pci.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <asm/uaccess.h> diff --git a/arch/ppc/8260_io/enet.c b/arch/ppc/8260_io/enet.c index 77b0eb279..95d58d523 100644 --- a/arch/ppc/8260_io/enet.c +++ b/arch/ppc/8260_io/enet.c @@ -30,7 +30,7 @@ #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/init.h> diff --git a/arch/ppc/8260_io/fcc_enet.c b/arch/ppc/8260_io/fcc_enet.c index c8c31bd42..faf267700 100644 --- a/arch/ppc/8260_io/fcc_enet.c +++ b/arch/ppc/8260_io/fcc_enet.c @@ -23,7 +23,7 @@ #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/init.h> @@ -46,7 +46,7 @@ #define TX_TIMEOUT (2*HZ) /* The number of Tx and Rx buffers. These are allocated from the page - * pool. The code may assume these are power of two, so it it best + * pool. The code may assume these are power of two, so it is best * to keep them that size. * We don't need to allocate pages for the transmitter. We just use * the skbuffer directly. diff --git a/arch/ppc/8260_io/uart.c b/arch/ppc/8260_io/uart.c index e6e223b61..ad61c9b7f 100644 --- a/arch/ppc/8260_io/uart.c +++ b/arch/ppc/8260_io/uart.c @@ -38,7 +38,7 @@ #include <linux/fcntl.h> #include <linux/ptrace.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> diff --git a/arch/ppc/8xx_io/commproc.h b/arch/ppc/8xx_io/commproc.h index 7faae4ca3..a8194cb21 100644 --- a/arch/ppc/8xx_io/commproc.h +++ b/arch/ppc/8xx_io/commproc.h @@ -76,7 +76,7 @@ typedef struct cpm_buf_desc { uint cbd_bufaddr; /* Buffer address in host memory */ } cbd_t; -#define BD_SC_EMPTY ((ushort)0x8000) /* Recieve is empty */ +#define BD_SC_EMPTY ((ushort)0x8000) /* Receive is empty */ #define BD_SC_READY ((ushort)0x8000) /* Transmit is ready */ #define BD_SC_WRAP ((ushort)0x2000) /* Last buffer descriptor */ #define BD_SC_INTRPT ((ushort)0x1000) /* Interrupt on change */ diff --git a/arch/ppc/8xx_io/enet.c b/arch/ppc/8xx_io/enet.c index 01eb2758b..39628da04 100644 --- a/arch/ppc/8xx_io/enet.c +++ b/arch/ppc/8xx_io/enet.c @@ -29,7 +29,7 @@ #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/init.h> diff --git a/arch/ppc/8xx_io/fec.c b/arch/ppc/8xx_io/fec.c index 6e5e9e686..33849ea0e 100644 --- a/arch/ppc/8xx_io/fec.c +++ b/arch/ppc/8xx_io/fec.c @@ -33,7 +33,7 @@ #include <linux/ptrace.h> #include <linux/errno.h> #include <linux/ioport.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/interrupt.h> #include <linux/pci.h> #include <linux/init.h> @@ -73,7 +73,7 @@ typedef struct { } phy_info_t; /* The number of Tx and Rx buffers. These are allocated from the page - * pool. The code may assume these are power of two, so it it best + * pool. The code may assume these are power of two, so it is best * to keep them that size. * We don't need to allocate pages for the transmitter. We just use * the skbuffer directly. @@ -953,7 +953,7 @@ static phy_info_t phy_info_lxt970 = { /* * I had some nice ideas of running the MDIO faster... * The 971 should support 8MHz and I tried it, but things acted really - * wierd, so 2.5 MHz ought to be enough for anyone... + * weird, so 2.5 MHz ought to be enough for anyone... */ static void mii_parse_lxt971_sr2(uint mii_reg, struct net_device *dev) diff --git a/arch/ppc/8xx_io/uart.c b/arch/ppc/8xx_io/uart.c index 598e13ac0..4688d57a8 100644 --- a/arch/ppc/8xx_io/uart.c +++ b/arch/ppc/8xx_io/uart.c @@ -34,7 +34,7 @@ #include <linux/fcntl.h> #include <linux/ptrace.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/uaccess.h> diff --git a/arch/ppc/amiga/amiints.c b/arch/ppc/amiga/amiints.c index b895e6c02..b919e6bff 100644 --- a/arch/ppc/amiga/amiints.c +++ b/arch/ppc/amiga/amiints.c @@ -119,7 +119,7 @@ void amiga_init_IRQ(void) custom.intreq = 0x7fff; #ifdef CONFIG_APUS - /* Clear any inter-CPU interupt requests. Circumvents bug in + /* Clear any inter-CPU interrupt requests. Circumvents bug in Blizzard IPL emulation HW (or so it appears). */ APUS_WRITE(APUS_INT_LVL, INTLVL_SETRESET | INTLVL_MASK); diff --git a/arch/ppc/kernel/checks.c b/arch/ppc/kernel/checks.c index 9bb8e690e..41e35f878 100644 --- a/arch/ppc/kernel/checks.c +++ b/arch/ppc/kernel/checks.c @@ -7,7 +7,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> diff --git a/arch/ppc/kernel/chrp_setup.c b/arch/ppc/kernel/chrp_setup.c index 5682f3fad..04b812268 100644 --- a/arch/ppc/kernel/chrp_setup.c +++ b/arch/ppc/kernel/chrp_setup.c @@ -18,7 +18,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> diff --git a/arch/ppc/kernel/idle.c b/arch/ppc/kernel/idle.c index 6ca616979..5aaabccfa 100644 --- a/arch/ppc/kernel/idle.c +++ b/arch/ppc/kernel/idle.c @@ -21,7 +21,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/pgtable.h> #include <asm/uaccess.h> diff --git a/arch/ppc/kernel/irq.c b/arch/ppc/kernel/irq.c index 02ff4d964..279bccf0a 100644 --- a/arch/ppc/kernel/irq.c +++ b/arch/ppc/kernel/irq.c @@ -40,7 +40,7 @@ #include <linux/timex.h> #include <linux/config.h> #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/pci.h> #include <linux/delay.h> #include <linux/irq.h> diff --git a/arch/ppc/kernel/m8260_setup.c b/arch/ppc/kernel/m8260_setup.c index a55f5235d..91114e459 100644 --- a/arch/ppc/kernel/m8260_setup.c +++ b/arch/ppc/kernel/m8260_setup.c @@ -22,7 +22,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> diff --git a/arch/ppc/kernel/m8xx_setup.c b/arch/ppc/kernel/m8xx_setup.c index f9813fa75..fb585641c 100644 --- a/arch/ppc/kernel/m8xx_setup.c +++ b/arch/ppc/kernel/m8xx_setup.c @@ -22,7 +22,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> diff --git a/arch/ppc/kernel/pmac_pic.c b/arch/ppc/kernel/pmac_pic.c index a9a1777ca..1593b3b1c 100644 --- a/arch/ppc/kernel/pmac_pic.c +++ b/arch/ppc/kernel/pmac_pic.c @@ -457,7 +457,7 @@ pmac_pic_init(void) /* * These procedures are used in implementing sleep on the powerbooks. * sleep_save_intrs() saves the states of all interrupt enables - * and disables all interupts except for the nominated one. + * and disables all interrupts except for the nominated one. * sleep_restore_intrs() restores the states of all interrupt enables. */ unsigned int sleep_save_mask[2]; diff --git a/arch/ppc/kernel/pmac_setup.c b/arch/ppc/kernel/pmac_setup.c index e7be1114e..839f66d80 100644 --- a/arch/ppc/kernel/pmac_setup.c +++ b/arch/ppc/kernel/pmac_setup.c @@ -30,7 +30,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> diff --git a/arch/ppc/kernel/ppc_ksyms.c b/arch/ppc/kernel/ppc_ksyms.c index 6de2d94fe..43a05659d 100644 --- a/arch/ppc/kernel/ppc_ksyms.c +++ b/arch/ppc/kernel/ppc_ksyms.c @@ -6,6 +6,7 @@ #include <linux/sched.h> #include <linux/string.h> #include <linux/interrupt.h> +#include <linux/tty.h> #include <linux/vt_kern.h> #include <linux/nvram.h> #include <linux/spinlock.h> diff --git a/arch/ppc/kernel/prep_nvram.c b/arch/ppc/kernel/prep_nvram.c index 3d34a853f..770808c6e 100644 --- a/arch/ppc/kernel/prep_nvram.c +++ b/arch/ppc/kernel/prep_nvram.c @@ -6,7 +6,7 @@ */ #include <linux/init.h> #include <linux/delay.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/ioport.h> #include <asm/init.h> diff --git a/arch/ppc/kernel/prep_setup.c b/arch/ppc/kernel/prep_setup.c index 64ad519cb..0c23c51ac 100644 --- a/arch/ppc/kernel/prep_setup.c +++ b/arch/ppc/kernel/prep_setup.c @@ -19,7 +19,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> diff --git a/arch/ppc/kernel/process.c b/arch/ppc/kernel/process.c index c4c76adec..259481bcd 100644 --- a/arch/ppc/kernel/process.c +++ b/arch/ppc/kernel/process.c @@ -29,7 +29,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/elf.h> #include <linux/init.h> diff --git a/arch/ppc/kernel/residual.c b/arch/ppc/kernel/residual.c index 1c4bdfbd8..7aa75dbbb 100644 --- a/arch/ppc/kernel/residual.c +++ b/arch/ppc/kernel/residual.c @@ -30,7 +30,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> diff --git a/arch/ppc/kernel/setup.c b/arch/ppc/kernel/setup.c index 58fb87e9c..73afefc56 100644 --- a/arch/ppc/kernel/setup.c +++ b/arch/ppc/kernel/setup.c @@ -12,6 +12,7 @@ #include <linux/delay.h> #include <linux/blk.h> #include <linux/ide.h> +#include <linux/tty.h> #include <linux/bootmem.h> #include <asm/init.h> diff --git a/arch/ppc/kernel/softemu8xx.c b/arch/ppc/kernel/softemu8xx.c index a97c272d3..26a8fe165 100644 --- a/arch/ppc/kernel/softemu8xx.c +++ b/arch/ppc/kernel/softemu8xx.c @@ -21,7 +21,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/interrupt.h> diff --git a/arch/ppc/kernel/traps.c b/arch/ppc/kernel/traps.c index 8bd9ebded..0269254cf 100644 --- a/arch/ppc/kernel/traps.c +++ b/arch/ppc/kernel/traps.c @@ -23,7 +23,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/interrupt.h> diff --git a/arch/s390/Makefile b/arch/s390/Makefile index 7af7bb937..3023d54a2 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile @@ -14,6 +14,7 @@ # LD=$(CROSS_COMPILE)ld -m elf_s390 +CPP=$(CC) -E OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S LDFLAGS=-e start LINKFLAGS =-T $(TOPDIR)/arch/s390/vmlinux.lds $(LDFLAGS) diff --git a/arch/s390/boot/Makefile b/arch/s390/boot/Makefile index 817152810..fb112b964 100644 --- a/arch/s390/boot/Makefile +++ b/arch/s390/boot/Makefile @@ -10,12 +10,11 @@ OBJCOPY = $(CROSS_COMPILE)objcopy O_TARGET := -O_OBJS := include $(TOPDIR)/Rules.make .S.o: - $(CC) $(AFLAGS) -traditional -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o %.lnk: %.o $(LD) -Ttext 0x0 -o $@ $< diff --git a/arch/s390/boot/ipldump.S b/arch/s390/boot/ipldump.S index 7868268af..3a09d1fea 100644 --- a/arch/s390/boot/ipldump.S +++ b/arch/s390/boot/ipldump.S @@ -38,7 +38,7 @@ _start: # # find out memory size # - mvc 104(8,0),.Lpcmem0 # setup program check handler + mvc 104(8),.Lpcmem0 # setup program check handler slr %r3,%r3 lhi %r2,1 sll %r2,20 diff --git a/arch/s390/boot/ipleckd.S b/arch/s390/boot/ipleckd.S index 63b0330b2..d66a8d684 100644 --- a/arch/s390/boot/ipleckd.S +++ b/arch/s390/boot/ipleckd.S @@ -3,7 +3,7 @@ # IPL record for 3380/3390 DASD # # S390 version -# Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation +# Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation # Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com> # # @@ -11,6 +11,8 @@ # FIXME: should insert zeroes into memory when filling holes # FIXME: calculate blkpertrack from rdc data and blksize +# change 09/20/00 removed obsolete store of ipldevice to textesegment + # Usage of registers # r1: ipl subchannel ( general use, dont overload without save/restore !) # r10: @@ -39,7 +41,7 @@ .org 0xf0 # Lets start now... _start: .globl _start - l %r1,__LC_SUBCHANNEL_ID # get IPL-subchannel from lowcore + l %r1,__LC_SUBCHANNEL_ID # get IPL-subchannel from lowcore st %r1,__LC_IPLDEV # keep it for reipl stsch .Lrdcdata oi .Lrdcdata+5,0x84 # enable ssch and multipath mode @@ -111,10 +113,10 @@ _start: .globl _start mvc 0x600(256,%r3),0x180(%r4) mvc 0x700(256,%r3),0x280(%r4) .Lrunkern: - lhi %r2,17 - sll %r2,12 - st %r1,0xc6c(%r2) # store iplsubchannel to lowcore - st %r1,0xc6c # store iplsubchannel to lowcore +# lhi %r2,17 +# sll %r2,12 +# st %r1,0xc6c(%r2) # store iplsubchannel to lowcore +# st %r1,0xc6c # store iplsubchannel to lowcore br %r3 # This function does the start IO # r2: number of first block to read ( input by caller ) @@ -140,17 +142,16 @@ _start: .globl _start lr %r15,%r4 # save number or blocks slr %r7,%r7 icm %r7,3,.Lrdcdata+14 # load heads to r7 + lhi %r6,9 + clc .Lrdcdata+3(2),.L9345 + je .L011 + lhi %r6,10 + clc .Lrdcdata+3(2),.L3380 + je .L011 + lhi %r6,12 clc .Lrdcdata+3(2),.L3390 - jne .L010 # 3380 or 3390 ? - lhi %r6,12 # setup r6 correct! - j .L011 -.L010: - clc .Lrdcdata+3(2),.L9343 - jne .L013 - lhi %r6,9 - j .L011 -.L013: - lhi %r6,10 + je .L011 + bras %r14,.Ldisab .L011: # loop for nbl times .Lrdloop: @@ -245,10 +246,13 @@ _start: .globl _start .long 0x00008000 # they are loaded with a LM .L3390: .word 0x3390 -.L9343: - .word 0x9343 +.L9345: + .word 0x9345 +.L3380: + .word 0x3380 .Lnull: .long 0x00000000,0x00000000 + .align 4 .Lrdcdata: .long 0x00000000,0x00000000 .long 0x00000000,0x00000000 diff --git a/arch/s390/config.in b/arch/s390/config.in index 4415799ff..fc3b430a6 100644 --- a/arch/s390/config.in +++ b/arch/s390/config.in @@ -44,31 +44,20 @@ bool 'Networking support' CONFIG_NET bool 'System V IPC' CONFIG_SYSVIPC bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT bool 'Sysctl support' CONFIG_SYSCTL +define CONFIG_KCORE ELF tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF - +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG endmenu source drivers/s390/Config.in -mainmenu_option next_comment -comment 'Character devices' -bool 'Unix98 PTY support' CONFIG_UNIX98_PTYS -if [ "$CONFIG_UNIX98_PTYS" = "y" ]; then - int 'Maximum number of Unix98 PTYs in use (0-2048)' CONFIG_UNIX98_PTY_COUNT 256 -fi - -endmenu - if [ "$CONFIG_NET" = "y" ]; then source net/Config.in fi source fs/Config.in -# source drivers/char/Config.in - -# source drivers/media/Config.in - mainmenu_option next_comment comment 'Kernel hacking' diff --git a/arch/s390/defconfig b/arch/s390/defconfig index 459d51ebb..c4e590e75 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -1,6 +1,9 @@ # # Automatically generated by make menuconfig: don't edit # +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_MCA is not set CONFIG_UID16=y CONFIG_ARCH_S390=y @@ -20,7 +23,7 @@ CONFIG_IEEEFPU_EMULATION=y # CONFIG_MODULES=y # CONFIG_MODVERSIONS is not set -# CONFIG_KMOD is not set +CONFIG_KMOD=y # # General setup @@ -34,41 +37,60 @@ CONFIG_SYSVIPC=y # CONFIG_BSD_PROCESS_ACCT is not set CONFIG_SYSCTL=y CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PROCESS_DEBUG is not set # -# S/390 block device drivers +# Block device drivers # CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_NBD is not set -# CONFIG_BLK_DEV_MD is not set CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=24576 CONFIG_BLK_DEV_INITRD=y -# CONFIG_MDISK is not set +CONFIG_BLK_DEV_XPRAM=m CONFIG_DASD=y CONFIG_DASD_ECKD=y -# CONFIG_DASD_MDSK is not set +CONFIG_DASD_FBA=y # -# S/390 Network device support +# Multi-device support (RAID and LVM) # -# CONFIG_CHANDEV is not set -CONFIG_NETDEVICES=y -CONFIG_CTC=y -CONFIG_IUCV=y -# CONFIG_DUMMY is not set -CONFIG_NET_ETHERNET=y -CONFIG_TR=y -# CONFIG_FDDI is not set +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +# CONFIG_MD_LINEAR is not set +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID5=m +CONFIG_BLK_DEV_LVM=m +CONFIG_LVM_PROC_FS=y # -# S/390 Terminal and Console options +# Character device drivers # +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 CONFIG_3215=y CONFIG_3215_CONSOLE=y CONFIG_HWC=y CONFIG_HWC_CONSOLE=y -CONFIG_UNIX98_PTYS=y -CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_S390_TAPE=m +CONFIG_S390_TAPE_CHAR=y +CONFIG_S390_TAPE_BLOCK=y +CONFIG_S390_TAPE_3490=y +CONFIG_S390_TAPE_3480=y + +# +# Network device drivers +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +CONFIG_NET_ETHERNET=y +CONFIG_TR=y +# CONFIG_FDDI is not set +# CONFIG_CHANDEV is not set +CONFIG_CTC=m +CONFIG_IUCV=m # # Networking options @@ -82,25 +104,24 @@ CONFIG_NETLINK=y # CONFIG_FILTER is not set CONFIG_UNIX=y CONFIG_INET=y -# CONFIG_IP_MULTICAST is not set +CONFIG_IP_MULTICAST=y # CONFIG_IP_ADVANCED_ROUTER is not set # CONFIG_IP_PNP is not set -# CONFIG_IP_ROUTER is not set # CONFIG_NET_IPIP is not set # CONFIG_NET_IPGRE is not set -# CONFIG_IP_ALIAS is not set +# CONFIG_INET_ECN is not set # CONFIG_SYN_COOKIES is not set -CONFIG_SKB_LARGE=y # CONFIG_IPV6 is not set # CONFIG_KHTTPD is not set # CONFIG_ATM is not set # CONFIG_IPX is not set # CONFIG_ATALK is not set # CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set # CONFIG_X25 is not set # CONFIG_LAPB is not set -# CONFIG_BRIDGE is not set # CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set # CONFIG_ECONET is not set # CONFIG_WAN_ROUTER is not set # CONFIG_NET_FASTROUTE is not set @@ -118,6 +139,7 @@ CONFIG_SKB_LARGE=y # CONFIG_AUTOFS_FS is not set # CONFIG_AUTOFS4_FS is not set # CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set # CONFIG_AFFS_FS is not set # CONFIG_HFS_FS is not set # CONFIG_BFS_FS is not set @@ -126,28 +148,37 @@ CONFIG_SKB_LARGE=y # CONFIG_UMSDOS_FS is not set # CONFIG_VFAT_FS is not set # CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set # CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set # CONFIG_ISO9660_FS is not set # CONFIG_JOLIET is not set # CONFIG_MINIX_FS is not set # CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set # CONFIG_HPFS_FS is not set CONFIG_PROC_FS=y # CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set # CONFIG_DEVFS_DEBUG is not set # CONFIG_DEVPTS_FS is not set # CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set # CONFIG_ROMFS_FS is not set CONFIG_EXT2_FS=y # CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set # CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set # CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set # # Network File Systems # # CONFIG_CODA_FS is not set CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set # CONFIG_ROOT_NFS is not set CONFIG_NFSD=y # CONFIG_NFSD_V3 is not set @@ -155,6 +186,16 @@ CONFIG_SUNRPC=y CONFIG_LOCKD=y # CONFIG_SMB_FS is not set # CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set +# CONFIG_NCPFS_NDS_DOMAINS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set # # Partition Types @@ -168,10 +209,10 @@ CONFIG_IBM_PARTITION=y # CONFIG_MAC_PARTITION is not set # CONFIG_MSDOS_PARTITION is not set # CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set # CONFIG_SUN_PARTITION is not set # CONFIG_NLS is not set # # Kernel hacking # -# CONFIG_REMOTE_DEBUG is not set diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index f04b1cd74..dfaadc490 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile @@ -8,47 +8,25 @@ # Note 2! The CFLAGS definitions are now in the main makefile... .S.o: - $(CC) $(AFLAGS) -traditional -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o all: kernel.o head.o init_task.o O_TARGET := kernel.o -O_OBJS := lowcore.o entry.o bitmap.o traps.o time.o process.o irq.o \ + +export-objs := s390_ksyms.o +obj-y := lowcore.o entry.o bitmap.o traps.o time.o process.o irq.o \ setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ - semaphore.o s390fpu.o s390io.o s390mach.o s390dyn.o reipl.o -OX_OBJS := s390_ksyms.o -MX_OBJS := - -ifdef CONFIG_SMP -O_OBJS += smp.o -endif - -ifdef CONFIG_PCI -O_OBJS += bios32.o -endif - -ifdef CONFIG_MCA -O_OBJS += mca.o -endif - -ifeq ($(CONFIG_MTRR),y) -OX_OBJS += mtrr.o -else - ifeq ($(CONFIG_MTRR),m) - MX_OBJS += mtrr.o - endif -endif - -ifeq ($(CONFIG_IEEEFPU_EMULATION),y) - O_OBJS += mathemu.o floatlib.o -endif + semaphore.o s390fpu.o reipl.o s390_ext.o debug.o + +obj-$(CONFIG_MODULES) += s390_ksyms.o +obj-$(CONFIG_SMP) += smp.o +obj-$(CONFIG_IEEEFPU_EMULATION) += mathemu.o floatlib.o # # Kernel debugging # -ifdef CONFIG_REMOTE_DEBUG -O_OBJS += gdb-stub.o #gdb-low.o -endif +obj-$(CONFIG_REMOTE_DEBUG) += gdb-stub.o #gdb-low.o include $(TOPDIR)/Rules.make diff --git a/arch/s390/kernel/cpcmd.c b/arch/s390/kernel/cpcmd.c index 818958b4f..68abd7d50 100644 --- a/arch/s390/kernel/cpcmd.c +++ b/arch/s390/kernel/cpcmd.c @@ -8,7 +8,7 @@ #include <linux/stddef.h> #include <linux/kernel.h> -#include <asm/string.h> +#include <linux/string.h> #include <asm/ebcdic.h> void cpcmd(char *cmd, char *response, int rlen) @@ -22,10 +22,10 @@ void cpcmd(char *cmd, char *response, int rlen) ASCEBC(obuffer,olen); if (response != NULL && rlen > 0) { - asm volatile ("LRA 2,0(0,%0)\n\t" + asm volatile ("LRA 2,0(%0)\n\t" "LR 4,%1\n\t" "O 4,%4\n\t" - "LRA 3,0(0,%2)\n\t" + "LRA 3,0(%2)\n\t" "LR 5,%3\n\t" ".long 0x83240008 # Diagnose 83\n\t" : /* no output */ @@ -34,7 +34,7 @@ void cpcmd(char *cmd, char *response, int rlen) : "2", "3", "4", "5" ); EBCASC(response, rlen); } else { - asm volatile ("LRA 2,0(0,%0)\n\t" + asm volatile ("LRA 2,0(%0)\n\t" "LR 3,%1\n\t" ".long 0x83230008 # Diagnose 83\n\t" : /* no output */ diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c new file mode 100644 index 000000000..bb3dfe5de --- /dev/null +++ b/arch/s390/kernel/debug.c @@ -0,0 +1,1167 @@ +/* + * arch/s390/kernel/debug.c + * S/390 debug facility + * + * Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH, + * IBM Corporation + * Author(s): Michael Holzheu (holzheu@de.ibm.com), + * Holger Smolinski (Holger.Smolinski@de.ibm.com) + * + * Bugreports to: <Linux390@de.ibm.com> + */ + +#include <linux/config.h> +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/vmalloc.h> +#include <linux/ctype.h> +#include <linux/version.h> +#include <asm/uaccess.h> +#include <asm/semaphore.h> + +#ifdef MODULE +#include <linux/module.h> +#endif + +#include <asm/debug.h> + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#if defined(CONFIG_ARCH_S390X) +#define DEBUG_PROC_HEADER_SIZE 46 +#else +#define DEBUG_PROC_HEADER_SIZE 38 +#endif + +#define ADD_BUFFER 1000 + +/* typedefs */ + +typedef struct file_private_info { + loff_t len; /* length of output in byte */ + int size; /* size of buffer for output */ + char *data; /* buffer for output */ + debug_info_t *debug_info; /* the debug information struct */ + struct debug_view *view; /* used view of debug info */ +} file_private_info_t; + +extern void tod_to_timeval(uint64_t todval, struct timeval *xtime); + +/* internal function prototyes */ + +static int debug_init(void); +static int debug_format_output(debug_info_t * debug_area, char *buf, + int size, struct debug_view *view); +static ssize_t debug_output(struct file *file, char *user_buf, + size_t user_len, loff_t * offset); +static ssize_t debug_input(struct file *file, const char *user_buf, + size_t user_len, loff_t * offset); +static int debug_open(struct inode *inode, struct file *file); +static int debug_close(struct inode *inode, struct file *file); +static struct proc_dir_entry +*debug_create_proc_dir_entry(struct proc_dir_entry *root, + const char *name, mode_t mode, + struct inode_operations *iops, + struct file_operations *fops); +static void debug_delete_proc_dir_entry(struct proc_dir_entry *root, + struct proc_dir_entry *entry); +static debug_info_t* debug_info_create(char *name, int page_order, int nr_areas, int buf_size); +static void debug_info_get(debug_info_t *); +static void debug_info_put(debug_info_t *); +static int debug_prolog_level_fn(debug_info_t * id, + struct debug_view *view, char *out_buf); +static int debug_input_level_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char *user_buf, + size_t user_buf_size, loff_t * offset); +static int debug_hex_ascii_format_fn(debug_info_t * id, struct debug_view *view, + char *out_buf, const char *in_buf); +static int debug_raw_format_fn(debug_info_t * id, + struct debug_view *view, char *out_buf, + const char *in_buf); +static int debug_raw_header_fn(debug_info_t * id, struct debug_view *view, + int area, debug_entry_t * entry, char *out_buf); + +/* globals */ + +struct debug_view debug_raw_view = { + "raw", + NULL, + &debug_raw_header_fn, + &debug_raw_format_fn, + NULL +}; + +struct debug_view debug_hex_ascii_view = { + "hex_ascii", + NULL, + &debug_dflt_header_fn, + &debug_hex_ascii_format_fn, + NULL +}; + +struct debug_view debug_level_view = { + "level", + &debug_prolog_level_fn, + NULL, + NULL, + &debug_input_level_fn +}; + +/* static globals */ + +static debug_info_t *debug_area_first = NULL; +static debug_info_t *debug_area_last = NULL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) +static struct semaphore debug_lock = MUTEX; +#else +DECLARE_MUTEX(debug_lock); +#endif + +static int initialized = 0; + +static struct file_operations debug_file_ops = { + read: debug_output, + write: debug_input, + open: debug_open, + release: debug_close, +}; + +static struct inode_operations debug_inode_ops = { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) + default_file_ops: &debug_file_ops, /* file ops */ +#endif +}; + + +static struct proc_dir_entry *debug_proc_root_entry; + + +/* functions */ + +/* + * debug_info_create + * - create new debug-info + */ + +static debug_info_t* debug_info_create(char *name, int page_order, + int nr_areas, int buf_size) +{ + debug_info_t* rc; + int i; + + /* alloc everything */ + + rc = (debug_info_t*) kmalloc(sizeof(debug_info_t), GFP_ATOMIC); + if(!rc) + goto fail_malloc_rc; + rc->active_entry = (int*)kmalloc(nr_areas * sizeof(int), GFP_ATOMIC); + if(!rc->active_entry) + goto fail_malloc_active_entry; + memset(rc->active_entry, 0, nr_areas * sizeof(int)); + rc->areas = (debug_entry_t **) kmalloc(nr_areas * + sizeof(debug_entry_t *), + GFP_ATOMIC); + if (!rc->areas) + goto fail_malloc_areas; + for (i = 0; i < nr_areas; i++) { + rc->areas[i] = + (debug_entry_t *) __get_free_pages(GFP_ATOMIC, + page_order); + if (!rc->areas[i]) { + for (i--; i >= 0; i--) { + free_pages((unsigned long) rc->areas[i], + page_order); + } + goto fail_malloc_areas2; + } else { + memset(rc->areas[i], 0, PAGE_SIZE << page_order); + } + } + + /* initialize members */ + + spin_lock_init(&rc->lock); + rc->page_order = page_order; + rc->nr_areas = nr_areas; + rc->active_area = 0; + rc->level = DEBUG_DEFAULT_LEVEL; + rc->buf_size = buf_size; + rc->entry_size = sizeof(debug_entry_t) + buf_size; + strncpy(rc->name, name, MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1))); + rc->name[MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1))] = 0; + memset(rc->views, 0, DEBUG_MAX_VIEWS * sizeof(struct debug_view *)); + memset(rc->proc_entries, 0 ,DEBUG_MAX_VIEWS * + sizeof(struct proc_dir_entry*)); + atomic_set(&(rc->ref_count), 0); + rc->proc_root_entry = + debug_create_proc_dir_entry(debug_proc_root_entry, rc->name, + S_IFDIR | S_IRUGO | S_IXUGO | + S_IWUSR | S_IWGRP, NULL, NULL); + + /* append new element to linked list */ + + if(debug_area_first == NULL){ + /* first element in list */ + debug_area_first = rc; + rc->prev = NULL; + } + else{ + /* append element to end of list */ + debug_area_last->next = rc; + rc->prev = debug_area_last; + } + debug_area_last = rc; + rc->next = NULL; + + debug_info_get(rc); + return rc; + +fail_malloc_areas2: + kfree(rc->areas); +fail_malloc_areas: + kfree(rc->active_entry); +fail_malloc_active_entry: + kfree(rc); +fail_malloc_rc: + return NULL; +} + +/* + * debug_info_get + * - increments reference count for debug-info + */ + +static void debug_info_get(debug_info_t * db_info) +{ + if (db_info) + atomic_inc(&db_info->ref_count); +} + +/* + * debug_info_put: + * - decreases reference count for debug-info and frees it if necessary + */ + +static void debug_info_put(debug_info_t *db_info) +{ + int i; + + if (!db_info) + return; + if (atomic_dec_and_test(&db_info->ref_count)) { + printk(KERN_INFO "debug: freeing debug area %p (%s)\n", + db_info, db_info->name); + for (i = 0; i < DEBUG_MAX_VIEWS; i++) { + if (db_info->views[i] != NULL) + debug_delete_proc_dir_entry + (db_info->proc_root_entry, + db_info->proc_entries[i]); + } + debug_delete_proc_dir_entry(debug_proc_root_entry, + db_info->proc_root_entry); + for (i = 0; i < db_info->nr_areas; i++) { + free_pages((unsigned long) db_info->areas[i], + db_info->page_order); + } + kfree(db_info->areas); + kfree(db_info->active_entry); + if(db_info == debug_area_first) + debug_area_first = db_info->next; + if(db_info == debug_area_last) + debug_area_last = db_info->prev; + if(db_info->prev) db_info->prev->next = db_info->next; + if(db_info->next) db_info->next->prev = db_info->prev; + kfree(db_info); + } +} + + +/* + * debug_output: + * - called for user read() + * - copies formated output form private_data of the file + * handle to the user buffer + */ + +static ssize_t debug_output(struct file *file, /* file descriptor */ + char *user_buf, /* user buffer */ + size_t user_len, /* length of buffer */ + loff_t *offset /* offset in the file */ ) +{ + loff_t len; + int rc; + file_private_info_t *p_info; + + p_info = ((file_private_info_t *) file->private_data); + if (*offset >= p_info->len) { + return 0; /* EOF */ + } else { + len = MIN(user_len, (p_info->len - *offset)); + if ((rc = copy_to_user(user_buf, &(p_info->data[*offset]),len))) + return rc;; + (*offset) += len; + return len; /* number of bytes "read" */ + } +} + +/* + * debug_input: + * - called for user write() + * - calls input function of view + */ + +static ssize_t debug_input(struct file *file, + const char *user_buf, size_t length, + loff_t *offset) +{ + int rc = 0; + file_private_info_t *p_info; + + down(&debug_lock); + p_info = ((file_private_info_t *) file->private_data); + if (p_info->view->input_proc) + rc = p_info->view->input_proc(p_info->debug_info, + p_info->view, file, user_buf, + length, offset); + up(&debug_lock); + return rc; /* number of input characters */ +} + +/* + * debug_format_output: + * - calls prolog, header and format functions of view to format output + */ + +static int debug_format_output(debug_info_t * debug_area, char *buf, + int size, struct debug_view *view) +{ + int len = 0; + int i, j; + int nr_of_entries; + debug_entry_t *act_entry; + + /* print prolog */ + if (view->prolog_proc) + len += view->prolog_proc(debug_area, view, buf); + /* print debug records */ + if (!(view->format_proc) && !(view->header_proc)) + goto out; + nr_of_entries = PAGE_SIZE / debug_area->entry_size + << debug_area->page_order; + for (i = 0; i < debug_area->nr_areas; i++) { + act_entry = debug_area->areas[i]; + for (j = 0; j < nr_of_entries; j++) { + if (act_entry->id.fields.used == 0) + break; /* empty entry */ + if (view->header_proc) + len += view->header_proc(debug_area, view, i, + act_entry, buf + len); + if (view->format_proc) + len += view->format_proc(debug_area, view, + buf + len, + DEBUG_DATA(act_entry)); + if (len > size) { + printk(KERN_ERR + "debug: error -- memory exceeded for (%s/%s)\n", + debug_area->name, view->name); + printk(KERN_ERR "debug: fix view %s!!\n", + view->name); + printk(KERN_ERR + "debug: area: %i (0 - %i) entry: %i (0 - %i)\n", + i, debug_area->nr_areas - 1, j, + nr_of_entries - 1); + goto out; + } + act_entry = (debug_entry_t *) (((char *) act_entry) + + debug_area->entry_size); + } + } + out: + return len; +} + + +/* + * debug_open: + * - called for user open() + * - copies formated output to private_data area of the file + * handle + */ + +static int debug_open(struct inode *inode, struct file *file) +{ + int i = 0, size = 0, rc = 0, f_entry_size = 0; + file_private_info_t *p_info; + debug_info_t* debug_info; + +#ifdef DEBUG + printk("debug_open\n"); +#endif + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + down(&debug_lock); + + /* find debug log and view */ + + debug_info = debug_area_first; + while(debug_info != NULL){ + for (i = 0; i < DEBUG_MAX_VIEWS; i++) { + if (debug_info->views[i] == NULL) + continue; + else if (debug_info->proc_entries[i]->low_ino == + file->f_dentry->d_inode->i_ino) { + goto found; /* found view ! */ + } + } + debug_info = debug_info->next; + } + /* no entry found */ + rc = -EINVAL; + goto out; + found: + if ((file->private_data = + kmalloc(sizeof(file_private_info_t), GFP_ATOMIC)) == 0) { + printk(KERN_ERR "debug_open: kmalloc failed\n"); + rc = -ENOMEM; + goto out; + } + p_info = (file_private_info_t *) file->private_data; + + /* + * the size for the formated output is calculated + * with the following formula: + * + * prolog-size + * + + * (record header size + record data field size) + * * number of entries per page + * * number of pages per area + * * number of areas + */ + + if (debug_info->views[i]->prolog_proc) + size += + debug_info->views[i]->prolog_proc(debug_info, + debug_info-> + views[i], NULL); + + if (debug_info->views[i]->header_proc) + f_entry_size = + debug_info->views[i]->header_proc(debug_info, + debug_info-> + views[i], 0, NULL, + NULL); + if (debug_info->views[i]->format_proc) + f_entry_size += + debug_info->views[i]->format_proc(debug_info, + debug_info-> + views[i], NULL, + NULL); + + size += f_entry_size + * (PAGE_SIZE / debug_info->entry_size + << debug_info->page_order) + * debug_info->nr_areas + 1; /* terminating \0 */ +#ifdef DEBUG + printk("debug_open: size: %i\n", size); +#endif + + /* alloc some bytes more to be safe against bad views */ + if ((p_info->data = vmalloc(size + ADD_BUFFER)) == 0) { + printk(KERN_ERR "debug_open: vmalloc failed\n"); + vfree(file->private_data); + rc = -ENOMEM; + goto out; + } + + p_info->size = size; + p_info->debug_info = debug_info; + p_info->view = debug_info->views[i]; + + spin_lock_irq(&debug_info->lock); + + p_info->len = + debug_format_output(debug_info, p_info->data, size, + debug_info->views[i]); +#ifdef DEBUG + { + int ilen = p_info->len; + printk("debug_open: len: %i\n", ilen); + } +#endif + + spin_unlock_irq(&debug_info->lock); + debug_info_get(debug_info); + + out: + up(&debug_lock); +#ifdef MODULE + if (rc != 0) + MOD_DEC_USE_COUNT; +#endif + return rc; +} + +/* + * debug_close: + * - called for user close() + * - deletes private_data area of the file handle + */ + +static int debug_close(struct inode *inode, struct file *file) +{ + file_private_info_t *p_info; +#ifdef DEBUG + printk("debug_close\n"); +#endif + down(&debug_lock); + p_info = (file_private_info_t *) file->private_data; + debug_info_put(p_info->debug_info); + if (p_info->data) { + vfree(p_info->data); + kfree(file->private_data); + } + up(&debug_lock); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; /* success */ +} + +/* + * debug_create_proc_dir_entry: + * - initializes proc-dir-entry and registers it + */ + +static struct proc_dir_entry *debug_create_proc_dir_entry + (struct proc_dir_entry *root, const char *name, mode_t mode, + struct inode_operations *iops, struct file_operations *fops) +{ + struct proc_dir_entry *rc = NULL; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) + const char *fn = name; + int len; + len = strlen(fn); + + rc = (struct proc_dir_entry *) kmalloc(sizeof(struct proc_dir_entry) + + len + 1, GFP_ATOMIC); + if (!rc) + goto out; + + memset(rc, 0, sizeof(struct proc_dir_entry)); + memcpy(((char *) rc) + sizeof(*rc), fn, len + 1); + rc->name = ((char *) rc) + sizeof(*rc); + rc->namelen = len; + rc->low_ino = 0, rc->mode = mode; + rc->nlink = 1; + rc->uid = 0; + rc->gid = 0; + rc->size = 0; + rc->get_info = NULL; + rc->ops = iops; + + proc_register(root, rc); +#else + rc = create_proc_entry(name, mode, root); + if (!rc) + goto out; + if (fops) + rc->proc_fops = fops; +#endif + + out: + return rc; +} + + +/* + * delete_proc_dir_entry: + */ + +static void debug_delete_proc_dir_entry + (struct proc_dir_entry *root, struct proc_dir_entry *proc_entry) +{ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) + proc_unregister(root, proc_entry->low_ino); + kfree(proc_entry); +#else + remove_proc_entry(proc_entry->name, root); +#endif +} + +/* + * debug_register: + * - creates and initializes debug area for the caller + * - returns handle for debug area + */ + +debug_info_t *debug_register + (char *name, int page_order, int nr_areas, int buf_size) +{ + debug_info_t *rc = NULL; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + if (!initialized) + debug_init(); + down(&debug_lock); + + /* create new debug_info */ + + rc = debug_info_create(name, page_order, nr_areas, buf_size); + if(!rc) + goto out; + debug_register_view(rc, &debug_level_view); + printk(KERN_INFO + "debug: reserved %d areas of %d pages for debugging %s\n", + nr_areas, 1 << page_order, rc->name); + out: + if (rc == NULL){ + printk(KERN_ERR "debug: debug_register failed for %s\n",name); +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + } + up(&debug_lock); + return rc; +} + +/* + * debug_unregister: + * - give back debug area + */ + +void debug_unregister(debug_info_t * id) +{ + if (!id) + goto out; + down(&debug_lock); + printk(KERN_INFO "debug: unregistering %s\n", id->name); + debug_info_put(id); + up(&debug_lock); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + out: + return; +} + +/* + * debug_set_level: + * - set actual debug level + */ + +void debug_set_level(debug_info_t* id, int new_level) +{ + long flags; + if(!id) + return; + spin_lock_irqsave(&id->lock,flags); + if(new_level == DEBUG_OFF_LEVEL){ + id->level = DEBUG_OFF_LEVEL; + printk(KERN_INFO "debug: %s: switched off\n",id->name); + } else if ((new_level > DEBUG_MAX_LEVEL) || (new_level < 0)) { + printk(KERN_INFO + "debug: %s: level %i is out of range (%i - %i)\n", + id->name, new_level, 0, DEBUG_MAX_LEVEL); + } else { + id->level = new_level; + printk(KERN_INFO + "debug: %s: new level %i\n",id->name,id->level); + } + spin_unlock_irqrestore(&id->lock,flags); +} + + +/* + * proceed_active_entry: + * - set active entry to next in the ring buffer + */ + +static inline void proceed_active_entry(debug_info_t * id) +{ + if ((id->active_entry[id->active_area] += id->entry_size) + > ((PAGE_SIZE << (id->page_order)) - id->entry_size)) + id->active_entry[id->active_area] = 0; +} + +/* + * proceed_active_area: + * - set active area to next in the ring buffer + */ + +static inline void proceed_active_area(debug_info_t * id) +{ + id->active_area++; + id->active_area = id->active_area % id->nr_areas; +} + +/* + * get_active_entry: + */ + +static inline debug_entry_t *get_active_entry(debug_info_t * id) +{ + return (debug_entry_t *) ((char *) id->areas[id->active_area] + + id->active_entry[id->active_area]); +} + +/* + * debug_common: + * - set timestamp, caller address, cpu number etc. + */ + +static inline debug_entry_t *debug_common(debug_info_t * id) +{ + debug_entry_t *active; + + active = get_active_entry(id); + STCK(active->id.stck); + active->id.fields.cpuid = smp_processor_id(); + active->id.fields.used = 1; + active->caller = __builtin_return_address(0); + return active; +} + +/* + * debug_event: + */ + +debug_entry_t *debug_event(debug_info_t * id, int level, void *buf, + int len) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + active->id.fields.exception = 0; + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), buf, MIN(len, id->buf_size)); + proceed_active_entry(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; +} + +/* + * debug_int_event: + */ + +debug_entry_t *debug_int_event(debug_info_t * id, int level, + unsigned int tag) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + active->id.fields.exception = 0; + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), &tag, MIN(sizeof(unsigned int), id->buf_size)); + proceed_active_entry(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; +} + +/* + * debug_text_event: + */ + +debug_entry_t *debug_text_event(debug_info_t * id, int level, + const char *txt) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + memset(DEBUG_DATA(active), 0, id->buf_size); + strncpy(DEBUG_DATA(active), txt, MIN(strlen(txt), id->buf_size)); + active->id.fields.exception = 0; + proceed_active_entry(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; + +} + +/* + * debug_exception: + */ + +debug_entry_t *debug_exception(debug_info_t * id, int level, void *buf, + int len) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + active->id.fields.exception = 1; + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), buf, MIN(len, id->buf_size)); + proceed_active_entry(id); + proceed_active_area(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; +} + +/* + * debug_int_exception: + */ + +debug_entry_t *debug_int_exception(debug_info_t * id, int level, + unsigned int tag) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + active->id.fields.exception = 1; + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), &tag, + MIN(sizeof(unsigned int), id->buf_size)); + proceed_active_entry(id); + proceed_active_area(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; +} + +/* + * debug_text_exception: + */ + +debug_entry_t *debug_text_exception(debug_info_t * id, int level, + const char *txt) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + memset(DEBUG_DATA(active), 0, id->buf_size); + strncpy(DEBUG_DATA(active), txt, MIN(strlen(txt), id->buf_size)); + active->id.fields.exception = 1; + proceed_active_entry(id); + proceed_active_area(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; + +} + +/* + * debug_init: + * - is called exactly once to initialize the debug feature + */ + +int debug_init(void) +{ + int rc = 0; + + down(&debug_lock); + if (!initialized) { + debug_proc_root_entry = + debug_create_proc_dir_entry(&proc_root, DEBUG_DIR_ROOT, + S_IFDIR | S_IRUGO | S_IXUGO + | S_IWUSR | S_IWGRP, NULL, + NULL); + printk(KERN_INFO "debug: Initialization complete\n"); + initialized = 1; + } + up(&debug_lock); + + return rc; +} + +/* + * debug_register_view: + */ + +int debug_register_view(debug_info_t * id, struct debug_view *view) +{ + int rc = 0; + int i; + long flags; + mode_t mode = S_IFREG; + + if (!id) + goto out; + spin_lock_irqsave(&id->lock, flags); + for (i = 0; i < DEBUG_MAX_VIEWS; i++) { + if (id->views[i] == NULL) + break; + } + if (i == DEBUG_MAX_VIEWS) { + printk(KERN_WARNING "debug: cannot register view %s/%s\n", + id->name,view->name); + printk(KERN_WARNING + "debug: maximum number of views reached (%i)!\n", i); + rc = -1; + } + else { + id->views[i] = view; + if (view->prolog_proc || view->format_proc || view->header_proc) + mode |= S_IRUSR; + if (view->input_proc) + mode |= S_IWUSR; + id->proc_entries[i] = + debug_create_proc_dir_entry(id->proc_root_entry, + view->name, mode, + &debug_inode_ops, + &debug_file_ops); + rc = 0; + } + spin_unlock_irqrestore(&id->lock, flags); + out: + return rc; +} + +/* + * debug_unregister_view: + */ + +int debug_unregister_view(debug_info_t * id, struct debug_view *view) +{ + int rc = 0; + int i; + long flags; + + if (!id) + goto out; + spin_lock_irqsave(&id->lock, flags); + for (i = 0; i < DEBUG_MAX_VIEWS; i++) { + if (id->views[i] == view) + break; + } + if (i == DEBUG_MAX_VIEWS) + rc = -1; + else { + debug_delete_proc_dir_entry(id->proc_root_entry, + id->proc_entries[i]); + id->views[i] = NULL; + rc = 0; + } + spin_unlock_irqrestore(&id->lock, flags); + out: + return rc; +} + +/* + * functions for debug-views + *********************************** +*/ + +/* + * prints out actual debug level + */ + +static int debug_prolog_level_fn(debug_info_t * id, + struct debug_view *view, char *out_buf) +{ + int rc = 0; + + if (out_buf == NULL) { + rc = 2; + goto out; + } + if(id->level == -1) rc = sprintf(out_buf,"-\n"); + else rc = sprintf(out_buf, "%i\n", id->level); + out: + return rc; +} + +/* + * reads new debug level + */ + +static int debug_input_level_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char *user_buf, + size_t in_buf_size, loff_t * offset) +{ + char input_buf[1]; + int rc = in_buf_size; + + if (*offset != 0) + goto out; + if ((rc = copy_from_user(input_buf, user_buf, 1))) + goto out; + if (isdigit(input_buf[0])) { + int new_level = ((int) input_buf[0] - (int) '0'); + debug_set_level(id, new_level); + } else if(input_buf[0] == '-') { + debug_set_level(id, DEBUG_OFF_LEVEL); + } else { + printk(KERN_INFO "debug: level `%c` is not valid\n", + input_buf[0]); + } + out: + *offset += in_buf_size; + return rc; /* number of input characters */ +} + +/* + * prints debug header in raw format + */ + +int debug_raw_header_fn(debug_info_t * id, struct debug_view *view, + int area, debug_entry_t * entry, char *out_buf) +{ + int rc; + + rc = sizeof(debug_entry_t); + if (out_buf == NULL) + goto out; + memcpy(out_buf,entry,sizeof(debug_entry_t)); + out: + return rc; +} + +/* + * prints debug data in raw format + */ + +static int debug_raw_format_fn(debug_info_t * id, struct debug_view *view, + char *out_buf, const char *in_buf) +{ + int rc; + + rc = id->buf_size; + if (out_buf == NULL || in_buf == NULL) + goto out; + memcpy(out_buf, in_buf, id->buf_size); + out: + return rc; +} + +/* + * prints debug data in hex/ascii format + */ + +static int debug_hex_ascii_format_fn(debug_info_t * id, struct debug_view *view, + char *out_buf, const char *in_buf) +{ + int i, rc = 0; + + if (out_buf == NULL || in_buf == NULL) { + rc = id->buf_size * 4 + 3; + goto out; + } + for (i = 0; i < id->buf_size; i++) { + rc += sprintf(out_buf + rc, "%02x ", + ((unsigned char *) in_buf)[i]); + } + rc += sprintf(out_buf + rc, "| "); + for (i = 0; i < id->buf_size; i++) { + unsigned char c = in_buf[i]; + if (!isprint(c)) + rc += sprintf(out_buf + rc, "."); + else + rc += sprintf(out_buf + rc, "%c", c); + } + rc += sprintf(out_buf + rc, "\n"); + out: + return rc; +} + +/* + * prints header for debug entry + */ + +int debug_dflt_header_fn(debug_info_t * id, struct debug_view *view, + int area, debug_entry_t * entry, char *out_buf) +{ + struct timeval time_val; + unsigned long long time; + char *except_str; + unsigned long caller; + int rc = 0; + + if (out_buf == NULL) { + rc = DEBUG_PROC_HEADER_SIZE; + goto out; + } + + time = entry->id.stck; + /* adjust todclock to 1970 */ + time -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); + tod_to_timeval(time, &time_val); + + if (entry->id.fields.exception) + except_str = "*"; + else + except_str = "-"; + caller = (unsigned long) entry->caller; +#if defined(CONFIG_ARCH_S390X) + rc += sprintf(out_buf, "%02i %011lu:%06lu %1s %02i %016lx ", + area, time_val.tv_sec, + time_val.tv_usec, except_str, + entry->id.fields.cpuid, caller); +#else + caller &= 0x7fffffff; + rc += sprintf(out_buf, "%02i %011lu:%06lu %1s %02i %08lx ", + area, time_val.tv_sec, + time_val.tv_usec, except_str, + entry->id.fields.cpuid, caller); +#endif + out: + return rc; +} + +/* + * init_module: + */ + +#ifdef MODULE +int init_module(void) +{ + int rc = 0; +#ifdef DEBUG + printk("debug_module_init: \n"); +#endif + rc = debug_init(); + if (rc) + printk(KERN_INFO "debug: an error occurred with debug_init\n"); + return rc; +} + +/* + * cleanup_module: + */ + +void cleanup_module(void) +{ +#ifdef DEBUG + printk("debug_cleanup_module: \n"); +#endif + debug_delete_proc_dir_entry(&proc_root, debug_proc_root_entry); + return; +} + +#endif /* MODULE */ diff --git a/arch/s390/kernel/ebcdic.c b/arch/s390/kernel/ebcdic.c index 3c70c4aa5..fc7740649 100644 --- a/arch/s390/kernel/ebcdic.c +++ b/arch/s390/kernel/ebcdic.c @@ -161,7 +161,156 @@ __u8 _ebcasc[256] = /* - * EBCDIC 037 conversion table: + * ASCII (IBM PC 437) -> EBCDIC 500 + */ +__u8 _ascebc_500[256] = +{ + /*00 NUL SOH STX ETX EOT ENQ ACK BEL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + /* ->NL */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /*18 CAN EM SUB ESC FS GS RS US */ + /* ->IGS ->IRS ->IUS */ + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x4F, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0x4A, 0xE0, 0x5A, 0x5F, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0xBB, 0xD0, 0xA1, 0x07, + /*80*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*88*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*90*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*98*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E0 sz */ + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F8*/ + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +/* + * EBCDIC 500 -> ASCII (IBM PC 437) + */ +__u8 _ebcasc_500[256] = +{ + /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + /* 0x40 SP RSP ä ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + /* 0x48 [ . < ( + ! */ + 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21, + /* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + /* 0x58 ß ] $ * ) ; ^ */ + 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + /* 0x60 - / ---- Ä ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + /* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70 ---- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + /* 0x90 ° j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + /* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + /* 0xB0 ---- § ---- */ + 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + /* 0xB8 ---- | ---- ---- ---- ---- */ + 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07, + /* 0xC0 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8 H I ---- ö ---- */ + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + /* 0xD0 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8 Q R ---- ü */ + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + /* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8 Y Z ---- Ö ---- ---- ---- */ + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + /* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 +}; + + +/* + * EBCDIC 037/500 conversion table: * from upper to lower case */ __u8 _ebc_tolower[256] = @@ -202,7 +351,7 @@ __u8 _ebc_tolower[256] = /* - * EBCDIC 037 conversion table: + * EBCDIC 037/500 conversion table: * from lower to upper case */ __u8 _ebc_toupper[256] = diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index e39e18623..a1125e933 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -81,6 +81,7 @@ state = 0 flags = 4 sigpending = 8 need_resched = 24 +tsk_ptrace = 28 processor = 60 /* PSW related defines */ @@ -88,6 +89,13 @@ disable = 0xFC enable = 0x03 daton = 0x04 +/* + * Base Address of this Module --- saved in __LC_ENTRY_BASE + */ + .globl entry_base +entry_base: + +#define BASED(name) name-entry_base(%r13) #if 0 /* some code left lying around in case we need a @@ -118,39 +126,37 @@ sysc_dn: */ #define SAVE_ALL(psworg) \ - st %r15,__LC_SAVE_AREA ; \ + stm %r13,%r15,__LC_SAVE_AREA ; \ + stam %a2,%a4,__LC_SAVE_AREA+12 ; \ + basr %r13,0 ; /* temp base pointer */ \ + l %r13,.Lentry_base-.(%r13) ; /* load &entry_base to %r13 */ \ tm psworg+1,0x01 ; /* test problem state bit */ \ - jz 0f ; /* skip stack setup save */ \ + bz BASED(.+12) ; /* skip stack setup save */ \ l %r15,__LC_KERNEL_STACK ; /* problem state -> load ksp */ \ -0: ahi %r15,-SP_SIZE ; /* make room for registers & psw */ \ - srl %r15,3 ; \ - sll %r15,3 ; /* align stack pointer to 8 */ \ - stm %r0,%r14,SP_R0(%r15) ; /* store gprs 0-14 to kernel stack */ \ + lam %a2,%a4,BASED(.Lc_ac) ; /* set ac.reg. 2 to primary space */ \ + /* and access reg. 4 to home space */ \ +0: s %r15,BASED(.Lc_spsize); /* make room for registers & psw */ \ + n %r15,BASED(.Lc0xfffffff8) ; /* align stack pointer to 8 */ \ + stm %r0,%r12,SP_R0(%r15) ; /* store gprs 0-12 to kernel stack */ \ st %r2,SP_ORIG_R2(%r15) ; /* store original content of gpr 2 */ \ - mvc SP_RF(4,%r15),__LC_SAVE_AREA ; /* move R15 to stack */ \ + mvc SP_RD(12,%r15),__LC_SAVE_AREA ; /* move R13-R15 to stack */ \ stam %a0,%a15,SP_AREGS(%r15) ; /* store access registers to kst. */ \ + mvc SP_AREGS+8(12,%r15),__LC_SAVE_AREA+12 ; /* store ac. regs */ \ mvc SP_PSW(8,%r15),psworg ; /* move user PSW to stack */ \ - lhi %r0,psworg ; /* store trap indication */ \ + la %r0,psworg ; /* store trap indication */ \ st %r0,SP_TRAP(%r15) ; \ - xc 0(4,%r15),0(%r15) ; /* clear back chain */ \ - tm psworg+1,0x01 ; /* kmod.c .wishes the set_fs & gs */ \ - jz 1f ; /* to work across syscalls */ \ - slr %r0,%r0 ; \ - sar %a2,%r0 ; /* set ac.reg. 2 to primary space */ \ - lhi %r0,1 ; \ - sar %a4,%r0 ; /* set access reg. 4 to home space */ \ -1: + xc 0(4,%r15),0(%r15) ; /* clear back chain */ #define RESTORE_ALL \ - mvc __LC_RETURN_PSW(8,0),SP_PSW(%r15) ; /* move user PSW to lowcore */ \ - lam %a0,%a15,SP_AREGS(%r15) ; /* load the access registers */ \ - lm %r0,%r15,SP_R0(%r15) ; /* load gprs 0-15 of user */ \ - ni __LC_RETURN_PSW+1(0),0xfd ; /* clear wait state bit */ \ - lpsw __LC_RETURN_PSW /* back to caller */ + mvc __LC_RETURN_PSW(8),SP_PSW(%r15) ; /* move user PSW to lowcore */ \ + lam %a0,%a15,SP_AREGS(%r15) ; /* load the access registers */ \ + lm %r0,%r15,SP_R0(%r15) ; /* load gprs 0-15 of user */ \ + ni __LC_RETURN_PSW+1,0xfd ; /* clear wait state bit */ \ + lpsw __LC_RETURN_PSW /* back to caller */ #define GET_CURRENT /* load pointer to task_struct to R9 */ \ - lhi %r9,-8192 ; \ - nr %r9,15 + lr %r9,%r15 ; \ + n %r9,BASED(.Lc0xffffe000) /* @@ -162,22 +168,24 @@ sysc_dn: */ .globl resume resume: + basr %r1,0 +resume_base: l %r4,_TSS_PTREGS(%r3) tm SP_PSW-SP_PTREGS(%r4),0x40 # is the new process using per ? - jz RES_DN1 # if not we're fine - stctl %r9,%r11,24(%r15) # We are using per stuff + bz resume_noper-resume_base(%r1) # if not we're fine + stctl %r9,%r11,24(%r15) # We are using per stuff clc _TSS_PER(12,%r3),24(%r15) - je RES_DN1 # we got away without bashing TLB's - lctl %c9,%c11,_TSS_PER(%r3) # Nope we didn't -RES_DN1: + be resume_noper-resume_base(%r1) # we got away w/o bashing TLB's + lctl %c9,%c11,_TSS_PER(%r3) # Nope we didn't +resume_noper: stm %r6,%r15,24(%r15) # store resume registers of prev task st %r15,_TSS_KSP(%r2) # store kernel stack ptr to prev->tss.ksp - lhi %r0,-8192 - nr %r0,%r15 + lr %r0,%r15 + n %r0,.Lc0xffffe000-resume_base(%r1) l %r15,_TSS_KSP(%r3) # load kernel stack ptr from next->tss.ksp - lhi %r1,8191 + l %r1,.Lc8191-resume_base(%r1) or %r1,%r15 - ahi %r1,1 + la %r1,1(%r1) st %r1,__LC_KERNEL_STACK # __LC_KERNEL_STACK = new kernel stack stam %a2,%a2,_TSS_AR2(%r2) # store kernel access reg. 2 stam %a4,%a4,_TSS_AR4(%r2) # store kernel access reg. 4 @@ -192,38 +200,19 @@ RES_DN1: * are executed with interrupts enabled. */ -sysc_lit: - sysc_do_signal: .long do_signal - sysc_do_softirq: .long do_softirq - sysc_schedule: .long schedule - sysc_trace: .long syscall_trace -#ifdef CONFIG_SMP - sysc_schedtail: .long schedule_tail -#endif - sysc_clone: .long sys_clone - sysc_fork: .long sys_fork - sysc_vfork: .long sys_vfork - sysc_sigreturn: .long sys_sigreturn - sysc_rt_sigreturn: .long sys_rt_sigreturn - sysc_execve: .long sys_execve - sysc_sigsuspend: .long sys_sigsuspend - sysc_rt_sigsuspend: .long sys_rt_sigsuspend - .globl system_call system_call: SAVE_ALL(0x20) - XC SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) + xc SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) pgm_system_call: - basr %r13,0 - ahi %r13,sysc_lit-. # setup base pointer R13 to sysc_lit slr %r8,%r8 # gpr 8 is call save (-> tracesys) ic %r8,0x8B # get svc number from lowcore stosm 24(%r15),0x03 # reenable interrupts GET_CURRENT # load pointer to task_struct to R9 sll %r8,2 - l %r8,sys_call_table-sysc_lit(8,%r13) # get address of system call - tm flags+3(%r9),0x20 # PF_TRACESYS - jnz sysc_tracesys + l %r8,sys_call_table-entry_base(8,%r13) # get address of system call + tm tsk_ptrace+3(%r9),0x02 # PT_TRACESYS + bnz BASED(sysc_tracesys) basr %r14,%r8 # call sys_xxxx st %r2,SP_R2(%r15) # store return value (change R2 on stack) # ATTENTION: check sys_execve_glue before @@ -232,24 +221,24 @@ pgm_system_call: sysc_return: GET_CURRENT # load pointer to task_struct to R9 tm SP_PSW+1(%r15),0x01 # returning to user ? - jno sysc_leave # no-> skip bottom half, resched & signal + bno BASED(sysc_leave) # no-> skip bottom half, resched & signal # # check, if bottom-half has to be done # l %r0,__LC_IRQ_STAT # get softirq_active n %r0,__LC_IRQ_STAT+4 # and it with softirq_mask - jnz sysc_handle_bottom_half + bnz BASED(sysc_handle_bottom_half) # # check, if reschedule is needed # sysc_return_bh: icm %r0,15,need_resched(%r9) # get need_resched from task_struct - jnz sysc_reschedule + bnz BASED(sysc_reschedule) icm %r0,15,sigpending(%r9) # get sigpending from task_struct - jnz sysc_signal_return + bnz BASED(sysc_signal_return) sysc_leave: icm %r0,15,SP_SVC_STEP(%r15) # get sigpending from task_struct - jnz pgm_svcret + bnz BASED(pgm_svcret) stnsm 24(%r15),disable # disable I/O and ext. interrupts RESTORE_ALL @@ -259,24 +248,24 @@ sysc_leave: sysc_signal_return: la %r2,SP_PTREGS(%r15) # load pt_regs sr %r3,%r3 # clear *oldset - l %r1,sysc_do_signal-sysc_lit(%r13) - la %r14,sysc_leave-sysc_lit(%r13) + l %r1,BASED(.Ldo_signal) + la %r14,BASED(sysc_leave) br %r1 # return point is sysc_leave # # call trace before and after sys_call # sysc_tracesys: - l %r1,sysc_trace-sysc_lit(%r13) - lhi %r2,-ENOSYS + l %r1,BASED(.Ltrace) + l %r2,BASED(.Lc_ENOSYS) st %r2,SP_R2(%r15) # give sysc_trace an -ENOSYS retval basr %r14,%r1 lm %r3,%r6,SP_R3(%r15) l %r2,SP_ORIG_R2(%r15) basr %r14,%r8 # call sys_xxx st %r2,SP_R2(%r15) # store return value - l %r1,sysc_trace-sysc_lit(%r13) - la %r14,sysc_return-sysc_lit(%r13) + l %r1,BASED(.Ltrace) + la %r14,BASED(sysc_return) br %r1 # return point is sysc_return @@ -285,16 +274,16 @@ sysc_tracesys: # is zero # sysc_handle_bottom_half: - l %r1,sysc_do_softirq-sysc_lit(%r13) - la %r14,sysc_return_bh-sysc_lit(%r13) + l %r1,BASED(.Ldo_softirq) + la %r14,BASED(sysc_return_bh) br %r1 # call do_softirq # # call schedule with sysc_return as return-address # sysc_reschedule: - l %r1,sysc_schedule-sysc_lit(%r13) - la %r14,sysc_return-sysc_lit(%r13) + l %r1,BASED(.Lschedule) + la %r14,BASED(sysc_return) br %r1 # call scheduler, return to sysc_return # @@ -303,17 +292,17 @@ sysc_reschedule: .globl ret_from_fork ret_from_fork: basr %r13,0 - ahi %r13,sysc_lit-. # setup base pointer R13 to $SYSCDAT + l %r13,.Lentry_base-.(%r13) # setup base pointer to &entry_base GET_CURRENT # load pointer to task_struct to R9 stosm 24(%r15),0x03 # reenable interrupts sr %r0,%r0 # child returns 0 st %r0,SP_R2(%r15) # store return value (change R2 on stack) #ifdef CONFIG_SMP - l %r1,sysc_schedtail-sysc_lit(%r13) - la %r14,sysc_return-sysc_lit(%r13) + l %r1,BASED(.Lschedtail) + la %r14,BASED(sysc_return) br %r1 # call schedule_tail, return to sysc_return #else - j sysc_return + b BASED(sysc_return) #endif # @@ -324,22 +313,22 @@ ret_from_fork: # sys_clone_glue: la %r2,SP_PTREGS(%r15) # load pt_regs - l %r1,sysc_clone-sysc_lit(%r13) + l %r1,BASED(.Lclone) br %r1 # branch to sys_clone sys_fork_glue: la %r2,SP_PTREGS(%r15) # load pt_regs - l %r1,sysc_fork-sysc_lit(%r13) + l %r1,BASED(.Lfork) br %r1 # branch to sys_fork sys_vfork_glue: la %r2,SP_PTREGS(%r15) # load pt_regs - l %r1,sysc_vfork-sysc_lit(%r13) + l %r1,BASED(.Lvfork) br %r1 # branch to sys_vfork sys_execve_glue: la %r2,SP_PTREGS(%r15) # load pt_regs - l %r1,sysc_execve-sysc_lit(%r13) + l %r1,BASED(.Lexecve) lr %r12,%r14 # save return address basr %r14,%r1 # call sys_execve ltr %r2,%r2 # check if execve failed @@ -349,12 +338,12 @@ sys_execve_glue: sys_sigreturn_glue: la %r2,SP_PTREGS(%r15) # load pt_regs as parameter - l %r1,sysc_sigreturn-sysc_lit(%r13) + l %r1,BASED(.Lsigreturn) br %r1 # branch to sys_sigreturn sys_rt_sigreturn_glue: la %r2,SP_PTREGS(%r15) # load pt_regs as parameter - l %r1,sysc_rt_sigreturn-sysc_lit(%r13) + l %r1,BASED(.Lrt_sigreturn) br %r1 # branch to sys_sigreturn # @@ -369,7 +358,7 @@ sys_sigsuspend_glue: lr %r4,%r3 # move history1 parameter lr %r3,%r2 # move history0 parameter la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter - l %r1,sysc_sigsuspend-sysc_lit(%r13) + l %r1,BASED(.Lsigsuspend) la %r14,4(%r14) # skip store of return value br %r1 # branch to sys_sigsuspend @@ -377,10 +366,15 @@ sys_rt_sigsuspend_glue: lr %r4,%r3 # move sigsetsize parameter lr %r3,%r2 # move unewset parameter la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter - l %r1,sysc_rt_sigsuspend-sysc_lit(%r13) + l %r1,BASED(.Lrt_sigsuspend) la %r14,4(%r14) # skip store of return value br %r1 # branch to sys_rt_sigsuspend +sys_sigaltstack_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + l %r1,BASED(.Lsigaltstack) + br %r1 # branch to sys_sigreturn + .globl sys_call_table sys_call_table: .long sys_ni_syscall /* 0 */ @@ -459,7 +453,7 @@ sys_call_table: .long sys_sigpending .long sys_sethostname .long sys_setrlimit /* 75 */ - .long sys_getrlimit + .long sys_old_getrlimit .long sys_getrusage .long sys_gettimeofday .long sys_settimeofday @@ -569,13 +563,13 @@ sys_call_table: .long sys_getcwd .long sys_capget .long sys_capset /* 185 */ - .long sys_sigaltstack + .long sys_sigaltstack_glue .long sys_sendfile .long sys_ni_syscall /* streams1 */ .long sys_ni_syscall /* streams2 */ .long sys_vfork_glue /* 190 */ .long sys_getrlimit - .long sys_ni_syscall /* FIXME: problem with sys_mmap2: 6 parms */ + .long sys_mmap2 .long sys_truncate64 .long sys_ftruncate64 .long sys_stat64 /* 195 */ @@ -604,7 +598,8 @@ sys_call_table: .long sys_mincore .long sys_madvise .long sys_getdents64 /* 220 */ - .rept 255-220 + .long sys_fcntl64 + .rept 255-221 .long sys_ni_syscall .endr @@ -612,13 +607,6 @@ sys_call_table: * Program check handler routine */ -pgm_lit: - pgm_handle_per: .long handle_per_exception - pgm_jump_table: .long pgm_check_table - pgm_sysc_ret: .long sysc_return - pgm_sysc_lit: .long sysc_lit - pgm_do_signal: .long do_signal - .globl pgm_check_handler pgm_check_handler: /* @@ -634,110 +622,131 @@ pgm_check_handler: * we just ignore the PER event (FIXME: is there anything we have to do * for LPSW?). */ + stm %r13,%r15,__LC_SAVE_AREA + stam %a2,%a4,__LC_SAVE_AREA+12 + basr %r13,0 # temp base pointer + l %r13,.Lentry_base-.(%r13)# load &entry_base to %r13 tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception - jz pgm_sv # skip if not + bz BASED(pgm_sv) # skip if not tm __LC_PGM_OLD_PSW,0x40 # test if per event recording is on - jnz pgm_sv # skip if it is + bnz BASED(pgm_sv) # skip if it is # ok its one of the special cases, now we need to find out which one clc __LC_PGM_OLD_PSW(8),__LC_SVC_NEW_PSW - je pgm_svcper + be BASED(pgm_svcper) # no interesting special case, ignore PER event - lpsw 0x28 + lm %r13,%r15,__LC_SAVE_AREA + lpsw 0x28 # it was a single stepped SVC that is causing all the trouble pgm_svcper: - SAVE_ALL(0x20) + tm 0x21,0x01 # test problem state bit + bz BASED(.+12) # skip stack & access regs setup + l %r15,__LC_KERNEL_STACK # problem state -> load ksp + lam %a2,%a4,BASED(.Lc_ac) # set ac.reg. 2 to primary space + # and access reg. 4 to home space + s %r15,BASED(.Lc_spsize) # make room for registers & psw + n %r15,BASED(.Lc0xfffffff8) # align stack pointer to 8 + stm %r0,%r12,SP_R0(%r15) # store gprs 0-12 to kernel stack + st %r2,SP_ORIG_R2(%r15) # store original content of gpr 2 + mvc SP_RD(12,%r15),__LC_SAVE_AREA # move R13-R15 to stack + stam %a0,%a15,SP_AREGS(%r15) # store access registers to kst. + mvc SP_AREGS+8(12,%r15),__LC_SAVE_AREA+12 # store ac. regs + mvc SP_PSW(8,%r15),0x20 # move user PSW to stack + la %r0,0x20 # store trap indication + st %r0,SP_TRAP(%r15) + xc 0(4,%r15),0(%r15) # clear back chain mvi SP_SVC_STEP(%r15),1 # make SP_SVC_STEP nonzero mvc SP_PGM_OLD_ILC(4,%r15),__LC_PGM_ILC # save program check information - j pgm_system_call # now do the svc + b BASED(pgm_system_call) # now do the svc pgm_svcret: - lh %r7,SP_PGM_OLD_ILC(%r15) # get ilc from stack - lhi %r0,0x28 - st %r0,SP_TRAP(%r15) # set new trap indicator + mvi SP_TRAP+3(%r15),0x28 # set trap indication back to pgm_chk + lh %r7,SP_PGM_OLD_ILC(%r15) # get ilc from stack xc SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) - basr %r13,0 - ahi %r13,pgm_lit-. # setup base pointer - j pgm_no_sv + b BASED(pgm_no_sv) pgm_sv: - SAVE_ALL(0x28) - XC SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) - basr %r13,0 - ahi %r13,pgm_lit-. # setup base pointer R13 to $PGMDAT + tm 0x29,0x01 # test problem state bit + bz BASED(.+12) # skip stack & access regs setup + l %r15,__LC_KERNEL_STACK # problem state -> load ksp + lam %a2,%a4,BASED(.Lc_ac) # set ac.reg. 2 to primary space + # and access reg. 4 to home space + s %r15,BASED(.Lc_spsize) # make room for registers & psw + n %r15,BASED(.Lc0xfffffff8) # align stack pointer to 8 + stm %r0,%r12,SP_R0(%r15) # store gprs 0-12 to kernel stack + st %r2,SP_ORIG_R2(%r15) # store original content of gpr 2 + mvc SP_RD(12,%r15),__LC_SAVE_AREA # move R13-R15 to stack + stam %a0,%a15,SP_AREGS(%r15) # store access registers to kst. + mvc SP_AREGS+8(12,%r15),__LC_SAVE_AREA+12 # store ac. regs + mvc SP_PSW(8,%r15),0x28 # move user PSW to stack + la %r0,0x28 # store trap indication + st %r0,SP_TRAP(%r15) + xc 0(4,%r15),0(%r15) # clear back chain + xc SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) lh %r7,__LC_PGM_ILC # load instruction length pgm_no_sv: lh %r8,__LC_PGM_INT_CODE # N.B. saved int code used later KEEP it stosm 24(%r15),0x03 # reenable interrupts lr %r3,%r8 - lhi %r0,0x7f + la %r0,0x7f nr %r3,%r0 # clear per-event-bit - je pgm_dn # none of Martins exceptions occured bypass - l %r9,pgm_jump_table-pgm_lit(%r13) + be BASED(pgm_dn) # none of Martins exceptions occured bypass + l %r9,BASED(.Ljump_table) sll %r3,2 l %r9,0(%r3,%r9) # load address of handler routine la %r2,SP_PTREGS(%r15) # address of register-save area srl %r3,2 - chi %r3,0x4 # protection-exception ? - jne pgm_go # if not, + cl %r3,BASED(.Lc4) # protection-exception ? + bne BASED(pgm_go) # if not, l %r5,SP_PSW+4(15) # load psw addr sr %r5,%r7 # substract ilc from psw st %r5,SP_PSW+4(15) # store corrected psw addr pgm_go: basr %r14,%r9 # branch to interrupt-handler -pgm_dn: lhi %r0,0x80 +pgm_dn: la %r0,0x80 nr %r8,%r0 # check for per exception - je pgm_return + be BASED(pgm_return) la %r2,SP_PTREGS(15) # address of register-save area - l %r9,pgm_handle_per-pgm_lit(%r13) # load adr. of per handler - l %r14,pgm_sysc_ret-pgm_lit(%r13) # load adr. of system return - l %r13,pgm_sysc_lit-pgm_lit(%r13) + l %r9,BASED(.Lhandle_per) # load adr. of per handler + la %r14,BASED(sysc_return) # load adr. of system return br %r9 # branch to handle_per_exception + # # the backend code is the same as for sys-call # pgm_return: - l %r14,pgm_sysc_ret-pgm_lit(%r13) - l %r13,pgm_sysc_lit-pgm_lit(%r13) - br %r14 + b BASED(sysc_return) /* * IO interrupt handler routine */ -io_lit: - io_do_IRQ: .long do_IRQ - io_schedule: .long schedule - io_do_signal: .long do_signal - io_do_softirq: .long do_softirq - .globl io_int_handler io_int_handler: SAVE_ALL(0x38) - basr %r13,0 - ahi %r13,io_lit-. # setup base pointer R13 to $IODAT la %r2,SP_PTREGS(%r15) # address of register-save area sr %r3,%r3 icm %r3,%r3,__LC_SUBCHANNEL_NR # load subchannel nr & extend to int - l %r4,__LC_IO_INT_PARM # load interuption parm - l %r9,io_do_IRQ-io_lit(%r13) # load address of do_IRQ + l %r4,__LC_IO_INT_PARM # load interruption parm + l %r5,__LC_IO_INT_WORD # load interruption word + l %r9,BASED(.Ldo_IRQ) # load address of do_IRQ basr %r14,%r9 # branch to standard irq handler io_return: GET_CURRENT # load pointer to task_struct to R9 tm SP_PSW+1(%r15),0x01 # returning to user ? - jz io_leave # no-> skip resched & signal + bz BASED(io_leave) # no-> skip resched & signal stosm 24(%r15),0x03 # reenable interrupts # # check, if bottom-half has to be done # l %r0,__LC_IRQ_STAT # get softirq_active n %r0,__LC_IRQ_STAT+4 # and it with softirq_mask - jnz io_handle_bottom_half + bnz BASED(io_handle_bottom_half) io_return_bh: # # check, if reschedule is needed # icm %r0,15,need_resched(%r9) # get need_resched from task_struct - jnz io_reschedule + bnz BASED(io_reschedule) icm %r0,15,sigpending(%r9) # get sigpending from task_struct - jnz io_signal_return + bnz BASED(io_signal_return) io_leave: stnsm 24(%r15),disable # disable I/O and ext. interrupts RESTORE_ALL @@ -747,16 +756,16 @@ io_leave: # is zero # io_handle_bottom_half: - l %r1,io_do_softirq-io_lit(%r13) - la %r14,io_return_bh-io_lit(%r13) + l %r1,BASED(.Ldo_softirq) + la %r14,BASED(io_return_bh) br %r1 # call do_softirq # # call schedule with io_return as return-address # io_reschedule: - l %r1,io_schedule-io_lit(%r13) - la %r14,io_return-io_lit(%r13) + l %r1,BASED(.Lschedule) + la %r14,BASED(io_return) br %r1 # call scheduler, return to io_return # @@ -765,103 +774,46 @@ io_reschedule: io_signal_return: la %r2,SP_PTREGS(%r15) # load pt_regs sr %r3,%r3 # clear *oldset - l %r1,io_do_signal-io_lit(%r13) - la %r14,io_leave-io_lit(%r13) + l %r1,BASED(.Ldo_signal) + la %r14,BASED(io_leave) br %r1 # return point is io_leave /* * External interrupt handler routine */ -ext_lit: - ext_timer_int: .long do_timer_interrupt -#ifdef CONFIG_SMP - ext_call_int: .long do_ext_call_interrupt -#endif -#ifdef CONFIG_HWC - ext_hwc_int: .long do_hwc_interrupt -#endif -#ifdef CONFIG_MDISK - ext_mdisk_int: .long do_mdisk_interrupt -#endif -#ifdef CONFIG_IUCV - ext_iucv_int: .long do_iucv_interrupt -#endif - ext_io_lit: .long io_lit - ext_io_return: .long io_return - .globl ext_int_handler ext_int_handler: SAVE_ALL(0x18) - basr %r13,0 - ahi %r13,ext_lit-. # setup base pointer R13 to $EXTDAT la %r2,SP_PTREGS(%r15) # address of register-save area lh %r3,__LC_EXT_INT_CODE # error code -#ifdef CONFIG_SMP - chi %r3,0x1202 # EXTERNAL_CALL - jne ext_no_extcall - l %r9,ext_call_int-ext_lit(%r13) # load ext_call_interrupt - l %r14,ext_io_return-ext_lit(%r13) - l %r13,ext_io_lit-ext_lit(%r13) - br %r9 # branch to ext call handler -ext_no_extcall: -#endif - chi %r3,0x1004 # CPU_TIMER - jne ext_no_timer - l %r9,ext_timer_int-ext_lit(%r13) # load timer_interrupt - l %r14,ext_io_return-ext_lit(%r13) - l %r13,ext_io_lit-ext_lit(%r13) - br %r9 # branch to ext call handler -ext_no_timer: -#ifdef CONFIG_HWC - chi %r3,0x2401 # HWC interrupt - jne ext_no_hwc - l %r9,ext_hwc_int-ext_lit(%r13) # load addr. of hwc routine - l %r14,ext_io_return-ext_lit(%r13) - l %r13,ext_io_lit-ext_lit(%r13) - br %r9 # branch to ext call handler -ext_no_hwc: -#endif -#ifdef CONFIG_MDISK - chi %r3,0x2603 # diag 250 (VM) interrupt - jne ext_no_mdisk - l %r9,ext_mdisk_int-ext_lit(%r13) - l %r14,ext_io_return-ext_lit(%r13) - l %r13,ext_io_lit-ext_lit(%r13) - br %r9 # branch to ext call handler -ext_no_mdisk: -#endif -#ifdef CONFIG_IUCV - chi %r3,0x4000 # diag 250 (VM) interrupt - jne ext_no_iucv - l %r9,ext_iucv_int-ext_lit(%r13) - l %r14,ext_io_return-ext_lit(%r13) - l %r13,ext_io_lit-ext_lit(%r13) - br %r9 # branch to ext call handler -ext_no_iucv: -#endif - - l %r14,ext_io_return-ext_lit(%r13) - l %r13,ext_io_lit-ext_lit(%r13) - br %r14 # use backend code of io_int_handler + lr %r1,%r3 # calculate index = code & 0xff + n %r1,BASED(.Lc0xff) + sll %r1,2 + l %r9,BASED(.Lext_hash) + l %r9,0(%r1,%r9) # get first list entry for hash value + ltr %r9,%r9 # == NULL ? + bz BASED(io_return) # yes, nothing to do, exit +ext_int_loop: + ch %r3,8(%r9) # compare external interrupt code + be BASED(ext_int_found) + icm %r9,15,0(%r9) # next list entry + bnz BASED(ext_int_loop) + b BASED(io_return) +ext_int_found: + l %r9,4(%r9) # get handler address + la %r14,BASED(io_return) + br %r9 # branch to ext call handler /* * Machine check handler routines */ -mcck_lit: - mcck_crw_pending: .long do_crw_pending - .globl mcck_int_handler mcck_int_handler: SAVE_ALL(0x30) - basr %r13,0 - ahi %r13,mcck_lit-. # setup base pointer R13 to $MCCKDAT - tm __LC_MCCK_CODE+1,0x40 - jno mcck_no_crw - l %r1,mcck_crw_pending-mcck_lit(%r13) - basr %r14,%r1 # call do_crw_pending -mcck_no_crw: + l %r1,BASED(.Ls390_mcck) + basr %r14,%r1 # call machine check handler mcck_return: RESTORE_ALL @@ -876,11 +828,11 @@ restart_int_handler: lam %a0,%a15,__LC_AREGS_SAVE_AREA stosm 0(%r15),daton # now we can turn dat on lm %r6,%r15,24(%r15) # load registers from clone - bras %r14,restart_go - .long start_secondary -restart_go: - l %r14,0(%r14) + basr %r14,0 + l %r14,restart_addr-.(%r14) br %r14 # branch to start_secondary +restart_addr: + .long start_secondary #else /* * If we do not run with SMP enabled, let the new CPU crash ... @@ -896,3 +848,48 @@ restart_crash: restart_go: #endif +/* + * Integer constants + */ + .align 4 +.Lc0xfffffff8: .long -8 # to align stack pointer to 8 +.Lc0xffffe000: .long -8192 # to round stack pointer to &task_struct +.Lc8191: .long 8191 +.Lc_spsize: .long SP_SIZE +.Lc_ac: .long 0,0,1 +.Lc_ENOSYS: .long -ENOSYS +.Lc4: .long 4 +.Lc0x1202: .long 0x1202 +.Lc0x1004: .long 0x1004 +.Lc0x2401: .long 0x2401 +.Lc0x4000: .long 0x4000 +.Lc0xff: .long 0xff + +/* + * Symbol constants + */ +.Ls390_mcck: .long s390_do_machine_check +.Ldo_IRQ: .long do_IRQ +.Ldo_signal: .long do_signal +.Ldo_softirq: .long do_softirq +.Lentry_base: .long entry_base +.Lext_hash: .long ext_int_hash +.Lhandle_per: .long handle_per_exception +.Ljump_table: .long pgm_check_table +.Lschedule: .long schedule +.Lclone: .long sys_clone +.Lexecve: .long sys_execve +.Lfork: .long sys_fork +.Lrt_sigreturn:.long sys_rt_sigreturn +.Lrt_sigsuspend: + .long sys_rt_sigsuspend +.Lsigreturn: .long sys_sigreturn +.Lsigsuspend: .long sys_sigsuspend +.Lsigaltstack: .long sys_sigaltstack +.Ltrace: .long syscall_trace +.Lvfork: .long sys_vfork + +#ifdef CONFIG_SMP +.Lschedtail: .long schedule_tail +#endif + diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S index a3b5df25c..d41f398d8 100644 --- a/arch/s390/kernel/head.S +++ b/arch/s390/kernel/head.S @@ -2,20 +2,29 @@ * arch/s390/kernel/head.S * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Hartmut Penner (hp@de.ibm.com), * Martin Schwidefsky (schwidefsky@de.ibm.com), + * Rob van der Heij (rvdhei@iae.nl) * - * There are 4 different IPL methods + * There are 5 different IPL methods * 1) load the image directly into ram at address 0 and do an PSW restart * 2) linload will load the image from address 0x10000 to memory 0x10000 * and start the code thru LPSW 0x0008000080010000 (VM only, deprecated) * 3) generate the tape ipl header, store the generated image on a tape * and ipl from it + * In case of SL tape you need to IPL 5 times to get past VOL1 etc * 4) generate the vm reader ipl header, move the generated image to the * VM reader (use option NOH!) and do a ipl from reader (VM only) + * 5) direct call of start by the SALIPL loader * We use the cpuid to distinguish between VM and native ipl * params for kernel are pushed to 0x10400 (see setup.h) + + Changes: + Okt 25 2000 <rvdheij@iae.nl> + added code to skip HDR and EOF to allow SL tape IPL (5 retries) + changed first CCW from rewind to backspace block + */ #include <linux/config.h> @@ -24,40 +33,13 @@ #ifndef CONFIG_IPL .org 0 - .long 0x00080000,0x80000000+iplstart # Just a restart PSW - -iplstart: - l %r12,.Lparm # pointer to parameter area -# -# find out memory size -# - mvc 104(8,0),.Lpcmem0 # setup program check handler - slr %r2,%r2 - lhi %r3,1 - sll %r3,20 -.Lloop0: - l %r0,0(%r2) # test page - ar %r2,%r3 # add 1M - jnm .Lloop0 # r1 < 0x80000000 -> loop -.Lchkmem0: - n %r2,.L4malign0 # align to multiples of 4M - st %r2,MEMORY_SIZE-PARMAREA(%r12) # store memory size - slr %r2,%r2 - st %r2,INITRD_SIZE-PARMAREA(%r12) # null ramdisk - st %r2,INITRD_START-PARMAREA(%r12) - j start - -.Lparm: .long PARMAREA -.L4malign0:.long 0xffc00000 - .align 8 -.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 - + .long 0x00080000,0x80000000+startup # Just a restart PSW #else #ifdef CONFIG_IPL_TAPE #define IPL_BS 1024 .org 0 .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded - .long 0x07000000,0x60000001 # by ipl to addresses 0-23. + .long 0x27000000,0x60000001 # by ipl to addresses 0-23. .long 0x02000000,0x20000000+IPL_BS # (a PSW and two CCWs). .long 0x00000000,0x00000000 # external old psw .long 0x00000000,0x00000000 # svc old psw @@ -74,92 +56,6 @@ iplstart: .long 0x00080000,0x80000000+.Lioint # io new psw .org 0x100 -iplstart: - l %r1,0xb8 # load ipl subchannel number - lhi %r2,IPL_BS # load start address - bras %r14,.Lloader # load rest of ipl image - st %r1,__LC_IPLDEV # store ipl device number - l %r12,.Lparm # pointer to parameter area - -# -# find out memory size -# - mvc 104(8,0),.Lpcmem0 # setup program check handler - slr %r2,%r2 - lhi %r3,1 - sll %r3,20 -.Lloop0: - l %r0,0(%r2) # test page - ar %r2,%r3 # add 1M - jnm .Lloop0 # r1 < 0x80000000 -> loop -.Lchkmem0: - n %r2,.L4malign0 # align to multiples of 4M - st %r2,MEMORY_SIZE-PARMAREA(%r12) # store memory size - c %r2,.Lbigmem # more than 64 MB of memory ? - jl .Lmemok # if yes load ramdisk to 32 MB - mvc INITRD_START-PARMAREA(4,%r12),.Lrdstart -.Lmemok: - -# -# load parameter file from tape -# - l %r2,INITRD_START-PARMAREA(%r12) # use ramdisk location as temp - bras %r14,.Lloader # load parameter file - ltr %r2,%r2 # got anything ? - jz .Lnopf - chi %r2,895 - jnh .Lnotrunc - lhi %r2,895 -.Lnotrunc: - l %r4,INITRD_START-PARMAREA(%r12) - la %r5,0(%r4,%r2) - lr %r3,%r2 -.Lidebc: - tm 0(%r5),0x80 # high order bit set ? - jo .Ldocv # yes -> convert from EBCDIC - ahi %r5,-1 - brct %r3,.Lidebc - j .Lnocv -.Ldocv: - l %r3,.Lcvtab - tr 0(256,%r4),0(%r3) # convert parameters to ascii - tr 256(256,%r4),0(%r3) - tr 512(256,%r4),0(%r3) - tr 768(122,%r4),0(%r3) -.Lnocv: la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line - mvc 0(256,%r3),0(%r4) - mvc 256(256,%r3),256(%r4) - mvc 512(256,%r3),512(%r4) - mvc 768(122,%r3),768(%r4) - slr %r0,%r0 - j .Lcntlp -.Ldelspc: - ic %r0,0(%r2,%r3) - chi %r0,0x20 # is it a space ? - je .Lcntlp - ahi %r2,1 - j .Leolp -.Lcntlp: - brct %r2,.Ldelspc -.Leolp: - slr %r0,%r0 - stc %r0,0(%r2,%r3) # terminate buffer -.Lnopf: - -# -# load ramdisk from tape -# - l %r2,INITRD_START-PARMAREA(%r12) # load adr. of ramdisk - bras %r14,.Lloader # load ramdisk - st %r2,INITRD_SIZE-PARMAREA(%r12) # store size of ramdisk - ltr %r2,%r2 - jnz .Lrdcont - st %r2,INITRD_START-PARMAREA(%r12) # no ramdisk found, null it -.Lrdcont: -# -# everything loaded, go for it -# - j start # # subroutine for loading from tape # Paramters: @@ -173,32 +69,32 @@ iplstart: lctl %c6,%c6,.Lcr6 slr %r2,%r2 .Lldlp: - lhi %r6,3 # 3 retries + la %r6,3 # 3 retries .Lssch: ssch 0(%r3) # load chunk of IPL_BS bytes - jnz .Llderr + bnz .Llderr .Lw4end: - bras %r14,.Lwait4io + bas %r14,.Lwait4io tm 8(%r5),0x82 # do we have a problem ? - jnz .Lrecov + bnz .Lrecov slr %r7,%r7 icm %r7,3,10(%r5) # get residual count lcr %r7,%r7 - ahi %r7,IPL_BS # IPL_BS-residual=#bytes read + la %r7,IPL_BS(%r7) # IPL_BS-residual=#bytes read ar %r2,%r7 # add to total size tm 8(%r5),0x01 # found a tape mark ? - jnz .Ldone + bnz .Ldone l %r0,.Lccwread+4 # update CCW data addresses ar %r0,%r7 st %r0,.Lccwread+4 - j .Lldlp + b .Lldlp .Ldone: l %r14,.Lldret br %r14 # r2 contains the total size .Lrecov: - bras %r14,.Lsense # do the sensing - brct %r6,.Lssch # dec. retry count & branch - j .Llderr + bas %r14,.Lsense # do the sensing + bct %r6,.Lssch # dec. retry count & branch + b .Llderr # # Sense subroutine # @@ -206,11 +102,11 @@ iplstart: st %r14,.Lsnsret la %r7,.Lorbsense ssch 0(%r7) # start sense command - jnz .Llderr - bras %r14,.Lwait4io + bnz .Llderr + bas %r14,.Lwait4io l %r14,.Lsnsret tm 8(%r5),0x82 # do we have a problem ? - jnz .Llderr + bnz .Llderr br %r14 # # Wait for interrupt subroutine @@ -219,13 +115,13 @@ iplstart: lpsw .Lwaitpsw .Lioint: c %r1,0xb8 # compare subchannel number - jne .Lwait4io + bne .Lwait4io tsch 0(%r5) slr %r0,%r0 tm 8(%r5),0x82 # do we have a problem ? - jnz .Lwtexit + bnz .Lwtexit tm 8(%r5),0x04 # got device end ? - jz .Lwait4io + bz .Lwait4io .Lwtexit: br %r14 .Llderr: @@ -249,18 +145,12 @@ iplstart: .Lcr6: .long 0xff000000 .align 8 .Lcrash:.long 0x000a0000,0x00000000 -.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 -.Lparm: .long PARMAREA -.L4malign0:.long 0xffc00000 -.Lbigmem:.long 0x04000000 -.Lrdstart:.long 0x02000000 .Lldret:.long 0 .Lsnsret: .long 0 -.Lcvtab:.long _ebcasc # ebcdic to ascii table - #endif /* CONFIG_IPL_TAPE */ #ifdef CONFIG_IPL_VM +#define IPL_BS 0x730 .org 0 .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded .long 0x02000018,0x60000050 # by ipl to addresses 0-23. @@ -287,103 +177,7 @@ iplstart: .long 0x02000690,0x60000050 .long 0x020006e0,0x20000050 - .org 0xf0 -iplstart: - l %r1,0xb8 # load ipl subchannel number - lhi %r2,0x730 # load start address - bras %r14,.Lloader # load rest of ipl image - st %r1,__LC_IPLDEV # store ipl device number - l %r12,.Lparm # pointer to parameter area - -# -# find out memory size -# - mvc 104(8,0),.Lpcmem0 # setup program check handler - slr %r2,%r2 - lhi %r3,1 - sll %r3,20 -.Lloop0: - l %r0,0(%r2) # test page - ar %r2,%r3 # add 1M - jnm .Lloop0 # r1 < 0x80000000 -> loop -.Lchkmem0: - n %r2,.L4malign0 # align to multiples of 4M - st %r2,MEMORY_SIZE-PARMAREA(%r12) # store memory size - c %r2,.Lbigmem # more than 64 MB of memory ? - jl .Lmemok # if yes load ramdisk to 32 MB - mvc INITRD_START-PARMAREA(4,%r12),.Lrdstart -.Lmemok: - -# -# load parameter file from reader -# - l %r2,INITRD_START-PARMAREA(%r12) # use ramdisk location as temp - bras %r14,.Lloader # load parameter file - ltr %r2,%r2 # got anything ? - jz .Lnopf - chi %r2,895 - jnh .Lnotrunc - lhi %r2,895 -.Lnotrunc: - l %r4,INITRD_START-PARMAREA(%r12) - la %r5,0(%r4,%r2) - lr %r3,%r2 -.Lidebc: - tm 0(%r5),0x80 # high order bit set ? - jo .Ldocv # yes -> convert from EBCDIC - ahi %r5,-1 - brct %r3,.Lidebc - j .Lnocv -.Ldocv: - l %r3,.Lcvtab - tr 0(256,%r4),0(%r3) # convert parameters to ascii - tr 256(256,%r4),0(%r3) - tr 512(256,%r4),0(%r3) - tr 768(122,%r4),0(%r3) -.Lnocv: la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line - mvc 0(256,%r3),0(%r4) - mvc 256(256,%r3),256(%r4) - mvc 512(256,%r3),512(%r4) - mvc 768(122,%r3),768(%r4) - slr %r0,%r0 - j .Lcntlp -.Ldelspc: - ic %r0,0(%r2,%r3) - chi %r0,0x20 # is it a space ? - je .Lcntlp - ahi %r2,1 - j .Leolp -.Lcntlp: - brct %r2,.Ldelspc -.Leolp: - slr %r0,%r0 - stc %r0,0(%r2,%r3) # terminate buffer -.Lnopf: - -# -# load ramdisk from reader -# - l %r2,INITRD_START-PARMAREA(%r12) # load adr. of ramdisk - bras %r14,.Lloader # load ramdisk - st %r2,INITRD_SIZE-PARMAREA(%r12) # store size of ramdisk - ltr %r2,%r2 - jnz .Lrdcont - st %r2,INITRD_START-PARMAREA(%r12) # no ramdisk found, null it -.Lrdcont: - -# -# everything loaded, reset files in reader, then go for it -# - stidp __LC_CPUID # store cpuid - lh %r0,__LC_CPUID+4 # get cpu version - chi %r0,0x7490 # running on P/390 ? - je start # no -> skip reset - la %r2,.Lreset - lhi %r3,26 - .long 0x83230008 - j start - # # subroutine for loading cards from the reader # @@ -394,29 +188,29 @@ iplstart: la %r7,20 .Linit: st %r2,4(%r6) # initialize CCW data addresses - ahi %r2,0x50 - ahi %r6,8 - brct 7,.Linit + la %r2,0x50(%r2) + la %r6,8(%r6) + bct 7,.Linit lctl %c6,%c6,.Lcr6 # set IO subclass mask slr %r2,%r2 .Lldlp: ssch 0(%r3) # load chunk of 1600 bytes - jnz .Llderr + bnz .Llderr .Lwait4irq: mvc __LC_IO_NEW_PSW(8),.Lnewpsw # set up IO interrupt psw lpsw .Lwaitpsw .Lioint: c %r1,0xb8 # compare subchannel number - jne .Lwait4irq + bne .Lwait4irq tsch 0(%r5) slr %r0,%r0 ic %r0,8(%r5) # get device status chi %r0,8 # channel end ? - je .Lcont + be .Lcont chi %r0,12 # channel end + device end ? - je .Lcont + be .Lcont l %r0,4(%r5) s %r0,8(%r3) # r0/8 = number of ccws executed @@ -436,9 +230,9 @@ iplstart: ahi %r0,0x640 st %r0,4(%r6) ahi %r6,8 - brct 7,.Lincr + bct 7,.Lincr - j .Lldlp + b .Lldlp .Llderr: lpsw .Lcrash @@ -447,16 +241,7 @@ iplstart: .Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 .Lcr6: .long 0xff000000 .Lloadp:.long 0,0 -.Lparm: .long PARMAREA -.L4malign0:.long 0xffc00000 -.Lbigmem:.long 0x04000000 -.Lrdstart:.long 0x02000000 -.Lcvtab:.long _ebcasc # ebcdic to ascii table -.Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40 - .byte 0xc1,0xd3,0xd3,0x40,0xd2,0xc5,0xc5,0xd7,0x40,0xd5,0xd6 - .byte 0xc8,0xd6,0xd3,0xc4 # "change rdr all keep nohold" .align 8 -.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 .Lcrash:.long 0x000a0000,0x00000000 .Lnewpsw: .long 0x00080000,0x80000000+.Lioint @@ -468,79 +253,330 @@ iplstart: .long 0x02600050,0x00000000 .endr .long 0x02200050,0x00000000 - - .org 0x730 # end of the area loaded by the ipl channel program #endif /* CONFIG_IPL_VM */ +iplstart: + lh %r1,0xb8 # test if subchannel number + bct %r1,.Lnoload # is valid + l %r1,0xb8 # load ipl subchannel number + la %r2,IPL_BS # load start address + bas %r14,.Lloader # load rest of ipl image + st %r1,__LC_IPLDEV # store ipl device number + l %r12,.Lparm # pointer to parameter area + +# +# load parameter file from ipl device +# +.Lagain1: + l %r2,INITRD_START-PARMAREA(%r12) # use ramdisk location as temp + bas %r14,.Lloader # load parameter file + ltr %r2,%r2 # got anything ? + bz .Lnopf + chi %r2,895 + bnh .Lnotrunc + la %r2,895 +.Lnotrunc: + l %r4,INITRD_START-PARMAREA(%r12) + clc 0(3,%r4),.L_hdr # if it is HDRx + bz .Lagain1 # skip dataset header + clc 0(3,%r4),.L_eof # if it is EOFx + bz .Lagain1 # skip dateset trailer + la %r5,0(%r4,%r2) + lr %r3,%r2 +.Lidebc: + tm 0(%r5),0x80 # high order bit set ? + bo .Ldocv # yes -> convert from EBCDIC + ahi %r5,-1 + bct %r3,.Lidebc + b .Lnocv +.Ldocv: + l %r3,.Lcvtab + tr 0(256,%r4),0(%r3) # convert parameters to ascii + tr 256(256,%r4),0(%r3) + tr 512(256,%r4),0(%r3) + tr 768(122,%r4),0(%r3) +.Lnocv: la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line + mvc 0(256,%r3),0(%r4) + mvc 256(256,%r3),256(%r4) + mvc 512(256,%r3),512(%r4) + mvc 768(122,%r3),768(%r4) + slr %r0,%r0 + b .Lcntlp +.Ldelspc: + ic %r0,0(%r2,%r3) + chi %r0,0x20 # is it a space ? + be .Lcntlp + ahi %r2,1 + b .Leolp +.Lcntlp: + brct %r2,.Ldelspc +.Leolp: + slr %r0,%r0 + stc %r0,0(%r2,%r3) # terminate buffer +.Lnopf: + +# +# load ramdisk from ipl device +# +.Lagain2: + l %r2,INITRD_START-PARMAREA(%r12) # load adr. of ramdisk + bas %r14,.Lloader # load ramdisk + st %r2,INITRD_SIZE-PARMAREA(%r12) # store size of ramdisk + ltr %r2,%r2 + bnz .Lrdcont + st %r2,INITRD_START-PARMAREA(%r12) # no ramdisk found, null it +.Lrdcont: + l %r2,INITRD_START-PARMAREA(%r12) + + clc 0(3,%r2),.L_hdr # skip HDRx and EOFx + bz .Lagain2 + clc 0(3,%r2),.L_eof + bz .Lagain2 + +#ifdef CONFIG_IPL_VM +# +# reset files in VM reader +# + stidp __LC_CPUID # store cpuid + lh %r0,__LC_CPUID+4 # get cpu version + chi %r0,0x7490 # running on P/390 ? + be start # no -> skip reset + la %r2,.Lreset + lhi %r3,26 + .long 0x83230008 +#endif + +# +# everything loaded, go for it +# +.Lnoload: + l %r1,.Lstartup + br %r1 + +.Lparm: .long PARMAREA +.Lstartup: .long startup +.Lcvtab:.long _ebcasc # ebcdic to ascii table +.Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40 + .byte 0xc1,0xd3,0xd3,0x40,0xd2,0xc5,0xc5,0xd7,0x40,0xd5,0xd6 + .byte 0xc8,0xd6,0xd3,0xc4 # "change rdr all keep nohold" +.L_eof: .long 0xc5d6c600 /* C'EOF' */ +.L_hdr: .long 0xc8c4d900 /* C'HDR' */ + #endif /* CONFIG_IPL */ # +# SALIPL loader support. Based on a patch by Rob van der Heij. +# This entry point is called directly from the SALIPL loader and +# doesn't need a builtin ipl record. +# + .org 0x800 + .globl start +start: + stm %r0,%r15,0x07b0 # store registers + basr %r12,%r0 +.base: + l %r11,.parm + l %r8,.cmd # pointer to command buffer + + ltr %r9,%r9 # do we have SALIPL parameters? + bp .sk8x8 + + mvc 0(64,%r8),0x00b0 # copy saved registers + xc 64(240-64,%r8),0(%r8) # remainder of buffer + tr 0(64,%r8),.lowcase + b .gotr +.sk8x8: + mvc 0(240,%r8),0(%r9) # copy iplparms into buffer +.gotr: + l %r10,.tbl # EBCDIC to ASCII table + tr 0(240,%r8),0(%r10) + stidp __LC_CPUID # Are we running on VM maybe + cli __LC_CPUID,0xff + bnz .test + .long 0x83300060 # diag 3,0,x'0060' - storage size + b .done +.test: + mvc 0x68(8),.pgmnw # set up pgm check handler + l %r2,.fourmeg + lr %r3,%r2 + bctr %r3,%r0 # 4M-1 +.loop: iske %r0,%r3 + ar %r3,%r2 +.pgmx: + sr %r3,%r2 + la %r3,1(%r3) +.done: + st %r3,MEMORY_SIZE-PARMAREA(%r11) + slr %r0,%r0 + st %r0,INITRD_SIZE-PARMAREA(%r11) + st %r0,INITRD_START-PARMAREA(%r11) + j startup # continue with startup +.tbl: .long _ebcasc # translate table +.cmd: .long COMMAND_LINE # address of command line buffer +.parm: .long PARMAREA +.fourmeg: .long 0x00400000 # 4M +.pgmnw: .long 0x00080000,.pgmx +.lowcase: + .byte 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07 + .byte 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f + .byte 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17 + .byte 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f + .byte 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27 + .byte 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f + .byte 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37 + .byte 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f + .byte 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47 + .byte 0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f + .byte 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57 + .byte 0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f + .byte 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67 + .byte 0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f + .byte 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77 + .byte 0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f + + .byte 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87 + .byte 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f + .byte 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97 + .byte 0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f + .byte 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7 + .byte 0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf + .byte 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7 + .byte 0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf + .byte 0xc0,0x81,0x82,0x83,0x84,0x85,0x86,0x87 # .abcdefg + .byte 0x88,0x89,0xca,0xcb,0xcc,0xcd,0xce,0xcf # hi + .byte 0xd0,0x91,0x92,0x93,0x94,0x95,0x96,0x97 # .jklmnop + .byte 0x98,0x99,0xda,0xdb,0xdc,0xdd,0xde,0xdf # qr + .byte 0xe0,0xe1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7 # ..stuvwx + .byte 0xa8,0xa9,0xea,0xeb,0xec,0xed,0xee,0xef # yz + .byte 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7 + .byte 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff + +# # startup-code at 0x10000, running in real mode -# this is called either by the ipl loader or directly by PSW restart or linload +# this is called either by the ipl loader or directly by PSW restart +# or linload or SALIPL # .org 0x10000 - .globl start -start: basr %r13,0 # get base -.LPG1: lctl %c1,%c1,.Lpstd-.LPG1(%r13) # load pstd - lctl %c7,%c7,.Lpstd-.LPG1(%r13) # load sstd - lctl %c13,%c13,.Lpstd-.LPG1(%r13) # load hstd - lctl %c0,%c0,.Lcr0-.LPG1(%r13) # set CR0 +startup:basr %r13,0 # get base +.LPG1: lctl %c0,%c15,.Lctl-.LPG1(%r13) # load control registers l %r12,.Lparm1-.LPG1(%r13) # pointer to parameter area # -# find out memory size. That is done in the ipl loader too but for -# ipl from dasd the size of the memory has to be detected too... +# find out memory size. # - icm %r0,15,MEMORY_SIZE-PARMAREA(%r12) - jnz .Lsizeok - mvc 104(8,0),.Lpcmem-.LPG1(%r13) # setup program check handler - slr %r1,%r1 + mvc 104(8),.Lpcmem-.LPG1(%r13) # setup program check handler lhi %r2,1 - sll %r2,20 + sll %r2,17 # test in increments of 128KB + lr %r1,%r2 + ahi %r1,-4 # test last word in the segment .Lloop: - l %r0,0(%r1) # test page - ar %r1,%r2 # add 1M - jnm .Lloop # r1 < 0x80000000 -> loop + l %r0,0(%r1) # test 128KB segment + st %r0,0(%r1) + ar %r1,%r2 # add 128KB + bnm .Lloop-.LPG1(%r13) # r1 < 0x80000000 -> loop .Lchkmem: n %r1,.L4malign-.LPG1(%r13) # align to multiples of 4M st %r1,MEMORY_SIZE-PARMAREA(%r12) # store memory size .Lsizeok: # +# Now we have to move the ramdisk to a location approriate for the +# memory size. If we have more than 64 MB of memory we move it to 32MB +# to make room for the page tables set up by paging_init. +# + l %r1,MEMORY_SIZE-PARMAREA(%r12) + cl %r1,.Lbigmem-.LPG1(%r13) # memory < 64mb ? + bl .Lnomove-.LPG1(%r13) # if yes ramdisk @8MB is ok + icm %r4,15,INITRD_START-PARMAREA(%r12) + bz .Lnomove-.LPG1(%r13) + l %r2,.Lrdstart-.LPG1(%r13) # new address of ramdisk + st %r2,INITRD_START-PARMAREA(%r12) + l %r1,INITRD_SIZE-PARMAREA(%r12) + ar %r2,%r1 # we start moving at the end + ar %r4,%r1 # because new location > old location +.Lmove: lr %r0,%r2 # new - old is the maximum we can move + sr %r0,%r4 # because of overlapping + cr %r0,%r1 # we shouldn't move more than there is + bnh .Lnoend-.LPG1(%r13) + lr %r0,%r1 +.Lnoend:cl %r0,.Lmaxchunk-.LPG1(%r13) # mvcl can move 2^24-1 in one go + bnh .Lchunk-.LPG1(%r13) + l %r0,.Lmaxchunk-.LPG1(%r13) +.Lchunk:sr %r2,%r0 # make source & destination pointer + sr %r4,%r0 + lr %r3,%r0 # set source & destination length + lr %r5,%r0 + mvcl %r2,%r4 + sr %r2,%r0 # substract length again, since + sr %r4,%r0 # mvcl added it to the pointers + sr %r1,%r0 # substract chunk size from length + bnz .Lmove-.LPG1(%r13) +.Lnomove: + +# # find out if we are running under VM # stidp __LC_CPUID # store cpuid tm __LC_CPUID,0xff # running under VM ? - jno .Lnovm + bno .Lnovm-.LPG1(%r13) oi MACHINE_FLAGS+3-PARMAREA(%r12),1 # set VM flag .Lnovm: lh %r0,__LC_CPUID+4 # get cpu version chi %r0,0x7490 # running on a P/390 ? - jne .Lnop390 + bne .Lnop390-.LPG1(%r13) oi MACHINE_FLAGS+3-PARMAREA(%r12),4 # set P/390 flag .Lnop390: # # find out if we have an IEEE fpu # - mvc 104(8,0),.Lpcfpu-.LPG1(%r13) # setup program check handler + mvc 104(8),.Lpcfpu-.LPG1(%r13) # setup program check handler ld %f0,.Lflt0-.LPG1(%r13) # load (float) 0.0 ldr %f2,%f0 adbr %f0,%f2 # test IEEE add instruction oi MACHINE_FLAGS+3-PARMAREA(%r12),2 # set IEEE fpu flag .Lchkfpu: +# +# find out if we have the CSP instruction +# + mvc 104(8),.Lpccsp-.LPG1(%r13) # setup program check handler + la %r0,0 + lr %r1,%r0 + la %r2,.Lflt0-.LPG1(%r13) + csp %r0,%r2 # Test CSP instruction + oi MACHINE_FLAGS+3-PARMAREA(%r12),8 # set CSP flag +.Lchkcsp: + lpsw .Lentry-.LPG1(13) # jump to _stext in primary-space, # virtual and never return ... .align 8 -.Lentry:.long 0x04080000,0x80000000 + _stext -.Lpstd: .long .Lpgd+0x7F # segment-table -.Lcr0: .long 0x04b50002 +.Lentry:.long 0x00080000,0x80000000 + _stext +.Lctl: .long 0x04b50002 # cr0: various things + .long 0 # cr1: primary space segment table + .long 0 # cr2: access register translation + .long 0 # cr3: instruction authorization + .long 0 # cr4: instruction authorization + .long 0 # cr5: various things + .long 0 # cr6: I/O interrupts + .long 0 # cr7: secondary space segment table + .long 0 # cr8: access registers translation + .long 0 # cr9: tracing off + .long 0 # cr10: tracing off + .long 0 # cr11: tracing off + .long 0 # cr12: tracing off + .long 0 # cr13: home space segment table + .long 0xc0000000 # cr14: machine check handling off + .long 0 # cr15: linkage stack operations .Lpcmem:.long 0x00080000,0x80000000 + .Lchkmem .Lpcfpu:.long 0x00080000,0x80000000 + .Lchkfpu +.Lpccsp:.long 0x00080000,0x80000000 + .Lchkcsp .Lflt0: .double 0 .Lparm1:.long PARMAREA .L4malign:.long 0xffc00000 +.Lbigmem:.long 0x04000000 +.Lrdstart:.long 0x02000000 +.Lmaxchunk:.long 0x00ffffff # # params at 10400 (setup.h) @@ -555,17 +591,8 @@ start: basr %r13,0 # get base .word 0 # RAMDISK_FLAGS .org COMMAND_LINE -# .byte "root=/dev/nfs rw nfsroot=9.164.160.7:/home/mschwide/nfsboot " -# .byte "ip=9.164.147.12:9.164.160.7:9.164.147.1:255.255.255.0:vmlinux:tr0:off" -# .byte "root=/dev/nfs nfsroot=9.164.160.7:/home/mschwide/nfsboot " -# .byte "ip=9.164.181.228:9.164.160.7:9.164.181.1:255.255.224.0:vmlinux:tr0:off" -# .byte "root=/dev/nfs nfsroot=9.164.160.7:/home/pasch/nfsboot " -# .byte "ip=9.164.185.120:9.164.160.7:9.164.181.1:255.255.224.0:vmlinux:tr0:off" -# .byte "mdisk=402:65536:1229,403:131072:2780 root=/dev/mnda ro" -# .byte "root=/dev/nfs rw nfsroot=9.164.160.209:/usr/local/nfsboot " -# .byte "ip=9.164.181.228:9.164.160.209:9.164.181.1:255.255.224.0:vmlinux:tr0:off" .byte "root=/dev/ram0 ro" -# .byte 0 + .byte 0 # # startup-code, running in virtual mode @@ -626,43 +653,3 @@ _stext: basr %r13,0 # get base .align 8 .Ldw: .long 0x000a0000,0x00000000 -# -# tempory segment-table at 0x11000 -# - .org 0x11000 -.Lpgd: .long .Lpt0+0x1f # 00000000-000fffff - .long .Lpt1+0x1f # 00100000-001fffff - .long .Lpt2+0x1f # 00200000-002fffff - .long .Lpt3+0x1f # 00300000-003fffff - .fill 2044,4,0x20 # 00400000-7fffffff - -# -# tempory page-tables at 0x12000-0x15fff -# - .macro mktable from,to - .long \from*0x10000 - .long \from*0x10000+0x1000 - .long \from*0x10000+0x2000 - .long \from*0x10000+0x3000 - .long \from*0x10000+0x4000 - .long \from*0x10000+0x5000 - .long \from*0x10000+0x6000 - .long \from*0x10000+0x7000 - .long \from*0x10000+0x8000 - .long \from*0x10000+0x9000 - .long \from*0x10000+0xa000 - .long \from*0x10000+0xb000 - .long \from*0x10000+0xc000 - .long \from*0x10000+0xd000 - .long \from*0x10000+0xe000 - .long \from*0x10000+0xf000 - .if \to-\from - mktable "(\from+1)",\to - .endif - .endm - -.Lpt0: mktable 0,15 -.Lpt1: mktable 16,31 -.Lpt2: mktable 32,47 -.Lpt3: mktable 48,63 - diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index ba513325a..989aa0720 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -20,11 +20,11 @@ #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/string.h> #include <linux/random.h> #include <linux/smp.h> -#include <linux/tasks.h> +#include <linux/threads.h> #include <linux/smp_lock.h> #include <linux/init.h> @@ -205,6 +205,8 @@ static inline void wait_on_irq(int cpu) if (!local_bh_count(cpu) && atomic_read(&global_bh_count)) continue; + /* this works even though global_irq_lock not + a long, but is arch-specific --RR */ if (!test_and_set_bit(0,&global_irq_lock)) break; } @@ -243,6 +245,8 @@ void synchronize_irq(void) static inline void get_irqlock(int cpu) { + /* this works even though global_irq_lock not a long, but is + arch-specific --RR */ if (test_and_set_bit(0,&global_irq_lock)) { /* do we already hold the lock? */ if ( cpu == atomic_read(&global_irq_holder)) @@ -398,7 +402,7 @@ void enable_nop(int irq) void __init init_IRQ(void) { - s390_init_IRQ(); + s390_init_IRQ(); } diff --git a/arch/s390/kernel/mathemu.c b/arch/s390/kernel/mathemu.c index 78b6e5ec6..23c6c72a2 100644 --- a/arch/s390/kernel/mathemu.c +++ b/arch/s390/kernel/mathemu.c @@ -9,6 +9,7 @@ * that does not have the IEEE fpu */ +#include <linux/config.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/ptrace.h> @@ -16,7 +17,59 @@ #include <asm/uaccess.h> #include <asm/mathemu.h> -static void set_CC_df(__u64 val1,__u64 val2) { +#ifdef CONFIG_SYSCTL +int sysctl_ieee_emulation_warnings=1; +#endif + +#define mathemu_put_user(x, ptr) \ +{ \ + if(put_user((x),(ptr))) \ + return 1; \ +} + +#define mathemu_get_user(x, ptr) \ +{ \ + if(get_user((x),(ptr))) \ + return 1; \ +} + + +#define mathemu_copy_from_user(to,from,n) \ +{ \ + if(copy_from_user((to),(from),(n))==-EFAULT) \ + return 1; \ +} + + +#define mathemu_copy_to_user(to, from, n) \ +{ \ + if(copy_to_user((to),(from),(n))==-EFAULT) \ + return 1; \ +} + + + +static void display_emulation_not_implemented(char *instr) +{ + struct pt_regs *regs; + __u16 *location; + +#if CONFIG_SYSCTL + if(sysctl_ieee_emulation_warnings) +#endif + { + regs=current->thread.regs; + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + printk("%s ieee fpu instruction not emulated process name: %s pid: %d \n", + instr, + current->comm, current->pid); + printk("%s's PSW: %08lx %08lx\n",instr, + (unsigned long) regs->psw.mask, + (unsigned long) location); + } +} + +static int set_CC_df(__u64 val1,__u64 val2) { int rc; rc = __cmpdf2(val1,val2); current->thread.regs->psw.mask &= 0xFFFFCFFF; @@ -28,9 +81,10 @@ static void set_CC_df(__u64 val1,__u64 val2) { current->thread.regs->psw.mask |= 0x00002000; break; } + return 0; } -static void set_CC_sf(__u32 val1,__u32 val2) { +static int set_CC_sf(__u32 val1,__u32 val2) { int rc; rc = __cmpsf2(val1,val2); current->thread.regs->psw.mask &= 0xFFFFCFFF; @@ -42,384 +96,473 @@ static void set_CC_sf(__u32 val1,__u32 val2) { current->thread.regs->psw.mask |= 0x00002000; break; } + return 0; } -static void emu_adb (int rx, __u64 val) { +static int emu_adb (int rx, __u64 val) { current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d,val); set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_adbr (int rx, int ry) { +static int emu_adbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d, current->thread.fp_regs.fprs[ry].d); set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_aeb (int rx, __u32 val) { +static int emu_aeb (int rx, __u32 val) { current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f,val); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_aebr (int rx, int ry) { +static int emu_aebr (int rx, int ry) { current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f, current->thread.fp_regs.fprs[ry].f); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_axbr (int rx, int ry) { - printk("axbr emulation not implemented!\n"); +static int emu_axbr (int rx, int ry) { + display_emulation_not_implemented("axbr"); + return 0; } -static void emu_cdb (int rx, __u64 val) { +static int emu_cdb (int rx, __u64 val) { set_CC_df(current->thread.fp_regs.fprs[rx].d,val); + return 0; } -static void emu_cdbr (int rx, int ry) { +static int emu_cdbr (int rx, int ry) { set_CC_df(current->thread.fp_regs.fprs[rx].d,current->thread.fp_regs.fprs[ry].d); + return 0; } -static void emu_cdfbr (int rx, int ry) { +static int emu_cdfbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].d = __floatsidf(current->thread.regs->gprs[ry]); + return 0; } -static void emu_ceb (int rx, __u32 val) { +static int emu_ceb (int rx, __u32 val) { set_CC_sf(current->thread.fp_regs.fprs[rx].f,val); + return 0; } -static void emu_cebr (int rx, int ry) { +static int emu_cebr (int rx, int ry) { set_CC_sf(current->thread.fp_regs.fprs[rx].f,current->thread.fp_regs.fprs[ry].f); + return 0; } -static void emu_cefbr (int rx, int ry) { +static int emu_cefbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].f = __floatsisf(current->thread.regs->gprs[ry]); + return 0; } -static void emu_cfdbr (int rx, int ry, int mask) { +static int emu_cfdbr (int rx, int ry, int mask) { current->thread.regs->gprs[rx] = __fixdfsi(current->thread.fp_regs.fprs[ry].d); + return 0; } -static void emu_cfebr (int rx, int ry, int mask) { +static int emu_cfebr (int rx, int ry, int mask) { current->thread.regs->gprs[rx] = __fixsfsi(current->thread.fp_regs.fprs[ry].f); + return 0; } -static void emu_cfxbr (int rx, int ry, int mask) { - printk("cfxbr emulation not implemented!\n"); +static int emu_cfxbr (int rx, int ry, int mask) { + display_emulation_not_implemented("cfxbr"); + return 0; } -static void emu_cxbr (int rx, int ry) { - printk("cxbr emulation not implemented!\n"); +static int emu_cxbr (int rx, int ry) { + display_emulation_not_implemented("cxbr"); + return 0; } -static void emu_cxfbr (int rx, int ry) { - printk("cxfbr emulation not implemented!\n"); +static int emu_cxfbr (int rx, int ry) { + display_emulation_not_implemented("cxfbr"); + return 0; } -static void emu_ddb (int rx, __u64 val) { +static int emu_ddb (int rx, __u64 val) { current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d,val); set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_ddbr (int rx, int ry) { +static int emu_ddbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d, current->thread.fp_regs.fprs[ry].d); set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_deb (int rx, __u32 val) { +static int emu_deb (int rx, __u32 val) { current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f,val); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_debr (int rx, int ry) { +static int emu_debr (int rx, int ry) { current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f, current->thread.fp_regs.fprs[ry].f); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_didbr (int rx, int ry, int mask) { - printk("didbr emulation not implemented!\n"); +static int emu_didbr (int rx, int ry, int mask) { + display_emulation_not_implemented("didbr"); + return 0; } -static void emu_diebr (int rx, int ry, int mask) { - printk("diebr emulation not implemented!\n"); +static int emu_diebr (int rx, int ry, int mask) { + display_emulation_not_implemented("diebr"); + return 0; } -static void emu_dxbr (int rx, int ry) { - printk("dxbr emulation not implemented!\n"); +static int emu_dxbr (int rx, int ry) { + display_emulation_not_implemented("dxbr"); + return 0; } -static void emu_efpc (int rx, int ry) { - printk("efpc emulation not implemented!\n"); +static int emu_efpc (int rx, int ry) { + current->thread.regs->gprs[rx]=current->thread.fp_regs.fpc; + return 0; } -static void emu_fidbr (int rx, int ry, int mask) { - printk("fidbr emulation not implemented!\n"); +static int emu_fidbr (int rx, int ry, int mask) { + display_emulation_not_implemented("fidbr"); + return 0; } -static void emu_fiebr (int rx, int ry, int mask) { - printk("fiebr emulation not implemented!\n"); +static int emu_fiebr (int rx, int ry, int mask) { + display_emulation_not_implemented("fiebr"); + return 0; } -static void emu_fixbr (int rx, int ry, int mask) { - printk("fixbr emulation not implemented!\n"); +static int emu_fixbr (int rx, int ry, int mask) { + display_emulation_not_implemented("fixbr"); + return 0; } -static void emu_kdb (int rx, __u64 val) { - printk("kdb emulation not implemented!\n"); +static int emu_kdb (int rx, __u64 val) { + display_emulation_not_implemented("kdb"); + return 0; } -static void emu_kdbr (int rx, int ry) { - printk("kdbr emulation not implemented!\n"); +static int emu_kdbr (int rx, int ry) { + display_emulation_not_implemented("kdbr"); + return 0; } -static void emu_keb (int rx, __u32 val) { - printk("keb emulation not implemented!\n"); +static int emu_keb (int rx, __u32 val) { + display_emulation_not_implemented("keb"); + return 0; } -static void emu_kebr (int rx, int ry) { - printk("kebr emulation not implemented!\n"); +static int emu_kebr (int rx, int ry) { + display_emulation_not_implemented("kebr"); + return 0; } -static void emu_kxbr (int rx, int ry) { - printk("kxbr emulation not implemented!\n"); +static int emu_kxbr (int rx, int ry) { + display_emulation_not_implemented("kxbr"); + return 0; } -static void emu_lcdbr (int rx, int ry) { +static int emu_lcdbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].d = __negdf2(current->thread.fp_regs.fprs[ry].d); set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_lcebr (int rx, int ry) { +static int emu_lcebr (int rx, int ry) { current->thread.fp_regs.fprs[rx].f = __negsf2(current->thread.fp_regs.fprs[ry].f); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_lcxbr (int rx, int ry) { - printk("lcxbr emulation not implemented!\n"); +static int emu_lcxbr (int rx, int ry) { + display_emulation_not_implemented("lcxbr"); + return 0; } -static void emu_ldeb (int rx, __u32 val) { +static int emu_ldeb (int rx, __u32 val) { current->thread.fp_regs.fprs[rx].d = __extendsfdf2(val); + return 0; } -static void emu_ldebr (int rx, int ry) { +static int emu_ldebr (int rx, int ry) { current->thread.fp_regs.fprs[rx].d = __extendsfdf2(current->thread.fp_regs.fprs[ry].f); + return 0; } -static void emu_ldxbr (int rx, int ry) { - printk("ldxbr emulation not implemented!\n"); +static int emu_ldxbr (int rx, int ry) { + display_emulation_not_implemented("ldxbr"); + return 0; } -static void emu_ledbr (int rx, int ry) { +static int emu_ledbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].f = __truncdfsf2(current->thread.fp_regs.fprs[ry].d); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_lexbr (int rx, int ry) { - printk("lexbr emulation not implemented!\n"); +static int emu_lexbr (int rx, int ry) { + display_emulation_not_implemented("lexbr"); + return 0; } -static void emu_lndbr (int rx, int ry) { - printk("lndbr emulation not implemented!\n"); +static int emu_lndbr (int rx, int ry) { + display_emulation_not_implemented("lndbr"); + return 0; } -static void emu_lnebr (int rx, int ry) { - printk("lnebr emulation not implemented!\n"); +static int emu_lnebr (int rx, int ry) { + display_emulation_not_implemented("lnebr"); + return 0; } -static void emu_lnxbr (int rx, int ry) { - printk("lnxbr emulation not implemented!\n"); +static int emu_lnxbr (int rx, int ry) { + display_emulation_not_implemented("lnxbr"); + return 0; } -static void emu_lpdbr (int rx, int ry) { +static int emu_lpdbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].d = __absdf2(current->thread.fp_regs.fprs[ry].d); set_CC_df(current->thread.fp_regs.fprs[rx].d,0); + return 0; } -static void emu_lpebr (int rx, int ry) { +static int emu_lpebr (int rx, int ry) { current->thread.fp_regs.fprs[rx].f = __abssf2(current->thread.fp_regs.fprs[ry].f); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_lpxbr (int rx, int ry) { - printk("lpxbr emulation not implemented!\n"); +static int emu_lpxbr (int rx, int ry) { + display_emulation_not_implemented("lpxbr"); + return 0; } -static void emu_ltdbr (int rx, int ry) { +static int emu_ltdbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].d = current->thread.fp_regs.fprs[ry].d; set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_ltebr (int rx, int ry) { +static int emu_ltebr (int rx, int ry) { current->thread.fp_regs.fprs[rx].f = current->thread.fp_regs.fprs[ry].f; set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_ltxbr (int rx, int ry) { - printk("ltxbr emulation not implemented!\n"); +static int emu_ltxbr (int rx, int ry) { + display_emulation_not_implemented("ltxbr"); + return 0; } -static void emu_lxdb (int rx, __u64 val) { - printk("lxdb emulation not implemented!\n"); +static int emu_lxdb (int rx, __u64 val) { + display_emulation_not_implemented("lxdb"); + return 0; } -static void emu_lxdbr (int rx, int ry) { - printk("lxdbr emulation not implemented!\n"); +static int emu_lxdbr (int rx, int ry) { + display_emulation_not_implemented("lxdbr"); + return 0; } -static void emu_lxeb (int rx, __u32 val) { - printk("lxeb emulation not implemented!\n"); +static int emu_lxeb (int rx, __u32 val) { + display_emulation_not_implemented("lxeb"); + return 0; } -static void emu_lxebr (int rx, int ry) { - printk("lxebr emulation not implemented!\n"); +static int emu_lxebr (int rx, int ry) { + display_emulation_not_implemented("lxebr"); + return 0; } -static void emu_madb (int rx, __u64 val, int mask) { - printk("madb emulation not implemented!\n"); +static int emu_madb (int rx, __u64 val, int mask) { + display_emulation_not_implemented("madb"); + return 0; } -static void emu_madbr (int rx, int ry, int mask) { - printk(" emulation not implemented!\n"); +static int emu_madbr (int rx, int ry, int mask) { + display_emulation_not_implemented("madbr"); + return 0; } -static void emu_maeb (int rx, __u32 val, int mask) { - printk("maeb emulation not implemented!\n"); +static int emu_maeb (int rx, __u32 val, int mask) { + display_emulation_not_implemented("maeb"); + return 0; } -static void emu_maebr (int rx, int ry, int mask) { - printk("maebr emulation not implemented!\n"); +static int emu_maebr (int rx, int ry, int mask) { + display_emulation_not_implemented("maebr"); + return 0; } -static void emu_mdb (int rx, __u64 val) { +static int emu_mdb (int rx, __u64 val) { current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d,val); set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_mdbr (int rx, int ry) { +static int emu_mdbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d, current->thread.fp_regs.fprs[ry].d); set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_mdeb (int rx, __u32 val) { - printk("mdeb emulation not implemented!\n"); +static int emu_mdeb (int rx, __u32 val) { + display_emulation_not_implemented("mdeb"); + return 0; } -static void emu_mdebr (int rx, int ry) { - printk("mdebr emulation not implemented!\n"); +static int emu_mdebr (int rx, int ry) { + display_emulation_not_implemented("mdebr"); + return 0; } -static void emu_meeb (int rx, __u32 val) { +static int emu_meeb (int rx, __u32 val) { current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f, val); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_meebr (int rx, int ry) { +static int emu_meebr (int rx, int ry) { current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f, current->thread.fp_regs.fprs[ry].f); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_msdb (int rx, __u64 val, int mask) { - printk("msdb emulation not implemented!\n"); +static int emu_msdb (int rx, __u64 val, int mask) { + display_emulation_not_implemented("msdb"); + return 0; } -static void emu_msdbr (int rx, int ry, int mask) { - printk("msdbr emulation not implemented!\n"); +static int emu_msdbr (int rx, int ry, int mask) { + display_emulation_not_implemented("msdbr"); + return 0; } -static void emu_mseb (int rx, __u32 val, int mask) { - printk("mseb emulation not implemented!\n"); +static int emu_mseb (int rx, __u32 val, int mask) { + display_emulation_not_implemented("mseb"); + return 0; } -static void emu_msebr (int rx, int ry, int mask) { - printk("msebr emulation not implemented!\n"); +static int emu_msebr (int rx, int ry, int mask) { + display_emulation_not_implemented("msebr"); + return 0; } -static void emu_mxbr (int rx, int ry) { - printk("mxbr emulation not implemented!\n"); +static int emu_mxbr (int rx, int ry) { + display_emulation_not_implemented("mxbr"); + return 0; } -static void emu_mxdb (int rx, __u64 val) { - printk("mxdb emulation not implemented!\n"); +static int emu_mxdb (int rx, __u64 val) { + display_emulation_not_implemented("mxdb"); + return 0; } -static void emu_mxdbr (int rx, int ry) { - printk("mxdbr emulation not implemented!\n"); +static int emu_mxdbr (int rx, int ry) { + display_emulation_not_implemented("mxdbr"); + return 0; } -static void emu_sdb (int rx, __u64 val) { +static int emu_sdb (int rx, __u64 val) { current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d, val); set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_sdbr (int rx, int ry) { +static int emu_sdbr (int rx, int ry) { current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d, current->thread.fp_regs.fprs[ry].d); set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL); + return 0; } -static void emu_seb (int rx, __u32 val) { +static int emu_seb (int rx, __u32 val) { current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f, val); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_sebr (int rx, int ry) { +static int emu_sebr (int rx, int ry) { current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f, current->thread.fp_regs.fprs[ry].f); set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); + return 0; } -static void emu_sfpc (int rx, int ry) { - printk("sfpc emulation not implemented!\n"); +static int emu_sfpc (int rx, int ry) { + __u32 val=current->thread.regs->gprs[rx]; + if(val==0) + current->thread.fp_regs.fpc=val; + else + display_emulation_not_implemented("sfpc"); + return 0; } -static void emu_sqdb (int rx, __u64 val) { - printk("sqdb emulation not implemented!\n"); +static int emu_sqdb (int rx, __u64 val) { + display_emulation_not_implemented("sqdb"); + return 0; } -static void emu_sqdbr (int rx, int ry) { - printk("sqdbr emulation not implemented!\n"); +static int emu_sqdbr (int rx, int ry) { + display_emulation_not_implemented("sqdbr"); + return 0; } -static void emu_sqeb (int rx, __u32 val) { - printk("sqeb emulation not implemented!\n"); +static int emu_sqeb (int rx, __u32 val) { + display_emulation_not_implemented("sqeb"); + return 0; } -static void emu_sqebr (int rx, int ry) { - printk("sqebr emulation not implemented!\n"); +static int emu_sqebr (int rx, int ry) { + display_emulation_not_implemented("sqebr"); + return 0; } -static void emu_sqxbr (int rx, int ry) { - printk("sqxbr emulation not implemented!\n"); +static int emu_sqxbr (int rx, int ry) { + display_emulation_not_implemented("sqxbr"); + return 0; } -static void emu_sxbr (int rx, int ry) { - printk("sxbr emulation not implemented!\n"); +static int emu_sxbr (int rx, int ry) { + display_emulation_not_implemented("sxbr"); + return 0; } -static void emu_tcdb (int rx, __u64 val) { - printk("tcdb emulation not implemented!\n"); +static int emu_tcdb (int rx, __u64 val) { + display_emulation_not_implemented("tcdb"); + return 0; } -static void emu_tceb (int rx, __u32 val) { - printk("tceb emulation not implemented!\n"); +static int emu_tceb (int rx, __u32 val) { + display_emulation_not_implemented("tceb"); + return 0; } -static void emu_tcxb (int rx, __u64 val) { - printk("tcxb emulation not implemented!\n"); +static int emu_tcxb (int rx, __u64 val) { + display_emulation_not_implemented("tcxb"); + return 0; } @@ -473,6 +616,7 @@ static inline void emu_store_rege(int reg) { } int math_emu_b3(__u8 *opcode, struct pt_regs * regs) { + int rc=0; static const __u8 format_table[] = { 2, 2, 2, 2, 9, 1, 2, 1, 2, 2, 2, 2, 9, 2, 4, 4, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 3, @@ -538,84 +682,82 @@ int math_emu_b3(__u8 *opcode, struct pt_regs * regs) { emu_store_regd((opcode[3]>>4)&15); emu_store_regd(opcode[3]&15); /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15); - emu_load_regd((opcode[3]>>4)&15); - emu_load_regd(opcode[3]&15); - return 0; + emu_load_regd((opcode[3]>>4)&15); + emu_load_regd(opcode[3]&15); + return rc; case 2: /* RRE format, float operation */ emu_store_rege((opcode[3]>>4)&15); emu_store_rege(opcode[3]&15); /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15); - emu_load_rege((opcode[3]>>4)&15); - emu_load_rege(opcode[3]&15); - return 0; + emu_load_rege((opcode[3]>>4)&15); + emu_load_rege(opcode[3]&15); + return rc; case 3: /* RRF format, double operation */ emu_store_regd((opcode[3]>>4)&15); emu_store_regd(opcode[3]&15); /* call the emulation function */ - ((void (*)(int, int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); - emu_load_regd((opcode[3]>>4)&15); - emu_load_regd(opcode[3]&15); - return 0; + emu_load_regd((opcode[3]>>4)&15); + emu_load_regd(opcode[3]&15); + return rc; case 4: /* RRF format, float operation */ emu_store_rege((opcode[3]>>4)&15); emu_store_rege(opcode[3]&15); /* call the emulation function */ - ((void (*)(int, int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); - emu_load_rege((opcode[3]>>4)&15); - emu_load_rege(opcode[3]&15); - return 0; + emu_load_rege((opcode[3]>>4)&15); + emu_load_rege(opcode[3]&15); + return rc; case 5: /* RRE format, cefbr instruction */ emu_store_rege((opcode[3]>>4)&15); /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15); - emu_load_rege((opcode[3]>>4)&15); - return 0; + emu_load_rege((opcode[3]>>4)&15); + return rc; case 6: /* RRE format, cdfbr & cxfbr instruction */ emu_store_regd((opcode[3]>>4)&15); /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15); - emu_load_regd((opcode[3]>>4)&15); - return 0; - /* FIXME !! */ - return 0; - case 7: /* RRF format, cfebr instruction */ + emu_load_regd((opcode[3]>>4)&15); + return rc; + case 7: /* RRF format, cfebr instruction */ emu_store_rege(opcode[3]&15); /* call the emulation function */ - ((void (*)(int, int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); - return 0; + return rc; case 8: /* RRF format, cfdbr & cfxbr instruction */ emu_store_regd(opcode[3]&15); /* call the emulation function */ - ((void (*)(int, int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); - return 0; + return rc; case 9: /* RRE format, ldebr & mdebr instruction */ /* float store but double load */ emu_store_rege((opcode[3]>>4)&15); emu_store_rege(opcode[3]&15); /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15); - emu_load_regd((opcode[3]>>4)&15); - return 0; + emu_load_regd((opcode[3]>>4)&15); + return rc; case 10: /* RRE format, ledbr instruction */ /* double store but float load */ emu_store_regd((opcode[3]>>4)&15); emu_store_regd(opcode[3]&15); /* call the emulation function */ - ((void (*)(int, int))jump_table[opcode[1]]) + rc=((int (*)(int, int))jump_table[opcode[1]]) (opcode[3]>>4,opcode[3]&15); - emu_load_rege((opcode[3]>>4)&15); - return 0; + emu_load_rege((opcode[3]>>4)&15); + return rc; default: return 1; } @@ -632,6 +774,8 @@ static void* calc_addr(struct pt_regs *regs,int rx,int rb,int disp) } int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { + int rc=0; + static const __u8 format_table[] = { 0, 0, 0, 0, 5, 1, 2, 1, 2, 2, 2, 2, 5, 2, 4, 4, 2, 1, 1, 0, 2, 1, 0, 2, 1, 1, 1, 1, 1, 1, 3, 3, @@ -669,13 +813,12 @@ int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { emu_store_regd((opcode[1]>>4)&15); opc = *((__u32 *) opcode); dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if copy_from_user fails ? */ - copy_from_user(&temp, dxb, 8); + mathemu_copy_from_user(&temp, dxb, 8); /* call the emulation function */ - ((void (*)(int, __u64))jump_table[opcode[5]]) + rc=((int (*)(int, __u64))jump_table[opcode[5]]) (opcode[1]>>4,temp); - emu_load_regd((opcode[1]>>4)&15); - return 0; + emu_load_regd((opcode[1]>>4)&15); + return rc; } case 2: /* RXE format, __u32 constant */ { __u32 *dxb, temp; @@ -684,13 +827,12 @@ int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { emu_store_rege((opcode[1]>>4)&15); opc = *((__u32 *) opcode); dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if get_user fails ? */ - get_user(temp, dxb); + mathemu_get_user(temp, dxb); /* call the emulation function */ - ((void (*)(int, __u32))jump_table[opcode[5]]) + rc=((int (*)(int, __u32))jump_table[opcode[5]]) (opcode[1]>>4,temp); - emu_load_rege((opcode[1]>>4)&15); - return 0; + emu_load_rege((opcode[1]>>4)&15); + return rc; } case 3: /* RXF format, __u64 constant */ { __u32 *dxb, temp; @@ -699,13 +841,12 @@ int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { emu_store_regd((opcode[1]>>4)&15); opc = *((__u32 *) opcode); dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if copy_from_user fails ? */ - copy_from_user(&temp, dxb, 8); + mathemu_copy_from_user(&temp, dxb, 8); /* call the emulation function */ - ((void (*)(int, __u32, int))jump_table[opcode[5]]) + rc=((int (*)(int, __u32, int))jump_table[opcode[5]]) (opcode[1]>>4,temp,opcode[4]>>4); - emu_load_regd((opcode[1]>>4)&15); - return 0; + emu_load_regd((opcode[1]>>4)&15); + return rc; } case 4: /* RXF format, __u32 constant */ { __u32 *dxb, temp; @@ -714,29 +855,27 @@ int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { emu_store_rege((opcode[1]>>4)&15); opc = *((__u32 *) opcode); dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if get_user fails ? */ - get_user(temp, dxb); + mathemu_get_user(temp, dxb); /* call the emulation function */ - ((void (*)(int, __u32, int))jump_table[opcode[5]]) + rc=((int (*)(int, __u32, int))jump_table[opcode[5]]) (opcode[1]>>4,temp,opcode[4]>>4); emu_load_rege((opcode[1]>>4)&15); - return 0; + return rc; } case 5: /* RXE format, __u32 constant */ /* store_rege and load_regd */ - { + { __u32 *dxb, temp; __u32 opc; emu_store_rege((opcode[1]>>4)&15); opc = *((__u32 *) opcode); dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if get_user fails ? */ - get_user(temp, dxb); + mathemu_get_user(temp, dxb); /* call the emulation function */ - ((void (*)(int, __u32))jump_table[opcode[5]]) + rc=((int (*)(int, __u32))jump_table[opcode[5]]) (opcode[1]>>4,temp); emu_load_regd((opcode[1]>>4)&15); - return 0; + return rc; } default: return 1; @@ -746,7 +885,7 @@ int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { /* * Emulate LDR Rx,Ry with Rx or Ry not in {0, 2, 4, 6} */ -void math_emu_ldr(__u8 *opcode) { +int math_emu_ldr(__u8 *opcode) { __u16 opc = *((__u16 *) opcode); if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ @@ -772,12 +911,13 @@ void math_emu_ldr(__u8 *opcode) { current->thread.fp_regs.fprs[(opc&0x00f0)>>4] = current->thread.fp_regs.fprs[opc&0x000f]; } + return 0; } /* * Emulate LER Rx,Ry with Rx or Ry not in {0, 2, 4, 6} */ -void math_emu_ler(__u8 *opcode) { +int math_emu_ler(__u8 *opcode) { __u16 opc = *((__u16 *) opcode); if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ @@ -803,61 +943,68 @@ void math_emu_ler(__u8 *opcode) { current->thread.fp_regs.fprs[(opc&0x00f0)>>4] = current->thread.fp_regs.fprs[opc&0x000f]; } + return 0; } /* * Emulate LD R,D(X,B) with R not in {0, 2, 4, 6} */ -void math_emu_ld(__u8 *opcode, struct pt_regs * regs) { +int math_emu_ld(__u8 *opcode, struct pt_regs * regs) { __u32 opc = *((__u32 *) opcode); __u64 *dxb; dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if copy_from_user fails ? */ - copy_from_user(¤t->thread.fp_regs.fprs[(opc>>20)&15].d, dxb, 8); + mathemu_copy_from_user(¤t->thread.fp_regs.fprs[(opc>>20)&15].d, dxb, 8); + return 0; } /* * Emulate LE R,D(X,B) with R not in {0, 2, 4, 6} */ -void math_emu_le(__u8 *opcode, struct pt_regs * regs) { +int math_emu_le(__u8 *opcode, struct pt_regs * regs) { __u32 opc = *((__u32 *) opcode); __u32 *mem, *dxb; dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if get_user fails ? */ mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f); - get_user(mem[0], dxb); + mathemu_get_user(mem[0], dxb); + return 0; } /* * Emulate STD R,D(X,B) with R not in {0, 2, 4, 6} */ -void math_emu_std(__u8 *opcode, struct pt_regs * regs) { +int math_emu_std(__u8 *opcode, struct pt_regs * regs) { __u32 opc = *((__u32 *) opcode); __u64 *dxb; dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if copy_to_user fails ? */ - copy_to_user(dxb, ¤t->thread.fp_regs.fprs[(opc>>20)&15].d, 8); + mathemu_copy_to_user(dxb, ¤t->thread.fp_regs.fprs[(opc>>20)&15].d, 8); + return 0; } /* * Emulate STE R,D(X,B) with R not in {0, 2, 4, 6} */ -void math_emu_ste(__u8 *opcode, struct pt_regs * regs) { +int math_emu_ste(__u8 *opcode, struct pt_regs * regs) { __u32 opc = *((__u32 *) opcode); __u32 *mem, *dxb; dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); - /* FIXME: how to react if put_user fails ? */ + /* FIXME: how to react if mathemu_put_user fails ? */ mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f); - put_user(mem[0], dxb); + mathemu_put_user(mem[0], dxb); + return 0; } /* * Emulate LFPC D(B) */ int math_emu_lfpc(__u8 *opcode, struct pt_regs *regs) { - /* FIXME: how to do that ?!? */ + __u32 *dxb,temp; + __u32 opc = *((__u32 *) opcode); + dxb= (__u32 *) calc_addr(regs,0,opc>>12,opc); + mathemu_get_user(temp, dxb); + if(temp!=0) + display_emulation_not_implemented("lfpc"); return 0; } @@ -865,7 +1012,10 @@ int math_emu_lfpc(__u8 *opcode, struct pt_regs *regs) { * Emulate STFPC D(B) */ int math_emu_stfpc(__u8 *opcode, struct pt_regs *regs) { - /* FIXME: how to do that ?!? */ + __u32 *dxb; + __u32 opc = *((__u32 *) opcode); + dxb= (__u32 *) calc_addr(regs,0,opc>>12,opc); + mathemu_put_user(current->thread.fp_regs.fpc, dxb); return 0; } @@ -874,6 +1024,7 @@ int math_emu_stfpc(__u8 *opcode, struct pt_regs *regs) { */ int math_emu_srnm(__u8 *opcode, struct pt_regs *regs) { /* FIXME: how to do that ?!? */ + display_emulation_not_implemented("srnm"); return 0; } diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 2fc6d08fa..f8238de1e 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -28,7 +28,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/user.h> #include <linux/a.out.h> @@ -67,7 +67,9 @@ int cpu_idle(void *unused) if (softirq_active(smp_processor_id()) & softirq_mask(smp_processor_id())) { do_softirq(); - continue; + __sti(); + if (!current->need_resched) + continue; } if (current->need_resched) { schedule(); @@ -92,12 +94,13 @@ idle_wakeup: 0 returned you know you've got all the lines */ -int sprintf_regs(int line, char *buff, struct task_struct * task, - struct thread_struct *thread, struct pt_regs * regs) -{ +static int sprintf_regs(int line, char *buff, struct task_struct *task, struct pt_regs *regs) +{ int linelen=0; int regno,chaincnt; u32 backchain,prev_backchain,endchain; + u32 ksp = 0; + char *mode = "???"; enum { @@ -118,106 +121,125 @@ int sprintf_regs(int line, char *buff, struct task_struct * task, sp_kern_backchain1 }; - if(task) - thread = &task->thread; - if(thread) - regs = thread->regs; - switch (line) { - case sp_linefeed: + if (task) + ksp = task->thread.ksp; + if (regs && !(regs->psw.mask & PSW_PROBLEM_STATE)) + ksp = regs->gprs[15]; + + if (regs) + mode = (regs->psw.mask & PSW_PROBLEM_STATE)? + "User" : "Kernel"; + + switch(line) + { + case sp_linefeed: linelen=sprintf(buff,"\n"); break; case sp_psw: if(regs) - linelen = sprintf(buff,"User PSW: %08lx %08lx\n", - (unsigned long) regs->psw.mask, - (unsigned long) regs->psw.addr); + linelen=sprintf(buff, "%s PSW: %08lx %08lx\n", mode, + (unsigned long) regs->psw.mask, + (unsigned long) regs->psw.addr); else - linelen = sprintf(buff,"pt_regs=NULL some info unavailable\n"); + linelen=sprintf(buff,"pt_regs=NULL some info unavailable\n"); break; case sp_ksp: - if (task) - linelen += sprintf(&buff[linelen], - "task: %08x ", (addr_t)task); - if (thread) - linelen += sprintf(&buff[linelen], - "thread: %08x ksp: %08x ", - (addr_t)thread,(addr_t)thread->ksp); - if (regs) - linelen += sprintf(&buff[linelen], - "pt_regs: %08x\n", (addr_t)regs); + linelen=sprintf(&buff[linelen], + "task: %08x ksp: %08x pt_regs: %08x\n", + (addr_t)task, (addr_t)ksp, (addr_t)regs); break; case sp_gprs: - if (regs) - linelen = sprintf(buff,"User GPRS:\n"); + if(regs) + linelen=sprintf(buff, "%s GPRS:\n", mode); break; case sp_gprs1 ... sp_gprs4: - if (regs) { - regno = (line-sp_gprs1)*4; - linelen = sprintf(buff,"%08x %08x %08x %08x\n", - regs->gprs[regno], - regs->gprs[regno+1], - regs->gprs[regno+2], - regs->gprs[regno+3]); + if(regs) + { + regno=(line-sp_gprs1)*4; + linelen=sprintf(buff,"%08x %08x %08x %08x\n", + regs->gprs[regno], + regs->gprs[regno+1], + regs->gprs[regno+2], + regs->gprs[regno+3]); } break; case sp_acrs: - if (regs) - linelen = sprintf(buff,"User ACRS:\n"); + if(regs) + linelen=sprintf(buff, "%s ACRS:\n", mode); break; case sp_acrs1 ... sp_acrs4: - if (regs) { - regno = (line-sp_acrs1)*4; - linelen = sprintf(buff,"%08x %08x %08x %08x\n", - regs->acrs[regno], - regs->acrs[regno+1], - regs->acrs[regno+2], - regs->acrs[regno+3]); + if(regs) + { + regno=(line-sp_acrs1)*4; + linelen=sprintf(buff,"%08x %08x %08x %08x\n", + regs->acrs[regno], + regs->acrs[regno+1], + regs->acrs[regno+2], + regs->acrs[regno+3]); } break; case sp_kern_backchain: - if (thread && thread->ksp && regs) - linelen = sprintf(buff,"Kernel BackChain CallChain BackChain CallChain\n"); + if (regs && (regs->psw.mask & PSW_PROBLEM_STATE)) + break; + if (ksp) + linelen=sprintf(buff, "Kernel BackChain CallChain\n"); break; default: - if(thread && thread->ksp && regs) { - backchain = (thread->ksp & PSW_ADDR_MASK); - endchain = ((backchain & (-8192)) + 8192); - prev_backchain = backchain - 1; - line -= sp_kern_backchain1; - for (chaincnt = 0; ; chaincnt++) { - if ((backchain == 0) || - (backchain >= endchain) || - (chaincnt >= 8) || - (prev_backchain >= backchain)) + if (ksp) + { + + backchain=ksp&PSW_ADDR_MASK; + endchain=((backchain&(-8192))+8192); + prev_backchain=backchain-1; + line-=sp_kern_backchain1; + for(chaincnt=0;;chaincnt++) + { + if((backchain==0)||(backchain>=endchain) + ||(chaincnt>=8)||(prev_backchain>=backchain)) break; - if ((chaincnt >> 1) == line) { - linelen += sprintf(&buff[linelen],"%s%08x %08x ", - (chaincnt&1) ? "":" ", - backchain,*(u32 *)(backchain+56)); - } - if ((chaincnt >> 1) > line) + if(chaincnt==line) + { + linelen+=sprintf(&buff[linelen]," %08x [<%08lx>]\n", + backchain, + *(u32 *)(backchain+56)&PSW_ADDR_MASK); break; - prev_backchain = backchain; - backchain = (*((u32 *)backchain)) & PSW_ADDR_MASK; + } + prev_backchain=backchain; + backchain=(*((u32 *)backchain))&PSW_ADDR_MASK; } - if (linelen) - linelen += sprintf(&buff[linelen],"\n"); } } - return linelen; + return(linelen); } -void show_regs(struct task_struct *task, struct thread_struct *thread, - struct pt_regs *regs) +void show_regs(struct pt_regs *regs) { char buff[80]; int line; + + printk("CPU: %d\n",smp_processor_id()); + printk("Process %s (pid: %d, stackpage=%08X)\n", + current->comm, current->pid, 4096+(addr_t)current); - for (line = 0; sprintf_regs(line,buff,task,thread,regs); line++) + for (line = 0; sprintf_regs(line, buff, current, regs); line++) printk(buff); } +char *task_show_regs(struct task_struct *task, char *buffer) +{ + int line, len; + + for (line = 0; ; line++) + { + len = sprintf_regs(line, buffer, task, task->thread.regs); + if (!len) break; + buffer += len; + } + return buffer; +} + + int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) { int clone_arg = flags | CLONE_VM; @@ -327,7 +349,7 @@ asmlinkage int sys_clone(struct pt_regs regs) lock_kernel(); clone_flags = regs.gprs[3]; - newsp = regs.gprs[2]; + newsp = regs.orig_gpr2; if (!newsp) newsp = regs.gprs[15]; ret = do_fork(clone_flags, newsp, ®s, 0); @@ -365,7 +387,19 @@ asmlinkage int sys_execve(struct pt_regs regs) goto out; error = do_execve(filename, (char **) regs.gprs[3], (char **) regs.gprs[4], ®s); if (error == 0) - current->flags &= ~PF_DTRACE; + { + current->ptrace &= ~PT_DTRACE; + current->thread.fp_regs.fpc=0; + if(MACHINE_HAS_IEEE) + { + __asm__ __volatile__ + ("sr 0,0\n\t" + "sfpc 0,0\n\t" + : + : + :"0"); + } + } putname(filename); out: return error; @@ -412,21 +446,77 @@ extern void scheduling_functions_end_here(void); unsigned long get_wchan(struct task_struct *p) { - unsigned long r14, r15; + unsigned long r14, r15, bc; unsigned long stack_page; int count = 0; if (!p || p == current || p->state == TASK_RUNNING) return 0; stack_page = (unsigned long) p; r15 = p->thread.ksp; + if (!stack_page || r15 < stack_page || r15 >= 8188+stack_page) + return 0; + bc = (*(unsigned long *) r15) & 0x7fffffff; do { - r14 = *(unsigned long *) (r15+56); + if (bc < stack_page || bc >= 8188+stack_page) + return 0; + r14 = (*(unsigned long *) (bc+56)) & 0x7fffffff; if (r14 < first_sched || r14 >= last_sched) return r14; - r15 = *(unsigned long *) (r15+60); + bc = (*(unsigned long *) bc) & 0x7fffffff; } while (count++ < 16); return 0; } #undef last_sched #undef first_sched +/* + * This should be safe even if called from tq_scheduler + * A typical mask would be sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM) or 0. + * + */ +void s390_daemonize(char *name,unsigned long mask,int use_init_fs) +{ + struct fs_struct *fs; + extern struct task_struct *child_reaper; + struct task_struct *this_process=current; + + /* + * If we were started as result of loading a module, close all of the + * user space pages. We don't need them, and if we didn't close them + * they would be locked into memory. + */ + exit_mm(current); + + this_process->session = 1; + this_process->pgrp = 1; + if(name) + { + strncpy(current->comm,name,15); + current->comm[15]=0; + } + else + current->comm[0]=0; + /* set signal mask to what we want to respond */ + siginitsetinv(¤t->blocked,mask); + /* exit_signal isn't set up */ + /* if we inherit from cpu idle */ + this_process->exit_signal=SIGCHLD; + /* if priority=0 schedule can go into a tight loop */ + this_process->policy= SCHED_OTHER; + /* nice goes priority=20-nice; */ + this_process->nice=10; + if(use_init_fs) + { + exit_fs(this_process); /* current->fs->count--; */ + fs = init_task.fs; + current->fs = fs; + atomic_inc(&fs->count); + exit_files(current); + } + write_lock_irq(&tasklist_lock); + /* We want init as our parent */ + REMOVE_LINKS(this_process); + this_process->p_opptr=this_process->p_pptr=child_reaper; + SET_LINKS(this_process); + write_unlock_irq(&tasklist_lock); +} diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index 2643f100a..29a81418a 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c @@ -66,10 +66,16 @@ void FixPerRegisters(struct task_struct *task) regs->psw.mask |=PSW_PER_MASK; else regs->psw.mask &= ~PSW_PER_MASK; - if(per_info->control_regs.bits.storage_alt_space_ctl) - task->thread.user_seg|=USER_STD_MASK; + if (per_info->control_regs.bits.em_storage_alteration) + { + per_info->control_regs.bits.storage_alt_space_ctl=1; + //((pgd_t *)__pa(task->mm->pgd))->pgd |= USER_STD_MASK; + } else - task->thread.user_seg&=~USER_STD_MASK; + { + per_info->control_regs.bits.storage_alt_space_ctl=0; + //((pgd_t *)__pa(task->mm->pgd))->pgd &= ~USER_STD_MASK; + } } void set_single_step(struct task_struct *task) @@ -209,10 +215,10 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if (request == PTRACE_TRACEME) { /* are we already being traced? */ - if (current->flags & PF_PTRACED) + if (current->ptrace & PT_PTRACED) goto out; /* set the ptrace bit in the process flags. */ - current->flags |= PF_PTRACED; + current->ptrace |= PT_PTRACED; ret = 0; goto out; } @@ -237,9 +243,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) (current->gid != child->sgid)) && !capable(CAP_SYS_PTRACE)) goto out; /* the same process cannot be attached many times */ - if (child->flags & PF_PTRACED) + if (child->ptrace & PT_PTRACED) goto out; - child->flags |= PF_PTRACED; + child->ptrace |= PT_PTRACED; write_lock_irqsave(&tasklist_lock, flags); if (child->p_pptr != current) @@ -256,16 +262,20 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) } ret = -ESRCH; // printk("child=%lX child->flags=%lX",child,child->flags); - if (!(child->flags & PF_PTRACED)) - goto out; - if (child->state != TASK_STOPPED) + /* I added child!=current line so we can get the */ + /* ieee_instruction_pointer from the user structure DJB */ + if(child!=current) { - if (request != PTRACE_KILL) + if (!(child->ptrace & PT_PTRACED)) + goto out; + if (child->state != TASK_STOPPED) + { + if (request != PTRACE_KILL) + goto out; + } + if (child->p_pptr != current) goto out; } - if (child->p_pptr != current) - goto out; - switch (request) { /* If I and D space are separate, these will need to be fixed. */ @@ -303,9 +313,9 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) if ((unsigned long) data >= _NSIG) break; if (request == PTRACE_SYSCALL) - child->flags |= PF_TRACESYS; + child->ptrace |= PT_TRACESYS; else - child->flags &= ~PF_TRACESYS; + child->ptrace &= ~PT_TRACESYS; child->exit_code = data; /* make sure the single step bit is not set. */ clear_single_step(child); @@ -332,7 +342,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ret = -EIO; if ((unsigned long) data >= _NSIG) break; - child->flags &= ~PF_TRACESYS; + child->ptrace &= ~PT_TRACESYS; child->exit_code = data; set_single_step(child); /* give it a chance to run. */ @@ -344,7 +354,7 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) ret = -EIO; if ((unsigned long) data >= _NSIG) break; - child->flags &= ~(PF_PTRACED|PF_TRACESYS); + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); child->exit_code = data; write_lock_irqsave(&tasklist_lock, flags); REMOVE_LINKS(child); @@ -374,11 +384,11 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data) asmlinkage void syscall_trace(void) { lock_kernel(); - if ((current->flags & (PF_PTRACED|PF_TRACESYS)) - != (PF_PTRACED|PF_TRACESYS)) + if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) + != (PT_PTRACED|PT_TRACESYS)) goto out; current->exit_code = SIGTRAP; - current->state = TASK_STOPPED; + set_current_state(TASK_STOPPED); notify_parent(current, SIGCHLD); schedule(); /* diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S index a77940905..c30fe433c 100644 --- a/arch/s390/kernel/reipl.S +++ b/arch/s390/kernel/reipl.S @@ -1,4 +1,13 @@ +/* + * arch/s390/kernel/reipl.S + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Holger Smolinski (Holger.Smolinski@de.ibm.com) + */ + #include <asm/lowcore.h> + .globl do_reipl do_reipl: basr %r13,0 .Lpg0: lpsw .Lnewpsw-.Lpg0(%r13) @@ -7,7 +16,7 @@ do_reipl: basr %r13,0 ni .Lctlsave-.Lpg0(%r13),0xef lctl %c0,%c0,.Lctlsave-.Lpg0(%r13) lr %r1,%r2 - mvc __LC_PGM_NEW_PSW(8,0),.Lpcnew-.Lpg0(%r13) + mvc __LC_PGM_NEW_PSW(8),.Lpcnew-.Lpg0(%r13) stsch .Lschib-.Lpg0(%r13) oi .Lschib+5-.Lpg0(%r13),0x84 .Lecs: xi .Lschib+27-.Lpg0(%r13),0x01 @@ -15,9 +24,9 @@ do_reipl: basr %r13,0 ssch .Liplorb-.Lpg0(%r13) jz .L001 bas %r14,.Ldisab-.Lpg0(%r13) -.L001: mvc __LC_IO_NEW_PSW(8,0),.Lionew-.Lpg0(%r13) +.L001: mvc __LC_IO_NEW_PSW(8),.Lionew-.Lpg0(%r13) .Ltpi: lpsw .Lwaitpsw-.Lpg0(%r13) -.Lcont: c %r1,__LC_SUBCHANNEL_ID(%r0) +.Lcont: c %r1,__LC_SUBCHANNEL_ID jnz .Ltpi clc __LC_IO_INT_PARM(4),.Liplorb-.Lpg0(%r13) jnz .Ltpi @@ -29,7 +38,7 @@ do_reipl: basr %r13,0 jz .L003 bas %r14,.Ldisab-.Lpg0(%r13) .L003: spx .Lnull-.Lpg0(%r13) - st %r1,__LC_SUBCHANNEL_ID(%r0) + st %r1,__LC_SUBCHANNEL_ID lpsw 0 sigp 0,0,0(6) .Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13) diff --git a/arch/s390/kernel/s390_ext.c b/arch/s390/kernel/s390_ext.c new file mode 100644 index 000000000..6a7be9496 --- /dev/null +++ b/arch/s390/kernel/s390_ext.c @@ -0,0 +1,77 @@ +/* + * arch/s390/kernel/s390_ext.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Holger Smolinski (Holger.Smolinski@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <asm/lowcore.h> +#include <asm/s390_ext.h> + +/* + * Simple hash strategy: index = code & 0xff; + * ext_int_hash[index] is the start of the list for all external interrupts + * that hash to this index. With the current set of external interrupts + * (0x1202 external call, 0x1004 cpu timer, 0x2401 hwc console and 0x4000 + * iucv) this is always the first element. + */ +ext_int_info_t *ext_int_hash[256] = { 0, }; +ext_int_info_t ext_int_info_timer; +ext_int_info_t ext_int_info_hwc; + +int register_external_interrupt(__u16 code, ext_int_handler_t handler) { + ext_int_info_t *p; + int index; + + index = code & 0xff; + p = ext_int_hash[index]; + while (p != NULL) { + if (p->code == code) + return -EBUSY; + p = p->next; + } + if (code == 0x1004) /* time_init is done before kmalloc works :-/ */ + p = &ext_int_info_timer; + else if (code == 0x2401) /* hwc_init is done too early too */ + p = &ext_int_info_hwc; + else + p = (ext_int_info_t *) + kmalloc(sizeof(ext_int_info_t), GFP_ATOMIC); + if (p == NULL) + return -ENOMEM; + p->code = code; + p->handler = handler; + p->next = ext_int_hash[index]; + ext_int_hash[index] = p; + return 0; +} + +int unregister_external_interrupt(__u16 code, ext_int_handler_t handler) { + ext_int_info_t *p, *q; + int index; + + index = code & 0xff; + q = NULL; + p = ext_int_hash[index]; + while (p != NULL) { + if (p->code == code && p->handler == handler) + break; + q = p; + p = p->next; + } + if (p == NULL) + return -ENOENT; + if (q != NULL) + q->next = p->next; + else + ext_int_hash[index] = p->next; + if (code != 0x1004 && code != 0x2401) + kfree(p); + return 0; +} + + diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c index 85ecb5ce0..b0a6d0225 100644 --- a/arch/s390/kernel/s390_ksyms.c +++ b/arch/s390/kernel/s390_ksyms.c @@ -5,14 +5,27 @@ */ #include <linux/config.h> #include <linux/module.h> +#include <linux/string.h> +#include <asm/ccwcache.h> +#include <asm/debug.h> #include <asm/irq.h> -#include <asm/string.h> +#include <asm/s390_ext.h> +#include <asm/s390dyn.h> +#include <asm/ebcdic.h> #include <asm/checksum.h> +#include <asm/delay.h> +#if CONFIG_CHANDEV +#include <asm/chandev.h> +#endif +#if CONFIG_IP_MULTICAST +#include <net/arp.h> +#endif /* * I/O subsystem */ EXPORT_SYMBOL(halt_IO); +EXPORT_SYMBOL(clear_IO); EXPORT_SYMBOL(do_IO); EXPORT_SYMBOL(resume_IO); EXPORT_SYMBOL(ioinfo); @@ -22,6 +35,35 @@ EXPORT_SYMBOL(get_irq_by_devno); EXPORT_SYMBOL(get_devno_by_irq); EXPORT_SYMBOL(get_irq_first); EXPORT_SYMBOL(get_irq_next); +EXPORT_SYMBOL(read_conf_data); +EXPORT_SYMBOL(read_dev_chars); +EXPORT_SYMBOL(s390_request_irq_special); +EXPORT_SYMBOL(s390_device_register); +EXPORT_SYMBOL(s390_device_unregister); + +EXPORT_SYMBOL(ccw_alloc_request); +EXPORT_SYMBOL(ccw_free_request); + +EXPORT_SYMBOL(register_external_interrupt); +EXPORT_SYMBOL(unregister_external_interrupt); + +/* + * debug feature + */ +EXPORT_SYMBOL(debug_register); +EXPORT_SYMBOL(debug_unregister); +EXPORT_SYMBOL(debug_set_level); +EXPORT_SYMBOL(debug_register_view); +EXPORT_SYMBOL(debug_unregister_view); +EXPORT_SYMBOL(debug_event); +EXPORT_SYMBOL(debug_int_event); +EXPORT_SYMBOL(debug_text_event); +EXPORT_SYMBOL(debug_exception); +EXPORT_SYMBOL(debug_int_exception); +EXPORT_SYMBOL(debug_text_exception); +EXPORT_SYMBOL(debug_hex_ascii_view); +EXPORT_SYMBOL(debug_raw_view); +EXPORT_SYMBOL(debug_dflt_header_fn); /* * memory management @@ -29,6 +71,16 @@ EXPORT_SYMBOL(get_irq_next); EXPORT_SYMBOL(_oi_bitmap); EXPORT_SYMBOL(_ni_bitmap); EXPORT_SYMBOL(_zb_findmap); +EXPORT_SYMBOL(__copy_from_user_fixup); +EXPORT_SYMBOL(__copy_to_user_fixup); + +/* + * semaphore ops + */ +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_interruptible); +EXPORT_SYMBOL(__down_trylock); /* * string functions @@ -36,6 +88,7 @@ EXPORT_SYMBOL(_zb_findmap); EXPORT_SYMBOL_NOVERS(memcmp); EXPORT_SYMBOL_NOVERS(memset); EXPORT_SYMBOL_NOVERS(memmove); +EXPORT_SYMBOL_NOVERS(strlen); EXPORT_SYMBOL_NOVERS(strchr); EXPORT_SYMBOL_NOVERS(strcmp); EXPORT_SYMBOL_NOVERS(strncat); @@ -46,19 +99,42 @@ EXPORT_SYMBOL_NOVERS(strrchr); EXPORT_SYMBOL_NOVERS(strtok); EXPORT_SYMBOL_NOVERS(strpbrk); +EXPORT_SYMBOL_NOVERS(_ascebc_500); +EXPORT_SYMBOL_NOVERS(_ebcasc_500); +EXPORT_SYMBOL_NOVERS(_ascebc); +EXPORT_SYMBOL_NOVERS(_ebcasc); +EXPORT_SYMBOL_NOVERS(_ebc_tolower); +EXPORT_SYMBOL_NOVERS(_ebc_toupper); + /* * misc. */ +EXPORT_SYMBOL(module_list); +EXPORT_SYMBOL(__udelay); #ifdef CONFIG_SMP #include <asm/smplock.h> EXPORT_SYMBOL(__global_cli); EXPORT_SYMBOL(__global_sti); EXPORT_SYMBOL(__global_save_flags); EXPORT_SYMBOL(__global_restore_flags); +EXPORT_SYMBOL(lowcore_ptr); EXPORT_SYMBOL(global_bh_lock); EXPORT_SYMBOL(kernel_flag); +EXPORT_SYMBOL(smp_ctl_set_bit); +EXPORT_SYMBOL(smp_ctl_clear_bit); #endif EXPORT_SYMBOL(kernel_thread); EXPORT_SYMBOL(csum_fold); - +#if CONFIG_CHANDEV +EXPORT_SYMBOL(chandev_register_and_probe); +EXPORT_SYMBOL(chandev_request_irq); +EXPORT_SYMBOL(chandev_unregister); +EXPORT_SYMBOL(chandev_initdevice); +EXPORT_SYMBOL(chandev_initnetdevice); +#endif +#if CONFIG_IP_MULTICAST +/* Required for lcs gigabit ethernet multicast support */ +EXPORT_SYMBOL(arp_mc_map); +#endif +EXPORT_SYMBOL(s390_daemonize); diff --git a/arch/s390/kernel/s390dyn.c b/arch/s390/kernel/s390dyn.c deleted file mode 100644 index 0a5625830..000000000 --- a/arch/s390/kernel/s390dyn.c +++ /dev/null @@ -1,36 +0,0 @@ -/* - * arch/s390/kernel/s390dyn.c - * S/390 dynamic device attachment - * - * S390 version - * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Ingo Adlung (adlung@de.ibm.com) - */ - -#include <linux/init.h> - -#include <asm/irq.h> -#include <asm/s390io.h> -#include <asm/s390dyn.h> - -int s390_device_register( devreg_t *drinfo ) -{ - return -EOPNOTSUPP; -} - - -int s390_device_deregister ( devreg_t *dreg ) -{ - return -EOPNOTSUPP; -} - -int s390_request_irq_special( int irq, - io_handler_func_t io_handler, - not_oper_handler_func_t not_oper_handler, - unsigned long irqflags, - const char *devname, - void *dev_id) -{ - return -EOPNOTSUPP; -} - diff --git a/arch/s390/kernel/s390fpu.c b/arch/s390/kernel/s390fpu.c index 42048abbc..c56c50d66 100644 --- a/arch/s390/kernel/s390fpu.c +++ b/arch/s390/kernel/s390fpu.c @@ -81,6 +81,11 @@ int restore_fp_regs1(s390_fp_regs *fpregs) { int has_ieee=MACHINE_HAS_IEEE; + /* If we don't mask with the FPC_VALID_MASK here + * we've got a very quick shutdown -h now command + * via a kernel specification exception. + */ + fpregs->fpc&=FPC_VALID_MASK; asm volatile ("LD 0,8(%0)\n\t" "LD 2,24(%0)\n\t" "LD 4,40(%0)\n\t" diff --git a/arch/s390/kernel/s390io.c b/arch/s390/kernel/s390io.c deleted file mode 100644 index b1db16707..000000000 --- a/arch/s390/kernel/s390io.c +++ /dev/null @@ -1,4605 +0,0 @@ -/* - * arch/s390/kernel/s390io.c - * S/390 common I/O routines - * - * S390 version - * Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH, - * IBM Corporation - * Author(s): Ingo Adlung (adlung@de.ibm.com) - */ - -#include <linux/config.h> -#include <linux/errno.h> -#include <linux/kernel_stat.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/malloc.h> -#include <linux/string.h> -#include <linux/smp.h> -#include <linux/tasks.h> -#include <linux/smp_lock.h> -#include <linux/init.h> -#include <linux/bootmem.h> - -#include <asm/system.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/bitops.h> -#include <asm/smp.h> -#include <asm/pgtable.h> -#include <asm/delay.h> -#include <asm/processor.h> -#include <asm/lowcore.h> - -#include <asm/s390io.h> -#include <asm/s390dyn.h> -#include <asm/s390mach.h> - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - -#undef CONFIG_DEBUG_IO - -#define REIPL_DEVID_MAGIC 0x87654321 - -struct irqaction init_IRQ_action; -unsigned int highest_subchannel; -ioinfo_t *ioinfo_head = NULL; -ioinfo_t *ioinfo_tail = NULL; -ioinfo_t *ioinfo[__MAX_SUBCHANNELS] = { - [0 ... (__MAX_SUBCHANNELS-1)] = INVALID_STORAGE_AREA -}; - -static spinlock_t sync_isc; // synchronous irq processing lock -static psw_t io_sync_wait; // wait PSW for sync IO, prot. by sync_isc -static psw_t io_new_psw; // save I/O new PSW, prot. by sync_isc -static int cons_dev = -1; // identify console device -static int init_IRQ_complete = 0; -static schib_t init_schib; -static __u64 irq_IPL_TOD; - -/* - * Dummy controller type for unused interrupts - */ -int do_none(unsigned int irq, int cpu, struct pt_regs * regs) { return 0;} -int enable_none(unsigned int irq) { return(-ENODEV); } -int disable_none(unsigned int irq) { return(-ENODEV); } - -struct hw_interrupt_type no_irq_type = { - "none", - do_none, - enable_none, - disable_none -}; - -static void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs); -static int s390_setup_irq(unsigned int irq, struct irqaction * new); -static void s390_process_subchannels( void); -static void s390_device_recognition( void); -static int s390_validate_subchannel( int irq); -static int s390_SenseID( int irq, senseid_t *sid); -static int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid); -static int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid); -static int s390_process_IRQ( unsigned int irq ); -static int s390_DevicePathVerification( int irq ); - -extern int do_none(unsigned int irq, int cpu, struct pt_regs * regs); -extern int enable_none(unsigned int irq); -extern int disable_none(unsigned int irq); -extern void tod_wait(unsigned long usecs); - -asmlinkage void do_IRQ( struct pt_regs regs, - unsigned int irq, - __u32 s390_intparm ); - -void s390_displayhex(char *str,void *ptr,s32 cnt); - -void s390_displayhex(char *str,void *ptr,s32 cnt) -{ - s32 cnt1,cnt2,maxcnt2; - u32 *currptr=(__u32 *)ptr; - - printk("\n%s\n",str); - - for(cnt1=0;cnt1<cnt;cnt1+=16) - { - printk("%08lX ",(unsigned long)currptr); - maxcnt2=cnt-cnt1; - if(maxcnt2>16) - maxcnt2=16; - for(cnt2=0;cnt2<maxcnt2;cnt2+=4) - printk("%08X ",*currptr++); - printk("\n"); - } -} - -int s390_request_irq( unsigned int irq, - void (*handler)(int, void *, struct pt_regs *), - unsigned long irqflags, - const char *devname, - void *dev_id) -{ - int retval; - struct irqaction *action; - - if (irq >= __MAX_SUBCHANNELS) - return -EINVAL; - - if ( !handler || !dev_id ) - return -EINVAL; - - /* - * during init_IRQ() processing we don't have memory - * management yet, thus need to use a statically - * allocated irqaction control block - */ - if ( init_IRQ_complete ) - { - action = (struct irqaction *) - kmalloc(sizeof(struct irqaction), GFP_KERNEL); - } - else - { - action = &init_IRQ_action; - - } /* endif */ - - if (!action) - { - return -ENOMEM; - - } /* endif */ - - action->handler = handler; - action->flags = irqflags; - action->mask = 0; - action->name = devname; - action->next = NULL; - action->dev_id = dev_id; - - retval = s390_setup_irq( irq, action); - - if ( !retval ) - { - retval = s390_DevicePathVerification( irq ); - } - else if ( retval && init_IRQ_complete ) - { - kfree(action); - - } /* endif */ - - return retval; -} - -void s390_free_irq(unsigned int irq, void *dev_id) -{ - unsigned int flags; - int ret; - - unsigned int count = 0; - - if ( irq >= __MAX_SUBCHANNELS || ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return; - - } /* endif */ - - s390irq_spin_lock_irqsave( irq, flags); - -#ifdef CONFIG_KERNEL_DEBUG - if ( irq != cons_dev ) - { - printk("Trying to free IRQ%d\n",irq); - - } /* endif */ -#endif - - /* - * disable the device and reset all IRQ info if - * the IRQ is actually owned by the handler ... - */ - if ( ioinfo[irq]->irq_desc.action ) - { - if ( (dev_id == ioinfo[irq]->irq_desc.action->dev_id ) - || (dev_id == (devstat_t *)REIPL_DEVID_MAGIC) ) - { - /* start deregister */ - ioinfo[irq]->ui.flags.unready = 1; - - do - { - ret = ioinfo[irq]->irq_desc.handler->disable(irq); - - count++; - - if ( ret == -EBUSY ) - { - int iret; - - /* - * kill it ! - * ... we first try sync and eventually - * try terminating the current I/O by - * an async request, twice halt, then - * clear. - */ - if ( count < 3 ) - { - iret = halt_IO( irq, - 0xC8C1D3E3, - DOIO_WAIT_FOR_INTERRUPT ); - - if ( iret == -EBUSY ) - { - halt_IO( irq, 0xC8C1D3E3, 0); - s390irq_spin_unlock_irqrestore( irq, flags); - tod_wait( 200000 ); /* 200 ms */ - s390irq_spin_lock_irqsave( irq, flags); - - } /* endif */ - } - else - { - iret = clear_IO( irq, - 0x40C3D3D9, - DOIO_WAIT_FOR_INTERRUPT ); - - if ( iret == -EBUSY ) - { - clear_IO( irq, 0xC8C1D3E3, 0); - s390irq_spin_unlock_irqrestore( irq, flags); - tod_wait( 1000000 ); /* 1000 ms */ - s390irq_spin_lock_irqsave( irq, flags); - - } /* endif */ - - } /* endif */ - - if ( count == 3 ) - { - /* give it a very last try ... */ - ioinfo[irq]->irq_desc.handler->disable(irq); - - if ( ioinfo[irq]->ui.flags.busy ) - { - printk( KERN_CRIT"free_irq(%04X) " - "- device %04X busy, retry " - "count exceeded\n", - irq, - ioinfo[irq]->devstat.devno); - - } /* endif */ - - break; /* sigh, let's give up ... */ - - } /* endif */ - - } /* endif */ - - } while ( ret == -EBUSY ); - - if ( init_IRQ_complete ) - kfree( ioinfo[irq]->irq_desc.action ); - - ioinfo[irq]->irq_desc.action = NULL; - ioinfo[irq]->ui.flags.ready = 0; - - ioinfo[irq]->irq_desc.handler->enable = &enable_none; - ioinfo[irq]->irq_desc.handler->disable = &disable_none; - - ioinfo[irq]->ui.flags.unready = 0; /* deregister ended */ - - s390irq_spin_unlock_irqrestore( irq, flags); - } - else - { - s390irq_spin_unlock_irqrestore( irq, flags); - - printk("free_irq() : error, dev_id does not match !"); - - } /* endif */ - - } - else - { - s390irq_spin_unlock_irqrestore( irq, flags); - - printk("free_irq() : error, no action block ... !"); - - } /* endif */ - -} - -/* - * Generic enable/disable code - */ -int disable_irq(unsigned int irq) -{ - unsigned long flags; - int ret; - - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - return( -ENODEV); - - s390irq_spin_lock_irqsave(irq, flags); - - /* - * At this point we may actually have a pending interrupt being active - * on another CPU. So don't touch the IRQ_INPROGRESS bit.. - */ - ioinfo[irq]->irq_desc.status |= IRQ_DISABLED; - ret = ioinfo[irq]->irq_desc.handler->disable(irq); - s390irq_spin_unlock_irqrestore(irq, flags); - - synchronize_irq(); - - return( ret); -} - -int enable_irq(unsigned int irq) -{ - unsigned long flags; - int ret; - - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - return( -ENODEV); - - s390irq_spin_lock_irqsave(irq, flags); - - ioinfo[irq]->irq_desc.status = 0; - ret = ioinfo[irq]->irq_desc.handler->enable(irq); - - s390irq_spin_unlock_irqrestore(irq, flags); - - return(ret); -} - -/* - * Enable IRQ by modifying the subchannel - */ -static int enable_subchannel( unsigned int irq) -{ - int ret; - int ccode; - int retry = 5; - - if ( irq > highest_subchannel || irq < 0 ) - { - return( -ENODEV ); - - } /* endif */ - - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - return( -ENODEV); - - /* - * If a previous disable request is pending we reset it. However, this - * status implies that the device may (still) be not-operational. - */ - if ( ioinfo[irq]->ui.flags.d_disable ) - { - ioinfo[irq]->ui.flags.d_disable = 0; - ret = 0; - } - else - { - - ccode = stsch(irq, &(ioinfo[irq]->schib) ); - - if ( ccode ) - { - ret = -ENODEV; - } - else - { - ioinfo[irq]->schib.pmcw.ena = 1; - - do - { - ccode = msch( irq, &(ioinfo[irq]->schib) ); - - switch (ccode) { - case 0: - ret = 0; - break; - - case 1: - /* - * very bad, requires interrupt alike - * processing, where "rbh" is a dummy - * parameter for interface compatibility - * only. Bottom-half handling cannot be - * required as this must be an - * unsolicited interrupt (!busy). - */ - - ioinfo[irq]->ui.flags.s_pend = 1; - - s390_process_IRQ( irq ); - - ioinfo[irq]->ui.flags.s_pend = 0; - - ret = -EIO; /* might be overwritten */ - /* ... on re-driving */ - /* ... the msch() */ - retry--; - break; - - case 3: - ioinfo[irq]->ui.flags.oper = 0; - ret = -ENODEV; - break; - - default: - printk( KERN_CRIT"enable_subchannel(%04X) " - " : ccode 2 on msch() for device " - "%04X received !\n", - irq, - ioinfo[irq]->devstat.devno); - ret = -ENODEV; // never reached - } - - } while ( (ccode == 1) && retry ); - - } /* endif */ - - } /* endif */ - - return( ret ); -} - - -/* - * Disable IRQ by modifying the subchannel - */ -static int disable_subchannel( unsigned int irq) -{ - int cc; /* condition code */ - int ret; /* function return value */ - int retry = 5; - - if ( irq > highest_subchannel ) - { - ret = -ENODEV; - } - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - else if ( ioinfo[irq]->ui.flags.busy ) - { - /* - * the disable function must not be called while there are - * requests pending for completion ! - */ - ret = -EBUSY; - } - else - { - /* - * If device isn't operational we have to perform delayed - * disabling when the next interrupt occurs - unless the - * irq is re-requested prior to the interrupt to occur. - */ - cc = stsch(irq, &(ioinfo[irq]->schib) ); - - if ( cc == 3 ) - { - ioinfo[irq]->ui.flags.oper = 0; - ioinfo[irq]->ui.flags.d_disable = 1; - - ret = 0; - } - else // cc == 0 - { - ioinfo[irq]->schib.pmcw.ena = 0; - - do - { - cc = msch( irq, &(ioinfo[irq]->schib) ); - - switch (cc) { - case 0 : - ret = 0; /* done */ - break; - - case 1 : - /* - * very bad, requires interrupt alike - * processing, where "rbh" is a dummy - * parm for interface compatibility - * only. Bottom-half handling cannot - * be required as this must be an - * unsolicited interrupt (!busy). - */ - ioinfo[irq]->ui.flags.s_pend = 1; - s390_process_IRQ( irq ); - ioinfo[irq]->ui.flags.s_pend = 0; - - ret = -EBUSY; /* might be overwritten */ - /* ... on re-driving the */ - /* ... msch() call */ - retry--; - break; - - case 2 : - /* - * *** must not occur ! *** - * *** *** - * *** indicates our internal *** - * *** interrupt accounting is out *** - * *** of sync ===> panic() *** - */ - printk( KERN_CRIT"disable_subchannel(%04X) " - "- unexpected busy condition for " - "device %04X received !\n", - irq, - ioinfo[irq]->devstat.devno); - - ret = -ENODEV; // never reached - break; - - case 3 : - /* - * should hardly occur ?! - */ - ioinfo[irq]->ui.flags.oper = 0; - ioinfo[irq]->ui.flags.d_disable = 1; - - ret = 0; /* if the device has gone we */ - /* ... don't need to disable */ - /* ... it anymore ! */ - break; - - default : - ret = -ENODEV; // never reached ... - break; - - } /* endswitch */ - - } while ( (cc == 1) && retry ); - - } /* endif */ - - } /* endif */ - - return( ret); -} - - - -int s390_setup_irq( unsigned int irq, struct irqaction * new) -{ - unsigned long flags; - int rc = 0; - - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - - /* - * The following block of code has to be executed atomically - */ - s390irq_spin_lock_irqsave( irq, flags); - - if ( ioinfo[irq]->irq_desc.action == NULL ) - { - ioinfo[irq]->irq_desc.action = new; - ioinfo[irq]->irq_desc.status = 0; - ioinfo[irq]->irq_desc.handler->enable = &enable_subchannel; - ioinfo[irq]->irq_desc.handler->disable = &disable_subchannel; - ioinfo[irq]->irq_desc.handler->handle = &handle_IRQ_event; - - ioinfo[irq]->ui.flags.ready = 1; - - ioinfo[irq]->irq_desc.handler->enable(irq); - } - else - { - /* - * interrupt already owned, and shared interrupts - * aren't supported on S/390. - */ - rc = -EBUSY; - - } /* endif */ - - s390irq_spin_unlock_irqrestore(irq,flags); - - return( rc); -} - -void s390_init_IRQ( void ) -{ - unsigned long flags; /* PSW flags */ - long cr6 __attribute__ ((aligned (8))); - - // Hopefully bh_count's will get set when we copy the prefix lowcore - // structure to other CPI's ( DJB ) - softirq_active(smp_processor_id()) = 0; - softirq_mask(smp_processor_id()) = 0; - local_bh_count(smp_processor_id()) = 0; - local_irq_count(smp_processor_id()) = 0; - syscall_count(smp_processor_id()) = 0; - - asm volatile ("STCK %0" : "=m" (irq_IPL_TOD)); - - /* - * As we don't know about the calling environment - * we assure running disabled. Before leaving the - * function we resestablish the old environment. - * - * Note : as we don't need a system wide lock, therefore - * we shouldn't use cli(), but __cli() as this - * affects the current CPU only. - */ - __save_flags(flags); - __cli(); - - /* - * disable all interrupts - */ - cr6 = 0; - asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); - - s390_process_subchannels(); - - /* - * enable default I/O-interrupt sublass 3 - */ - cr6 = 0x10000000; - asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); - - s390_device_recognition(); - - init_IRQ_complete = 1; - - s390_init_machine_check(); - - __restore_flags(flags); - - return; -} - - -/* - * dummy handler, used during init_IRQ() processing for compatibility only - */ -void init_IRQ_handler( int irq, void *dev_id, struct pt_regs *regs) -{ - /* this is a dummy handler only ... */ -} - - -int s390_start_IO( int irq, /* IRQ */ - ccw1_t *cpa, /* logical channel prog addr */ - unsigned long user_intparm, /* interruption parameter */ - __u8 lpm, /* logical path mask */ - unsigned long flag) /* flags */ -{ - int ccode; - unsigned long psw_flags; - - int sync_isc_locked = 0; - int ret = 0; - - /* - * The flag usage is mutal exclusive ... - */ - if ( (flag & DOIO_RETURN_CHAN_END) - && (flag & DOIO_REPORT_ALL ) ) - { - return( -EINVAL ); - - } /* endif */ - - memset( &(ioinfo[irq]->orb), '\0', sizeof( orb_t) ); - - /* - * setup ORB - */ - ioinfo[irq]->orb.intparm = (__u32)&ioinfo[irq]->u_intparm; - ioinfo[irq]->orb.fmt = 1; - - ioinfo[irq]->orb.pfch = !(flag & DOIO_DENY_PREFETCH); - ioinfo[irq]->orb.spnd = (flag & DOIO_ALLOW_SUSPEND); - ioinfo[irq]->orb.ssic = ( (flag & DOIO_ALLOW_SUSPEND ) - && (flag & DOIO_SUPPRESS_INTER) ); - - if ( flag & DOIO_VALID_LPM ) - { - ioinfo[irq]->orb.lpm = lpm; - } - else - { - ioinfo[irq]->orb.lpm = ioinfo[irq]->opm; - - } /* endif */ - - ioinfo[irq]->orb.cpa = (__u32)virt_to_phys( cpa); - - /* - * If sync processing was requested we lock the sync ISC, modify the - * device to present interrupts for this ISC only and switch the - * CPU to handle this ISC + the console ISC exclusively. - */ - if ( flag & DOIO_WAIT_FOR_INTERRUPT ) - { - // - // check whether we run recursively (sense processing) - // - if ( !ioinfo[irq]->ui.flags.syncio ) - { - spin_lock_irqsave( &sync_isc, psw_flags); - - ret = enable_cpu_sync_isc( irq); - - if ( ret ) - { - spin_unlock_irqrestore( &sync_isc, psw_flags); - return( ret); - } - else - { - sync_isc_locked = 1; // local - ioinfo[irq]->ui.flags.syncio = 1; // global - - } /* endif */ - - } /* endif */ - - } /* endif */ - - /* - * Issue "Start subchannel" and process condition code - */ - ccode = ssch( irq, &(ioinfo[irq]->orb) ); - - switch ( ccode ) { - case 0: - - if ( !ioinfo[irq]->ui.flags.w4sense ) - { - /* - * init the device driver specific devstat irb area - * - * Note : don´t clear saved irb info in case of sense ! - */ - memset( &((devstat_t *)ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, - '\0', sizeof( irb_t) ); - } /* endif */ - - /* - * initialize device status information - */ - ioinfo[irq]->ui.flags.busy = 1; - ioinfo[irq]->ui.flags.doio = 1; - - ioinfo[irq]->u_intparm = user_intparm; - ioinfo[irq]->devstat.cstat = 0; - ioinfo[irq]->devstat.dstat = 0; - ioinfo[irq]->devstat.lpum = 0; - ioinfo[irq]->devstat.flag = DEVSTAT_START_FUNCTION; - ioinfo[irq]->devstat.scnt = 0; - - ioinfo[irq]->ui.flags.fast = 0; - ioinfo[irq]->ui.flags.repall = 0; - - /* - * Check for either early (FAST) notification requests - * or if we are to return all interrupt info. - * Default is to call IRQ handler at secondary status only - */ - if ( flag & DOIO_RETURN_CHAN_END ) - { - ioinfo[irq]->ui.flags.fast = 1; - } - else if ( flag & DOIO_REPORT_ALL ) - { - ioinfo[irq]->ui.flags.repall = 1; - - } /* endif */ - - ioinfo[irq]->ulpm = ioinfo[irq]->orb.lpm; - - /* - * If synchronous I/O processing is requested, we have - * to wait for the corresponding interrupt to occur by - * polling the interrupt condition. However, as multiple - * interrupts may be outstanding, we must not just wait - * for the first interrupt, but must poll until ours - * pops up. - */ - if ( flag & DOIO_WAIT_FOR_INTERRUPT ) - { - // __u32 io_parm; - psw_t io_new_psw; - int ccode; - - int ready = 0; - int io_sub = -1; - struct _lowcore *lc = NULL; - int count = 30000; - - /* - * We shouldn't perform a TPI loop, waiting for an - * interrupt to occur, but should load a WAIT PSW - * instead. Otherwise we may keep the channel subsystem - * busy, not able to present the interrupt. When our - * sync. interrupt arrived we reset the I/O old PSW to - * its original value. - */ - memcpy( &io_new_psw, &lc->io_new_psw, sizeof(psw_t)); - - ccode = iac(); - - switch (ccode) { - case 0: // primary-space - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_PRIM_SPACE_MODE - | _PSW_IO_WAIT; - break; - case 1: // secondary-space - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_SEC_SPACE_MODE - | _PSW_IO_WAIT; - break; - case 2: // access-register - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_ACC_REG_MODE - | _PSW_IO_WAIT; - break; - case 3: // home-space - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_HOME_SPACE_MODE - | _PSW_IO_WAIT; - break; - default: - panic( "start_IO() : unexpected " - "address-space-control %d\n", - ccode); - break; - } /* endswitch */ - - io_sync_wait.addr = FIX_PSW(&&io_wakeup); - - /* - * Martin didn't like modifying the new PSW, now we take - * a fast exit in do_IRQ() instead - */ - *(__u32 *)__LC_SYNC_IO_WORD = 1; - - do - { - if ( flag & DOIO_TIMEOUT ) - { - tpi_info_t tpi_info; - - do - { - if ( tpi(&tpi_info) == 1 ) - { - io_sub = tpi_info.irq; - break; - } - else - { - count--; - tod_wait(100); /* usecs */ - - } /* endif */ - - } while ( count ); - } - else - { - asm volatile ("lpsw %0" : : "m" (io_sync_wait)); - -io_wakeup: - io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; - - } /* endif */ - - if ( count ) - ready = s390_process_IRQ( io_sub ); - - /* - * surrender when retry count's exceeded ... - */ - - } while ( !( ( io_sub == irq ) - && ( ready == 1 )) - && count ); - - *(__u32 *)__LC_SYNC_IO_WORD = 0; - - if ( !count ) - ret = -ETIMEDOUT; - - } /* endif */ - - break; - - case 1 : /* status pending */ - - ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; - - /* - * initialize the device driver specific devstat irb area - */ - memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, - '\0', sizeof( irb_t) ); - - /* - * Let the common interrupt handler process the pending status. - * However, we must avoid calling the user action handler, as - * it won't be prepared to handle a pending status during - * do_IO() processing inline. This also implies that process_IRQ - * must terminate synchronously - especially if device sensing - * is required. - */ - ioinfo[irq]->ui.flags.s_pend = 1; - ioinfo[irq]->ui.flags.busy = 1; - ioinfo[irq]->ui.flags.doio = 1; - - s390_process_IRQ( irq ); - - ioinfo[irq]->ui.flags.s_pend = 0; - ioinfo[irq]->ui.flags.busy = 0; - ioinfo[irq]->ui.flags.doio = 0; - ioinfo[irq]->ui.flags.repall = 0; - ioinfo[irq]->ui.flags.w4final = 0; - - ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; - - /* - * In multipath mode a condition code 3 implies the last path - * has gone, except we have previously restricted the I/O to - * a particular path. A condition code 1 (0 won't occur) - * results in return code EIO as well as 3 with another path - * than the one used (i.e. path available mask is non-zero). - */ - if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 ) - { - ret = -ENODEV; - ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; - ioinfo[irq]->ui.flags.oper = 0; - -#if CONFIG_DEBUG_IO - { - char buffer[80]; - - stsch(irq, &(ioinfo[irq]->schib) ); - - sprintf( buffer, "s390_start_IO(%04X) - irb for " - "device %04X, after status pending\n", - irq, - ioinfo[irq]->devstat.devno ); - - s390_displayhex( buffer, - &(ioinfo[irq]->devstat.ii.irb) , - sizeof(irb_t)); - - sprintf( buffer, "s390_start_IO(%04X) - schib for " - "device %04X, after status pending\n", - irq, - ioinfo[irq]->devstat.devno ); - - s390_displayhex( buffer, - &(ioinfo[irq]->schib) , - sizeof(schib_t)); - - - if (ioinfo[irq]->devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL) - { - sprintf( buffer, "s390_start_IO(%04X) - sense " - "data for " - "device %04X, after status pending\n", - irq, - ioinfo[irq]->devstat.devno ); - - s390_displayhex( buffer, - ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->ii.sense.data, - ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->rescnt); - - } - } -#endif - } - else - { - ret = -EIO; - ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER; - ioinfo[irq]->ui.flags.oper = 1; - - } /* endif */ - - break; - - case 2 : /* busy */ - - ret = -EBUSY; - break; - - default: /* device not operational */ - - ret = -ENODEV; - ioinfo[irq]->ui.flags.oper = 0; - - ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; - - memcpy( ioinfo[irq]->irq_desc.action->dev_id, - &(ioinfo[irq]->devstat), - sizeof( devstat_t) ); - -#if CONFIG_DEBUG_IO - { - char buffer[80]; - - stsch(irq, &(ioinfo[irq]->schib) ); - - sprintf( buffer, "s390_start_IO(%04X) - schib for " - "device %04X, after 'not oper' status\n", - irq, - ioinfo[irq]->devstat.devno ); - - s390_displayhex( buffer, - &(ioinfo[irq]->schib), - sizeof(schib_t)); - } -#endif - break; - - } /* endswitch */ - - if ( ( flag & DOIO_WAIT_FOR_INTERRUPT ) - && ( sync_isc_locked ) ) - { - disable_cpu_sync_isc( irq ); - - spin_unlock_irqrestore( &sync_isc, psw_flags); - - sync_isc_locked = 0; // local setting - ioinfo[irq]->ui.flags.syncio = 0; // global setting - - } /* endif */ - - return( ret); -} - -int do_IO( int irq, /* IRQ */ - ccw1_t *cpa, /* channel program address */ - unsigned long user_intparm, /* interruption parameter */ - __u8 lpm, /* logical path mask */ - unsigned long flag) /* flags : see above */ -{ - int ret = 0; - - if ( irq > highest_subchannel || irq < 0 ) - { - return( -ENODEV ); - - } /* endif */ - - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - - /* handler registered ? or free_irq() in process already ? */ - if ( !ioinfo[irq]->ui.flags.ready || ioinfo[irq]->ui.flags.unready ) - { - return( -ENODEV ); - - } /* endif */ - - /* - * Note: We ignore the device operational status - if not operational, - * the SSCH will lead to an -ENODEV condition ... - */ - if ( !ioinfo[irq]->ui.flags.busy ) /* last I/O completed ? */ - { - ret = s390_start_IO( irq, cpa, user_intparm, lpm, flag); - } - else if ( ioinfo[irq]->ui.flags.fast ) - { - /* - * If primary status was received and ending status is missing, - * the device driver won't be notified on the ending status - * if early (fast) interrupt notification was requested. - * Therefore we have to queue the next incoming request. If - * halt_IO() is issued while there is a request queued, a HSCH - * needs to be issued and the queued request must be deleted - * but its intparm must be returned (see halt_IO() processing) - */ - if ( ioinfo[irq]->ui.flags.w4final - && !ioinfo[irq]->ui.flags.doio_q ) - { - ioinfo[irq]->qflag = flag; - ioinfo[irq]->qcpa = cpa; - ioinfo[irq]->qintparm = user_intparm; - ioinfo[irq]->qlpm = lpm; - } - else - { - ret = -EBUSY; - - } /* endif */ - } - else - { - ret = -EBUSY; - - } /* endif */ - - return( ret ); - -} - -/* - * resume suspended I/O operation - */ -int resume_IO( int irq) -{ - int ret = 0; - - if ( irq > highest_subchannel || irq < 0 ) - { - return( -ENODEV ); - - } /* endif */ - - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - - /* - * We allow for 'resume' requests only for active I/O operations - */ - if ( ioinfo[irq]->ui.flags.busy ) - { - int ccode; - - ccode = rsch( irq); - - switch (ccode) { - case 0 : - break; - - case 1 : - s390_process_IRQ( irq ); - ret = -EBUSY; - break; - - case 2 : - ret = -EINVAL; - break; - - case 3 : - /* - * useless to wait for request completion - * as device is no longer operational ! - */ - ioinfo[irq]->ui.flags.oper = 0; - ioinfo[irq]->ui.flags.busy = 0; - ret = -ENODEV; - break; - - } /* endswitch */ - - } - else - { - ret = -ENOTCONN; - - } /* endif */ - - return( ret); -} - -/* - * Note: The "intparm" parameter is not used by the halt_IO() function - * itself, as no ORB is built for the HSCH instruction. However, - * it allows the device interrupt handler to associate the upcoming - * interrupt with the halt_IO() request. - */ -int halt_IO( int irq, - unsigned long user_intparm, - unsigned long flag) /* possible DOIO_WAIT_FOR_INTERRUPT */ -{ - int ret; - int ccode; - unsigned long psw_flags; - - int sync_isc_locked = 0; - - if ( irq > highest_subchannel || irq < 0 ) - { - ret = -ENODEV; - } - - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - - /* - * we only allow for halt_IO if the device has an I/O handler associated - */ - else if ( !ioinfo[irq]->ui.flags.ready ) - { - ret = -ENODEV; - } - /* - * we ignore the halt_io() request if ending_status was received but - * a SENSE operation is waiting for completion. - */ - else if ( ioinfo[irq]->ui.flags.w4sense ) - { - ret = 0; - } - /* - * We don't allow for halt_io with a sync do_IO() requests pending. - */ - else if ( ioinfo[irq]->ui.flags.syncio ) - { - ret = -EBUSY; - } - else - { - /* - * If sync processing was requested we lock the sync ISC, - * modify the device to present interrupts for this ISC only - * and switch the CPU to handle this ISC + the console ISC - * exclusively. - */ - if ( flag & DOIO_WAIT_FOR_INTERRUPT ) - { - // - // check whether we run recursively (sense processing) - // - if ( !ioinfo[irq]->ui.flags.syncio ) - { - spin_lock_irqsave( &sync_isc, psw_flags); - - ret = enable_cpu_sync_isc( irq); - - if ( ret ) - { - spin_unlock_irqrestore( &sync_isc, - psw_flags); - return( ret); - } - else - { - sync_isc_locked = 1; // local - ioinfo[irq]->ui.flags.syncio = 1; // global - - } /* endif */ - - } /* endif */ - - } /* endif */ - - /* - * Issue "Halt subchannel" and process condition code - */ - ccode = hsch( irq ); - - switch ( ccode ) { - case 0: - - ioinfo[irq]->ui.flags.haltio = 1; - - if ( !ioinfo[irq]->ui.flags.doio ) - { - ioinfo[irq]->ui.flags.busy = 1; - ioinfo[irq]->u_intparm = user_intparm; - ioinfo[irq]->devstat.cstat = 0; - ioinfo[irq]->devstat.dstat = 0; - ioinfo[irq]->devstat.lpum = 0; - ioinfo[irq]->devstat.flag = DEVSTAT_HALT_FUNCTION; - ioinfo[irq]->devstat.scnt = 0; - - } - else - { - ioinfo[irq]->devstat.flag |= DEVSTAT_HALT_FUNCTION; - - } /* endif */ - - /* - * If synchronous I/O processing is requested, we have - * to wait for the corresponding interrupt to occur by - * polling the interrupt condition. However, as multiple - * interrupts may be outstanding, we must not just wait - * for the first interrupt, but must poll until ours - * pops up. - */ - if ( flag & DOIO_WAIT_FOR_INTERRUPT ) - { - int io_sub; - __u32 io_parm; - psw_t io_new_psw; - int ccode; - - int ready = 0; - struct _lowcore *lc = NULL; - - /* - * We shouldn't perform a TPI loop, waiting for - * an interrupt to occur, but should load a - * WAIT PSW instead. Otherwise we may keep the - * channel subsystem busy, not able to present - * the interrupt. When our sync. interrupt - * arrived we reset the I/O old PSW to its - * original value. - */ - memcpy( &io_new_psw, - &lc->io_new_psw, - sizeof(psw_t)); - - ccode = iac(); - - switch (ccode) { - case 0: // primary-space - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_PRIM_SPACE_MODE - | _PSW_IO_WAIT; - break; - case 1: // secondary-space - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_SEC_SPACE_MODE - | _PSW_IO_WAIT; - break; - case 2: // access-register - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_ACC_REG_MODE - | _PSW_IO_WAIT; - break; - case 3: // home-space - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_HOME_SPACE_MODE - | _PSW_IO_WAIT; - break; - default: - panic( "halt_IO() : unexpected " - "address-space-control %d\n", - ccode); - break; - } /* endswitch */ - - io_sync_wait.addr = FIX_PSW(&&hio_wakeup); - - /* - * Martin didn't like modifying the new PSW, now we take - * a fast exit in do_IRQ() instead - */ - *(__u32 *)__LC_SYNC_IO_WORD = 1; - - do - { - - asm volatile ( "lpsw %0" : : "m" (io_sync_wait) ); -hio_wakeup: - io_parm = *(__u32 *)__LC_IO_INT_PARM; - io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; - - ready = s390_process_IRQ( io_sub ); - - } while ( !((io_sub == irq) && (ready == 1)) ); - - *(__u32 *)__LC_SYNC_IO_WORD = 0; - - } /* endif */ - - ret = 0; - break; - - case 1 : /* status pending */ - - ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; - - /* - * initialize the device driver specific devstat irb area - */ - memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, - '\0', sizeof( irb_t) ); - - /* - * Let the common interrupt handler process the pending - * status. However, we must avoid calling the user - * action handler, as it won't be prepared to handle - * a pending status during do_IO() processing inline. - * This also implies that s390_process_IRQ must - * terminate synchronously - especially if device - * sensing is required. - */ - ioinfo[irq]->ui.flags.s_pend = 1; - ioinfo[irq]->ui.flags.busy = 1; - ioinfo[irq]->ui.flags.doio = 1; - - s390_process_IRQ( irq ); - - ioinfo[irq]->ui.flags.s_pend = 0; - ioinfo[irq]->ui.flags.busy = 0; - ioinfo[irq]->ui.flags.doio = 0; - ioinfo[irq]->ui.flags.repall = 0; - ioinfo[irq]->ui.flags.w4final = 0; - - ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; - - /* - * In multipath mode a condition code 3 implies the last - * path has gone, except we have previously restricted - * the I/O to a particular path. A condition code 1 - * (0 won't occur) results in return code EIO as well - * as 3 with another path than the one used (i.e. path available mask is non-zero). - */ - if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 ) - { - ret = -ENODEV; - ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; - ioinfo[irq]->ui.flags.oper = 0; - } - else - { - ret = -EIO; - ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER; - ioinfo[irq]->ui.flags.oper = 1; - - } /* endif */ - - break; - - case 2 : /* busy */ - - ret = -EBUSY; - break; - - default: /* device not operational */ - - ret = -ENODEV; - break; - - } /* endswitch */ - - if ( ( flag & DOIO_WAIT_FOR_INTERRUPT ) - && ( sync_isc_locked ) ) - { - sync_isc_locked = 0; // local setting - ioinfo[irq]->ui.flags.syncio = 0; // global setting - - disable_cpu_sync_isc( irq ); - - spin_unlock_irqrestore( &sync_isc, psw_flags); - - } /* endif */ - - } /* endif */ - - return( ret ); -} - -/* - * Note: The "intparm" parameter is not used by the clear_IO() function - * itself, as no ORB is built for the CSCH instruction. However, - * it allows the device interrupt handler to associate the upcoming - * interrupt with the clear_IO() request. - */ -int clear_IO( int irq, - unsigned long user_intparm, - unsigned long flag) /* possible DOIO_WAIT_FOR_INTERRUPT */ -{ - int ret; - int ccode; - unsigned long psw_flags; - - int sync_isc_locked = 0; - - if ( irq > highest_subchannel || irq < 0 ) - { - ret = -ENODEV; - } - - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - - /* - * we only allow for halt_IO if the device has an I/O handler associated - */ - else if ( !ioinfo[irq]->ui.flags.ready ) - { - ret = -ENODEV; - } - /* - * we ignore the halt_io() request if ending_status was received but - * a SENSE operation is waiting for completion. - */ - else if ( ioinfo[irq]->ui.flags.w4sense ) - { - ret = 0; - } - /* - * We don't allow for halt_io with a sync do_IO() requests pending. - * Concurrent I/O is possible in SMP environments only, but the - * sync. I/O request can be gated to one CPU at a time only. - */ - else if ( ioinfo[irq]->ui.flags.syncio ) - { - ret = -EBUSY; - } - else - { - /* - * If sync processing was requested we lock the sync ISC, - * modify the device to present interrupts for this ISC only - * and switch the CPU to handle this ISC + the console ISC - * exclusively. - */ - if ( flag & DOIO_WAIT_FOR_INTERRUPT ) - { - // - // check whether we run recursively (sense processing) - // - if ( !ioinfo[irq]->ui.flags.syncio ) - { - spin_lock_irqsave( &sync_isc, psw_flags); - - ret = enable_cpu_sync_isc( irq); - - if ( ret ) - { - spin_unlock_irqrestore( &sync_isc, - psw_flags); - return( ret); - } - else - { - sync_isc_locked = 1; // local - ioinfo[irq]->ui.flags.syncio = 1; // global - - } /* endif */ - - } /* endif */ - - } /* endif */ - - /* - * Issue "Halt subchannel" and process condition code - */ - ccode = csch( irq ); - - switch ( ccode ) { - case 0: - - ioinfo[irq]->ui.flags.haltio = 1; - - if ( !ioinfo[irq]->ui.flags.doio ) - { - ioinfo[irq]->ui.flags.busy = 1; - ioinfo[irq]->u_intparm = user_intparm; - ioinfo[irq]->devstat.cstat = 0; - ioinfo[irq]->devstat.dstat = 0; - ioinfo[irq]->devstat.lpum = 0; - ioinfo[irq]->devstat.flag = DEVSTAT_CLEAR_FUNCTION; - ioinfo[irq]->devstat.scnt = 0; - - } - else - { - ioinfo[irq]->devstat.flag |= DEVSTAT_CLEAR_FUNCTION; - - } /* endif */ - - /* - * If synchronous I/O processing is requested, we have - * to wait for the corresponding interrupt to occur by - * polling the interrupt condition. However, as multiple - * interrupts may be outstanding, we must not just wait - * for the first interrupt, but must poll until ours - * pops up. - */ - if ( flag & DOIO_WAIT_FOR_INTERRUPT ) - { - int io_sub; - __u32 io_parm; - psw_t io_new_psw; - int ccode; - - int ready = 0; - struct _lowcore *lc = NULL; - - /* - * We shouldn't perform a TPI loop, waiting for - * an interrupt to occur, but should load a - * WAIT PSW instead. Otherwise we may keep the - * channel subsystem busy, not able to present - * the interrupt. When our sync. interrupt - * arrived we reset the I/O old PSW to its - * original value. - */ - memcpy( &io_new_psw, - &lc->io_new_psw, - sizeof(psw_t)); - - ccode = iac(); - - switch (ccode) { - case 0: // primary-space - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_PRIM_SPACE_MODE - | _PSW_IO_WAIT; - break; - case 1: // secondary-space - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_SEC_SPACE_MODE - | _PSW_IO_WAIT; - break; - case 2: // access-register - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_ACC_REG_MODE - | _PSW_IO_WAIT; - break; - case 3: // home-space - io_sync_wait.mask = _IO_PSW_MASK - | _PSW_HOME_SPACE_MODE - | _PSW_IO_WAIT; - break; - default: - panic( "halt_IO() : unexpected " - "address-space-control %d\n", - ccode); - break; - } /* endswitch */ - - io_sync_wait.addr = FIX_PSW(&&cio_wakeup); - - /* - * Martin didn't like modifying the new PSW, now we take - * a fast exit in do_IRQ() instead - */ - *(__u32 *)__LC_SYNC_IO_WORD = 1; - - do - { - - asm volatile ( "lpsw %0" : : "m" (io_sync_wait) ); -cio_wakeup: - io_parm = *(__u32 *)__LC_IO_INT_PARM; - io_sub = (__u32)*(__u16 *)__LC_SUBCHANNEL_NR; - - ready = s390_process_IRQ( io_sub ); - - } while ( !((io_sub == irq) && (ready == 1)) ); - - *(__u32 *)__LC_SYNC_IO_WORD = 0; - - } /* endif */ - - ret = 0; - break; - - case 1 : /* status pending */ - - ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; - - /* - * initialize the device driver specific devstat irb area - */ - memset( &((devstat_t *) ioinfo[irq]->irq_desc.action->dev_id)->ii.irb, - '\0', sizeof( irb_t) ); - - /* - * Let the common interrupt handler process the pending - * status. However, we must avoid calling the user - * action handler, as it won't be prepared to handle - * a pending status during do_IO() processing inline. - * This also implies that s390_process_IRQ must - * terminate synchronously - especially if device - * sensing is required. - */ - ioinfo[irq]->ui.flags.s_pend = 1; - ioinfo[irq]->ui.flags.busy = 1; - ioinfo[irq]->ui.flags.doio = 1; - - s390_process_IRQ( irq ); - - ioinfo[irq]->ui.flags.s_pend = 0; - ioinfo[irq]->ui.flags.busy = 0; - ioinfo[irq]->ui.flags.doio = 0; - ioinfo[irq]->ui.flags.repall = 0; - ioinfo[irq]->ui.flags.w4final = 0; - - ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; - - /* - * In multipath mode a condition code 3 implies the last - * path has gone, except we have previously restricted - * the I/O to a particular path. A condition code 1 - * (0 won't occur) results in return code EIO as well - * as 3 with another path than the one used (i.e. path available mask is non-zero). - */ - if ( ioinfo[irq]->devstat.ii.irb.scsw.cc == 3 ) - { - ret = -ENODEV; - ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; - ioinfo[irq]->ui.flags.oper = 0; - } - else - { - ret = -EIO; - ioinfo[irq]->devstat.flag &= ~DEVSTAT_NOT_OPER; - ioinfo[irq]->ui.flags.oper = 1; - - } /* endif */ - - break; - - case 2 : /* busy */ - - ret = -EBUSY; - break; - - default: /* device not operational */ - - ret = -ENODEV; - break; - - } /* endswitch */ - - if ( ( flag & DOIO_WAIT_FOR_INTERRUPT ) - && ( sync_isc_locked ) ) - { - sync_isc_locked = 0; // local setting - ioinfo[irq]->ui.flags.syncio = 0; // global setting - - disable_cpu_sync_isc( irq ); - - spin_unlock_irqrestore( &sync_isc, psw_flags); - - } /* endif */ - - } /* endif */ - - return( ret ); -} - - -/* - * do_IRQ() handles all normal I/O device IRQ's (the special - * SMP cross-CPU interrupts have their own specific - * handlers). - * - * Returns: 0 - no ending status received, no further action taken - * 1 - interrupt handler was called with ending status - */ -asmlinkage void do_IRQ( struct pt_regs regs, - unsigned int irq, - __u32 s390_intparm ) -{ -#ifdef CONFIG_FAST_IRQ - int ccode; - tpi_info_t tpi_info; - int new_irq; -#endif - int use_irq = irq; -// __u32 use_intparm = s390_intparm; - - // - // fix me !!! - // - // We need to schedule device recognition, the interrupt stays - // pending. We need to dynamically allocate an ioinfo structure. - // - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return; /* this keeps the device boxed ... */ - } - - /* - * take fast exit if CPU is in sync. I/O state - * - * Note: we have to turn off the WAIT bit and re-disable - * interrupts prior to return as this was the initial - * entry condition to synchronous I/O. - */ - if ( *(__u32 *)__LC_SYNC_IO_WORD ) - { - regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT); - - return; - - } /* endif */ - - s390irq_spin_lock(use_irq); - -#ifdef CONFIG_FAST_IRQ - do { -#endif /* CONFIG_FAST_IRQ */ - - s390_process_IRQ( use_irq ); - -#ifdef CONFIG_FAST_IRQ - - /* - * more interrupts pending ? - */ - ccode = tpi( &tpi_info ); - - if ( ! ccode ) - break; // no, leave ... - - new_irq = tpi_info.irq; -// use_intparm = tpi_info.intparm; - - /* - * if the interrupt is for a different irq we - * release the current irq lock and obtain - * a new one ... - */ - if ( new_irq != use_irq ) - { - s390irq_spin_unlock(use_irq); - use_irq = new_irq; - s390irq_spin_lock(use_irq); - - } /* endif */ - - } while ( 1 ); - -#endif /* CONFIG_FAST_IRQ */ - - s390irq_spin_unlock(use_irq); - - return; -} - -/* - * s390_process_IRQ() handles status pending situations and interrupts - * - * Called by : do_IRQ() - for "real" interrupts - * s390_start_IO, halt_IO() - * - status pending cond. after SSCH, or HSCH - * disable_subchannel() - status pending conditions (after MSCH) - * - * Returns: 0 - no ending status received, no further action taken - * 1 - interrupt handler was called with ending status - */ -int s390_process_IRQ( unsigned int irq ) -{ - int ccode; /* condition code from tsch() operation */ - int irb_cc; /* condition code from irb */ - int sdevstat; /* effective struct devstat size to copy */ - unsigned int fctl; /* function control */ - unsigned int stctl; /* status control */ - unsigned int actl; /* activity control */ - struct irqaction *action; - struct pt_regs regs; /* for interface compatibility only */ - - int issense = 0; - int ending_status = 0; - int allow4handler = 1; - int chnchk = 0; -#if 0 - int cpu = smp_processor_id(); - - kstat.irqs[cpu][irq]++; -#endif - action = ioinfo[irq]->irq_desc.action; - - /* - * It might be possible that a device was not-oper. at the time - * of free_irq() processing. This means the handler is no longer - * available when the device possibly becomes ready again. In - * this case we perform delayed disable_subchannel() processing. - */ - if ( action == NULL ) - { - if ( !ioinfo[irq]->ui.flags.d_disable ) - { - printk( KERN_CRIT"s390_process_IRQ(%04X) " - "- no interrupt handler registered" - "for device %04X !\n", - irq, - ioinfo[irq]->devstat.devno); - - } /* endif */ - - } /* endif */ - - /* - * retrieve the i/o interrupt information (irb), - * update the device specific status information - * and possibly call the interrupt handler. - * - * Note 1: At this time we don't process the resulting - * condition code (ccode) from tsch(), although - * we probably should. - * - * Note 2: Here we will have to check for channel - * check conditions and call a channel check - * handler. - * - * Note 3: If a start function was issued, the interruption - * parameter relates to it. If a halt function was - * issued for an idle device, the intparm must not - * be taken from lowcore, but from the devstat area. - */ - ccode = tsch( irq, &(ioinfo[irq]->devstat.ii.irb) ); - - // - // We must only accumulate the status if initiated by do_IO() or halt_IO() - // - if ( ioinfo[irq]->ui.flags.busy ) - { - ioinfo[irq]->devstat.dstat |= ioinfo[irq]->devstat.ii.irb.scsw.dstat; - ioinfo[irq]->devstat.cstat |= ioinfo[irq]->devstat.ii.irb.scsw.cstat; - } - else - { - ioinfo[irq]->devstat.dstat = ioinfo[irq]->devstat.ii.irb.scsw.dstat; - ioinfo[irq]->devstat.cstat = ioinfo[irq]->devstat.ii.irb.scsw.cstat; - - ioinfo[irq]->devstat.flag = 0; // reset status flags - - } /* endif */ - - ioinfo[irq]->devstat.lpum = ioinfo[irq]->devstat.ii.irb.esw.esw1.lpum; - - if ( ioinfo[irq]->ui.flags.busy) - { - ioinfo[irq]->devstat.intparm = ioinfo[irq]->u_intparm; - - } /* endif */ - - /* - * reset device-busy bit if no longer set in irb - */ - if ( (ioinfo[irq]->devstat.dstat & DEV_STAT_BUSY ) - && ((ioinfo[irq]->devstat.ii.irb.scsw.dstat & DEV_STAT_BUSY) == 0)) - { - ioinfo[irq]->devstat.dstat &= ~DEV_STAT_BUSY; - - } /* endif */ - - /* - * Save residual count and CCW information in case primary and - * secondary status are presented with different interrupts. - */ - if ( ioinfo[irq]->devstat.ii.irb.scsw.stctl & SCSW_STCTL_PRIM_STATUS ) - { - ioinfo[irq]->devstat.rescnt = ioinfo[irq]->devstat.ii.irb.scsw.count; - -#if CONFIG_DEBUG_IO - if ( irq != cons_dev ) - printk( "s390_process_IRQ( %04X ) : " - "residual count from irb after tsch() %d\n", - irq, ioinfo[irq]->devstat.rescnt ); -#endif - } /* endif */ - - if ( ioinfo[irq]->devstat.ii.irb.scsw.cpa != 0 ) - { - ioinfo[irq]->devstat.cpa = ioinfo[irq]->devstat.ii.irb.scsw.cpa; - - } /* endif */ - - irb_cc = ioinfo[irq]->devstat.ii.irb.scsw.cc; - - // - // check for any kind of channel or interface control check but don't - // issue the message for the console device - // - if ( (ioinfo[irq]->devstat.ii.irb.scsw.cstat - & ( SCHN_STAT_CHN_DATA_CHK - | SCHN_STAT_CHN_CTRL_CHK - | SCHN_STAT_INTF_CTRL_CHK ) ) - && (irq != cons_dev ) ) - { - printk( "Channel-Check or Interface-Control-Check " - "received\n" - " ... device %04X on subchannel %04X, dev_stat " - ": %02X sch_stat : %02X\n", - ioinfo[irq]->devstat.devno, - irq, - ioinfo[irq]->devstat.dstat, - ioinfo[irq]->devstat.cstat); - - chnchk = 1; - - } /* endif */ - - issense = ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.cons; - - if ( issense ) - { - ioinfo[irq]->devstat.scnt = - ioinfo[irq]->devstat.ii.irb.esw.esw0.erw.scnt; - ioinfo[irq]->devstat.flag |= - DEVSTAT_FLAG_SENSE_AVAIL; - - sdevstat = sizeof( devstat_t); - -#if CONFIG_DEBUG_IO - if ( irq != cons_dev ) - printk( "s390_process_IRQ( %04X ) : " - "concurrent sense bytes avail %d\n", - irq, ioinfo[irq]->devstat.scnt ); -#endif - } - else - { - /* don't copy the sense data area ! */ - sdevstat = sizeof( devstat_t) - SENSE_MAX_COUNT; - - } /* endif */ - - switch ( irb_cc ) { - case 1: /* status pending */ - - ioinfo[irq]->devstat.flag |= DEVSTAT_STATUS_PENDING; - - case 0: /* normal i/o interruption */ - - fctl = ioinfo[irq]->devstat.ii.irb.scsw.fctl; - stctl = ioinfo[irq]->devstat.ii.irb.scsw.stctl; - actl = ioinfo[irq]->devstat.ii.irb.scsw.actl; - - if ( chnchk && (ioinfo[irq]->senseid.cu_type == 0x3088)) - { - char buffer[80]; - - sprintf( buffer, "s390_process_IRQ(%04X) - irb for " - "device %04X after channel check\n", - irq, - ioinfo[irq]->devstat.devno ); - - s390_displayhex( buffer, - &(ioinfo[irq]->devstat.ii.irb) , - sizeof(irb_t)); - } /* endif */ - - ioinfo[irq]->stctl |= stctl; - - ending_status = ( stctl & SCSW_STCTL_SEC_STATUS ) - || ( stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND) ) - || ( (fctl == SCSW_FCTL_HALT_FUNC) && (stctl == SCSW_STCTL_STATUS_PEND) ); - - /* - * Check for unsolicited interrupts - for debug purposes only - * - * We only consider an interrupt as unsolicited, if the device was not - * actively in use (busy) and an interrupt other than an ALERT status - * was received. - * - * Note: We must not issue a message to the console, if the - * unsolicited interrupt applies to the console device - * itself ! - */ -#if CONFIG_DEBUG_IO - if ( ( irq != cons_dev ) - && !( stctl & SCSW_STCTL_ALERT_STATUS ) - && ( ioinfo[irq]->ui.flags.busy == 0 ) ) - { - char buffer[80]; - - printk( "Unsolicited interrupt received for device %04X on subchannel %04X\n" - " ... device status : %02X subchannel status : %02X\n", - ioinfo[irq]->devstat.devno, - irq, - ioinfo[irq]->devstat.dstat, - ioinfo[irq]->devstat.cstat); - - sprintf( buffer, "s390_process_IRQ(%04X) - irb for " - "device %04X, ending_status %d\n", - irq, - ioinfo[irq]->devstat.devno, - ending_status); - - s390_displayhex( buffer, - &(ioinfo[irq]->devstat.ii.irb) , - sizeof(irb_t)); - - } /* endif */ - - /* - * take fast exit if no handler is available - */ - if ( !action ) - return( ending_status ); - -#endif - /* - * Check whether we must issue a SENSE CCW ourselves if there is no - * concurrent sense facility installed for the subchannel. - * - * Note: We should check for ioinfo[irq]->ui.flags.consns but VM - * violates the ESA/390 architecture and doesn't present an - * operand exception for virtual devices without concurrent - * sense facility available/supported when enabling the - * concurrent sense facility. - */ - if ( ( ( ioinfo[irq]->devstat.ii.irb.scsw.dstat & DEV_STAT_UNIT_CHECK ) - && ( !issense ) ) - || ( ioinfo[irq]->ui.flags.delsense && ending_status ) ) - { - int ret_io; - ccw1_t *s_ccw = &ioinfo[irq]->senseccw; - unsigned long s_flag = 0; - - if ( ending_status ) - { - /* - * We copy the current status information into the device driver - * status area. Then we can use the local devstat area for device - * sensing. When finally calling the IRQ handler we must not overlay - * the original device status but copy the sense data only. - */ - memcpy( action->dev_id, - &(ioinfo[irq]->devstat), - sizeof( devstat_t) ); - - s_ccw->cmd_code = CCW_CMD_BASIC_SENSE; - s_ccw->cda = (__u32)virt_to_phys( ioinfo[irq]->devstat.ii.sense.data); - s_ccw->count = SENSE_MAX_COUNT; - s_ccw->flags = CCW_FLAG_SLI; - - /* - * If free_irq() or a sync do_IO/s390_start_IO() is in - * process we have to sense synchronously - */ - if ( ioinfo[irq]->ui.flags.unready || ioinfo[irq]->ui.flags.syncio ) - { - s_flag = DOIO_WAIT_FOR_INTERRUPT; - - } /* endif */ - - /* - * Reset status info - * - * It does not matter whether this is a sync. or async. - * SENSE request, but we have to assure we don't call - * the irq handler now, but keep the irq in busy state. - * In sync. mode s390_process_IRQ() is called recursively, - * while in async. mode we re-enter do_IRQ() with the - * next interrupt. - * - * Note : this may be a delayed sense request ! - */ - allow4handler = 0; - - ioinfo[irq]->ui.flags.fast = 0; - ioinfo[irq]->ui.flags.repall = 0; - ioinfo[irq]->ui.flags.w4final = 0; - ioinfo[irq]->ui.flags.delsense = 0; - - ioinfo[irq]->devstat.cstat = 0; - ioinfo[irq]->devstat.dstat = 0; - ioinfo[irq]->devstat.rescnt = SENSE_MAX_COUNT; - - ioinfo[irq]->ui.flags.w4sense = 1; - - ret_io = s390_start_IO( irq, - s_ccw, - 0xE2C5D5E2, // = SENSe - 0, // n/a - s_flag); - } - else - { - /* - * we received an Unit Check but we have no final - * status yet, therefore we must delay the SENSE - * processing. However, we must not report this - * intermediate status to the device interrupt - * handler. - */ - ioinfo[irq]->ui.flags.fast = 0; - ioinfo[irq]->ui.flags.repall = 0; - - ioinfo[irq]->ui.flags.delsense = 1; - allow4handler = 0; - - } /* endif */ - - } /* endif */ - - /* - * we allow for the device action handler if . - * - we received ending status - * - the action handler requested to see all interrupts - * - we received a PCI - * - fast notification was requested (primary status) - * - unsollicited interrupts - * - */ - if ( allow4handler ) - { - allow4handler = ending_status - || ( ioinfo[irq]->ui.flags.repall ) - || ( ioinfo[irq]->devstat.ii.irb.scsw.cstat & SCHN_STAT_PCI ) - || ( (ioinfo[irq]->ui.flags.fast ) && (stctl & SCSW_STCTL_PRIM_STATUS) ) - || ( ioinfo[irq]->ui.flags.oper == 0 ); - - } /* endif */ - - /* - * We used to copy the device status information right before - * calling the device action handler. However, in status - * pending situations during do_IO() or halt_IO(), as well as - * enable_subchannel/disable_subchannel processing we must - * synchronously return the status information and must not - * call the device action handler. - * - */ - if ( allow4handler ) - { - /* - * if we were waiting for sense data we copy the sense - * bytes only as the original status information was - * saved prior to sense already. - */ - if ( ioinfo[irq]->ui.flags.w4sense ) - { - int sense_count = SENSE_MAX_COUNT-ioinfo[irq]->devstat.rescnt; - -#if CONFIG_DEBUG_IO - if ( irq != cons_dev ) - printk( "s390_process_IRQ( %04X ) : " - "BASIC SENSE bytes avail %d\n", - irq, sense_count ); -#endif - ioinfo[irq]->ui.flags.w4sense = 0; - ((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FLAG_SENSE_AVAIL; - ((devstat_t *)(action->dev_id))->scnt = sense_count; - - if ( sense_count >= 0 ) - { - memcpy( ((devstat_t *)(action->dev_id))->ii.sense.data, - &(ioinfo[irq]->devstat.ii.sense.data), - sense_count); - } - else - { -#if 1 - panic( "s390_process_IRQ(%04x) encountered " - "negative sense count\n", - irq); -#else - printk( KERN_CRIT"s390_process_IRQ(%04x) encountered " - "negative sense count\n", - irq); -#endif - } /* endif */ - } - else - { - memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat ); - - } /* endif */ - - } /* endif */ - - /* - * for status pending situations other than deferred interrupt - * conditions detected by s390_process_IRQ() itself we must not - * call the handler. This will synchronously be reported back - * to the caller instead, e.g. when detected during do_IO(). - */ - if ( ioinfo[irq]->ui.flags.s_pend || ioinfo[irq]->ui.flags.unready ) - allow4handler = 0; - - /* - * Call device action handler if applicable - */ - if ( allow4handler ) - { - - /* - * We only reset the busy condition when we are sure that no further - * interrupt is pending for the current I/O request (ending_status). - */ - if ( ending_status || !ioinfo[irq]->ui.flags.oper ) - { - ioinfo[irq]->ui.flags.oper = 1; /* dev IS oper */ - - ioinfo[irq]->ui.flags.busy = 0; - ioinfo[irq]->ui.flags.doio = 0; - ioinfo[irq]->ui.flags.haltio = 0; - ioinfo[irq]->ui.flags.fast = 0; - ioinfo[irq]->ui.flags.repall = 0; - ioinfo[irq]->ui.flags.w4final = 0; - - ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; - ((devstat_t *)(action->dev_id))->flag |= DEVSTAT_FINAL_STATUS; - - action->handler( irq, action->dev_id, ®s); - - // - // reset intparm after final status or we will badly present unsolicited - // interrupts with a intparm value possibly no longer valid. - // - ioinfo[irq]->devstat.intparm = 0; - - // - // Was there anything queued ? Start the pending channel program - // if there is one. - // - if ( ioinfo[irq]->ui.flags.doio_q ) - { - int ret; - - ret = s390_start_IO( irq, - ioinfo[irq]->qcpa, - ioinfo[irq]->qintparm, - ioinfo[irq]->qlpm, - ioinfo[irq]->qflag); - - ioinfo[irq]->ui.flags.doio_q = 0; - - /* - * If s390_start_IO() failed call the device's interrupt - * handler, the IRQ related devstat area was setup by - * s390_start_IO() accordingly already (status pending - * condition). - */ - if ( ret ) - { - action->handler( irq, action->dev_id, ®s); - - } /* endif */ - - } /* endif */ - - } - else - { - ioinfo[irq]->ui.flags.w4final = 1; - action->handler( irq, action->dev_id, ®s); - - } /* endif */ - - } /* endif */ - - break; - - case 3: /* device not operational */ - - ioinfo[irq]->ui.flags.oper = 0; - - ioinfo[irq]->ui.flags.busy = 0; - ioinfo[irq]->ui.flags.doio = 0; - ioinfo[irq]->ui.flags.haltio = 0; - - ioinfo[irq]->devstat.cstat = 0; - ioinfo[irq]->devstat.dstat = 0; - ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; - ioinfo[irq]->devstat.flag |= DEVSTAT_FINAL_STATUS; - - /* - * When we find a device "not oper" we save the status - * information into the device status area and call the - * device specific interrupt handler. - * - * Note: currently we don't have any way to reenable - * the device unless an unsolicited interrupt - * is presented. We don't check for spurious - * interrupts on "not oper" conditions. - */ - - if ( ( ioinfo[irq]->ui.flags.fast ) - && ( ioinfo[irq]->ui.flags.w4final ) ) - { - /* - * If a new request was queued already, we have - * to simulate the "not oper" status for the - * queued request by switching the "intparm" value - * and notify the interrupt handler. - */ - if ( ioinfo[irq]->ui.flags.doio_q ) - { - ioinfo[irq]->devstat.intparm = ioinfo[irq]->qintparm; - - } /* endif */ - - } /* endif */ - - ioinfo[irq]->ui.flags.fast = 0; - ioinfo[irq]->ui.flags.repall = 0; - ioinfo[irq]->ui.flags.w4final = 0; - - memcpy( action->dev_id, &(ioinfo[irq]->devstat), sdevstat ); - - ioinfo[irq]->devstat.intparm = 0; - - if ( !ioinfo[irq]->ui.flags.s_pend ) - action->handler( irq, action->dev_id, ®s); - - ending_status = 1; - - break; - - } /* endswitch */ - - return( ending_status ); -} - -/* - * Set the special i/o-interruption sublass 7 for the - * device specified by parameter irq. There can only - * be a single device been operated on this special - * isc. This function is aimed being able to check - * on special device interrupts in disabled state, - * without having to delay I/O processing (by queueing) - * for non-console devices. - * - * Setting of this isc is done by set_cons_dev(), while - * reset_cons_dev() resets this isc and re-enables the - * default isc3 for this device. wait_cons_dev() allows - * to actively wait on an interrupt for this device in - * disabed state. When the interrupt condition is - * encountered, wait_cons_dev(9 calls do_IRQ() to have - * the console device driver processing the interrupt. - */ -int set_cons_dev( int irq ) -{ - int ccode; - unsigned long cr6 __attribute__ ((aligned (8))); - int rc = 0; - - if ( cons_dev != -1 ) - { - rc = -EBUSY; - } - else if ( (irq > highest_subchannel) || (irq < 0) ) - { - rc = -ENODEV; - } - else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - else - { - /* - * modify the indicated console device to operate - * on special console interrupt sublass 7 - */ - ccode = stsch( irq, &(ioinfo[irq]->schib) ); - - if (ccode) - { - rc = -ENODEV; - ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; - } - else - { - ioinfo[irq]->schib.pmcw.isc = 7; - - ccode = msch( irq, &(ioinfo[irq]->schib) ); - - if (ccode) - { - rc = -EIO; - } - else - { - cons_dev = irq; - - /* - * enable console I/O-interrupt sublass 7 - */ - asm volatile ("STCTL 6,6,%0": "=m" (cr6)); - cr6 |= 0x01000000; - asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); - - } /* endif */ - - } /* endif */ - - } /* endif */ - - return( rc); -} - -int reset_cons_dev( int irq) -{ - int rc = 0; - int ccode; - long cr6 __attribute__ ((aligned (8))); - - if ( cons_dev != -1 ) - { - rc = -EBUSY; - } - else if ( (irq > highest_subchannel) || (irq < 0) ) - { - rc = -ENODEV; - } - else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - else - { - /* - * reset the indicated console device to operate - * on default console interrupt sublass 3 - */ - ccode = stsch( irq, &(ioinfo[irq]->schib) ); - - if (ccode) - { - rc = -ENODEV; - ioinfo[irq]->devstat.flag |= DEVSTAT_NOT_OPER; - } - else - { - - ioinfo[irq]->schib.pmcw.isc = 3; - - ccode = msch( irq, &(ioinfo[irq]->schib) ); - - if (ccode) - { - rc = -EIO; - } - else - { - cons_dev = -1; - - /* - * disable special console I/O-interrupt sublass 7 - */ - asm volatile ("STCTL 6,6,%0": "=m" (cr6)); - cr6 &= 0xFEFFFFFF; - asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); - - } /* endif */ - - } /* endif */ - - } /* endif */ - - return( rc); -} - -int wait_cons_dev( int irq ) -{ - int rc = 0; - long save_cr6; - - if ( irq == cons_dev ) - { - - /* - * before entering the spinlock we may already have - * processed the interrupt on a different CPU ... - */ - if ( ioinfo[irq]->ui.flags.busy == 1 ) - { - long cr6 __attribute__ ((aligned (8))); - - /* - * disable all, but isc 7 (console device) - */ - asm volatile ("STCTL 6,6,%0": "=m" (cr6)); - save_cr6 = cr6; - cr6 &= 0x01FFFFFF; - asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); - - do { - tpi_info_t tpi_info; - if (tpi(&tpi_info) == 1) { - s390_process_IRQ( tpi_info.irq ); - } else { - s390irq_spin_unlock(irq); - tod_wait(100); - s390irq_spin_lock(irq); - } - eieio(); - } while (ioinfo[irq]->ui.flags.busy == 1); - - /* - * restore previous isc value - */ - asm volatile ("STCTL 6,6,%0": "=m" (cr6)); - cr6 = save_cr6; - asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); - - } /* endif */ - - } - else - { - rc = EINVAL; - - } /* endif */ - - - return(rc); -} - - -int enable_cpu_sync_isc( int irq ) -{ - int ccode; - long cr6 __attribute__ ((aligned (8))); - - int count = 0; - int rc = 0; - - if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA ) - { - ccode = stsch( irq, &(ioinfo[irq]->schib) ); - - if ( !ccode ) - { - ioinfo[irq]->schib.pmcw.isc = 5; - - do - { - ccode = msch( irq, &(ioinfo[irq]->schib) ); - - if (ccode == 0 ) - { - /* - * enable interrupt subclass in CPU - */ - asm volatile ("STCTL 6,6,%0": "=m" (cr6)); - cr6 |= 0x04000000; // enable sync isc 5 - cr6 &= 0xEFFFFFFF; // disable standard isc 3 - asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); - } - else if (ccode == 3) - { - rc = -ENODEV; // device not-oper - very unlikely - - } - else if (ccode == 2) - { - rc = -EBUSY; // device busy - should not happen - - } - else if (ccode == 1) - { - // - // process pending status - // - ioinfo[irq]->ui.flags.s_pend = 1; - - s390_process_IRQ( irq ); - - ioinfo[irq]->ui.flags.s_pend = 0; - - count++; - - } /* endif */ - - } while ( ccode == 1 && count < 3 ); - - if ( count == 3) - { - rc = -EIO; - - } /* endif */ - } - else - { - rc = -ENODEV; // device is not-operational - - } /* endif */ - } - else - { - rc = -EINVAL; - - } /* endif */ - - return( rc); -} - -int disable_cpu_sync_isc( int irq) -{ - int rc = 0; - int ccode; - long cr6 __attribute__ ((aligned (8))); - - if ( irq <= highest_subchannel && ioinfo[irq] != INVALID_STORAGE_AREA ) - { - ccode = stsch( irq, &(ioinfo[irq]->schib) ); - - ioinfo[irq]->schib.pmcw.isc = 3; - - ccode = msch( irq, &(ioinfo[irq]->schib) ); - - if (ccode) - { - rc = -EIO; - } - else - { - - /* - * enable interrupt subclass in CPU - */ - asm volatile ("STCTL 6,6,%0": "=m" (cr6)); - cr6 &= 0xFBFFFFFF; // disable sync isc 5 - cr6 |= 0x10000000; // enable standard isc 3 - asm volatile ("LCTL 6,6,%0":: "m" (cr6):"memory"); - - } /* endif */ - - } - else - { - rc = -EINVAL; - - } /* endif */ - - return( rc); -} - -// -// Input : -// devno - device number -// ps - pointer to sense ID data area -// -// Output : none -// -void VM_virtual_device_info( __u16 devno, - senseid_t *ps ) -{ - diag210_t diag_data; - int ccode; - - int error = 0; - - diag_data.vrdcdvno = devno; - diag_data.vrdclen = sizeof( diag210_t); - ccode = diag210( (diag210_t *)virt_to_phys( &diag_data ) ); - ps->reserved = 0xff; - - switch (diag_data.vrdcvcla) { - case 0x80: - - switch (diag_data.vrdcvtyp) { - case 00: - - ps->cu_type = 0x3215; - - break; - - default: - - error = 1; - - break; - - } /* endswitch */ - - break; - - case 0x40: - - switch (diag_data.vrdcvtyp) { - case 0xC0: - - ps->cu_type = 0x5080; - - break; - - case 0x80: - - ps->cu_type = 0x2250; - - break; - - case 0x04: - - ps->cu_type = 0x3277; - - break; - - case 0x01: - - ps->cu_type = 0x3278; - - break; - - default: - - error = 1; - - break; - - } /* endswitch */ - - break; - - case 0x20: - - switch (diag_data.vrdcvtyp) { - case 0x84: - - ps->cu_type = 0x3505; - - break; - - case 0x82: - - ps->cu_type = 0x2540; - - break; - - case 0x81: - - ps->cu_type = 0x2501; - - break; - - default: - - error = 1; - - break; - - } /* endswitch */ - - break; - - case 0x10: - - switch (diag_data.vrdcvtyp) { - case 0x84: - - ps->cu_type = 0x3525; - - break; - - case 0x82: - - ps->cu_type = 0x2540; - - break; - - case 0x4F: - case 0x4E: - case 0x48: - - ps->cu_type = 0x3820; - - break; - - case 0x4D: - case 0x49: - case 0x45: - - ps->cu_type = 0x3800; - - break; - - case 0x4B: - - ps->cu_type = 0x4248; - - break; - - case 0x4A: - - ps->cu_type = 0x4245; - - break; - - case 0x47: - - ps->cu_type = 0x3262; - - break; - - case 0x43: - - ps->cu_type = 0x3203; - - break; - - case 0x42: - - ps->cu_type = 0x3211; - - break; - - case 0x41: - - ps->cu_type = 0x1403; - - break; - - default: - - error = 1; - - break; - - } /* endswitch */ - - break; - - case 0x08: - - switch (diag_data.vrdcvtyp) { - case 0x82: - - ps->cu_type = 0x3422; - - break; - - case 0x81: - - ps->cu_type = 0x3490; - - break; - - case 0x10: - - ps->cu_type = 0x3420; - - break; - - case 0x02: - - ps->cu_type = 0x3430; - - break; - - case 0x01: - - ps->cu_type = 0x3480; - - break; - - case 0x42: - - ps->cu_type = 0x3424; - - break; - - case 0x44: - - ps->cu_type = 0x9348; - - break; - - default: - - error = 1; - - break; - - } /* endswitch */ - - break; - - default: - - error = 1; - - break; - - } /* endswitch */ - - if ( error ) - {printk( "DIAG X'210' for device %04X returned (cc = %d): vdev class : %02X, " - "vdev type : %04X \n ... rdev class : %02X, rdev type : %04X, rdev model: %02X\n", - devno, - ccode, - diag_data.vrdcvcla, - diag_data.vrdcvtyp, - diag_data.vrdcrccl, - diag_data.vrdccrty, - diag_data.vrdccrmd ); - - } /* endif */ - -} - -/* - * This routine returns the characteristics for the device - * specified. Some old devices might not provide the necessary - * command code information during SenseID processing. In this - * case the function returns -EINVAL. Otherwise the function - * allocates a decice specific data buffer and provides the - * device characteristics together with the buffer size. Its - * the callers responability to release the kernel memory if - * not longer needed. In case of persistent I/O problems -EBUSY - * is returned. - * - * The function may be called enabled or disabled. However, the - * caller must have locked the irq it is requesting data for. - * - * Note : It would have been nice to collect this information - * during init_IRQ() processing but this is not possible - * - * a) without statically pre-allocation fixed size buffers - * as virtual memory management isn't available yet. - * - * b) without unnecessarily increase system startup by - * evaluating devices eventually not used at all. - */ -int read_dev_chars( int irq, void **buffer, int length ) -{ - unsigned int flags; - ccw1_t *rdc_ccw; - devstat_t devstat; - char *rdc_buf; - int devflag; - - int ret = 0; - int emulated = 0; - int retry = 5; - - if ( !buffer || !length ) - { - return( -EINVAL ); - - } /* endif */ - - if ( (irq > highest_subchannel) || (irq < 0 ) ) - { - return( -ENODEV ); - - } - else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - - if ( ioinfo[irq]->ui.flags.oper == 0 ) - { - return( -ENODEV ); - - } /* endif */ - - /* - * Before playing around with irq locks we should assure - * running disabled on (just) our CPU. Sync. I/O requests - * also require to run disabled. - * - * Note : as no global lock is required, we must not use - * cli(), but __cli() instead. - */ - __save_flags(flags); - __cli(); - - rdc_ccw = &ioinfo[irq]->senseccw; - - if ( !ioinfo[irq]->ui.flags.ready ) - { - ret = request_irq( irq, - init_IRQ_handler, - 0, "RDC", &devstat ); - - if ( !ret ) - { - emulated = 1; - - } /* endif */ - - } /* endif */ - - if ( !ret ) - { - if ( ! *buffer ) - { - rdc_buf = kmalloc( length, GFP_KERNEL); - } - else - { - rdc_buf = *buffer; - - } /* endif */ - - if ( !rdc_buf ) - { - ret = -ENOMEM; - } - else - { - do - { - rdc_ccw->cmd_code = CCW_CMD_RDC; - rdc_ccw->cda = (__u32)virt_to_phys( rdc_buf ); - rdc_ccw->count = length; - rdc_ccw->flags = CCW_FLAG_SLI; - - ret = s390_start_IO( irq, - rdc_ccw, - 0x00524443, // RDC - 0, // n/a - DOIO_WAIT_FOR_INTERRUPT ); - retry--; - devflag = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag; - - } while ( ( retry ) - && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) ); - - } /* endif */ - - if ( !retry ) - { - ret = -EBUSY; - - } /* endif */ - - __restore_flags(flags); - - /* - * on success we update the user input parms - */ - if ( !ret ) - { - *buffer = rdc_buf; - - } /* endif */ - - if ( emulated ) - { - free_irq( irq, &devstat); - - } /* endif */ - - } /* endif */ - - return( ret ); -} - -/* - * Read Configuration data - */ -int read_conf_data( int irq, void **buffer, int *length ) -{ - int found = 0; - int ciw_cnt = 0; - unsigned int flags; - - int ret = 0; - - if ( (irq > highest_subchannel) || (irq < 0 ) ) - { - return( -ENODEV ); - } - else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - - } /* endif */ - - if ( ioinfo[irq]->ui.flags.oper == 0 ) - { - return( -ENODEV ); - - } /* endif */ - - /* - * scan for RCD command in extended SenseID data - */ - for ( ; (found == 0) && (ciw_cnt < 62); ciw_cnt++ ) - { - if ( ioinfo[irq]->senseid.ciw[ciw_cnt].ct == CIW_TYPE_RCD ) - { - found = 1; - break; - } /* endif */ - - } /* endfor */ - - if ( found ) - { - ccw1_t *rcd_ccw = &ioinfo[irq]->senseccw; - devstat_t devstat; - char *rcd_buf; - int devflag; - - int emulated = 0; - int retry = 5; - - __save_flags(flags); - __cli(); - - if ( !ioinfo[irq]->ui.flags.ready ) - { - ret = request_irq( irq, - init_IRQ_handler, - 0, "RCD", &devstat ); - - if ( !ret ) - { - emulated = 1; - - } /* endif */ - - } /* endif */ - - if ( !ret ) - { - rcd_buf = kmalloc( ioinfo[irq]->senseid.ciw[ciw_cnt].count, - GFP_KERNEL); - - do - { - rcd_ccw->cmd_code = ioinfo[irq]->senseid.ciw[ciw_cnt].cmd; - rcd_ccw->cda = (__u32)virt_to_phys( rcd_buf ); - rcd_ccw->count = ioinfo[irq]->senseid.ciw[ciw_cnt].count; - rcd_ccw->flags = CCW_FLAG_SLI; - - ret = s390_start_IO( irq, - rcd_ccw, - 0x00524344, // == RCD - 0, // n/a - DOIO_WAIT_FOR_INTERRUPT ); - - retry--; - - devflag = ((devstat_t *)(ioinfo[irq]->irq_desc.action->dev_id))->flag; - - } while ( ( retry ) - && ( ret || (devflag & DEVSTAT_STATUS_PENDING) ) ); - - if ( !retry ) - ret = -EBUSY; - - __restore_flags(flags); - - } /* endif */ - - /* - * on success we update the user input parms - */ - if ( !ret ) - { - *length = ioinfo[irq]->senseid.ciw[ciw_cnt].count; - *buffer = rcd_buf; - - } /* endif */ - - if ( emulated ) - free_irq( irq, &devstat); - } - else - { - ret = -EINVAL; - - } /* endif */ - - return( ret ); - -} - -int get_dev_info( int irq, dev_info_t * pdi) -{ - return( get_dev_info_by_irq( irq, pdi)); -} - -static int __inline__ get_next_available_irq( ioinfo_t *pi) -{ - int ret_val; - - while ( TRUE ) - { - if ( pi->ui.flags.oper ) - { - ret_val = pi->irq; - break; - } - else - { - pi = pi->next; - - // - // leave at end of list unconditionally - // - if ( pi == NULL ) - { - ret_val = -ENODEV; - break; - } - - } /* endif */ - - } /* endwhile */ - - return ret_val; -} - - -int get_irq_first( void ) -{ - int ret_irq; - - if ( ioinfo_head ) - { - if ( ioinfo_head->ui.flags.oper ) - { - ret_irq = ioinfo_head->irq; - } - else if ( ioinfo_head->next ) - { - ret_irq = get_next_available_irq( ioinfo_head->next ); - - } - else - { - ret_irq = -ENODEV; - - } /* endif */ - } - else - { - ret_irq = -ENODEV; - - } /* endif */ - - return ret_irq; -} - -int get_irq_next( int irq ) -{ - int ret_irq; - - if ( ioinfo[irq] != INVALID_STORAGE_AREA ) - { - if ( ioinfo[irq]->next ) - { - if ( ioinfo[irq]->next->ui.flags.oper ) - { - ret_irq = ioinfo[irq]->next->irq; - } - else - { - ret_irq = get_next_available_irq( ioinfo[irq]->next ); - - } /* endif */ - } - else - { - ret_irq = -ENODEV; - - } /* endif */ - } - else - { - ret_irq = -EINVAL; - - } /* endif */ - - return ret_irq; -} - -int get_dev_info_by_irq( int irq, dev_info_t *pdi) -{ - - if ( irq > highest_subchannel || irq < 0 ) - { - return -ENODEV; - } - else if ( pdi == NULL ) - { - return -EINVAL; - } - else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } - else - { - pdi->devno = ioinfo[irq]->schib.pmcw.dev; - pdi->irq = irq; - - if ( ioinfo[irq]->ui.flags.oper ) - { - pdi->status = 0; - memcpy( &(pdi->sid_data), - &ioinfo[irq]->senseid, - sizeof( senseid_t)); - } - else - { - pdi->status = DEVSTAT_NOT_OPER; - memcpy( &(pdi->sid_data), - '\0', - sizeof( senseid_t)); - pdi->sid_data.cu_type = 0xFFFF; - - } /* endif */ - - if ( ioinfo[irq]->ui.flags.ready ) - pdi->status |= DEVSTAT_DEVICE_OWNED; - - return 0; - - } /* endif */ - -} - - -int get_dev_info_by_devno( __u16 devno, dev_info_t *pdi) -{ - int i; - int rc = -ENODEV; - - if ( devno > 0x0000ffff ) - { - return -ENODEV; - } - else if ( pdi == NULL ) - { - return -EINVAL; - } - else - { - - for ( i=0; i <= highest_subchannel; i++ ) - { - - if ( ioinfo[i] != INVALID_STORAGE_AREA - && ioinfo[i]->schib.pmcw.dev == devno ) - { - if ( ioinfo[i]->ui.flags.oper ) - { - pdi->status = 0; - pdi->irq = i; - pdi->devno = devno; - - memcpy( &(pdi->sid_data), - &ioinfo[i]->senseid, - sizeof( senseid_t)); - } - else - { - pdi->status = DEVSTAT_NOT_OPER; - pdi->irq = i; - pdi->devno = devno; - - memcpy( &(pdi->sid_data), '\0', sizeof( senseid_t)); - pdi->sid_data.cu_type = 0xFFFF; - - } /* endif */ - - if ( ioinfo[i]->ui.flags.ready ) - pdi->status |= DEVSTAT_DEVICE_OWNED; - - rc = 0; /* found */ - break; - - } /* endif */ - - } /* endfor */ - - return( rc); - - } /* endif */ - -} - -int get_irq_by_devno( __u16 devno ) -{ - int i; - int rc = -1; - - if ( devno <= 0x0000ffff ) - { - for ( i=0; i <= highest_subchannel; i++ ) - { - if ( (ioinfo[i] != INVALID_STORAGE_AREA ) - && (ioinfo[i]->schib.pmcw.dev == devno) - && (ioinfo[i]->schib.pmcw.dnv == 1 ) ) - { - rc = i; - break; - - } /* endif */ - - } /* endfor */ - - } /* endif */ - - return( rc); -} - -unsigned int get_devno_by_irq( int irq ) -{ - - if ( ( irq > highest_subchannel ) - || ( irq < 0 ) - || ( ioinfo[irq] == INVALID_STORAGE_AREA ) ) - { - return -1; - - } /* endif */ - - /* - * we don't need to check for the device be operational - * as the initial STSCH will always present the device - * number defined by the IOCDS regardless of the device - * existing or not. However, there could be subchannels - * defined who's device number isn't valid ... - */ - if ( ioinfo[irq]->schib.pmcw.dnv ) - return( ioinfo[irq]->schib.pmcw.dev ); - else - return -1; -} - -/* - * s390_device_recognition - * - * Used for system wide device recognition. Issues the device - * independant SenseID command to obtain info the device type. - * - */ -void s390_device_recognition( void) -{ - - int irq = 0; /* let's start with subchannel 0 ... */ - - do - { - /* - * We issue the SenseID command on I/O subchannels we think are - * operational only. - */ - if ( ( ioinfo[irq] != INVALID_STORAGE_AREA ) - && ( ioinfo[irq]->schib.pmcw.st == 0 ) - && ( ioinfo[irq]->ui.flags.oper == 1 ) ) - { - s390_SenseID( irq, &ioinfo[irq]->senseid ); - - } /* endif */ - - irq ++; - - } while ( irq <= highest_subchannel ); - -} - - -/* - * s390_search_devices - * - * Determines all subchannels available to the system. - * - */ -void s390_process_subchannels( void) -{ - int isValid; - int irq = 0; /* Evaluate all subchannels starting with 0 ... */ - - do - { - isValid = s390_validate_subchannel( irq); - - irq++; - - } while ( isValid && irq < __MAX_SUBCHANNELS ); - - highest_subchannel = --irq; - - printk( "\nHighest subchannel number detected: %u\n", - highest_subchannel); -} - -/* - * s390_validate_subchannel() - * - * Process the subchannel for the requested irq. Returns 1 for valid - * subchannels, otherwise 0. - */ -int s390_validate_subchannel( int irq ) -{ - - int retry; /* retry count for status pending conditions */ - int ccode; /* condition code for stsch() only */ - int ccode2; /* condition code for other I/O routines */ - schib_t *p_schib; - - /* - * The first subchannel that is not-operational (ccode==3) - * indicates that there aren't any more devices available. - */ - if ( ( init_IRQ_complete ) - && ( ioinfo[irq] != INVALID_STORAGE_AREA ) ) - { - p_schib = &ioinfo[irq]->schib; - } - else - { - p_schib = &init_schib; - - } /* endif */ - - ccode = stsch( irq, p_schib); - - if ( ccode == 0) - { - /* - * ... just being curious we check for non I/O subchannels - */ - if ( p_schib->pmcw.st ) - { - printk( "Subchannel %04X reports " - "non-I/O subchannel type %04X\n", - irq, - p_schib->pmcw.st); - - if ( ioinfo[irq] != INVALID_STORAGE_AREA ) - ioinfo[irq]->ui.flags.oper = 0; - - } /* endif */ - - if ( p_schib->pmcw.dnv ) - { - if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - - if ( !init_IRQ_complete ) - { - ioinfo[irq] = - (ioinfo_t *)alloc_bootmem( sizeof(ioinfo_t)); - } - else - { - ioinfo[irq] = - (ioinfo_t *)kmalloc( sizeof(ioinfo_t), - GFP_KERNEL ); - - } /* endif */ - - memset( ioinfo[irq], '\0', sizeof( ioinfo_t)); - memcpy( &ioinfo[irq]->schib, - &init_schib, - sizeof( schib_t)); - ioinfo[irq]->irq_desc.status = IRQ_DISABLED; - ioinfo[irq]->irq_desc.handler = &no_irq_type; - - /* - * We have to insert the new ioinfo element - * into the linked list, either at its head, - * its tail or insert it. - */ - if ( ioinfo_head == NULL ) /* first element */ - { - ioinfo_head = ioinfo[irq]; - ioinfo_tail = ioinfo[irq]; - } - else if ( irq < ioinfo_head->irq ) /* new head */ - { - ioinfo[irq]->next = ioinfo_head; - ioinfo_head->prev = ioinfo[irq]; - ioinfo_head = ioinfo[irq]; - } - else if ( irq > ioinfo_tail->irq ) /* new tail */ - { - ioinfo_tail->next = ioinfo[irq]; - ioinfo[irq]->prev = ioinfo_tail; - ioinfo_tail = ioinfo[irq]; - } - else /* insert element */ - { - ioinfo_t *pi = ioinfo_head; - - do - { - if ( irq < pi->next->irq ) - { - ioinfo[irq]->next = pi->next; - ioinfo[irq]->prev = pi; - pi->next->prev = ioinfo[irq]; - pi->next = ioinfo[irq]; - break; - - } /* endif */ - - pi = pi->next; - - } while ( 1 ); - - } /* endif */ - - } /* endif */ - - // initialize some values ... - ioinfo[irq]->ui.flags.pgid_supp = 1; - - ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pam - & ioinfo[irq]->schib.pmcw.pom; - - printk( "Detected device %04X on subchannel %04X" - " - PIM = %02X, PAM = %02X, POM = %02X\n", - ioinfo[irq]->schib.pmcw.dev, - irq, - ioinfo[irq]->schib.pmcw.pim, - ioinfo[irq]->schib.pmcw.pam, - ioinfo[irq]->schib.pmcw.pom); - - /* - * We should have at least one CHPID ... - */ - if ( ioinfo[irq]->schib.pmcw.pim - & ioinfo[irq]->schib.pmcw.pam - & ioinfo[irq]->schib.pmcw.pom ) - { - ioinfo[irq]->ui.flags.oper = 0; - - /* - * We now have to initially ... - * ... set "interruption sublass" - * ... enable "concurrent sense" - * ... enable "multipath mode" if more than one - * CHPID is available. This is done regardless - * whether multiple paths are available for us. - * - * Note : we don't enable the device here, this is temporarily - * done during device sensing below. - */ - ioinfo[irq]->schib.pmcw.isc = 3; /* could be smth. else */ - ioinfo[irq]->schib.pmcw.csense = 1; /* concurrent sense */ - ioinfo[irq]->schib.pmcw.ena = 0; /* force disable it */ - ioinfo[irq]->schib.pmcw.intparm = - ioinfo[irq]->schib.pmcw.dev; - - if ( ( ioinfo[irq]->schib.pmcw.pim != 0 ) - && ( ioinfo[irq]->schib.pmcw.pim != 0x80 ) ) - { - ioinfo[irq]->schib.pmcw.mp = 1; /* multipath mode */ - - } /* endif */ - - /* - * initialize ioinfo structure - */ - ioinfo[irq]->irq = irq; - ioinfo[irq]->ui.flags.busy = 0; - ioinfo[irq]->ui.flags.ready = 0; - ioinfo[irq]->ui.flags.oper = 1; - ioinfo[irq]->devstat.intparm = 0; - ioinfo[irq]->devstat.devno = ioinfo[irq]->schib.pmcw.dev; - - retry = 5; - - do - { - ccode2 = msch_err( irq, &ioinfo[irq]->schib); - - switch (ccode2) { - case 0: // successful completion - // - // concurrent sense facility available ... - // - ioinfo[irq]->ui.flags.consns = 1; - break; - - case 1: // status pending - // - // How can we have a pending status as device is - // disabled for interrupts ? Anyway, clear it ... - // - tsch( irq, &(ioinfo[irq]->devstat.ii.irb) ); - retry--; - break; - - case 2: // busy - retry--; - break; - - case 3: // not operational - ioinfo[irq]->ui.flags.oper = 0; - retry = 0; - break; - - default: -#define PGMCHK_OPERAND_EXC 0x15 - - if ( (ccode2 & PGMCHK_OPERAND_EXC) == PGMCHK_OPERAND_EXC ) - { - /* - * re-issue the modify subchannel without trying to - * enable the concurrent sense facility - */ - ioinfo[irq]->schib.pmcw.csense = 0; - - ccode2 = msch_err( irq, &ioinfo[irq]->schib); - - if ( ccode2 != 0 ) - { - printk( " ... modify subchannel (2) failed with CC = %X\n", - ccode2 ); - ioinfo[irq]->ui.flags.oper = 0; - } - else - { - ioinfo[irq]->ui.flags.consns = 0; - - } /* endif */ - } - else - { - printk( " ... modify subchannel (1) failed with CC = %X\n", - ccode2); - ioinfo[irq]->ui.flags.oper = 0; - - } /* endif */ - - retry = 0; - break; - - } /* endswitch */ - - } while ( ccode2 && retry ); - - if ( (ccode2 < 3) && (!retry) ) - { - printk( " ... msch() retry count for " - "subchannel %04X exceeded, CC = %d\n", - irq, - ccode2); - - } /* endif */ - - } - else - { - ioinfo[irq]->ui.flags.oper = 0; - - } /* endif */ - - } /* endif */ - - } /* endif */ - - /* - * indicate whether the subchannel is valid - */ - if ( ccode == 3) - return(0); - else - return(1); -} - -/* - * s390_SenseID - * - * Try to obtain the 'control unit'/'device type' information - * associated with the subchannel. - * - * The function is primarily meant to be called without irq - * action handler in place. However, it also allows for - * use with an action handler in place. If there is already - * an action handler registered assure it can handle the - * s390_SenseID() related device interrupts - interruption - * parameter used is 0x00E2C9C4 ( SID ). - */ -int s390_SenseID( int irq, senseid_t *sid ) -{ - ccw1_t sense_ccw; /* ccw area for SenseID command */ - devstat_t devstat; /* required by request_irq() */ - - int irq_ret = 0; /* return code */ - int retry = 5; /* retry count */ - int inlreq = 0; /* inline request_irq() */ - - if ( (irq > highest_subchannel) || (irq < 0 ) ) - { - return( -ENODEV ); - - } - else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - } /* endif */ - - if ( ioinfo[irq]->ui.flags.oper == 0 ) - { - return( -ENODEV ); - - } /* endif */ - - if ( !ioinfo[irq]->ui.flags.ready ) - { - /* - * Perform SENSE ID command processing. We have to request device - * ownership and provide a dummy I/O handler. We issue sync. I/O - * requests and evaluate the devstat area on return therefore - * we don't need a real I/O handler in place. - */ - irq_ret = request_irq( irq, init_IRQ_handler, 0, "SID", &devstat); - - if ( irq_ret == 0 ) - inlreq = 1; - - } /* endif */ - - if ( irq_ret == 0 ) - { - s390irq_spin_lock( irq); - - sense_ccw.cmd_code = CCW_CMD_SENSE_ID; - sense_ccw.cda = (__u32)virt_to_phys( sid ); - sense_ccw.count = sizeof( senseid_t); - sense_ccw.flags = CCW_FLAG_SLI; - - ioinfo[irq]->senseid.cu_type = 0xFFFF; /* initialize fields ... */ - ioinfo[irq]->senseid.cu_model = 0; - ioinfo[irq]->senseid.dev_type = 0; - ioinfo[irq]->senseid.dev_model = 0; - - /* - * We now issue a SenseID request. In case of BUSY - * or STATUS PENDING conditions we retry 5 times. - */ - do - { - memset( &devstat, '\0', sizeof( devstat_t) ); - - irq_ret = s390_start_IO( irq, - &sense_ccw, - 0x00E2C9C4, // == SID - 0, // n/a - DOIO_WAIT_FOR_INTERRUPT - | DOIO_TIMEOUT ); - - if ( irq_ret == -ETIMEDOUT ) - { - halt_IO( irq, - 0x80E2C9C4, - DOIO_WAIT_FOR_INTERRUPT); - devstat.flag |= DEVSTAT_NOT_OPER; - - } /* endif */ - - if ( sid->cu_type == 0xFFFF && devstat.flag != DEVSTAT_NOT_OPER ) - { - if ( devstat.flag & DEVSTAT_STATUS_PENDING ) - { -#if CONFIG_DEBUG_IO - printk( "Device %04X on Subchannel %04X " - "reports pending status, retry : %d\n", - ioinfo[irq]->schib.pmcw.dev, - irq, - retry); -#endif - } /* endif */ - - if ( devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL ) - { - /* - * if the device doesn't support the SenseID - * command further retries wouldn't help ... - */ - if ( devstat.ii.sense.data[0] & SNS0_CMD_REJECT ) - { - retry = 0; - } -#if CONFIG_DEBUG_IO - else - { - printk( "Device %04X," - " UC/SenseID," - " retry %d, cnt %02d," - " sns :" - " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - ioinfo[irq]->schib.pmcw.dev, - retry, - devstat.scnt, - devstat.ii.sense.data[0], - devstat.ii.sense.data[1], - devstat.ii.sense.data[2], - devstat.ii.sense.data[3], - devstat.ii.sense.data[4], - devstat.ii.sense.data[5], - devstat.ii.sense.data[6], - devstat.ii.sense.data[7]); - - } /* endif */ -#endif - } - else if ( devstat.flag & DEVSTAT_NOT_OPER ) - { - printk( "Device %04X on Subchannel %04X " - "became 'not operational'\n", - ioinfo[irq]->schib.pmcw.dev, - irq); - - retry = 0; - - } /* endif */ - } - else // we got it or the device is not-operational ... - { - retry = 0; - - } /* endif */ - - retry--; - - } while ( retry > 0 ); - - s390irq_spin_unlock( irq); - - /* - * If we installed the irq action handler we have to - * release it too. - */ - if ( inlreq ) - free_irq( irq, &devstat); - - /* - * if running under VM check there ... perhaps we should do - * only if we suffered a command reject, but it doesn't harm - */ - if ( ( sid->cu_type == 0xFFFF ) - && ( MACHINE_IS_VM ) ) - { - VM_virtual_device_info( ioinfo[irq]->schib.pmcw.dev, - sid ); - } /* endif */ - - if ( sid->cu_type == 0xFFFF ) - { - /* - * SenseID CU-type of 0xffff indicates that no device - * information could be retrieved (pre-init value). - * - * If we can't couldn't identify the device type we - * consider the device "not operational". - */ - printk( "Unknown device %04X on subchannel %04X\n", - ioinfo[irq]->schib.pmcw.dev, - irq); - ioinfo[irq]->ui.flags.oper = 0; - - } /* endif */ - - /* - * Issue device info message if unit was operational . - */ - if ( ioinfo[irq]->ui.flags.oper ) - { - if ( sid->dev_type != 0 ) - { - printk( "Device %04X reports: CU Type/Mod = %04X/%02X," - " Dev Type/Mod = %04X/%02X\n", - ioinfo[irq]->schib.pmcw.dev, - sid->cu_type, - sid->cu_model, - sid->dev_type, - sid->dev_model); - } - else - { - printk( "Device %04X reports:" - " Dev Type/Mod = %04X/%02X\n", - ioinfo[irq]->schib.pmcw.dev, - sid->cu_type, - sid->cu_model); - - } /* endif */ - - } /* endif */ - - if ( ioinfo[irq]->ui.flags.oper ) - irq_ret = 0; - else - irq_ret = -ENODEV; - - } /* endif */ - - return( irq_ret ); -} - -static int __inline__ s390_SetMultiPath( int irq ) -{ - int cc; - - cc = stsch( irq, &ioinfo[irq]->schib ); - - if ( !cc ) - { - ioinfo[irq]->schib.pmcw.mp = 1; /* multipath mode */ - - cc = msch( irq, &ioinfo[irq]->schib ); - - } /* endif */ - - return( cc); -} - -/* - * Device Path Verification - * - * Path verification is accomplished by checking which paths (CHPIDs) are - * available. Further, a path group ID is set, if possible in multipath - * mode, otherwise in single path mode. - * - */ -int s390_DevicePathVerification( int irq ) -{ -#if 0 - int ccode; - __u8 pathmask; - - int ret = 0; - - if ( ioinfo[irq]->ui.flags.pgid_supp == 0 ) - { - ret = -EOPNOTSUPP; - - } /* endif */ - - ccode = stsch( irq, &(ioinfo[irq]->schib) ); - - if ( ccode ) - { - ret = -ENODEV; - } - else - { - int i; - pgid_t pgid; - - int first = 1; - __u8 dev_path = ioinfo[irq]->schib.pmcw.pam - & ioinfo[irq]->schib.pmcw.pom; - - /* - * let's build a path group ID if we don't have one yet - */ - if ( ioinfo[irq]->ui.flags.pgid == 0) - { - struct _lowcore *lowcore = &get_cpu_lowcore(cpu); - - ioinfo->pgid.cpu_addr = lowcore->cpu_data.cpu_addr; - ioinfo->pgid.cpu_id = lowcore->cpu_data.cpu_id.ident; - ioinfo->pgid.cpu_model = lowcore->cpu_data.cpu_id.machine; - ioinfo->pgid.tod_high = *(__u32 *)&irq_IPL_TOD; - - ioinfo[irq]->ui.flags.pgid = 1; - - } /* endif */ - - memcpy( &pgid, ioinfo[irq]->pgid, sizeof(pgid_t)); - - for ( i = 0; i < 8 && !ret ; i++) - { - pathmask = 0x80 >> i; - - if ( dev_path & pathmask ) - { - ret = s390_SetPGID( irq, pathmask, &pgid ); - - /* - * For the *first* path we are prepared - * for recovery - * - * - If we fail setting the PGID we assume its - * using a different PGID already (VM) we - * try to sense. - */ - if ( ret == -EOPNOTSUPP && first ) - { - *(int *)&pgid = 0; - - ret = s390_SensePGID( irq, pathmask, &pgid); - first = 0; - - if ( !ret ) - { - /* - * Check whether we retrieved - * a reasonable PGID ... - */ - if ( !ret && (*(int *)&pgid == 0) ) - { - ret = -EOPNOTSUPP; - } - else - { - ret = s390_SetPGID( irq, pathmask, &pgid ); - - } /* endif */ - - } /* endif */ - - if ( ret ) - { - ioinfo[irq]->ui.flags.pgid_supp = 0; - - printk( "PathVerification(%04X) " - "- Device %04X doesn't " - " support path grouping", - irq, - ioinfo[irq]->schib.pmcw.dev); - - } /* endif */ - } - else if ( ret ) - { - ioinfo[irq]->ui.flags.pgid_supp = 0; - - } /* endif */ - - } /* endif */ - - } /* endfor */ - - } /* endif */ - - return ret; -#else - return 0; -#endif -} - -/* - * s390_SetPGID - * - * Set Path Group ID - * - */ -int s390_SetPGID( int irq, __u8 lpm, pgid_t *pgid ) -{ - ccw1_t spid_ccw; /* ccw area for SPID command */ - devstat_t devstat; /* required by request_irq() */ - - int irq_ret = 0; /* return code */ - int retry = 5; /* retry count */ - int inlreq = 0; /* inline request_irq() */ - int mpath = 1; /* try multi-path first */ - - if ( (irq > highest_subchannel) || (irq < 0 ) ) - { - return( -ENODEV ); - - } - else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - - } /* endif */ - - if ( ioinfo[irq]->ui.flags.oper == 0 ) - { - return( -ENODEV ); - - } /* endif */ - - if ( !ioinfo[irq]->ui.flags.ready ) - { - /* - * Perform SENSE ID command processing. We have to request device - * ownership and provide a dummy I/O handler. We issue sync. I/O - * requests and evaluate the devstat area on return therefore - * we don't need a real I/O handler in place. - */ - irq_ret = request_irq( irq, init_IRQ_handler, 0, "SPID", &devstat); - - if ( irq_ret == 0 ) - inlreq = 1; - - } /* endif */ - - if ( irq_ret == 0 ) - { - s390irq_spin_lock( irq); - - spid_ccw.cmd_code = CCW_CMD_SET_PGID; - spid_ccw.cda = (__u32)virt_to_phys( pgid ); - spid_ccw.count = sizeof( pgid_t); - spid_ccw.flags = CCW_FLAG_SLI; - - - /* - * We now issue a SenseID request. In case of BUSY - * or STATUS PENDING conditions we retry 5 times. - */ - do - { - memset( &devstat, '\0', sizeof( devstat_t) ); - - irq_ret = s390_start_IO( irq, - &spid_ccw, - 0xE2D7C9C4, // == SPID - lpm, // n/a - DOIO_WAIT_FOR_INTERRUPT - | DOIO_VALID_LPM ); - - if ( !irq_ret ) - { - if ( devstat.flag & DEVSTAT_STATUS_PENDING ) - { -#if CONFIG_DEBUG_IO - printk( "SPID - Device %04X " - "on Subchannel %04X " - "reports pending status, " - "retry : %d\n", - ioinfo[irq]->schib.pmcw.dev, - irq, - retry); -#endif - } /* endif */ - - if ( devstat.flag == ( DEVSTAT_START_FUNCTION - | DEVSTAT_FINAL_STATUS ) ) - { - retry = 0; // successfully set ... - } - else if ( devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL ) - { - /* - * If the device doesn't support the - * Sense Path Group ID command - * further retries wouldn't help ... - */ - if ( devstat.ii.sense.data[0] & SNS0_CMD_REJECT ) - { - if ( mpath ) - { - pgid->inf.fc = SPID_FUNC_ESTABLISH; - mpath = 0; - retry--; - } - else - { - irq_ret = -EOPNOTSUPP; - retry = 0; - - } /* endif */ - } -#if CONFIG_DEBUG_IO - else - { - printk( "SPID - device %04X," - " unit check," - " retry %d, cnt %02d," - " sns :" - " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - ioinfo[irq]->schib.pmcw.dev, - retry, - devstat.scnt, - devstat.ii.sense.data[0], - devstat.ii.sense.data[1], - devstat.ii.sense.data[2], - devstat.ii.sense.data[3], - devstat.ii.sense.data[4], - devstat.ii.sense.data[5], - devstat.ii.sense.data[6], - devstat.ii.sense.data[7]); - - } /* endif */ -#endif - } - else if ( devstat.flag & DEVSTAT_NOT_OPER ) - { - printk( "SPID - Device %04X " - "on Subchannel %04X " - "became 'not operational'\n", - ioinfo[irq]->schib.pmcw.dev, - irq); - - retry = 0; - - } /* endif */ - } - else if ( irq_ret != -ENODEV ) - { - retry--; - } - else - { - retry = 0; - - } /* endif */ - - } while ( retry > 0 ); - - s390irq_spin_unlock( irq); - - /* - * If we installed the irq action handler we have to - * release it too. - */ - if ( inlreq ) - free_irq( irq, &devstat); - - } /* endif */ - - return( irq_ret ); -} - - -/* - * s390_SensePGID - * - * Sense Path Group ID - * - */ -int s390_SensePGID( int irq, __u8 lpm, pgid_t *pgid ) -{ - ccw1_t snid_ccw; /* ccw area for SNID command */ - devstat_t devstat; /* required by request_irq() */ - - int irq_ret = 0; /* return code */ - int retry = 5; /* retry count */ - int inlreq = 0; /* inline request_irq() */ - - if ( (irq > highest_subchannel) || (irq < 0 ) ) - { - return( -ENODEV ); - - } - else if ( ioinfo[irq] == INVALID_STORAGE_AREA ) - { - return( -ENODEV); - - } /* endif */ - - if ( ioinfo[irq]->ui.flags.oper == 0 ) - { - return( -ENODEV ); - - } /* endif */ - - if ( !ioinfo[irq]->ui.flags.ready ) - { - /* - * Perform SENSE ID command processing. We have to request device - * ownership and provide a dummy I/O handler. We issue sync. I/O - * requests and evaluate the devstat area on return therefore - * we don't need a real I/O handler in place. - */ - irq_ret = request_irq( irq, init_IRQ_handler, 0, "SNID", &devstat); - - if ( irq_ret == 0 ) - inlreq = 1; - - } /* endif */ - - if ( irq_ret == 0 ) - { - s390irq_spin_lock( irq); - - snid_ccw.cmd_code = CCW_CMD_SENSE_PGID; - snid_ccw.cda = (__u32)virt_to_phys( pgid ); - snid_ccw.count = sizeof( pgid_t); - snid_ccw.flags = CCW_FLAG_SLI; - - /* - * We now issue a SenseID request. In case of BUSY - * or STATUS PENDING conditions we retry 5 times. - */ - do - { - memset( &devstat, '\0', sizeof( devstat_t) ); - - irq_ret = s390_start_IO( irq, - &snid_ccw, - 0xE2D5C9C4, // == SNID - lpm, // n/a - DOIO_WAIT_FOR_INTERRUPT - | DOIO_VALID_LPM ); - - if ( !irq_ret ) - { - if ( devstat.flag & DEVSTAT_STATUS_PENDING ) - { -#if CONFIG_DEBUG_IO - printk( "SNID - Device %04X " - "on Subchannel %04X " - "reports pending status, " - "retry : %d\n", - ioinfo[irq]->schib.pmcw.dev, - irq, - retry); -#endif - } /* endif */ - - if ( devstat.flag & DEVSTAT_FLAG_SENSE_AVAIL ) - { - /* - * If the device doesn't support the - * Sense Path Group ID command - * further retries wouldn't help ... - */ - if ( devstat.ii.sense.data[0] & SNS0_CMD_REJECT ) - { - retry = 0; - irq_ret = -EOPNOTSUPP; - } -#if CONFIG_DEBUG_IO - else - { - printk( "SNID - device %04X," - " unit check," - " retry %d, cnt %02d," - " sns :" - " %02X%02X%02X%02X %02X%02X%02X%02X ...\n", - ioinfo[irq]->schib.pmcw.dev, - retry, - devstat.scnt, - devstat.ii.sense.data[0], - devstat.ii.sense.data[1], - devstat.ii.sense.data[2], - devstat.ii.sense.data[3], - devstat.ii.sense.data[4], - devstat.ii.sense.data[5], - devstat.ii.sense.data[6], - devstat.ii.sense.data[7]); - - } /* endif */ -#endif - } - else if ( devstat.flag & DEVSTAT_NOT_OPER ) - { - printk( "SNID - Device %04X " - "on Subchannel %04X " - "became 'not operational'\n", - ioinfo[irq]->schib.pmcw.dev, - irq); - - retry = 0; - - } /* endif */ - } - else if ( irq_ret != -ENODEV ) - { - retry--; - } - else - { - retry = 0; - - } /* endif */ - - } while ( retry > 0 ); - - s390irq_spin_unlock( irq); - - /* - * If we installed the irq action handler we have to - * release it too. - */ - if ( inlreq ) - free_irq( irq, &devstat); - - } /* endif */ - - return( irq_ret ); -} - - -void do_crw_pending( void ) -{ - return; -} - - -/* added by Holger Smolinski for reipl support in reipl.S */ -void -reipl ( int sch ) -{ - int i; - - for ( i = 0; i < highest_subchannel; i ++ ) { - free_irq ( i, (void*)REIPL_DEVID_MAGIC ); - } - do_reipl( 0x10000 | sch ); -} - diff --git a/arch/s390/kernel/s390mach.c b/arch/s390/kernel/s390mach.c deleted file mode 100644 index 750e50c30..000000000 --- a/arch/s390/kernel/s390mach.c +++ /dev/null @@ -1,157 +0,0 @@ -/* - * arch/s390/kernel/s390mach.c - * S/390 machine check handler, - * currently only channel-reports are supported - * - * S390 version - * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation - * Author(s): Ingo Adlung (adlung@de.ibm.com) - */ - -#include <linux/init.h> - -#include <asm/irq.h> -#include <asm/lowcore.h> -#include <asm/semaphore.h> -#include <asm/s390io.h> -#include <asm/s390dyn.h> -#include <asm/s390mach.h> - -#define S390_MACHCHK_DEBUG - -static mchchk_queue_element_t *mchchk_queue_head = NULL; -static mchchk_queue_element_t *mchchk_queue_tail = NULL; -static mchchk_queue_element_t *mchchk_queue_free = NULL; -static spinlock_t mchchk_queue_lock; -static struct semaphore s_sem[2]; - -// -// initialize machine check handling -// -void s390_init_machine_check( void ) -{ - init_MUTEX_LOCKED( &s_sem[0] ); - init_MUTEX_LOCKED( &s_sem[1] ); - -#if 0 - // - // fix me ! initialize a machine check queue with 100 elements - // -#ifdef S390_MACHCHK_DEBUG - printk( "init_mach : starting kernel thread\n"); -#endif - - kernel_thread( s390_machine_check_handler, s_sem, 0); - - // - // wait for the machine check handler to be ready - // -#ifdef S390_MACHCHK_DEBUG - printk( "init_mach : waiting for kernel thread\n"); -#endif - - down( &sem[0]); - -#ifdef S390_MACHCHK_DEBUG - printk( "init_mach : kernel thread ready\n"); -#endif - - // - // fix me ! we have to initialize CR14 to allow for CRW pending - // conditions - - // - // fix me ! enable machine checks in the PSW - // -#endif - return; -} - -// -// machine check pre-processor -// -void __init s390_do_machine_check( void ) -{ - // fix me ! we have to check for machine check and - // post the handler eventually - - return; -} - -// -// machine check handler -// -static void __init s390_machine_check_handler( struct semaphore *sem ) -{ -#ifdef S390_MACHCHK_DEBUG - printk( "mach_handler : kernel thread up\n"); -#endif - - up( &sem[0] ); - -#ifdef S390_MACHCHK_DEBUG - printk( "mach_handler : kernel thread ready\n"); -#endif - - do { - -#ifdef S390_MACHCHK_DEBUG - printk( "mach_handler : waiting for wakeup\n"); -#endif - - down_interruptible( &sem[1] ); -#ifdef S390_MACHCHK_DEBUG - printk( "mach_handler : wakeup\n"); -#endif - - break; // fix me ! unconditional surrender ... - - // fix me ! check for machine checks and - // call do_crw_pending() eventually - - } while (1); - - return; -} - -mchchk_queue_element_t *s390_get_mchchk( void ) -{ - unsigned long flags; - mchchk_queue_element_t *qe; - - spin_lock_irqsave( &mchchk_queue_lock, flags ); - - // fix me ! dequeue first element if available - qe = NULL; - - spin_unlock_irqrestore( &mchchk_queue_lock, flags ); - - return qe; -} - -void s390_free_mchchk( mchchk_queue_element_t *mchchk ) -{ - unsigned long flags; - - if ( mchchk != NULL) - { - spin_lock_irqsave( &mchchk_queue_lock, flags ); - - mchchk->next = mchchk_queue_free; - - if ( mchchk_queue_free != NULL ) - { - mchchk_queue_free->prev = mchchk; - - } /* endif */ - - mchchk->prev = NULL; - mchchk_queue_free = mchchk; - - spin_unlock_irqrestore( &mchchk_queue_lock, flags ); - - } /* endif */ - - return; -} - diff --git a/arch/s390/kernel/semaphore.c b/arch/s390/kernel/semaphore.c index a0a434581..8af6d8277 100644 --- a/arch/s390/kernel/semaphore.c +++ b/arch/s390/kernel/semaphore.c @@ -2,7 +2,7 @@ * linux/arch/S390/kernel/semaphore.c * * S390 version - * Copyright (C) 1998 IBM Corporation + * Copyright (C) 1998-2000 IBM Corporation * Author(s): Martin Schwidefsky * * Derived from "linux/arch/i386/kernel/semaphore.c diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 109af4e76..628d7bcf7 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -21,7 +21,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> @@ -45,6 +45,7 @@ __u16 boot_cpu_addr; int cpus_initialized = 0; unsigned long cpu_initialized = 0; +volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ /* * Setup options @@ -80,6 +81,7 @@ static struct resource data_resource = { "Kernel data", 0, 0 }; void __init cpu_init (void) { int nr = smp_processor_id(); + int addr = hard_smp_processor_id(); if (test_and_set_bit(nr,&cpu_initialized)) { printk("CPU#%d ALREADY INITIALIZED!!!!!!!!!\n", nr); @@ -91,7 +93,7 @@ void __init cpu_init (void) * Store processor id in lowcore (used e.g. in timer_interrupt) */ asm volatile ("stidp %0": "=m" (S390_lowcore.cpu_data.cpu_id)); - S390_lowcore.cpu_data.cpu_addr = hard_smp_processor_id(); + S390_lowcore.cpu_data.cpu_addr = addr; S390_lowcore.cpu_data.cpu_nr = nr; /* @@ -158,33 +160,18 @@ void machine_halt(void) { if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) cpcmd(vmhalt_cmd, NULL, 0); - disabled_wait(0); + signal_processor(smp_processor_id(), sigp_stop_and_store_status); } void machine_power_off(void) { if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) cpcmd(vmpoff_cmd, NULL, 0); - disabled_wait(0); + signal_processor(smp_processor_id(), sigp_stop_and_store_status); } #endif /* - * Waits for 'delay' microseconds using the tod clock - */ -void tod_wait(unsigned long delay) -{ - uint64_t start_cc, end_cc; - - if (delay == 0) - return; - asm volatile ("STCK %0" : "=m" (start_cc)); - do { - asm volatile ("STCK %0" : "=m" (end_cc)); - } while (((end_cc - start_cc)/4096) < delay); -} - -/* * Setup function called from init/main.c just after the banner * was printed. */ @@ -192,12 +179,11 @@ void __init setup_arch(char **cmdline_p) { unsigned long bootmap_size; unsigned long memory_start, memory_end; - char c = ' ', *to = command_line, *from = COMMAND_LINE; + char c = ' ', cn, *to = command_line, *from = COMMAND_LINE; struct resource *res; unsigned long start_pfn, end_pfn; static unsigned int smptrap=0; unsigned long delay = 0; - int len = 0; if (smptrap) return; @@ -210,6 +196,7 @@ void __init setup_arch(char **cmdline_p) */ cpu_init(); boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + __cpu_logical_map[0] = boot_cpu_addr; /* * print what head.S has found out about the machine @@ -227,10 +214,15 @@ void __init setup_arch(char **cmdline_p) rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); #endif - /* nasty stuff with PARMAREAs. we use head.S or parameterline - if (!MOUNT_ROOT_RDONLY) - root_mountflags &= ~MS_RDONLY; - */ + memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ + memory_end = MEMORY_SIZE; + /* + * We need some free virtual space to be able to do vmalloc. + * On a machine with 2GB memory we make sure that we have at + * least 128 MB free space for vmalloc. + */ + if (memory_end > 1920*1024*1024) + memory_end = 1920*1024*1024; memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ memory_end = MEMORY_SIZE; /* detected in head.s */ init_mm.start_code = PAGE_OFFSET; @@ -252,7 +244,6 @@ void __init setup_arch(char **cmdline_p) * "mem=XXX[kKmM]" sets memsize */ if (c == ' ' && strncmp(from, "mem=", 4) == 0) { - if (to != command_line) to--; memory_end = simple_strtoul(from+4, &from, 0); if ( *from == 'K' || *from == 'k' ) { memory_end = memory_end << 10; @@ -275,16 +266,22 @@ void __init setup_arch(char **cmdline_p) delay = delay*60*1000000; from++; } - /* now wait for the requestion amount of time */ - tod_wait(delay); + /* now wait for the requestedn amount of time */ + udelay(delay); } - c = *(from++); - if (!c) + cn = *(from++); + if (!cn) break; - if (COMMAND_LINE_SIZE <= ++len) + if (cn == '\n') + cn = ' '; /* replace newlines with space */ + if (cn == ' ' && c == ' ') + continue; /* remove additional spaces */ + c = cn; + if (to - command_line >= COMMAND_LINE_SIZE) break; *(to++) = c; } + if (c == ' ' && to > command_line) to--; *to = '\0'; *cmdline_p = command_line; @@ -317,7 +314,7 @@ void __init setup_arch(char **cmdline_p) paging_init(); #ifdef CONFIG_BLK_DEV_INITRD if (INITRD_START) { - if (INITRD_START + INITRD_SIZE < memory_end) { + if (INITRD_START + INITRD_SIZE <= memory_end) { reserve_bootmem(INITRD_START, INITRD_SIZE); initrd_start = INITRD_START; initrd_end = initrd_start + INITRD_SIZE; @@ -368,8 +365,8 @@ int get_cpuinfo(char * buffer) p += sprintf(p,"vendor_id : IBM/S390\n" "# processors : %i\n" "bogomips per cpu: %lu.%02lu\n", - smp_num_cpus, loops_per_sec/500000, - (loops_per_sec/5000)%100); + smp_num_cpus, loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ))%100); for (i = 0; i < smp_num_cpus; i++) { cpuinfo = &safe_get_cpu_lowcore(i).cpu_data; p += sprintf(p,"processor %i: " diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 7809a4a2b..de05ec764 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c @@ -2,7 +2,7 @@ * arch/s390/kernel/signal.c * * S390 version - * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) * * Based on Intel version @@ -37,7 +37,7 @@ #define SIGFRAME_COMMON \ __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; \ struct sigcontext sc; \ -sigregs sregs; \ +_sigregs sregs; \ __u8 retcode[S390_SYSCALL_SIZE]; typedef struct @@ -54,6 +54,41 @@ typedef struct asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset)); +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + /* * Atomically swap in the new signal mask, and wait for a signal. */ @@ -71,7 +106,7 @@ sys_sigsuspend(struct pt_regs * regs,int history0, int history1, old_sigset_t ma regs->gprs[2] = -EINTR; while (1) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule(); if (do_signal(regs, &saveset)) return -EINTR; @@ -99,7 +134,7 @@ sys_rt_sigsuspend(struct pt_regs * regs,sigset_t *unewset, size_t sigsetsize) regs->gprs[2] = -EINTR; while (1) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule(); if (do_signal(regs, &saveset)) return -EINTR; @@ -139,16 +174,15 @@ sys_sigaction(int sig, const struct old_sigaction *act, } asmlinkage int -sys_sigaltstack(const stack_t *uss, stack_t *uoss) +sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs *regs) { - struct pt_regs *regs = (struct pt_regs *) &uss; return do_sigaltstack(uss, uoss, regs->gprs[15]); } -static int save_sigregs(struct pt_regs *regs,sigregs *sregs) +static int save_sigregs(struct pt_regs *regs,_sigregs *sregs) { int err; s390_fp_regs fpregs; @@ -163,7 +197,7 @@ static int save_sigregs(struct pt_regs *regs,sigregs *sregs) } -static int restore_sigregs(struct pt_regs *regs,sigregs *sregs) +static int restore_sigregs(struct pt_regs *regs,_sigregs *sregs) { int err; s390_fp_regs fpregs; @@ -185,13 +219,13 @@ static int restore_sigregs(struct pt_regs *regs,sigregs *sregs) static int restore_sigcontext(struct sigcontext *sc, pt_regs *regs, - sigregs *sregs,sigset_t *set) + _sigregs *sregs,sigset_t *set) { unsigned int err; err=restore_sigregs(regs,sregs); if(!err) - err=__copy_from_user(&set->sig,&sc->oldmask,SIGMASK_COPY_SIZE); + err=__copy_from_user(&set->sig,&sc->oldmask,_SIGMASK_COPY_SIZE); return(err); } @@ -227,15 +261,12 @@ badframe: asmlinkage int sys_rt_sigreturn(struct pt_regs *regs) { rt_sigframe *frame = (rt_sigframe *)regs->gprs[15]; - stack_t st; if (sigreturn_common(regs,sizeof(rt_sigframe))) goto badframe; - if (__copy_from_user(&st, &frame->uc.uc_stack, sizeof(st))) - goto badframe; /* It is more difficult to avoid calling this function than to call it and ignore errors. */ - do_sigaltstack(&st, NULL, regs->gprs[15]); + do_sigaltstack(&frame->uc.uc_stack, NULL, regs->gprs[15]); return regs->gprs[2]; badframe: @@ -290,7 +321,7 @@ static void *setup_frame_common(int sig, struct k_sigaction *ka, err=__put_user(&frame->sregs,&frame->sc.sregs); if(!err) - err=__copy_to_user(&frame->sc.oldmask,&set->sig,SIGMASK_COPY_SIZE); + err=__copy_to_user(&frame->sc.oldmask,&set->sig,_SIGMASK_COPY_SIZE); if(!err) { regs->gprs[2]=(current->exec_domain @@ -316,14 +347,17 @@ static void *setup_frame_common(int sig, struct k_sigaction *ka, static void setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs * regs) { + sigframe *frame; - if(!setup_frame_common(sig,ka,set,regs,sizeof(sigframe), - (S390_SYSCALL_OPCODE|__NR_sigreturn))) + if((frame=setup_frame_common(sig,ka,set,regs,sizeof(sigframe), + (S390_SYSCALL_OPCODE|__NR_sigreturn)))==0) goto give_sigsegv; #if DEBUG_SIG printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", current->comm, current->pid, frame, regs->eip, frame->pretcode); #endif + /* Martin wants this for pthreads */ + regs->gprs[3] = (addr_t)&frame->sc; return; give_sigsegv: @@ -343,7 +377,7 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, (S390_SYSCALL_OPCODE|__NR_rt_sigreturn)))==0) goto give_sigsegv; - err = __copy_to_user(&frame->info, info, sizeof(*info)); + err = copy_siginfo_to_user(&frame->info, info); /* Create the ucontext. */ err |= __put_user(0, &frame->uc.uc_flags); @@ -352,8 +386,9 @@ static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, err |= __put_user(sas_ss_flags(orig_sp), &frame->uc.uc_stack.ss_flags); err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); - regs->gprs[3] = (u32)&frame->info; - regs->gprs[4] = (u32)&frame->uc; + err |= __put_user(&frame->sc,&frame->uc.sc); + regs->gprs[3] = (addr_t)&frame->info; + regs->gprs[4] = (addr_t)&frame->uc; if (err) goto give_sigsegv; @@ -452,10 +487,10 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) if (!signr) break; - if ((current->flags & PF_PTRACED) && signr != SIGKILL) { + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { /* Let the debugger run. */ current->exit_code = signr; - current->state = TASK_STOPPED; + set_current_state(TASK_STOPPED); notify_parent(current, SIGCHLD); schedule(); @@ -511,7 +546,7 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) /* FALLTHRU */ case SIGSTOP: - current->state = TASK_STOPPED; + set_current_state(TASK_STOPPED); current->exit_code = signr; if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) notify_parent(current, SIGCHLD); @@ -540,7 +575,7 @@ int do_signal(struct pt_regs *regs, sigset_t *oldset) } /* Did we come from a system call? */ - if ( regs->trap == 0x20 /* System Call! */ ) { + if ( regs->trap == __LC_SVC_OLD_PSW /* System Call! */ ) { /* Restart the system call - no handlers present */ if (regs->gprs[2] == -ERESTARTNOHAND || regs->gprs[2] == -ERESTARTSYS || diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 6cf2e6918..4b15b5501 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -32,16 +32,15 @@ #include <asm/sigp.h> #include <asm/pgalloc.h> #include <asm/irq.h> +#include <asm/s390_ext.h> #include "cpcmd.h" /* prototypes */ -extern void update_one_process( struct task_struct *p, - unsigned long ticks, unsigned long user, - unsigned long system, int cpu); extern int cpu_idle(void * unused); extern __u16 boot_cpu_addr; +extern volatile int __cpu_logical_map[]; /* * An array with a pointer the lowcore of every CPU. @@ -52,10 +51,8 @@ struct _lowcore *lowcore_ptr[NR_CPUS]; unsigned int prof_multiplier[NR_CPUS]; unsigned int prof_old_multiplier[NR_CPUS]; unsigned int prof_counter[NR_CPUS]; -volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ cycles_t cacheflush_time=0; int smp_threads_ready=0; /* Set when the idlers are all forked. */ -unsigned long ipi_count=0; /* Number of IPIs delivered. */ static atomic_t smp_commenced = ATOMIC_INIT(0); spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; @@ -104,7 +101,7 @@ void do_machine_restart(void) void machine_restart(char * __unused) { if (smp_processor_id() != 0) { - smp_ext_call_async(0, ec_restart); + smp_ext_bitcall(0, ec_restart); for (;;); } else do_machine_restart(); @@ -115,13 +112,13 @@ void do_machine_halt(void) smp_send_stop(); if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) cpcmd(vmhalt_cmd, NULL, 0); - disabled_wait(0); + signal_processor(smp_processor_id(), sigp_stop_and_store_status); } void machine_halt(void) { if (smp_processor_id() != 0) { - smp_ext_call_async(0, ec_halt); + smp_ext_bitcall(0, ec_halt); for (;;); } else do_machine_halt(); @@ -132,13 +129,13 @@ void do_machine_power_off(void) smp_send_stop(); if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) cpcmd(vmpoff_cmd, NULL, 0); - disabled_wait(0); + signal_processor(smp_processor_id(), sigp_stop_and_store_status); } void machine_power_off(void) { if (smp_processor_id() != 0) { - smp_ext_call_async(0, ec_power_off); + smp_ext_bitcall(0, ec_power_off); for (;;); } else do_machine_power_off(); @@ -149,7 +146,7 @@ void machine_power_off(void) * cpus are handled. */ -void do_ext_call_interrupt(__u16 source_cpu_addr) +void do_ext_call_interrupt(struct pt_regs *regs, __u16 code) { ec_ext_call *ec, *next; int bits; @@ -172,6 +169,8 @@ void do_ext_call_interrupt(__u16 source_cpu_addr) do_machine_halt(); if (test_bit(ec_power_off, &bits)) do_machine_power_off(); + if (test_bit(ec_ptlb, &bits)) + local_flush_tlb(); /* * Handle external call commands with a parameter area @@ -184,7 +183,7 @@ void do_ext_call_interrupt(__u16 source_cpu_addr) return; /* no command signals */ /* Make a fifo out of the lifo */ - next = ec; + next = ec->next; ec->next = NULL; while (next != NULL) { ec_ext_call *tmp = next->next; @@ -196,61 +195,21 @@ void do_ext_call_interrupt(__u16 source_cpu_addr) /* Execute every sigp command on the queue */ while (ec != NULL) { switch (ec->cmd) { - case ec_get_ctl: { - ec_creg_parms *pp; - pp = (ec_creg_parms *) ec->parms; + case ec_callback_async: { + void (*func)(void *info); + void *info; + + func = ec->func; + info = ec->info; atomic_set(&ec->status,ec_executing); - asm volatile ( - " bras 1,0f\n" - " stctl 0,0,0(%0)\n" - "0: ex %1,0(1)\n" - : : "a" (pp->cregs+pp->start_ctl), - "a" ((pp->start_ctl<<4) + pp->end_ctl) - : "memory", "1" ); - atomic_set(&ec->status,ec_done); + (func)(info); return; } - case ec_set_ctl: { - ec_creg_parms *pp; - pp = (ec_creg_parms *) ec->parms; + case ec_callback_sync: atomic_set(&ec->status,ec_executing); - asm volatile ( - " bras 1,0f\n" - " lctl 0,0,0(%0)\n" - "0: ex %1,0(1)\n" - : : "a" (pp->cregs+pp->start_ctl), - "a" ((pp->start_ctl<<4) + pp->end_ctl) - : "memory", "1" ); + (ec->func)(ec->info); atomic_set(&ec->status,ec_done); return; - } - case ec_set_ctl_masked: { - ec_creg_mask_parms *pp; - u32 cregs[16]; - int i; - - pp = (ec_creg_mask_parms *) ec->parms; - atomic_set(&ec->status,ec_executing); - asm volatile ( - " bras 1,0f\n" - " stctl 0,0,0(%0)\n" - "0: ex %1,0(1)\n" - : : "a" (cregs+pp->start_ctl), - "a" ((pp->start_ctl<<4) + pp->end_ctl) - : "memory", "1" ); - for (i = pp->start_ctl; i <= pp->end_ctl; i++) - cregs[i] = (cregs[i] & pp->andvals[i]) - | pp->orvals[i]; - asm volatile ( - " bras 1,0f\n" - " lctl 0,0,0(%0)\n" - "0: ex %1,0(1)\n" - : : "a" (cregs+pp->start_ctl), - "a" ((pp->start_ctl<<4) + pp->end_ctl) - : "memory", "1" ); - atomic_set(&ec->status,ec_done); - return; - } default: } ec = ec->next; @@ -258,17 +217,19 @@ void do_ext_call_interrupt(__u16 source_cpu_addr) } /* - * Send an external call sigp to another cpu and wait for its completion. + * Send a callback sigp to another cpu. */ -sigp_ccode smp_ext_call_sync(int cpu, ec_cmd_sig cmd, void *parms) +sigp_ccode +smp_ext_call(int cpu, void (*func)(void *info), void *info, int wait) { struct _lowcore *lowcore = &get_cpu_lowcore(cpu); sigp_ccode ccode; ec_ext_call ec; - ec.cmd = cmd; + ec.cmd = wait ? ec_callback_sync : ec_callback_async; atomic_set(&ec.status, ec_pending); - ec.parms = parms; + ec.func = func; + ec.info = info; do { ec.next = (ec_ext_call*) atomic_read(&lowcore->ext_call_queue); } while (atomic_compare_and_swap((int) ec.next, (int)(&ec), @@ -288,34 +249,15 @@ sigp_ccode smp_ext_call_sync(int cpu, ec_cmd_sig cmd, void *parms) if (ccode != sigp_not_operational) /* wait for completion, FIXME: possible seed of a deadlock */ - while (atomic_read(&ec.status) != ec_done); + while (atomic_read(&ec.status) != (wait?ec_done:ec_executing)); return ccode; } /* - * Send an external call sigp to another cpu and return without waiting - * for its completion. Currently we do not support parameters with - * asynchronous sigps. + * Send a callback sigp to every other cpu in the system. */ -sigp_ccode smp_ext_call_async(int cpu, ec_bit_sig sig) -{ - struct _lowcore *lowcore = &get_cpu_lowcore(cpu); - sigp_ccode ccode; - - /* - * Set signaling bit in lowcore of target cpu and kick it - */ - atomic_set_mask(1<<sig, &lowcore->ext_call_fast); - ccode = signal_processor(cpu, sigp_external_call); - return ccode; -} - -/* - * Send an external call sigp to every other cpu in the system and - * wait for the completion of the sigps. - */ -void smp_ext_call_sync_others(ec_cmd_sig cmd, void *parms) +void smp_ext_call_others(void (*func)(void *info), void *info, int wait) { struct _lowcore *lowcore; ec_ext_call ec[NR_CPUS]; @@ -326,9 +268,10 @@ void smp_ext_call_sync_others(ec_cmd_sig cmd, void *parms) if (smp_processor_id() == i) continue; lowcore = &get_cpu_lowcore(i); - ec[i].cmd = cmd; - atomic_set(&ec[i].status, ec_pending); - ec[i].parms = parms; + ec[i].cmd = wait ? ec_callback_sync : ec_callback_async; + atomic_set(&ec[i].status, ec_pending); + ec[i].func = func; + ec[i].info = info; do { ec[i].next = (ec_ext_call *) atomic_read(&lowcore->ext_call_queue); @@ -341,16 +284,33 @@ void smp_ext_call_sync_others(ec_cmd_sig cmd, void *parms) for (i = 0; i < smp_num_cpus; i++) { if (smp_processor_id() == i) continue; - while (atomic_read(&ec[i].status) != ec_done); + while (atomic_read(&ec[i].status) != + (wait ? ec_done:ec_executing)); } } /* + * Send an external call sigp to another cpu and return without waiting + * for its completion. + */ +sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig) +{ + struct _lowcore *lowcore = &get_cpu_lowcore(cpu); + sigp_ccode ccode; + + /* + * Set signaling bit in lowcore of target cpu and kick it + */ + atomic_set_mask(1<<sig, &lowcore->ext_call_fast); + ccode = signal_processor(cpu, sigp_external_call); + return ccode; +} + +/* * Send an external call sigp to every other cpu in the system and - * return without waiting for the completion of the sigps. Currently - * we do not support parameters with asynchronous sigps. + * return without waiting for its completion. */ -void smp_ext_call_async_others(ec_bit_sig sig) +void smp_ext_bitcall_others(ec_bit_sig sig) { struct _lowcore *lowcore; sigp_ccode ccode; @@ -420,7 +380,46 @@ int smp_signal_others(sigp_order_code order_code, u32 parameter, void smp_send_stop(void) { - smp_signal_others(sigp_stop, 0, 1, NULL); + int i; + u32 dummy; + unsigned long low_core_addr; + + /* write magic number to zero page (absolute 0) */ + + get_cpu_lowcore(smp_processor_id()).panic_magic = __PANIC_MAGIC; + + /* stop all processors */ + + smp_signal_others(sigp_stop, 0, TRUE, NULL); + + /* store status of all processors in their lowcores (real 0) */ + + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() != i) { + int ccode; + low_core_addr = (unsigned long)&get_cpu_lowcore(i); + do { + ccode = signal_processor_ps( + &dummy, + low_core_addr, + i, + sigp_store_status_at_address); + } while(ccode == sigp_busy); + } + } +} + +/* + * this function sends a 'purge tlb' signal to another CPU. + */ +void smp_ptlb_callback(void *info) +{ + local_flush_tlb(); +} + +void smp_ptlb_all(void) +{ + smp_ext_call_others(smp_ptlb_callback, NULL, 1); } /* @@ -431,7 +430,44 @@ void smp_send_stop(void) void smp_send_reschedule(int cpu) { - smp_ext_call_async(cpu, ec_schedule); + smp_ext_bitcall(cpu, ec_schedule); +} + +/* + * parameter area for the set/clear control bit callbacks + */ +typedef struct +{ + __u16 start_ctl; + __u16 end_ctl; + __u32 orvals[16]; + __u32 andvals[16]; +} ec_creg_mask_parms; + +/* + * callback for setting/clearing control bits + */ +void smp_ctl_bit_callback(void *info) { + ec_creg_mask_parms *pp; + u32 cregs[16]; + int i; + + pp = (ec_creg_mask_parms *) info; + asm volatile (" bras 1,0f\n" + " stctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + for (i = pp->start_ctl; i <= pp->end_ctl; i++) + cregs[i] = (cregs[i] & pp->andvals[i]) | pp->orvals[i]; + asm volatile (" bras 1,0f\n" + " lctl 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + return; } /* @@ -445,7 +481,7 @@ void smp_ctl_set_bit(int cr, int bit) { parms.end_ctl = cr; parms.orvals[cr] = 1 << bit; parms.andvals[cr] = 0xFFFFFFFF; - smp_ext_call_sync_others(ec_set_ctl_masked,&parms); + smp_ext_call_others(smp_ctl_bit_callback, &parms, 1); } __ctl_set_bit(cr, bit); } @@ -461,11 +497,35 @@ void smp_ctl_clear_bit(int cr, int bit) { parms.end_ctl = cr; parms.orvals[cr] = 0x00000000; parms.andvals[cr] = ~(1 << bit); - smp_ext_call_sync_others(ec_set_ctl_masked,&parms); + smp_ext_call_others(smp_ctl_bit_callback, &parms, 1); } __ctl_clear_bit(cr, bit); } +/* + * Call a function on all other processors + */ + +int +smp_call_function(void (*func)(void *info), void *info, int retry, int wait) +/* + * [SUMMARY] Run a function on all other CPUs. + * <func> The function to run. This must be fast and non-blocking. + * <info> An arbitrary pointer to pass to the function. + * <retry> currently unused. + * <wait> If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <<func>> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler, you may call it from a bottom half handler. + */ +{ + if (atomic_read(&smp_commenced) != 0) + smp_ext_call_others(func, info, 1); + (func)(info); + return 0; +} /* * Lets check how many CPUs we have. @@ -475,7 +535,6 @@ void smp_count_cpus(void) { int curr_cpu; - __cpu_logical_map[0] = boot_cpu_addr; current->processor = 0; smp_num_cpus = 1; for (curr_cpu = 0; @@ -527,7 +586,7 @@ static int __init fork_by_hand(void) struct pt_regs regs; /* don't care about the psw and regs settings since we'll never reschedule the forked task. */ - memset(®s,sizeof(pt_regs),0); + memset(®s,0,sizeof(pt_regs)); return do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0); } @@ -594,7 +653,10 @@ void __init smp_boot_cpus(void) struct _lowcore *curr_lowcore; sigp_ccode ccode; int i; - + + /* request the 0x1202 external interrupt */ + if (register_external_interrupt(0x1202, do_ext_call_interrupt) != 0) + panic("Couldn't request external interrupt 0x1202"); smp_count_cpus(); memset(lowcore_ptr,0,sizeof(lowcore_ptr)); @@ -705,24 +767,7 @@ void smp_local_timer_interrupt(struct pt_regs * regs) */ irq_enter(cpu, 0); - update_one_process(p, 1, user, system, cpu); - if (p->pid) { - p->counter -= 1; - if (p->counter <= 0) { - p->counter = 0; - p->need_resched = 1; - } - if (p->nice > 0) { - kstat.cpu_nice += user; - kstat.per_cpu_nice[cpu] += user; - } else { - kstat.cpu_user += user; - kstat.per_cpu_user[cpu] += user; - } - kstat.cpu_system += system; - kstat.per_cpu_system[cpu] += system; - - } + update_process_times(user); irq_exit(cpu, 0); } } diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c index b00d88a62..cc7700864 100644 --- a/arch/s390/kernel/sys_s390.c +++ b/arch/s390/kernel/sys_s390.c @@ -71,18 +71,10 @@ out: return error; } -/* FIXME: 6 parameters is one too much ... */ -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/i386 didn't use to be able to handle more than - * 4 system call parameters, so these system calls used a memory + * calls. Linux for S/390 isn't able to handle more than 5 + * system call parameters, so these system calls used a memory * block for parameter passing.. */ @@ -95,6 +87,18 @@ struct mmap_arg_struct { unsigned long offset; }; +asmlinkage long sys_mmap2(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + int error = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset); +out: + return error; +} + asmlinkage int old_mmap(struct mmap_arg_struct *arg) { struct mmap_arg_struct a; @@ -239,7 +243,7 @@ asmlinkage int sys_olduname(struct oldold_utsname * name) asmlinkage int sys_pause(void) { - current->state = TASK_INTERRUPTIBLE; + set_current_state(TASK_INTERRUPTIBLE); schedule(); return -ERESTARTNOHAND; } diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 8cd84ee54..f4bde27b3 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -11,7 +11,6 @@ * Copyright (C) 1991, 1992, 1995 Linus Torvalds */ -#include <linux/config.h> #include <linux/errno.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -27,15 +26,14 @@ #include <asm/uaccess.h> #include <asm/delay.h> +#include <asm/s390_ext.h> -#include <linux/mc146818rtc.h> #include <linux/timex.h> +#include <linux/config.h> #include <asm/irq.h> -extern volatile unsigned long lost_ticks; - /* change this if you have some constant time drift */ #define USECS_PER_JIFFY ((signed long)1000000/HZ) #define CLK_TICKS_PER_JIFFY ((signed long)USECS_PER_JIFFY<<12) @@ -45,6 +43,7 @@ extern volatile unsigned long lost_ticks; static uint64_t init_timer_cc, last_timer_cc; extern rwlock_t xtime_lock; +extern unsigned long wall_jiffies; void tod_to_timeval(uint64_t todval, struct timeval *xtime) { @@ -94,9 +93,9 @@ unsigned long do_gettimeoffset(void) */ void do_gettimeofday(struct timeval *tv) { - extern volatile unsigned long lost_ticks; unsigned long flags; unsigned long usec, sec; + unsigned long lost_ticks = jiffies - wall_jiffies; read_lock_irqsave(&xtime_lock, flags); usec = do_gettimeoffset(); @@ -149,7 +148,7 @@ void do_settimeofday(struct timeval *tv) extern __u16 boot_cpu_addr; #endif -void do_timer_interrupt(struct pt_regs *regs,int error_code) +void do_timer_interrupt(struct pt_regs *regs, __u16 error_code) { unsigned long flags; @@ -242,6 +241,9 @@ void __init time_init(void) printk("time_init: TOD clock stopped/non-operational\n"); break; } + /* request the 0x1004 external interrupt */ + if (register_external_interrupt(0x1004, do_timer_interrupt) != 0) + panic("Couldn't request external interrupts 0x1004"); init_100hz_timer(); init_timer_cc = S390_lowcore.jiffy_timer_cc; init_timer_cc -= 0x8126d60e46000000LL - diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index 4af4f6565..fc774a7db 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c @@ -42,122 +42,15 @@ extern void handle_per_exception(struct pt_regs *regs); typedef void pgm_check_handler_t(struct pt_regs *, long); pgm_check_handler_t *pgm_check_table[128]; -extern pgm_check_handler_t default_trap_handler; -extern pgm_check_handler_t do_page_fault; - -asmlinkage int system_call(void); - -#define DO_ERROR(trapnr, signr, str, name, tsk) \ -asmlinkage void name(struct pt_regs * regs, long error_code) \ -{ \ - tsk->thread.error_code = error_code; \ - tsk->thread.trap_no = trapnr; \ - die_if_no_fixup(str,regs,error_code); \ - force_sig(signr, tsk); \ -} - -/* TODO: define these as 'pgm_check_handler_t xxx;' -asmlinkage void divide_error(void); -asmlinkage void debug(void); -asmlinkage void nmi(void); -asmlinkage void int3(void); -asmlinkage void overflow(void); -asmlinkage void bounds(void); -asmlinkage void invalid_op(void); -asmlinkage void device_not_available(void); -asmlinkage void double_fault(void); -asmlinkage void coprocessor_segment_overrun(void); -asmlinkage void invalid_TSS(void); -asmlinkage void segment_not_present(void); -asmlinkage void stack_segment(void); -asmlinkage void general_protection(void); -asmlinkage void coprocessor_error(void); -asmlinkage void reserved(void); -asmlinkage void alignment_check(void); -asmlinkage void spurious_interrupt_bug(void); -*/ - -int kstack_depth_to_print = 24; - -/* - * These constants are for searching for possible module text - * segments. VMALLOC_OFFSET comes from mm/vmalloc.c; MODULE_RANGE is - * a guess of how much space is likely to be vmalloced. - */ -#define VMALLOC_OFFSET (8*1024*1024) -#define MODULE_RANGE (8*1024*1024) - -void show_crashed_task_info(void) -{ - printk("CPU: %d\n",smp_processor_id()); - printk("Process %s (pid: %d, stackpage=%08X)\n", - current->comm, current->pid, 4096+(addr_t)current); - show_regs(current,NULL,NULL); -} -#if 0 -static void show_registers(struct pt_regs *regs) -{ - printk("CPU: %d\nPSW: %08lx %08lx\n", - smp_processor_id(), (unsigned long) regs->psw.mask, - (unsigned long) regs->psw.addr); - printk("GPRS:\n"); - - printk("%08lx %08lx %08lx %08lx\n", - regs->gprs[0], regs->gprs[1], - regs->gprs[2], regs->gprs[3]); - printk("%08lx %08lx %08lx %08lx\n", - regs->gprs[4], regs->gprs[5], - regs->gprs[6], regs->gprs[7]); - printk("%08lx %08lx %08lx %08lx\n", - regs->gprs[8], regs->gprs[9], - regs->gprs[10], regs->gprs[11]); - printk("%08lx %08lx %08lx %08lx\n", - regs->gprs[12], regs->gprs[13], - regs->gprs[14], regs->gprs[15]); - printk("Process %s (pid: %d, stackpage=%08lx)\nStack: ", - current->comm, current->pid, 4096+(unsigned long)current); -/* - stack = (unsigned long *) esp; - for(i=0; i < kstack_depth_to_print; i++) { - if (((long) stack & 4095) == 0) - break; - if (i && ((i % 8) == 0)) - printk("\n "); - printk("%08lx ", get_seg_long(ss,stack++)); - } - printk("\nCall Trace: "); - stack = (unsigned long *) esp; - i = 1; - module_start = PAGE_OFFSET + (max_mapnr << PAGE_SHIFT); - module_start = ((module_start + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1)); - module_end = module_start + MODULE_RANGE; - while (((long) stack & 4095) != 0) { - addr = get_seg_long(ss, 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) &_stext) && - (addr <= (unsigned long) &_etext)) || - ((addr >= module_start) && (addr <= module_end))) { - if (i && ((i % 8) == 0)) - printk("\n "); - printk("[<%08lx>] ", addr); - i++; - } - } - printk("\nCode: "); - for(i=0;i<20;i++) - printk("%02x ",0xff & get_seg_byte(regs->xcs & 0xffff,(i+(char *)regs->eip))); - printk("\n"); -*/ -} +#ifdef CONFIG_SYSCTL +#ifdef CONFIG_PROCESS_DEBUG +int sysctl_userprocess_debug = 1; +#else +int sysctl_userprocess_debug = 0; +#endif #endif +extern pgm_check_handler_t do_page_fault; spinlock_t die_lock; @@ -166,29 +59,65 @@ void die(const char * str, struct pt_regs * regs, long err) console_verbose(); spin_lock_irq(&die_lock); printk("%s: %04lx\n", str, err & 0xffff); - show_crashed_task_info(); + show_regs(regs); spin_unlock_irq(&die_lock); do_exit(SIGSEGV); } -int check_for_fixup(struct pt_regs * regs) +#define DO_ERROR(signr, str, name) \ +asmlinkage void name(struct pt_regs * regs, long interruption_code) \ +{ \ + do_trap(interruption_code, signr, str, regs, NULL); \ +} + +#define DO_ERROR_INFO(signr, str, name, sicode, siaddr) \ +asmlinkage void name(struct pt_regs * regs, long interruption_code) \ +{ \ + siginfo_t info; \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *)siaddr; \ + do_trap(interruption_code, signr, str, regs, &info); \ +} + +static void inline do_trap(long interruption_code, int signr, char *str, + struct pt_regs *regs, siginfo_t *info) { - if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { - unsigned long fixup; - fixup = search_exception_table(regs->psw.addr); - if (fixup) { - regs->psw.addr = fixup; - return 1; + if (regs->psw.mask & PSW_PROBLEM_STATE) { + struct task_struct *tsk = current; + tsk->thread.trap_no = interruption_code; + if (info) + force_sig_info(signr, info, tsk); + else + force_sig(signr, tsk); +#ifndef CONFIG_SYSCTL +#ifdef CONFIG_PROCESS_DEBUG + printk("User process fault: interruption code 0x%lX\n", + interruption_code); + show_regs(regs); +#endif +#else + if (sysctl_userprocess_debug) { + printk("User process fault: interruption code 0x%lX\n", + interruption_code); + show_regs(regs); } - } - return 0; +#endif + } else { + unsigned long fixup = search_exception_table(regs->psw.addr); + if (fixup) + regs->psw.addr = fixup; + else + die(str, regs, interruption_code); + } } int do_debugger_trap(struct pt_regs *regs,int signal) { if(regs->psw.mask&PSW_PROBLEM_STATE) { - if(current->flags & PF_PTRACED) + if(current->ptrace & PT_PTRACED) force_sig(signal,current); else return 1; @@ -207,50 +136,16 @@ int do_debugger_trap(struct pt_regs *regs,int signal) return 0; } -static void die_if_no_fixup(const char * str, struct pt_regs * regs, long err) -{ - if (!(regs->psw.mask & PSW_PROBLEM_STATE)) { - unsigned long fixup; - fixup = search_exception_table(regs->psw.addr); - if (fixup) { - regs->psw.addr = fixup; - return; - } - die(str, regs, err); - } -} - -asmlinkage void default_trap_handler(struct pt_regs * regs, long error_code) -{ - current->thread.error_code = error_code; - current->thread.trap_no = error_code; - die_if_no_fixup("Unknown program exception",regs,error_code); - force_sig(SIGSEGV, current); -} - -DO_ERROR(2, SIGILL, "privileged operation", privileged_op, current) -DO_ERROR(3, SIGILL, "execute exception", execute_exception, current) -DO_ERROR(5, SIGSEGV, "addressing exception", addressing_exception, current) -DO_ERROR(9, SIGFPE, "fixpoint divide exception", divide_exception, current) -DO_ERROR(0x12, SIGILL, "translation exception", translation_exception, current) -DO_ERROR(0x13, SIGILL, "special operand exception", special_op_exception, current) -DO_ERROR(0x15, SIGILL, "operand exception", operand_exception, current) +DO_ERROR(SIGSEGV, "Unknown program exception", default_trap_handler) +DO_ERROR(SIGILL, "privileged operation", privileged_op) +DO_ERROR(SIGILL, "execute exception", execute_exception) +DO_ERROR(SIGSEGV, "addressing exception", addressing_exception) +DO_ERROR(SIGFPE, "fixpoint divide exception", divide_exception) +DO_ERROR(SIGILL, "translation exception", translation_exception) +DO_ERROR(SIGILL, "special operand exception", special_op_exception) +DO_ERROR(SIGILL, "operand exception", operand_exception) -/* need to define -DO_ERROR( 6, SIGILL, "invalid operand", invalid_op, current) -DO_ERROR( 8, SIGSEGV, "double fault", double_fault, current) -DO_ERROR( 9, SIGFPE, "coprocessor segment overrun", coprocessor_segment_overrun, last_task_used_math) -DO_ERROR(10, SIGSEGV, "invalid TSS", invalid_TSS, current) -DO_ERROR(11, SIGBUS, "segment not present", segment_not_present, current) -DO_ERROR(12, SIGBUS, "stack segment", stack_segment, current) -DO_ERROR(17, SIGSEGV, "alignment check", alignment_check, current) -DO_ERROR(18, SIGSEGV, "reserved", reserved, current) -DO_ERROR(19, SIGSEGV, "cache flush denied", cache_flush_denied, current) -*/ - -#ifdef CONFIG_IEEEFPU_EMULATION - -asmlinkage void illegal_op(struct pt_regs * regs, long error_code) +asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code) { __u8 opcode[6]; __u16 *location; @@ -268,6 +163,7 @@ asmlinkage void illegal_op(struct pt_regs * regs, long error_code) if(do_debugger_trap(regs,SIGTRAP)) do_sig=1; } +#ifdef CONFIG_IEEEFPU_EMULATION else if (problem_state ) { if (opcode[0] == 0xb3) { @@ -288,18 +184,19 @@ asmlinkage void illegal_op(struct pt_regs * regs, long error_code) do_sig = math_emu_lfpc(opcode, regs); } else do_sig = 1; - } else - do_sig = 1; - if (do_sig) { - current->thread.error_code = error_code; - current->thread.trap_no = 1; - force_sig(SIGILL, current); - die_if_no_fixup("illegal operation", regs, error_code); } +#endif + else + do_sig = 1; + if (do_sig) + do_trap(interruption_code, SIGILL, "illegal operation", regs, NULL); unlock_kernel(); } -asmlinkage void specification_exception(struct pt_regs * regs, long error_code) + + +#ifdef CONFIG_IEEEFPU_EMULATION +asmlinkage void specification_exception(struct pt_regs * regs, long interruption_code) { __u8 opcode[6]; __u16 *location; @@ -311,26 +208,26 @@ asmlinkage void specification_exception(struct pt_regs * regs, long error_code) get_user(*((__u16 *) opcode), location); switch (opcode[0]) { case 0x28: /* LDR Rx,Ry */ - math_emu_ldr(opcode); + do_sig=math_emu_ldr(opcode); break; case 0x38: /* LER Rx,Ry */ - math_emu_ler(opcode); + do_sig=math_emu_ler(opcode); break; case 0x60: /* STD R,D(X,B) */ get_user(*((__u16 *) (opcode+2)), location+1); - math_emu_std(opcode, regs); + do_sig=math_emu_std(opcode, regs); break; case 0x68: /* LD R,D(X,B) */ get_user(*((__u16 *) (opcode+2)), location+1); - math_emu_ld(opcode, regs); + do_sig=math_emu_ld(opcode, regs); break; case 0x70: /* STE R,D(X,B) */ get_user(*((__u16 *) (opcode+2)), location+1); - math_emu_ste(opcode, regs); + do_sig=math_emu_ste(opcode, regs); break; case 0x78: /* LE R,D(X,B) */ get_user(*((__u16 *) (opcode+2)), location+1); - math_emu_le(opcode, regs); + do_sig=math_emu_le(opcode, regs); break; default: do_sig = 1; @@ -338,47 +235,59 @@ asmlinkage void specification_exception(struct pt_regs * regs, long error_code) } } else do_sig = 1; - if (do_sig) { - current->thread.error_code = error_code; - current->thread.trap_no = 1; - force_sig(SIGILL, current); - die_if_no_fixup("illegal operation", regs, error_code); - } + if (do_sig) + do_trap(interruption_code, SIGILL, "specification exception", regs, NULL); unlock_kernel(); } +#else +DO_ERROR(SIGILL, "specification exception", specification_exception) +#endif -asmlinkage void data_exception(struct pt_regs * regs, long error_code) +asmlinkage void data_exception(struct pt_regs * regs, long interruption_code) { __u8 opcode[6]; __u16 *location; int do_sig = 0; lock_kernel(); - if (regs->psw.mask & 0x00010000L) { - location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + if(MACHINE_HAS_IEEE) + { + __asm__ volatile ("stfpc %0\n\t" + : "=m" (current->thread.fp_regs.fpc)); + } + /* Same code should work when we implement fpu emulation */ + /* provided we call data exception from the fpu emulator */ + if(current->thread.fp_regs.fpc&FPC_DXC_MASK) + { + current->thread.ieee_instruction_pointer=(addr_t)location; + force_sig(SIGFPE, current); + } +#ifdef CONFIG_IEEEFPU_EMULATION + else if (regs->psw.mask & 0x00010000L) { get_user(*((__u16 *) opcode), location); switch (opcode[0]) { case 0x28: /* LDR Rx,Ry */ - math_emu_ldr(opcode); + do_sig=math_emu_ldr(opcode); break; case 0x38: /* LER Rx,Ry */ - math_emu_ler(opcode); + do_sig=math_emu_ler(opcode); break; case 0x60: /* STD R,D(X,B) */ get_user(*((__u16 *) (opcode+2)), location+1); - math_emu_std(opcode, regs); + do_sig=math_emu_std(opcode, regs); break; case 0x68: /* LD R,D(X,B) */ get_user(*((__u16 *) (opcode+2)), location+1); - math_emu_ld(opcode, regs); + do_sig=math_emu_ld(opcode, regs); break; case 0x70: /* STE R,D(X,B) */ get_user(*((__u16 *) (opcode+2)), location+1); - math_emu_ste(opcode, regs); + do_sig=math_emu_ste(opcode, regs); break; case 0x78: /* LE R,D(X,B) */ get_user(*((__u16 *) (opcode+2)), location+1); - math_emu_le(opcode, regs); + do_sig=math_emu_le(opcode, regs); break; case 0xb3: get_user(*((__u16 *) (opcode+2)), location+1); @@ -406,22 +315,15 @@ asmlinkage void data_exception(struct pt_regs * regs, long error_code) do_sig = 1; break; } - } else - do_sig = 1; - if (do_sig) { - current->thread.error_code = error_code; - current->thread.trap_no = 1; - force_sig(SIGILL, current); - die_if_no_fixup("illegal operation", regs, error_code); } +#endif + else + do_sig = 1; + if (do_sig) + do_trap(interruption_code, SIGILL, "data exception", regs, NULL); unlock_kernel(); } -#else -DO_ERROR(1, SIGILL, "illegal operation", illegal_op, current) -DO_ERROR(6, SIGILL, "specification exception", specification_exception, current) -DO_ERROR(7, SIGILL, "data exception", data_exception, current) -#endif /* CONFIG_IEEEFPU_EMULATION */ /* init is done in lowcore.S and head.S */ @@ -463,7 +365,7 @@ void handle_per_exception(struct pt_regs *regs) /* I've seen this possibly a task structure being reused ? */ printk("Spurious per exception detected\n"); printk("switching off per tracing for this task.\n"); - show_crashed_task_info(); + show_regs(regs); /* Hopefully switching off per tracing will help us survive */ regs->psw.mask &= ~PSW_PER_MASK; } diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index aa8b0e5da..40ae70e8c 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile @@ -2,11 +2,17 @@ # Makefile for s390-specific library files.. # +ifdef SMP .S.o: - $(CC) $(AFLAGS) -traditional -c $< -o $*.o + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o +else +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o +endif L_TARGET = lib.a -L_OBJS = checksum.o delay.o memset.o strcmp.o strncpy.o + +obj-y = checksum.o delay.o memset.o strcmp.o strncpy.o uaccess.o include $(TOPDIR)/Rules.make diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c index ec3274487..012a95308 100644 --- a/arch/s390/lib/delay.c +++ b/arch/s390/lib/delay.c @@ -21,25 +21,30 @@ void __delay(unsigned long loops) { - __asm__ __volatile__( - "0: ahi %0,-1\n" - " jnm 0b" - : /* no outputs */ : "r" (loops) ); + /* + * To end the bloody studid and useless discussion about the + * BogoMips number I took the liberty to define the __delay + * function in a way that that resulting BogoMips number will + * yield the megahertz number of the cpu. The important function + * is udelay and that is done using the tod clock. -- martin. + */ + __asm__ __volatile__( + "0: brct %0,0b" + : /* no outputs */ : "r" (loops/2) ); } -inline void __const_udelay(unsigned long xloops) +/* + * Waits for 'usecs' microseconds using the tod clock + */ +void __udelay(unsigned long usecs) { + uint64_t start_cc, end_cc; - __asm__("LR 3,%1\n\t" - "MR 2,%2\n\t" - "LR %0,2\n\t" - : "=r" (xloops) - : "r" (xloops) , "r" (loops_per_sec) - : "2" , "3"); - __delay(xloops); + if (usecs == 0) + return; + asm volatile ("STCK %0" : "=m" (start_cc)); + do { + asm volatile ("STCK %0" : "=m" (end_cc)); + } while (((end_cc - start_cc)/4096) < usecs); } -void __udelay(unsigned long usecs) -{ - __const_udelay(usecs * 0x000010c6); /* 2**32 / 1000000 */ -} diff --git a/arch/s390/lib/strcmp.S b/arch/s390/lib/strcmp.S index d3f63942f..340edffb5 100644 --- a/arch/s390/lib/strcmp.S +++ b/arch/s390/lib/strcmp.S @@ -18,8 +18,8 @@ strcmp: CLST 2,3 JO .-4 JE strcmp_equal - IC 0,0(0,3) - IC 1,0(0,2) + IC 0,0(3) + IC 1,0(2) SR 1,0 strcmp_equal: LR 2,1 diff --git a/arch/s390/lib/strncpy.S b/arch/s390/lib/strncpy.S index 83578909c..3065be2b4 100644 --- a/arch/s390/lib/strncpy.S +++ b/arch/s390/lib/strncpy.S @@ -20,9 +20,9 @@ strncpy: SR 0,0 strncpy_loop: ICM 0,1,0(3) # ICM sets the cc, IC does not - LA 3,1(0,3) - STC 0,0(0,1) - LA 1,1(0,1) + LA 3,1(3) + STC 0,0(1) + LA 1,1(1) JZ strncpy_exit # ICM inserted a 0x00 BRCT 4,strncpy_loop # R4 -= 1, jump to strncpy_loop if > 0 strncpy_exit: diff --git a/arch/s390/lib/uaccess.S b/arch/s390/lib/uaccess.S new file mode 100644 index 000000000..8044d156d --- /dev/null +++ b/arch/s390/lib/uaccess.S @@ -0,0 +1,51 @@ +/* + * arch/s390/lib/uaccess.S + * fixup routines for copy_{from|to}_user functions. + * + * s390 + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * These functions have a non-standard call interface + */ + +#include <asm/lowcore.h> + + .text + .align 4 + .globl __copy_from_user_fixup +__copy_from_user_fixup: + l 1,__LC_PGM_OLD_PSW+4 + sll 4,1 + srl 4,1 +0: lhi 3,-4096 + sll 3,1 + srl 3,1 + n 3,__LC_TRANS_EXC_ADDR + sr 3,4 + bm 4(1) +1: mvcle 2,4,0 + b 4(1) + .section __ex_table,"a" + .long 1b,0b + .previous + + .align 4 + .text + .globl __copy_to_user_fixup +__copy_to_user_fixup: + l 1,__LC_PGM_OLD_PSW+4 + sll 4,1 + srl 4,1 +0: lhi 5,-4096 + sll 5,1 + srl 5,1 + n 5,__LC_TRANS_EXC_ADDR + sr 5,4 + bm 4(1) +1: mvcle 4,2,0 + b 4(1) + .section __ex_table,"a" + .long 1b,0b + .previous + diff --git a/arch/s390/mm/Makefile b/arch/s390/mm/Makefile index cee7d4e6d..73e25bd30 100644 --- a/arch/s390/mm/Makefile +++ b/arch/s390/mm/Makefile @@ -8,6 +8,7 @@ # Note 2! The CFLAGS definition is now in the main makefile... O_TARGET := mm.o -O_OBJS := init.o fault.o ioremap.o extable.o + +obj-y := init.o fault.o ioremap.o extable.o include $(TOPDIR)/Rules.make diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index f20a9d49e..2c75918af 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -9,6 +9,7 @@ * Copyright (C) 1995 Linus Torvalds */ +#include <linux/config.h> #include <linux/signal.h> #include <linux/sched.h> #include <linux/kernel.h> @@ -26,6 +27,10 @@ #include <asm/pgtable.h> #include <asm/hardirq.h> +#ifdef CONFIG_SYSCTL +extern int sysctl_userprocess_debug; +#endif + extern void die(const char *,struct pt_regs *,long); /* @@ -48,6 +53,8 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) int write; unsigned long psw_mask; unsigned long psw_addr; + int si_code = SEGV_MAPERR; + int kernel_address = 0; /* * get psw mask of Program old psw to find out, @@ -65,58 +72,106 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) address = S390_lowcore.trans_exc_code&0x7ffff000; - if (in_irq()) - die("page fault from irq handler",regs,error_code); - tsk = current; mm = tsk->mm; + if (in_interrupt() || !mm) + goto no_context; + + + /* + * Check which address space the address belongs to + */ + switch (S390_lowcore.trans_exc_code & 3) + { + case 0: /* Primary Segment Table Descriptor */ + kernel_address = 1; + goto no_context; + + case 1: /* STD determined via access register */ + if (S390_lowcore.exc_access_id == 0) + { + kernel_address = 1; + goto no_context; + } + if (regs && S390_lowcore.exc_access_id < NUM_ACRS) + { + if (regs->acrs[S390_lowcore.exc_access_id] == 0) + { + kernel_address = 1; + goto no_context; + } + if (regs->acrs[S390_lowcore.exc_access_id] == 1) + { + /* user space address */ + break; + } + } + die("page fault via unknown access register", regs, error_code); + break; + + case 2: /* Secondary Segment Table Descriptor */ + case 3: /* Home Segment Table Descriptor */ + /* user space address */ + break; + } + + + /* + * When we get here, the fault happened in the current + * task's user address space, so we search the VMAs + */ + down(&mm->mmap_sem); vma = find_vma(mm, address); - if (!vma) { - printk("no vma for address %lX\n",address); + if (!vma) goto bad_area; - } if (vma->vm_start <= address) goto good_area; - if (!(vma->vm_flags & VM_GROWSDOWN)) { - printk("VM_GROWSDOWN not set, but address %lX \n",address); - printk("not in vma %p (start %lX end %lX)\n",vma, - vma->vm_start,vma->vm_end); + if (!(vma->vm_flags & VM_GROWSDOWN)) goto bad_area; - } - if (expand_stack(vma, address)) { - printk("expand of vma failed address %lX\n",address); - printk("vma %p (start %lX end %lX)\n",vma, - vma->vm_start,vma->vm_end); + if (expand_stack(vma, address)) goto bad_area; - } /* * Ok, we have a good vm_area for this memory access, so * we can handle it.. */ good_area: write = 0; + si_code = SEGV_ACCERR; + switch (error_code & 0xFF) { case 0x04: /* write, present*/ write = 1; break; case 0x10: /* not present*/ case 0x11: /* not present*/ - if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) { - printk("flags %X of vma for address %lX wrong \n", - vma->vm_flags,address); - printk("vma %p (start %lX end %lX)\n",vma, - vma->vm_start,vma->vm_end); + if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) goto bad_area; - } break; default: printk("code should be 4, 10 or 11 (%lX) \n",error_code&0xFF); goto bad_area; } - handle_mm_fault(tsk, vma, address, write); + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + switch (handle_mm_fault(mm, vma, address, write)) { + case 1: + tsk->min_flt++; + break; + case 2: + tsk->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; + } up(&mm->mmap_sem); return; @@ -130,19 +185,32 @@ bad_area: /* User mode accesses just cause a SIGSEGV */ if (psw_mask & PSW_PROBLEM_STATE) { + struct siginfo si; tsk->thread.prot_addr = address; - tsk->thread.error_code = error_code; - tsk->thread.trap_no = 14; - + tsk->thread.trap_no = error_code; +#ifndef CONFIG_SYSCTL +#ifdef CONFIG_PROCESS_DEBUG printk("User process fault: interruption code 0x%lX\n",error_code); printk("failing address: %lX\n",address); - show_crashed_task_info(); - force_sig(SIGSEGV, tsk); + show_regs(regs); +#endif +#else + if (sysctl_userprocess_debug) { + printk("User process fault: interruption code 0x%lX\n", + error_code); + printk("failing address: %lX\n", address); + show_regs(regs); + } +#endif + si.si_signo = SIGSEGV; + si.si_code = si_code; + si.si_addr = (void*) address; + force_sig_info(SIGSEGV, &si, tsk); return; } +no_context: /* Are we prepared to handle this kernel fault? */ - if ((fixup = search_exception_table(regs->psw.addr)) != 0) { regs->psw.addr = fixup; return; @@ -151,53 +219,47 @@ bad_area: /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. - * - * First we check if it was the bootup rw-test, though.. */ - if (address < PAGE_SIZE) - printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); + + if (kernel_address) + printk(KERN_ALERT "Unable to handle kernel pointer dereference" + " at virtual kernel address %08lx\n", address); else - printk(KERN_ALERT "Unable to handle kernel paging request"); - printk(" at virtual address %08lx\n",address); + printk(KERN_ALERT "Unable to handle kernel paging request" + " at virtual user address %08lx\n", address); /* * need to define, which information is useful here */ - lock_kernel(); die("Oops", regs, error_code); do_exit(SIGKILL); - unlock_kernel(); -} -/* - { - char c; - int i,j; - char *addr; - addr = ((char*) psw_addr)-0x20; - for (i=0;i<16;i++) { - if (i == 2) - printk("\n"); - printk ("%08X: ",(unsigned long) addr); - for (j=0;j<4;j++) { - printk("%08X ",*(unsigned long*)addr); - addr += 4; - } - addr -=0x10; - printk(" | "); - for (j=0;j<16;j++) { - printk("%c",(c=*addr++) < 0x20 ? '.' : c ); - } - - printk("\n"); - } - printk("\n"); - } +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. */ +out_of_memory: + up(&mm->mmap_sem); + printk("VM: killing process %s\n", tsk->comm); + if (psw_mask & PSW_PROBLEM_STATE) + do_exit(SIGKILL); + goto no_context; +do_sigbus: + up(&mm->mmap_sem); + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + tsk->thread.prot_addr = address; + tsk->thread.trap_no = error_code; + force_sig(SIGBUS, tsk); - + /* Kernel mode? Handle exceptions or die */ + if (!(psw_mask & PSW_PROBLEM_STATE)) + goto no_context; +} diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 177e5e8f2..e1acfa063 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -52,10 +52,10 @@ static unsigned long totalram_pages; * data and COW. */ -pgd_t swapper_pg_dir[512] __attribute__ ((__aligned__ (4096))); -unsigned long empty_bad_page[1024] __attribute__ ((__aligned__ (4096))); -unsigned long empty_zero_page[1024] __attribute__ ((__aligned__ (4096))); -pte_t empty_bad_pte_table[1024] __attribute__ ((__aligned__ (4096))); +pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE))); +char empty_bad_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +char empty_zero_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +pte_t empty_bad_pte_table[PTRS_PER_PTE] __attribute__((__aligned__(PAGE_SIZE))); static int test_access(unsigned long loc) { @@ -104,47 +104,6 @@ static inline void invalidate_page(pte_t *pte) pte_clear(pte++); } -void __handle_bad_pmd(pmd_t *pmd) -{ - pmd_ERROR(*pmd); - pmd_val(*pmd) = _PAGE_TABLE + __pa(get_bad_pte_table()); -} - -void __handle_bad_pmd_kernel(pmd_t *pmd) -{ - pmd_ERROR(*pmd); - pmd_val(*pmd) = _KERNPG_TABLE + __pa(get_bad_pte_table()); -} - -pte_t *get_pte_kernel_slow(pmd_t *pmd, unsigned long offset) -{ - pte_t *pte; - - pte = (pte_t *) __get_free_page(GFP_KERNEL); - if (pmd_none(*pmd)) { - if (pte) { - invalidate_page(pte); - pmd_val(pmd[0]) = _KERNPG_TABLE + __pa(pte); - pmd_val(pmd[1]) = _KERNPG_TABLE + __pa(pte)+1024; - pmd_val(pmd[2]) = _KERNPG_TABLE + __pa(pte)+2048; - pmd_val(pmd[3]) = _KERNPG_TABLE + __pa(pte)+3072; - return pte + offset; - } - pte = get_bad_pte_table(); - pmd_val(pmd[0]) = _KERNPG_TABLE + __pa(pte); - pmd_val(pmd[1]) = _KERNPG_TABLE + __pa(pte)+1024; - pmd_val(pmd[2]) = _KERNPG_TABLE + __pa(pte)+2048; - pmd_val(pmd[3]) = _KERNPG_TABLE + __pa(pte)+3072; - return NULL; - } - free_page((unsigned long)pte); - if (pmd_bad(*pmd)) { - __handle_bad_pmd_kernel(pmd); - return NULL; - } - return (pte_t *) pmd_page(*pmd) + offset; -} - pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) { unsigned long pte; @@ -167,10 +126,8 @@ pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) return NULL; } free_page(pte); - if (pmd_bad(*pmd)) { - __handle_bad_pmd(pmd); - return NULL; - } + if (pmd_bad(*pmd)) + BUG(); return (pte_t *) pmd_page(*pmd) + offset; } @@ -180,7 +137,7 @@ int do_check_pgt_cache(int low, int high) if(pgtable_cache_size > high) { do { if(pgd_quicklist) - free_pgd_slow(get_pgd_fast()), freed++; + free_pgd_slow(get_pgd_fast()), freed += 2; if(pmd_quicklist) free_pmd_slow(get_pmd_fast()), freed++; if(pte_quicklist) @@ -245,6 +202,7 @@ void __init paging_init(void) unsigned long address=0; unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE; unsigned long end_mem = (unsigned long) __va(max_low_pfn*PAGE_SIZE); + static const int ssm_mask = 0x04000000L; /* unmap whole virtual address space */ @@ -283,8 +241,9 @@ void __init paging_init(void) /* enable virtual mapping in kernel mode */ __asm__ __volatile__(" LCTL 1,1,%0\n" " LCTL 7,7,%0\n" - " LCTL 13,13,%0" - : :"m" (pgdir_k)); + " LCTL 13,13,%0\n" + " SSM %1" + : : "m" (pgdir_k), "m" (ssm_mask)); local_flush_tlb(); @@ -378,6 +337,7 @@ void si_meminfo(struct sysinfo *val) val->sharedram = 0; val->freeram = nr_free_pages(); val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; val->mem_unit = PAGE_SIZE; - return; } diff --git a/arch/s390/mm/ioremap.c b/arch/s390/mm/ioremap.c index f9f0024c4..38acc4a22 100644 --- a/arch/s390/mm/ioremap.c +++ b/arch/s390/mm/ioremap.c @@ -33,8 +33,8 @@ static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned l printk("remap_area_pte: page already exists\n"); BUG(); } - set_pte(pte, mk_pte_phys(phys_addr, __pgprot(_PAGE_PRESENT | - _PAGE_DIRTY | _PAGE_ACCESSED | flags))); + set_pte(pte, mk_pte_phys(phys_addr, + __pgprot(_PAGE_PRESENT | flags))); address += PAGE_SIZE; phys_addr += PAGE_SIZE; pte++; diff --git a/arch/s390/tools/dasdfmt/Makefile b/arch/s390/tools/dasdfmt/Makefile index f63ff468b..b60641bbc 100644 --- a/arch/s390/tools/dasdfmt/Makefile +++ b/arch/s390/tools/dasdfmt/Makefile @@ -1,7 +1,7 @@ all: dasdfmt dasdfmt: dasdfmt.c - $(CROSS_COMPILE)gcc -o $@ $^ + $(CC) -o $@ $^ $(STRIP) $@ clean: diff --git a/arch/s390/tools/dasdfmt/dasdfmt.8 b/arch/s390/tools/dasdfmt/dasdfmt.8 index b08244322..9e6a4e89e 100644 --- a/arch/s390/tools/dasdfmt/dasdfmt.8 +++ b/arch/s390/tools/dasdfmt/dasdfmt.8 @@ -3,8 +3,7 @@ .SH NAME dasdfmt \- formatting of DSAD (ECKD) disk drives. .SH SYNOPSIS -\fBdasdfmt\fR [-tvyV] [-b \fIblockSize\fR] [\fIblockRange\fI] - \fIdiskSpec\fR +\fBdasdfmt\fR [-tvyLV] [-b \fIblockSize\fR] [-l \fIdiskLabel\fR] \fIdiskSpec\fR .SH DESCRIPTION \fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it for usage with Linux for S/390. \fBWARNING\fR: Incautious usage of @@ -25,6 +24,10 @@ Increases verbosity. Start formatting without further user-confirmation. .TP +\fB-L\fR +Omit the writing of a disk label after formatting. + +.TP \fB-V\fR Print version number and exit. @@ -35,26 +38,17 @@ and always be a power of two. Due due some limitations in the driver, it is \fBstrongly\fR recommended to use a \fIblockSize\fR of \fI4096\fR. .TP -\fIblockRange\fR -This parameter specifies the number of the first and last block to be -formatted. If this parameter is \fBomitted\fR, formatting the \fBwhole\fR disk -is assumed. The \fIblockRange\fR can be specified in two different formats: -.sp - \fB-s\fR \fIstartBlock\fR \fB-e\fR \fIendBlock\fR -.br -or -.br - \fB-r\fR \fIstartBlock\fR-\fIendBlock\fR -.sp -If \fIstartBlock\fR is omitted, block \fB0\fR is assumed. If -\fIendBlock\fR is omitted, the last block of the disk is assumed. +\fB-l\fR \fIdiskLabel\fR +Specify the label to be written to disk after formatting. If no label is +specified, a sensible default is used. \fIdiskLabel\fR is interpreted as +ASCII string and is automatically converted to EBCDIC. .TP \fIdiskSpec\fR This parameter specified the device to be formatted. It also can be given in two variants: .sp - \fB-f\fR \fB/dev/dd\fR\fIX\fR + \fB-f\fR \fB/dev/dasd\fR\fIX\fR .br or .br @@ -63,7 +57,7 @@ or The first form uses the commonly used .SM UNIX device notation where \fIX\fR is a single lowercase letter. -The second form uses simply the VM vdev number. +The second form uses simply the device number. .SH BUGS None so far ;-) diff --git a/arch/s390/tools/dasdfmt/dasdfmt.c b/arch/s390/tools/dasdfmt/dasdfmt.c index 1726e7061..2820fc91d 100644 --- a/arch/s390/tools/dasdfmt/dasdfmt.c +++ b/arch/s390/tools/dasdfmt/dasdfmt.c @@ -12,6 +12,8 @@ * detect non-switch parameters ("dasdfmt -n 170 XY") and complain about them */ +/* #define _LINUX_BLKDEV_H */ + #include <unistd.h> #include <stdio.h> #include <sys/types.h> @@ -25,19 +27,25 @@ #include <string.h> #include <dirent.h> #include <mntent.h> -#include "../../../drivers/s390/block/dasd.h" /* uses DASD_PARTN_BITS */ #define __KERNEL__ /* we want to use kdev_t and not have to define it */ #include <linux/kdev_t.h> #undef __KERNEL__ +#include <linux/fs.h> +#include <asm/dasd.h> +#include <linux/hdreg.h> + #define EXIT_MISUSE 1 #define EXIT_BUSY 2 #define TEMPFILENAME "/tmp/ddfXXXXXX" #define TEMPFILENAMECHARS 8 /* 8 characters are fixed in all temp filenames */ -#define IOCTL_COMMAND 'D' << 8 #define SLASHDEV "/dev/" #define PROC_DASD_DEVICES "/proc/dasd/devices" +/* _PATH_MOUNTED is /etc/mtab - /proc/mounts does not show root-fs correctly */ +#define PROC_MOUNTS _PATH_MOUNTED +#define PROC_SWAPS "/proc/swaps" #define DASD_DRIVER_NAME "dasd" +#define LABEL_LENGTH 10 #define PROC_LINE_LENGTH 80 #define ERR_LENGTH 80 @@ -66,24 +74,108 @@ ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ "is in invalid format\n",prog_name);} -typedef struct { - int start_unit; - int stop_unit; - int blksize; -} format_data_t; - -char prog_name[]="dasd_format"; +char *prog_name;/*="dasdfmt";*/ char tempfilename[]=TEMPFILENAME; +__u8 _ascebc[256] = +{ + /*00 NUL SOH STX ETX EOT ENQ ACK BEL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + /* ->NL */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /*18 CAN EM SUB ESC FS GS RS US */ + /* ->IGS ->IRS ->IUS */ + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /*80*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*88*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*90*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*98*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E0 sz */ + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F8*/ + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +void convert_label(char *str) +{ + int i; + for (i=0;i<LABEL_LENGTH;i++) str[i]=_ascebc[str[i]]; +} + void exit_usage(int exitcode) { - printf("Usage: %s [-htvyV] [-b blocksize] <range> <diskspec>\n\n", - prog_name); - printf(" where <range> is either\n"); - printf(" -s start_track -e end_track\n"); +#ifdef RANGE_FORMATTING + printf("Usage: %s [-htvyLV] [-l <label>] [-b <blocksize>] [<range>] " \ + "<diskspec>\n\n",prog_name); +#else /* RANGE_FORMATTING */ + printf("Usage: %s [-htvyLV] [-l <label>] [-b <blocksize>] " \ + "<diskspec>\n\n",prog_name); +#endif /* RANGE_FORMATTING */ + printf(" -t means testmode\n"); + printf(" -v means verbose mode\n"); + printf(" -V means print version\n"); + printf(" -L means don't write disk label\n"); + printf(" <label> is a label which is converted to EBCDIC and " \ + "written to disk\n"); + printf(" <blocksize> has to be power of 2 and at least 512\n"); +#ifdef RANGE_FORMATTING + printf(" <range> is either\n"); + printf(" -s <start_track> -e <end_track>\n"); printf(" or\n"); - printf(" -r start_track-end_track\n"); + printf(" -r <start_track>-<end_track>\n"); +#endif /* RANGE_FORMATTING */ printf(" and <diskspec> is either\n"); printf(" -f /dev/dasdX\n"); printf(" or\n"); @@ -106,9 +198,9 @@ get_xno_from_xno(int *devno,kdev_t *major_no,kdev_t *minor_no,int mode) PROC_DASD_DEVICES ": %s (do you have the /proc " \ "filesystem enabled?)\n",prog_name,strerror(errno)); - fgets(line,sizeof(line),file); /* omit first line */ + /* fgets(line,sizeof(line),file); omit first line */ while (fgets(line,sizeof(line),file)!=NULL) { - rc=sscanf(line,"%X%d%d",&d,&ma_i,&mi_i); + rc=sscanf(line,"%X %*[(A-Z)] at (%d:%d)",&d,&ma_i,&mi_i); ma=ma_i; mi=mi_i; if ( (rc==3) && @@ -253,6 +345,7 @@ ask_user_for_data(format_data_t params) char *str; char output[60],o2[12]; +#ifdef RANGE_FORMATTING i=params.start_unit; do { params.start_unit=i; @@ -284,6 +377,7 @@ ask_user_for_data(format_data_t params) ASK_CHECK_PARAM(CHECK_END); } } while (rc!=1); +#endif /* RANGE_FORMATTING */ i=params.blksize; do { @@ -320,8 +414,8 @@ check_mounted(int major, int minor) /* * first, check filesystems */ - if (!(f = fopen(_PATH_MOUNTED, "r"))) - ERRMSG_EXIT(EXIT_FAILURE, "%s: %s\n", _PATH_MOUNTED, + if (!(f = fopen(PROC_MOUNTS, "r"))) + ERRMSG_EXIT(EXIT_FAILURE, "%s: %s\n", PROC_MOUNTS, strerror(errno)); while ((ment = getmntent(f))) { if (stat(ment->mnt_fsname, &stbuf) == 0) @@ -337,8 +431,8 @@ check_mounted(int major, int minor) /* * second, check active swap spaces */ - if (!(f = fopen("/proc/swaps", "r"))) - ERRMSG_EXIT(EXIT_FAILURE, "/proc/swaps: %s", strerror(errno)); + if (!(f = fopen(PROC_SWAPS, "r"))) + ERRMSG_EXIT(EXIT_FAILURE, PROC_SWAPS " %s", strerror(errno)); /* * skip header line */ @@ -362,12 +456,15 @@ check_mounted(int major, int minor) void do_format_dasd(char *dev_name,format_data_t format_params,int testmode, - int verbosity,int withoutprompt) + int verbosity,int writenolabel,int labelspec, + char *label,int withoutprompt,int devno) { int fd,rc; struct stat stat_buf; kdev_t minor_no,major_no; - int devno; + int new_blksize; + unsigned int label_position; + struct hd_geometry new_geometry; char inp_buffer[5]; /* to contain yes */ fd=open(dev_name,O_RDWR); @@ -390,6 +487,10 @@ do_format_dasd(char *dev_name,format_data_t format_params,int testmode, minor_no=MINOR(stat_buf.st_rdev); } check_mounted(major_no, minor_no); + + if ((!writenolabel) && (!labelspec)) { + sprintf(label,"LNX1 x%04x",devno); + } if ( ((withoutprompt)&&(verbosity>=1)) || (!withoutprompt) ) { @@ -400,6 +501,11 @@ do_format_dasd(char *dev_name,format_data_t format_params,int testmode, printf(" Device number of device : 0x%x\n",devno); printf(" Major number of device : %u\n",major_no); printf(" Minor number of device : %u\n",minor_no); + printf(" Labelling device : %s\n",(writenolabel)? + "no":"yes"); + if (!writenolabel) + printf(" Disk label : %s\n",label); +#ifdef RANGE_FORMATTING printf(" Start track : %d\n" \ ,format_params.start_unit); printf(" End track : "); @@ -407,6 +513,7 @@ do_format_dasd(char *dev_name,format_data_t format_params,int testmode, printf("last track of disk\n"); else printf("%d\n",format_params.stop_unit); +#endif /* RANGE_FORMATTING */ printf(" Blocksize : %d\n" \ ,format_params.blksize); if (testmode) printf("Test mode active, omitting ioctl.\n"); @@ -416,8 +523,8 @@ do_format_dasd(char *dev_name,format_data_t format_params,int testmode, if (!withoutprompt) { printf("\n--->> ATTENTION! <<---\n"); printf("All data in the specified range of that " \ - "device will be lost.\nType yes to continue" \ - ", no will leave the disk untouched: "); + "device will be lost.\nType \"yes\" to " \ + "continue, no will leave the disk untouched: "); fgets(inp_buffer,sizeof(inp_buffer),stdin); if (strcasecmp(inp_buffer,"yes") && strcasecmp(inp_buffer,"yes\n")) { @@ -430,13 +537,70 @@ do_format_dasd(char *dev_name,format_data_t format_params,int testmode, if ( !( (withoutprompt)&&(verbosity<1) )) printf("Formatting the device. This may take a " \ "while (get yourself a coffee).\n"); - rc=ioctl(fd,IOCTL_COMMAND,format_params); + rc=ioctl(fd,BIODASDFORMAT,format_params); if (rc) ERRMSG_EXIT(EXIT_FAILURE,"%s: the dasd driver " \ "returned with the following error " \ "message:\n%s\n",prog_name,strerror(errno)); printf("Finished formatting the device.\n"); + if (!writenolabel) { + if (verbosity>0) + printf("Retrieving disk geometry... "); + + rc=ioctl(fd,HDIO_GETGEO,&new_geometry); + if (rc) { + ERRMSG("%s: the ioctl call to get geometry " \ + "returned with the following error " \ + "message:\n%s\n",prog_name, + strerror(errno)); + goto reread; + } + + + rc=ioctl(fd,BLKGETSIZE,&new_blksize); + if (rc) { + ERRMSG("%s: the ioctl call to get blocksize " \ + "returned with the following error " \ + "message:\n%s\n",prog_name, + strerror(errno)); + goto reread; + } + + if (verbosity>0) printf("done\n"); + + label_position=new_geometry.start*new_blksize; + + if (verbosity>0) printf("Writing label... "); + convert_label(label); + rc=lseek(fd,label_position,SEEK_SET); + if (rc!=label_position) { + ERRMSG("%s: lseek on the device to %i " \ + "failed with the following error " \ + "message:\n%s\n",prog_name, + label_position,strerror(errno)); + goto reread; + } + rc=write(fd,label,LABEL_LENGTH); + if (rc!=LABEL_LENGTH) { + ERRMSG("%s: writing the label only wrote %d " \ + "bytes.\n",prog_name,rc); + goto reread; + } + + sync(); + sync(); + + if (verbosity>0) printf("done\n"); + } + reread: + printf("Rereading the partition table... "); + rc=ioctl(fd,BLKRRPART,NULL); + if (rc) { + ERRMSG("%s: error during rereading the partition " \ + "table: %s.\n",prog_name,strerror(errno)); + } else printf("done.\n"); + break; } @@ -452,11 +616,13 @@ int main(int argc,char *argv[]) { int verbosity; int testmode; int withoutprompt; + int writenolabel,labelspec; char *dev_name; int devno; char *dev_filename,*devno_param_str,*range_param_str; char *start_param_str,*end_param_str,*blksize_param_str; + char label[LABEL_LENGTH+1]; format_data_t format_params; @@ -465,23 +631,30 @@ int main(int argc,char *argv[]) { char *endptr; char c1,c2,cbuffer[6]; /* should be able to contain -end plus 1 char */ - int i1,i2; + int i,i1,i2; char *str; int start_specified,end_specified,blksize_specified; int devfile_specified,devno_specified,range_specified; /******************* initialization ********************/ + prog_name=argv[0]; endptr=NULL; /* set default values */ - format_params.start_unit=0; - format_params.stop_unit=-1; - format_params.blksize=4096; + format_params.start_unit=DASD_FORMAT_DEFAULT_START_UNIT; + format_params.stop_unit=DASD_FORMAT_DEFAULT_STOP_UNIT; + format_params.blksize=DASD_FORMAT_DEFAULT_BLOCKSIZE; + format_params.intensity=DASD_FORMAT_DEFAULT_INTENSITY; testmode=0; verbosity=0; withoutprompt=0; + writenolabel=0; + labelspec=0; + for (i=0;i<LABEL_LENGTH;i++) label[i]=' '; + label[LABEL_LENGTH]=0; + start_specified=end_specified=blksize_specified=0; devfile_specified=devno_specified=range_specified=0; @@ -490,7 +663,10 @@ int main(int argc,char *argv[]) { /* avoid error message generated by getopt */ opterr=0; - while ( (oc=getopt(argc,argv,"r:s:e:b:n:f:hty?vV")) !=EOF) { +#ifdef RANGE_FORMATTING + while ( (oc=getopt(argc,argv,"r:s:e:b:n:l:f:hLty?vV")) !=EOF) { +#endif /* RANGE_FORMATTING */ + while ( (oc=getopt(argc,argv,"b:n:l:f:hLty?vV")) !=EOF) { switch (oc) { case 'y': withoutprompt=1; @@ -515,6 +691,18 @@ int main(int argc,char *argv[]) { printf("%s version 0.99\n",prog_name); exit(0); + case 'l': + strncpy(label,optarg,LABEL_LENGTH); + if (strlen(optarg)<LABEL_LENGTH) + label[strlen(optarg)]=' '; + labelspec++; + break; + + case 'L': + writenolabel++; + break; + +#ifdef RANGE_FORMATTING case 's' : start_param_str=optarg; start_specified++; @@ -525,6 +713,12 @@ int main(int argc,char *argv[]) { end_specified++; break; + case 'r' : + range_param_str=optarg; + range_specified++; + break; +#endif /* RANGE_FORMATTING */ + case 'b' : blksize_param_str=optarg; blksize_specified++; @@ -539,10 +733,6 @@ int main(int argc,char *argv[]) { dev_filename=optarg; devfile_specified++; break; - case 'r' : - range_param_str=optarg; - range_specified++; - break; } } @@ -594,6 +784,8 @@ int main(int argc,char *argv[]) { CHECK_SPEC_MAX_ONCE(start_specified,"start track"); CHECK_SPEC_MAX_ONCE(end_specified,"end track"); CHECK_SPEC_MAX_ONCE(blksize_specified,"blocksize"); + CHECK_SPEC_MAX_ONCE(labelspec,"label"); + CHECK_SPEC_MAX_ONCE(writenolabel,"omit-label-writing flag"); if (devno_specified) PARSE_PARAM_INTO(devno,devno_param_str,16,"device number"); @@ -616,9 +808,9 @@ int main(int argc,char *argv[]) { str=check_param(CHECK_ALL,format_params); if (str!=NULL) ERRMSG_EXIT(EXIT_MISUSE,"%s: %s\n",prog_name,str); - /*************** issue the real command *****************/ + /******* issue the real command and reread part table *******/ do_format_dasd(dev_name,format_params,testmode,verbosity, - withoutprompt); + writenolabel,labelspec,label,withoutprompt,devno); /*************** cleanup ********************************/ if (strncmp(dev_name,TEMPFILENAME,TEMPFILENAMECHARS)==0) { diff --git a/arch/s390/tools/silo/Makefile b/arch/s390/tools/silo/Makefile index fb100e1b9..62b11d7da 100644 --- a/arch/s390/tools/silo/Makefile +++ b/arch/s390/tools/silo/Makefile @@ -1,13 +1,13 @@ all: silo silo.o: silo.c - $(CROSS_COMPILE)gcc -c -o silo.o -O2 silo.c + $(CC) -c -o silo.o -O2 silo.c cfg.o: cfg.c - $(CROSS_COMPILE)gcc -c -o cfg.o -O2 cfg.c + $(CC) -c -o cfg.o -O2 cfg.c silo: silo.o cfg.o - $(CROSS_COMPILE)gcc -o $@ $^ + $(CC) -o $@ $^ $(STRIP) $@ clean: diff --git a/arch/s390/tools/silo/silo.c b/arch/s390/tools/silo/silo.c index 827082f5c..9ac04ac2b 100644 --- a/arch/s390/tools/silo/silo.c +++ b/arch/s390/tools/silo/silo.c @@ -45,7 +45,7 @@ CONFIG cf_options[] = { /* from dasd.h */ #define DASD_PARTN_BITS 2 -#define BIODASDRWTB _IOWR('D',5,int) +#define BIODASDRWTB _IOWR('D',0,int) /* end */ #define SILO_CFG "/etc/silo.conf" @@ -554,7 +554,7 @@ int main (int argct, char *argv[]) { int rc = 0; - char *save; + char *save=NULL; char *tmpdir=getenv("TMPDIR"); if (tmpdir) { NTRY( save=(char*)malloc(strlen(tmpdir))); diff --git a/arch/s390/vmlinux.lds b/arch/s390/vmlinux.lds index b1b556d14..4d93bcc7a 100644 --- a/arch/s390/vmlinux.lds +++ b/arch/s390/vmlinux.lds @@ -26,6 +26,10 @@ SECTIONS __ksymtab : { *(__ksymtab) } __stop___ksymtab = .; + __start___kallsyms = .; /* All kernel symbols */ + __kallsyms : { *(__kallsyms) } + __stop___kallsyms = .; + _etext = .; /* End of text section */ .data : { /* Data */ diff --git a/arch/s390x/Makefile b/arch/s390x/Makefile new file mode 100644 index 000000000..7ea330273 --- /dev/null +++ b/arch/s390x/Makefile @@ -0,0 +1,70 @@ + +# s390/Makefile +# +# This file is included by the global makefile so that you can add your own +# architecture-specific flags and dependencies. Remember to do have actions +# for "archclean" and "archdep" for cleaning up and making dependencies for +# this architecture +# +# 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. +# +# Copyright (C) 1994 by Linus Torvalds +# + +LD=$(CROSS_COMPILE)ld -m elf64_s390 +CPP=$(CC) -E +OBJCOPY=$(CROSS_COMPILE)objcopy -O binary -R .note -R .comment -S +LDFLAGS=-e start +LINKFLAGS =-T $(TOPDIR)/arch/s390x/vmlinux.lds $(LDFLAGS) +MODFLAGS += -fpic + +CFLAGS_PIPE := -pipe +CFLAGS_NSR := -fno-strength-reduce +CFLAGS := $(CFLAGS) $(CFLAGS_PIPE) $(CFLAGS_NSR) + +HEAD := arch/s390x/kernel/head.o arch/s390x/kernel/init_task.o + +SUBDIRS := $(SUBDIRS) arch/s390x/mm arch/s390x/kernel arch/s390x/lib \ + drivers/s390 +CORE_FILES := arch/s390x/mm/mm.o arch/s390x/kernel/kernel.o $(CORE_FILES) \ + drivers/s390/io.o +LIBS := $(TOPDIR)/arch/s390x/lib/lib.a $(LIBS) $(TOPDIR)/arch/s390x/lib/lib.a + +all: image listing + +listing: vmlinux + @$(MAKEBOOT) listing + +arch/s390x/kernel: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/s390x/kernel + +arch/s390x/mm: dummy + $(MAKE) linuxsubdirs SUBDIRS=arch/s390x/mm + +drivers/s390: dummy + $(MAKE) linuxsubdirs SUBDIRS=drivers/s390 + +MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot + +MAKESILO = $(MAKE) -C arch/$(ARCH)/tools/silo + +MAKEDASDFMT = $(MAKE) -C arch/$(ARCH)/tools/dasdfmt + +silo: + @$(MAKE) -C arch/$(ARCH)/tools/silo + +dasdfmt: + @$(MAKE) -C arch/$(ARCH)/tools/dasdfmt + +image: vmlinux + @$(MAKEBOOT) image + +archclean: + @$(MAKEBOOT) clean + +archmrproper: + +archdep: + @$(MAKEBOOT) dep diff --git a/arch/s390x/boot/Makefile b/arch/s390x/boot/Makefile new file mode 100644 index 000000000..fb112b964 --- /dev/null +++ b/arch/s390x/boot/Makefile @@ -0,0 +1,37 @@ +# +# Makefile for the linux s390-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +OBJCOPY = $(CROSS_COMPILE)objcopy + +O_TARGET := + +include $(TOPDIR)/Rules.make + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o + +%.lnk: %.o + $(LD) -Ttext 0x0 -o $@ $< + +%.boot: %.lnk + $(OBJCOPY) -O binary $< $@ + +image: $(CONFIGURE) $(TOPDIR)/vmlinux \ + iplfba.boot ipleckd.boot ipldump.boot + $(OBJCOPY) -O binary $(TOPDIR)/vmlinux image + $(NM) $(TOPDIR)/vmlinux | grep -v '\(compiled\)\|\( [aU] \)\|\(\.\)\|\(LASH[RL]DI\)' | sort > $(TOPDIR)/System.map + +listing: ../../../vmlinux + $(OBJDUMP) --disassemble --disassemble-all --disassemble-zeroes --reloc $(TOPDIR)/vmlinux > listing + +dep: + +clean: + rm -f image listing iplfba.boot ipleckd.boot ipldump.boot + diff --git a/arch/s390x/boot/ipldump.S b/arch/s390x/boot/ipldump.S new file mode 100644 index 000000000..84150b5af --- /dev/null +++ b/arch/s390x/boot/ipldump.S @@ -0,0 +1,178 @@ +/* + * arch/s390/boot/ipldump.S + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * Tape dump ipl record. Put it on a tape and ipl from it and it will + * write a dump of the real storage after the ipl record on that tape. + */ + +#include <asm/setup.h> +#include <asm/lowcore.h> + +#define IPL_BS 1024 + .org 0 + .long 0x00080000,0x80000000+_start # The first 24 bytes are loaded + .long 0x07000000,0x60000001 # by ipl to addresses 0-23. + .long 0x02000000,0x20000000+IPL_BS # (a PSW and two CCWs). + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 # svc old psw + .long 0x00000000,0x00000000 # program check old psw + .long 0x00000000,0x00000000 # machine check old psw + .long 0x00000000,0x00000000 # io old psw + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x000a0000,0x00000058 # external new psw + .long 0x000a0000,0x00000060 # svc new psw + .long 0x000a0000,0x00000068 # program check new psw + .long 0x000a0000,0x00000070 # machine check new psw + .long 0x00080000,0x80000000+.Lioint # io new psw + + .org 0x100 + .globl _start +_start: + l %r1,0xb8 # load ipl subchannel number +# +# find out memory size +# + mvc 104(8),.Lpcmem0 # setup program check handler + slr %r3,%r3 + lhi %r2,1 + sll %r2,20 +.Lloop0: + l %r0,0(%r3) # test page + ar %r3,%r2 # add 1M + jnm .Lloop0 # r1 < 0x80000000 -> loop +.Lchkmem0: + n %r3,.L4malign0 # align to multiples of 4M + st %r3,.Lmemsize # store memory size +.Lmemok: + +# +# first write a tape mark +# + bras %r14,.Ltapemark +# +# write real storage to tape +# + slr %r2,%r2 # start at address 0 + bras %r14,.Lwriter # load ramdisk +# +# write another tape mark +# + bras %r14,.Ltapemark +# +# everything written, stop processor +# + lpsw .Lstopped +# +# subroutine for writing to tape +# Paramters: +# R1 = device number +# R2 = start address +# R3 = length +.Lwriter: + st %r14,.Lldret + la %r12,.Lorbread # r12 = address of orb + la %r5,.Lirb # r5 = address of irb + st %r2,.Lccwwrite+4 # initialize CCW data addresses + lctl %c6,%c6,.Lcr6 + slr %r2,%r2 +.Lldlp: + lhi %r6,3 # 3 retries +.Lssch: + ssch 0(%r12) # write chunk of IPL_BS bytes + jnz .Llderr +.Lw4end: + bras %r14,.Lwait4io + tm 8(%r5),0x82 # do we have a problem ? + jnz .Lrecov + l %r0,.Lccwwrite+4 # update CCW data addresses + ahi %r0,IPL_BS + st %r0,.Lccwwrite+4 + clr %r0,%r3 # enough ? + jl .Lldlp +.Ldone: + l %r14,.Lldret + br %r14 # r2 contains the total size +.Lrecov: + bras %r14,.Lsense # do the sensing + brct %r6,.Lssch # dec. retry count & branch + j .Llderr +.Ltapemark: + st %r14,.Lldret + la %r12,.Lorbmark # r12 = address of orb + la %r5,.Lirb # r5 = address of irb + lctl %c6,%c6,.Lcr6 + ssch 0(%r12) # write a tape mark + jnz .Llderr + bras %r14,.Lwait4io + l %r14,.Lldret + br %r14 +# +# Sense subroutine +# +.Lsense: + st %r14,.Lsnsret + la %r7,.Lorbsense + ssch 0(%r7) # start sense command + jnz .Llderr + bras %r14,.Lwait4io + l %r14,.Lsnsret + tm 8(%r5),0x82 # do we have a problem ? + jnz .Llderr + br %r14 +# +# Wait for interrupt subroutine +# +.Lwait4io: + lpsw .Lwaitpsw +.Lioint: + c %r1,0xb8 # compare subchannel number + jne .Lwait4io + tsch 0(%r5) + slr %r0,%r0 + tm 8(%r5),0x82 # do we have a problem ? + jnz .Lwtexit + tm 8(%r5),0x04 # got device end ? + jz .Lwait4io +.Lwtexit: + br %r14 +.Llderr: + lpsw .Lcrash + + .align 8 +.Lorbread: + .long 0x00000000,0x0080ff00,.Lccwwrite + .align 8 +.Lorbsense: + .long 0x00000000,0x0080ff00,.Lccwsense + .align 8 +.Lorbmark: + .long 0x00000000,0x0080ff00,.Lccwmark + .align 8 +.Lccwwrite: + .long 0x01200000+IPL_BS,0x00000000 +.Lccwsense: + .long 0x04200001,0x00000000 +.Lccwmark: + .long 0x1f200001,0x00000000 +.Lwaitpsw: + .long 0x020a0000,0x80000000+.Lioint + +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lcr6: .long 0xff000000 + .align 8 +.Lcrash:.long 0x000a0000,0x00000000 +.Lstopped: .long 0x000a0000,0x00001234 +.Lpcmem0:.long 0x00080000,0x80000000 + .Lchkmem0 +.L4malign0:.long 0xffc00000 +.Lmemsize:.long 0 +.Lldret:.long 0 +.Lsnsret: .long 0 + + .org IPL_BS + diff --git a/arch/s390x/boot/ipleckd.S b/arch/s390x/boot/ipleckd.S new file mode 100644 index 000000000..d66a8d684 --- /dev/null +++ b/arch/s390x/boot/ipleckd.S @@ -0,0 +1,303 @@ +# +# arch/s390/boot/ipleckd.S +# IPL record for 3380/3390 DASD +# +# S390 version +# Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation +# Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com> +# +# +# FIXME: should use the countarea to determine the blocksize +# FIXME: should insert zeroes into memory when filling holes +# FIXME: calculate blkpertrack from rdc data and blksize + +# change 09/20/00 removed obsolete store of ipldevice to textesegment + +# Usage of registers +# r1: ipl subchannel ( general use, dont overload without save/restore !) +# r10: +# r13: base register index to 0x0000 +# r14: callers address +# r15: temporary save register (we have no stack!) + +# storage layout: + +#include <asm/lowcore.h> + + .org 0 +.psw: .long 0x00080000,0x80000000+_start +.ccw1: .long 0x06000000,0x00001000 # Re-Read enough of bootsector to start +.ccw2: .long 0x00000000,0x00000000 # read countarea of record 1 to s/w. + + .org 0x58 +.Lextn: .long 0x000a0000,0x00000000+.Lextn +.Lsvcn: .long 0x000a0000,0x00000000+.Lsvcn +.Lprgn: .long 0x00080000,0x00000000+.Lecs +.Lmcn: .long 0x000a0000,0x00000000+.Lmcn +.Lion: .long 0x00080000,0x80000000+.Lionewaddr + + .org 0xe0 +.Llstad:.long 0x00000000,0x00000000 # sectorno + ct of bootlist + + .org 0xf0 # Lets start now... +_start: .globl _start + l %r1,__LC_SUBCHANNEL_ID # get IPL-subchannel from lowcore + st %r1,__LC_IPLDEV # keep it for reipl + stsch .Lrdcdata + oi .Lrdcdata+5,0x84 # enable ssch and multipath mode +.Lecs: xi .Lrdcdata+27,0x01 # enable concurrent sense + msch .Lrdcdata + xi .Lprgn,6 # restore Wait and d/a bit in PCnew PSW + l %r2,.Lparm + mvc 0x0(8,%r2),.Lnull # set parmarea to null + lctl %c6,%c6,.Lc6 # enable all interrupts +.Lrdc: # read device characteristics + la %r6,.Lrdcccw + st %r6,.Lorb+8 # store cp-address to orb + bras %r15,.Lssch # start I/O + oi .Llodata+1,0x80 + lh %r5,.Lcountarea+6 # init r5 from countarea + stcm %r5,3,.Lrdccw+2 # and store into rd template *FIXME* + stcm %r5,3,.Llodata+14 # and store into lodata *FIXME* +.Lbootlist: + l %r2,.Llstad + l %r3,.Lblklst + lhi %r4,1 + bras %r14,.Lreadblks +.Lloader: + l %r10,.Lblklst # r10 is index to bootlist + lhi %r5,4 # r5: skip 4 blocks = firstpage.... +.Lkloop: + clc .Lnull(8),0(%r10) # test blocklist + jz .Lchkparm # end of list? + l %r2,0(%r10) # get startblock to r2 + slr %r4,%r4 # erase r4 + icm %r4,1,7(%r10) # get blockcount + slr %r3,%r3 # get address to r3 + icm %r3,0xe,4(%r10) + chi %r5,0 # still blocks to skip? + jz .Ldoread # no: start reading + cr %r5,%r4 # #skipblocks >= blockct? + jm .L007 # no: skip the blocks one by one +.L006: + sr %r5,%r4 # decrease number of blocks to skip + j .Lkcont # advance to next entry +.L007: + ahi %r2,1 # skip 1 block... + bctr %r4,0 # update blockct + ah %r3,.Lcountarea+6 # increment address + bct %r5,.L007 # 4 blocks skipped? +.Ldoread: + ltr %r2,%r2 # test startblock + jz .Lzeroes # startblocks is zero (hole) +.Ldiskread: + bras %r14,.Lreadblks + j .Lkcont +.Lzeroes: + lr %r2,%r3 +.L001: slr %r3,%r3 + icm %r3,3,.Lcountarea+6 # get blocksize + slr %r5,%r5 # no bytes to move +.L008: mvcle %r2,%r4,0 # fill zeroes to storage + jo .L008 # until block is filled + brct %r4,.L001 # skip to next block +.Lkcont: + ahi %r10,8 + j .Lkloop +.Lchkparm: + lm %r3,%r4,.Lstart # load .Lstart and .Lparm + clc 0x0(4,%r4),.Lnull + je .Lrunkern + mvc 0x480(128,%r3),0(%r4) # move 1k-0x80 to parmarea + mvc 0x500(256,%r3),0x80(%r4) + mvc 0x600(256,%r3),0x180(%r4) + mvc 0x700(256,%r3),0x280(%r4) +.Lrunkern: +# lhi %r2,17 +# sll %r2,12 +# st %r1,0xc6c(%r2) # store iplsubchannel to lowcore +# st %r1,0xc6c # store iplsubchannel to lowcore + br %r3 +# This function does the start IO +# r2: number of first block to read ( input by caller ) +# r3: address to read data to ( input by caller ) +# r4: number of blocks to read ( input by caller ) +# r5: destroyed +# r6: blocks per track ( input by caller ) +# r7: number of heads +# r8: +# r9: +# r10: +# r11: temporary register +# r12: local use for base address +# r13: base address for module +# r14: address of caller for subroutine +# r15: temporary save register (since we have no stack) +.Lreadblks: + la %r12,.Ldeccw + st %r12,8+.Lorb # store cpaddr to orb + ahi %r12,0x10 # increment r12 to point to rdccw + oi 1(%r12),0x40 # set CC in rd template + # first setup the read CCWs + lr %r15,%r4 # save number or blocks + slr %r7,%r7 + icm %r7,3,.Lrdcdata+14 # load heads to r7 + lhi %r6,9 + clc .Lrdcdata+3(2),.L9345 + je .L011 + lhi %r6,10 + clc .Lrdcdata+3(2),.L3380 + je .L011 + lhi %r6,12 + clc .Lrdcdata+3(2),.L3390 + je .L011 + bras %r14,.Ldisab +.L011: + # loop for nbl times +.Lrdloop: + mvc 0(8,%r12),.Lrdccw # copy template to this ccw + st %r3,4(%r12) # store target address to this ccw + bct %r4,.L005 # decrement no of blks still to do + ni 1(%r12),0x3f # delete CC from last ccw + lr %r4,%r15 # restore number of blocks + # read CCWs are setup now + stcm %r4,3,.Llodata+2 # store blockno to lodata clears r4 + ar %r4,%r2 # r4 (clear): ebl = blk + nbl + bctr %r4,0 # decrement r4 ( last blk touched + srda %r2,32 # trk = blk / bpt, bot = blk % bpt + dr %r2,%r6 # r3: trk, r2: bot + ahi %r2,1 # bot++ ( we start counting at 1 ) + stcm %r2,1,.Llodata+12 # store bot to lodata + xr %r2,%r2 # cy = trk / heads, hd = trk % heads + dr %r2,%r7 # r3: cy, r2: hd + sll %r3,16 # combine to CCHH in r3 + or %r3,%r2 + st %r3,.Ldedata+8 # store cchh to dedata + st %r3,.Llodata+4 # store cchh to lodata + st %r3,.Llodata+8 # store cchh to lodata + lr %r15,%r5 # save r5 + srda %r4,32 # tr2 = ebl / bpt + dr %r4,%r6 # r5: tr2, r4: bot2 + xr %r4,%r4 # cy2 = tr2 / heads, hd2 = hd2 % heads + dr %r4,%r7 # r5: cy2, r4: hd2 + stcm %r5,3,.Ldedata+12 # store cy2,hd2 to dedata + stcm %r4,3,.Ldedata+14 # store cy2,hd2 to dedata + lr %r5,%r15 # restore r5 + # CCWs are setup now, arent they? + bras %r15,.Lssch # start I/O + br %r14 # return to caller +.L005: + ah %r3,.Lcountarea+6 # add blocksize to target address + ahi %r12,8 # add sizeof(ccw) to base address + j .Lrdloop +# end of function +# This function does the start IO +# r1: Subchannel number +# r8: ORB address +# r9: IRB address +.Lssch: + lhi %r13,10 # initialize retries +.L012: + ssch .Lorb # start I/O + jz .Ltpi # ok? + bras %r14,.Ldisab # error +.Ltpi: + lpsw .Lwaitpsw # load wait-PSW +.Lionewaddr: + c %r1,0xb8 # compare to ipl subhchannel + jnz .Ltpi # not equal: loop + clc 0xbc(4),.Lorb # cross check the intparm + jnz .Ltpi # not equal: loop + tsch .Lirb # get status + tm .Lirb+9,0xff # channel status ? + jz .L003 # CS == 0x00 + bras %r14,.Ldisab # error +.L003: + tm .Lirb+8,0xf3 # DS different from CE/DE + jz .L004 # ok ? + bct %r13,.L012 # retries <= 5 ? + bras %r14,.Ldisab # error +.L004: + tm .Lirb+8,0x04 # DE set? + jz .Ltpi # DE not set, loop +.Lsschend: + br %r15 # return to caller +# end of function +# In case of error goto disabled wait with %r14 containing the caller +.Ldisab: + st %r14,.Ldisabpsw+4 + lpsw .Ldisabpsw + +# FIXME pre-initialized data should be listed first +# NULLed storage can be taken from anywhere ;) +.Lblklst: + .long 0x00002000 + .align 8 +.Ldisabpsw: + .long 0x000a0000,0x00000000 +.Lwaitpsw: + .long 0x020a0000,0x00000000+.Ltpi +.Lorb: + .long 0x0049504c,0x0080ff00 # intparm is " IPL" +.Lc6: .long 0xff000000 +.Lstart: + .long 0x00010000 # do not separate .Lstart and .Lparm +.Lparm: + .long 0x00008000 # they are loaded with a LM +.L3390: + .word 0x3390 +.L9345: + .word 0x9345 +.L3380: + .word 0x3380 +.Lnull: + .long 0x00000000,0x00000000 + .align 4 +.Lrdcdata: + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Lirb: + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Lcountarea: + .word 0x0000 # cyl; + .word 0x0000 # head; + .byte 0x00 # record; + .byte 0x00 # key length; + .word 0x0000 # data length == blocksize; +.Ldedata: + .long 0x40c00000,0x00000000 + .long 0x00000000,0x00000000 +.Llodata: + .long 0x06000001,0x00000000 + .long 0x00000000,0x01000000 + .long 0x12345678 + .org 0x7c8 +.Lrdcccw: # CCW read device characteristics + .long 0x64400040,0x00000000+.Lrdcdata + .long 0x63400010,0x00000000+.Ldedata + .long 0x47400010,0x00000000+.Llodata + .long 0x12000008,0x00000000+.Lcountarea +.Ldeccw: + .long 0x63400010,0x00000000+.Ldedata +.Lloccw: + .long 0x47400010,0x00000000+.Llodata +.Lrdccw: + .long 0x86400000,0x00000000 + .org 0x800 +# end of pre initialized data is here CCWarea follows +# from here we load 1k blocklist +# end of function + diff --git a/arch/s390x/boot/iplfba.S b/arch/s390x/boot/iplfba.S new file mode 100644 index 000000000..732a9848c --- /dev/null +++ b/arch/s390x/boot/iplfba.S @@ -0,0 +1,131 @@ +# +# Ipl block for fba devices +# Copyright (C) 1998 IBM Corporation +# Author(s): Martin Schwidefsky +# +# startup for ipl at address 0 +# start with restart + +# The first 24 byes are loaded by ipl to addresses 0-23 (a PSW and two CCWs). +# The CCWs on 8-23 are used as a continuation of the implicit ipl channel +# program. The fba ipl loader only uses the CCW on 8-15 to load the first 512 +# byte block to location 0-511 (the reading starts again at block 0, byte 0). +# The second CCW is used to store the location of the load list. + .org 0 + .long 0x00080000,0x80000000+_start # The first 24 byte are loaded + .long 0x02000000,0x20000200 # by ipl to addresses 0-23. + .long 0x00000001,0x00000001 # (PSW, one CCW & loadlist info). + + .globl _start +_start: + basr %r13,0 +.LPG0: + l %r1,0xb8 # load ipl subchannel number + lhi %r2,0x200 # location for the loadlist + lm %r3,%r4,0x10 # blocknr and length of loadlist + bras %r14,.Lloader # load loadlist + + lhi %r11,0x400 + lhi %r12,0x200 # load address of loadlist + l %r3,0(%r12) # get first block number + l %r4,4(%r12) # get first block count + la %r12,8(%r12) + j .Llistloop + .org 0x50 +.Llistloop: + lr %r2,%r11 # load address + lr %r5,%r4 # block count + mhi %r5,512 + la %r11,0(%r5,%r11) # update load address + bras %r14,.Lloader # load chunk of the image + l %r3,0(%r12) # get next block number + icm %r4,15,4(%r12) # get next block count + la %r12,8(%r12) + jnz .Llistloop + +# +# everything loaded, go for it +# + l %r1,.Lstart-.LPG0(%r13) + br %r1 + +# +# subroutine for loading a sequence of block from fba +# %r2: load address (24 bit address) +# %r3: number of first block (unsigned long) +# %r4: number of blocks to load (unsigned short) +# + .org 0xC0 +.Lloader: + la %r5,.Llo-.LPG0(%r13) + sth %r4,2(%r5) # initialize block count + st %r3,4(%r5) # initialize block number + la %r5,.Lccws-.LPG0(%r13) + mhi %r4,512 + sth %r4,22(%r5) # initialize byte count + icm %r2,8,16(%r5) + st %r2,16(%r5) # initialize CCW data address + + slr %r2,%r2 + la %r3,.Lorb-.LPG0(%r13) # r2 = address of orb into r2 + la %r4,.Ltinfo-.LPG0(%r13) # r3 = address of tpi info block + la %r5,.Lirb-.LPG0(%r13) # r4 = address of irb + + lctl %c6,%c6,.Lc6-.LPG0(%r13) +.Lldlp: + ssch 0(%r3) # read blocks +.Ltpi: + tpi 0(%r4) # test pending interrupt + jz .Ltpi + c %r1,0(%r4) # compare subchannel number + jne .Ltpi + tsch 0(%r5) + slr %r0,%r0 + tm 8(%r5),0x82 # do we have a problem ? + jnz .Ldwpsw + tm 8(%r5),0x04 # got device end ? + jz .Ltpi +.Lexit: + br %r14 + + .align 8 +.Ldwpsw:.long 0x000a0000,0x00000000 +.Lorb: .long 0x00000000,0x0000ff00,.Lccws +.Ltinfo:.long 0 +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lc6: .long 0xff000000 +.Lloadp:.long 0,0 +.Lparm: .long 0x10400 +.Lstart:.long 0x10000 + .align 8 +.Lccws: .long 0x63000000+.Lde,0x60000010 # define extent + .long 0x43000000+.Llo,0x60000008 # locate +# offset 1 in read CCW: data address (24 bit) +# offset 6 in read CCW: number of bytes (16 bit) + .long 0x42000000,0x20000000 # read +.Lde: .long 0x40000200,0x00000000 + .long 0x00000000,0x00001000 +# offset 2 in .Llo: block count (unsigned short) +# offset 4 in .Llo: block number (unsigned long) +.Llo: .long 0x06000000,0x00000000 + + .org 0x200 + .long 0x00000002,0x0000007f + .long 0x00000081,0x0000007f + .long 0x00000100,0x0000007f + .long 0x0000017f,0x0000007f + .long 0x000001fe,0x0000007f + .long 0x0000027d,0x0000007f + .long 0x000002fc,0x0000007f + .long 0x0000037b,0x0000007f + .long 0x000003fa,0x0000007f + .long 0x00000479,0x0000007f + .long 0x000004f8,0x0000007f + .long 0x00000577,0x0000007f + .long 0x000005f6,0x0000007f + .long 0x00000675,0x0000007f + .long 0x000006f4,0x0000007f + .long 0x00000773,0x0000003f + .long 0x00000000,0x00000000 + .org 0x400 + diff --git a/arch/s390x/config.in b/arch/s390x/config.in new file mode 100644 index 000000000..a94f4b7ba --- /dev/null +++ b/arch/s390x/config.in @@ -0,0 +1,73 @@ +# +# For a description of the syntax of this configuration file, +# see Documentation/kbuild/config-language.txt. +# + +define_bool CONFIG_ISA n +define_bool CONFIG_EISA n +define_bool CONFIG_MCA n + +mainmenu_name "Linux Kernel Configuration" +define_bool CONFIG_ARCH_S390 y +define_bool CONFIG_ARCH_S390X y + +mainmenu_option next_comment +comment 'Code maturity level options' +bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL +endmenu + +mainmenu_option next_comment +comment 'Processor type and features' +bool 'Symmetric multi-processing support' CONFIG_SMP +bool 'Kernel support for 31 bit emulation' CONFIG_S390_SUPPORT +if [ "$CONFIG_S390_SUPPORT" = "y" ]; then + tristate 'Kernel support for 31 bit ELF binaries' CONFIG_BINFMT_ELF32 +fi +endmenu + +mainmenu_option next_comment +comment 'Loadable module support' +bool 'Enable loadable module support' CONFIG_MODULES +if [ "$CONFIG_MODULES" = "y" ]; then + bool 'Set version information on all symbols for modules' CONFIG_MODVERSIONS + bool 'Kernel module loader' CONFIG_KMOD +fi +endmenu + +mainmenu_option next_comment +comment 'General setup' +bool 'Fast IRQ handling' CONFIG_FAST_IRQ +bool 'Builtin IPL record support' CONFIG_IPL +if [ "$CONFIG_IPL" = "y" ]; then + choice 'IPL method generated into head.S' \ + "tape CONFIG_IPL_TAPE \ + vm_reader CONFIG_IPL_VM" tape +fi +bool 'Networking support' CONFIG_NET +bool 'System V IPC' CONFIG_SYSVIPC +bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT +bool 'Sysctl support' CONFIG_SYSCTL +define CONFIG_KCORE ELF +tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF +tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC +bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG +endmenu + +source drivers/s390/Config.in + +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + +source fs/Config.in + +mainmenu_option next_comment +comment 'Kernel hacking' + +#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC +if [ "$CONFIG_CTC" = "y" ]; then + bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG +fi +# this does not work. bool 'Magic SysRq key' CONFIG_MAGIC_SYSRQ +endmenu + diff --git a/arch/s390x/defconfig b/arch/s390x/defconfig new file mode 100644 index 000000000..391f42823 --- /dev/null +++ b/arch/s390x/defconfig @@ -0,0 +1,219 @@ +# +# Automatically generated by make menuconfig: don't edit +# +# CONFIG_ISA is not set +# CONFIG_EISA is not set +# CONFIG_MCA is not set +CONFIG_ARCH_S390=y +CONFIG_ARCH_S390X=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y + +# +# Processor type and features +# +CONFIG_SMP=y +CONFIG_S390_SUPPORT=y +CONFIG_BINFMT_ELF32=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# General setup +# +CONFIG_FAST_IRQ=y +CONFIG_IPL=y +# CONFIG_IPL_TAPE is not set +CONFIG_IPL_VM=y +CONFIG_NET=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_PROCESS_DEBUG is not set + +# +# Block device drivers +# +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=24576 +CONFIG_BLK_DEV_INITRD=y +CONFIG_BLK_DEV_XPRAM=m +CONFIG_DASD=y +CONFIG_DASD_ECKD=y +CONFIG_DASD_FBA=y + +# +# Multi-device support (RAID and LVM) +# +CONFIG_MD=y +CONFIG_BLK_DEV_MD=m +# CONFIG_MD_LINEAR is not set +CONFIG_MD_RAID0=m +CONFIG_MD_RAID1=m +CONFIG_MD_RAID5=m +CONFIG_BLK_DEV_LVM=m +CONFIG_LVM_PROC_FS=y + +# +# Character device drivers +# +CONFIG_UNIX98_PTYS=y +CONFIG_UNIX98_PTY_COUNT=256 +CONFIG_3215=y +CONFIG_3215_CONSOLE=y +CONFIG_HWC=y +CONFIG_HWC_CONSOLE=y +CONFIG_S390_TAPE=m +CONFIG_S390_TAPE_CHAR=y +CONFIG_S390_TAPE_BLOCK=y +CONFIG_S390_TAPE_3490=y +CONFIG_S390_TAPE_3480=y + +# +# Network device drivers +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +CONFIG_NET_ETHERNET=y +CONFIG_TR=y +# CONFIG_FDDI is not set +# CONFIG_CHANDEV is not set +CONFIG_CTC=m +CONFIG_IUCV=m + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_NETLINK=y +# CONFIG_RTNETLINK is not set +# CONFIG_NETLINK_DEV is not set +# CONFIG_NETFILTER is not set +# CONFIG_FILTER is not set +CONFIG_UNIX=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_IPV6 is not set +# CONFIG_KHTTPD is not set +# CONFIG_ATM is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_LLC is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# File systems +# +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_ADFS_FS is not set +# CONFIG_ADFS_FS_RW is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_FAT_FS is not set +# CONFIG_MSDOS_FS is not set +# CONFIG_UMSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_RAMFS is not set +# CONFIG_ISO9660_FS is not set +# CONFIG_JOLIET is not set +# CONFIG_MINIX_FS is not set +# CONFIG_NTFS_FS is not set +# CONFIG_NTFS_RW is not set +# CONFIG_HPFS_FS is not set +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVFS_MOUNT is not set +# CONFIG_DEVFS_DEBUG is not set +# CONFIG_DEVPTS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_QNX4FS_RW is not set +# CONFIG_ROMFS_FS is not set +CONFIG_EXT2_FS=y +# CONFIG_SYSV_FS is not set +# CONFIG_SYSV_FS_WRITE is not set +# CONFIG_UDF_FS is not set +# CONFIG_UDF_RW is not set +# CONFIG_UFS_FS is not set +# CONFIG_UFS_FS_WRITE is not set + +# +# Network File Systems +# +# CONFIG_CODA_FS is not set +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_ROOT_NFS is not set +# CONFIG_NFSD is not set +# CONFIG_NFSD_V3 is not set +CONFIG_SUNRPC=y +CONFIG_LOCKD=y +# CONFIG_SMB_FS is not set +# CONFIG_NCP_FS is not set +# CONFIG_NCPFS_PACKET_SIGNING is not set +# CONFIG_NCPFS_IOCTL_LOCKING is not set +# CONFIG_NCPFS_STRONG is not set +# CONFIG_NCPFS_NFS_NS is not set +# CONFIG_NCPFS_OS2_NS is not set +# CONFIG_NCPFS_SMALLDOS is not set +# CONFIG_NCPFS_MOUNT_SUBDIR is not set +# CONFIG_NCPFS_NDS_DOMAINS is not set +# CONFIG_NCPFS_NLS is not set +# CONFIG_NCPFS_EXTRAS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +CONFIG_IBM_PARTITION=y +# CONFIG_MAC_PARTITION is not set +# CONFIG_MSDOS_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_NLS is not set + +# +# Kernel hacking +# diff --git a/arch/s390x/kernel/Makefile b/arch/s390x/kernel/Makefile new file mode 100644 index 000000000..21f353f67 --- /dev/null +++ b/arch/s390x/kernel/Makefile @@ -0,0 +1,38 @@ +# +# Makefile for the linux kernel. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o + +all: kernel.o head.o init_task.o + +O_TARGET := kernel.o + +export-objs := s390_ksyms.o +obj-y := lowcore.o entry.o bitmap.o traps.o time.o process.o irq.o \ + setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ + semaphore.o s390fpu.o reipl.o s390_ext.o debug.o + +obj-$(CONFIG_MODULES) += s390_ksyms.o +obj-$(CONFIG_SMP) += smp.o + +# +# Kernel debugging +# +obj-$(CONFIG_REMOTE_DEBUG) += gdb-stub.o #gdb-low.o + +obj-$(CONFIG_S390_SUPPORT) += linux32.o signal32.o ioctl32.o wrapper32.o exec32.o +obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o + +# +# This is just to get the dependencies... +# +binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c + +include $(TOPDIR)/Rules.make diff --git a/arch/s390x/kernel/binfmt_elf32.c b/arch/s390x/kernel/binfmt_elf32.c new file mode 100644 index 000000000..b08f0f686 --- /dev/null +++ b/arch/s390x/kernel/binfmt_elf32.c @@ -0,0 +1,203 @@ +/* + * Support for 32-bit Linux for S390 ELF binaries. + * + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Gerhard Tonn (ton@de.ibm.com) + * + * Heavily inspired by the 32-bit Sparc compat code which is + * Copyright (C) 1995, 1996, 1997, 1998 David S. Miller (davem@redhat.com) + * Copyright (C) 1995, 1996, 1997, 1998 Jakub Jelinek (jj@ultra.linux.cz) + */ + + +#define __ASMS390_ELF_H + +/* + * These are used to set parameters in the core dumps. + */ +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_S390 + +/* + * This is used to ensure we don't load something for the wrong architecture. + */ +#define elf_check_arch(x) \ + ((x)->e_machine == ELF_ARCH && (x)->e_ident[EI_CLASS] == ELF_CLASS) + +/* ELF register definitions */ +#define NUM_GPRS 16 +#define NUM_FPRS 16 +#define NUM_ACRS 16 + +#define TASK31_SIZE (0x80000000UL) + +/* For SVR4/S390 the function pointer to be registered with `atexit` is + passed in R14. */ +#define ELF_PLAT_INIT(_r) \ + do { \ + _r->gprs[14] = 0; \ + current->thread.flags |= S390_FLAG_31BIT; \ + } while(0) + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +/* This is the location that an ET_DYN program is loaded if exec'ed. Typical + use of this is to invoke "./ld.so someprog" to test out a new version of + the loader. We need to make sure that it is out of the way of the program + that it will "exec", and that there is sufficient room for the brk. */ + +#define ELF_ET_DYN_BASE ((TASK31_SIZE & 0x80000000) \ + ? TASK31_SIZE / 3 * 2 \ + : 2 * TASK31_SIZE / 3) + +/* Wow, the "main" arch needs arch dependent functions too.. :) */ + +/* regs is struct pt_regs, pr_reg is elf_gregset_t (which is + now struct_user_regs, they are different) */ + +#define ELF_CORE_COPY_REGS(pr_reg, regs) \ + { \ + int i; \ + memcpy(&pr_reg.psw.mask, ®s->psw.mask, 4); \ + memcpy(&pr_reg.psw.addr, ((char*)®s->psw.addr)+4, 4); \ + for(i=0; i<NUM_GPRS; i++) \ + pr_reg.gprs[i] = regs->gprs[i]; \ + for(i=0; i<NUM_ACRS; i++) \ + pr_reg.acrs[i] = regs->acrs[i]; \ + pr_reg.orig_gpr2 = regs->orig_gpr2; \ + } + + + +/* This yields a mask that user programs can use to figure out what + instruction set this CPU supports. */ + +#define ELF_HWCAP (0) + +/* This yields a string that ld.so will use to load implementation + specific libraries for optimization. This is more specific in + intent than poking at uname or /proc/cpuinfo. + + For the moment, we have only optimizations for the Intel generations, + but that could change... */ + +#define ELF_PLATFORM (NULL) + +#ifdef __KERNEL__ +#define SET_PERSONALITY(ex, ibcs2) set_personality((ibcs2)?PER_SVR4:PER_LINUX) +#endif + +#include "linux32.h" + +typedef _s390_fp_regs32 elf_fpregset_t; + +typedef struct +{ + + _psw_t32 psw; + __u32 gprs[__NUM_GPRS]; + __u32 acrs[__NUM_ACRS]; + __u32 orig_gpr2; +} s390_regs32; +typedef s390_regs32 elf_gregset_t; + +#include <asm/processor.h> +#include <linux/module.h> +#include <linux/config.h> +#include <linux/elfcore.h> + +int setup_arg_pages32(struct linux_binprm *bprm); + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +#define elf_prstatus elf_prstatus32 +struct elf_prstatus32 +{ + struct elf_siginfo pr_info; /* Info associated with signal */ + short pr_cursig; /* Current signal */ + u32 pr_sigpend; /* Set of pending signals */ + u32 pr_sighold; /* Set of held signals */ + pid_t pr_pid; + pid_t pr_ppid; + pid_t pr_pgrp; + pid_t pr_sid; + struct timeval32 pr_utime; /* User time */ + struct timeval32 pr_stime; /* System time */ + struct timeval32 pr_cutime; /* Cumulative user time */ + struct timeval32 pr_cstime; /* Cumulative system time */ + elf_gregset_t pr_reg; /* GP registers */ + int pr_fpvalid; /* True if math co-processor being used. */ +}; + +#define elf_prpsinfo elf_prpsinfo32 +struct elf_prpsinfo32 +{ + char pr_state; /* numeric process state */ + char pr_sname; /* char for pr_state */ + char pr_zomb; /* zombie */ + char pr_nice; /* nice val */ + u32 pr_flag; /* flags */ + u16 pr_uid; + u16 pr_gid; + pid_t pr_pid, pr_ppid, pr_pgrp, pr_sid; + /* Lots missing */ + char pr_fname[16]; /* filename of executable */ + char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ +}; + +#include <linux/highuid.h> + +#undef NEW_TO_OLD_UID +#undef NEW_TO_OLD_GID +#define NEW_TO_OLD_UID(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) +#define NEW_TO_OLD_GID(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) + +#define elf_addr_t u32 +#define elf_caddr_t u32 +/* +#define init_elf_binfmt init_elf32_binfmt +*/ +#undef CONFIG_BINFMT_ELF +#ifdef CONFIG_BINFMT_ELF32 +#define CONFIG_BINFMT_ELF CONFIG_BINFMT_ELF32 +#endif +#undef CONFIG_BINFMT_ELF_MODULE +#ifdef CONFIG_BINFMT_ELF32_MODULE +#define CONFIG_BINFMT_ELF_MODULE CONFIG_BINFMT_ELF32_MODULE +#endif + +#undef start_thread +#define start_thread start_thread31 +#define setup_arg_pages(bprm) setup_arg_pages32(bprm) +#define elf_map elf_map32 + +MODULE_DESCRIPTION("Binary format loader for compatibility with 32bit Linux for S390 binaries," + " Copyright 2000 IBM Corporation"); +MODULE_AUTHOR("Gerhard Tonn <ton@de.ibm.com>"); + +#undef MODULE_DESCRIPTION +#undef MODULE_AUTHOR + +#include "../../../fs/binfmt_elf.c" + +static unsigned long +elf_map32 (struct file *filep, unsigned long addr, struct elf_phdr *eppnt, int prot, int type) +{ + unsigned long map_addr; + + if(!addr) + addr = 0x40000000; + + down(¤t->mm->mmap_sem); + map_addr = do_mmap(filep, ELF_PAGESTART(addr), + eppnt->p_filesz + ELF_PAGEOFFSET(eppnt->p_vaddr), prot, type, + eppnt->p_offset - ELF_PAGEOFFSET(eppnt->p_vaddr)); + up(¤t->mm->mmap_sem); + return(map_addr); +} + diff --git a/arch/s390x/kernel/bitmap.S b/arch/s390x/kernel/bitmap.S new file mode 100644 index 000000000..a212ba450 --- /dev/null +++ b/arch/s390x/kernel/bitmap.S @@ -0,0 +1,37 @@ +/* + * arch/s390/kernel/bitmap.S + * Bitmaps for set_bit, clear_bit, test_and_set_bit, ... + * See include/asm-s390/{bitops.h|posix_types.h} for details + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + + .globl _oi_bitmap +_oi_bitmap: + .byte 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80 + + .globl _ni_bitmap +_ni_bitmap: + .byte 0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F + + .globl _zb_findmap +_zb_findmap: + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4 + .byte 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8 + diff --git a/arch/s390x/kernel/cpcmd.c b/arch/s390x/kernel/cpcmd.c new file mode 100644 index 000000000..7329b9b6c --- /dev/null +++ b/arch/s390x/kernel/cpcmd.c @@ -0,0 +1,49 @@ +/* + * arch/s390/kernel/cpcmd.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <asm/ebcdic.h> + +void cpcmd(char *cmd, char *response, int rlen) +{ + const int mask = 0x40000000L; + char obuffer[128]; + int olen; + + olen = strlen(cmd); + strcpy(obuffer, cmd); + ASCEBC(obuffer,olen); + + if (response != NULL && rlen > 0) { + asm volatile (" lrag 2,0(%0)\n" + " lgr 4,%1\n" + " o 4,%4\n" + " lrag 3,0(%2)\n" + " lgr 5,%3\n" + " sam31\n" + " .long 0x83240008 # Diagnose 83\n" + " sam64" + : /* no output */ + : "a" (obuffer), "d" (olen), + "a" (response), "d" (rlen), "m" (mask) + : "2", "3", "4", "5" ); + EBCASC(response, rlen); + } else { + asm volatile (" lrag 2,0(%0)\n" + " lgr 3,%1\n" + " sam31\n" + " .long 0x83230008 # Diagnose 83\n" + " sam64" + : /* no output */ + : "a" (obuffer), "d" (olen) + : "2", "3" ); + } +} + diff --git a/arch/s390x/kernel/cpcmd.h b/arch/s390x/kernel/cpcmd.h new file mode 100644 index 000000000..38b88d9ea --- /dev/null +++ b/arch/s390x/kernel/cpcmd.h @@ -0,0 +1,14 @@ +/* + * arch/s390/kernel/cpcmd.h + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#ifndef __CPCMD__ +#define __CPCMD__ + +extern void cpcmd(char *cmd, char *response, int rlen); + +#endif diff --git a/arch/s390x/kernel/cpprintk.c b/arch/s390x/kernel/cpprintk.c new file mode 100644 index 000000000..aa6d3b41e --- /dev/null +++ b/arch/s390x/kernel/cpprintk.c @@ -0,0 +1,25 @@ +#include "cpcmd.h" +#include <linux/mm.h> +#include <linux/tty_driver.h> +#include <linux/smp_lock.h> +#include <linux/console.h> +#include <linux/init.h> + +#include <asm/uaccess.h> + +static char buf[1024]; + +asmlinkage int s390printk(const char *fmt, ...) +{ + va_list args; + int i; + unsigned long flags; + spin_lock_irqsave(&console_lock, flags); + va_start(args, fmt); + i = vsprintf(&buf[0],"MSG * ",args); + i = vsprintf(&buf[i], fmt, args); + va_end(args); + cpcmd(buf,0,0); + spin_unlock_irqrestore(&console_lock, flags); + return i; +} diff --git a/arch/s390x/kernel/debug.c b/arch/s390x/kernel/debug.c new file mode 100644 index 000000000..bb3dfe5de --- /dev/null +++ b/arch/s390x/kernel/debug.c @@ -0,0 +1,1167 @@ +/* + * arch/s390/kernel/debug.c + * S/390 debug facility + * + * Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH, + * IBM Corporation + * Author(s): Michael Holzheu (holzheu@de.ibm.com), + * Holger Smolinski (Holger.Smolinski@de.ibm.com) + * + * Bugreports to: <Linux390@de.ibm.com> + */ + +#include <linux/config.h> +#include <linux/stddef.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/malloc.h> +#include <linux/vmalloc.h> +#include <linux/ctype.h> +#include <linux/version.h> +#include <asm/uaccess.h> +#include <asm/semaphore.h> + +#ifdef MODULE +#include <linux/module.h> +#endif + +#include <asm/debug.h> + +#define MIN(a,b) (((a)<(b))?(a):(b)) + +#if defined(CONFIG_ARCH_S390X) +#define DEBUG_PROC_HEADER_SIZE 46 +#else +#define DEBUG_PROC_HEADER_SIZE 38 +#endif + +#define ADD_BUFFER 1000 + +/* typedefs */ + +typedef struct file_private_info { + loff_t len; /* length of output in byte */ + int size; /* size of buffer for output */ + char *data; /* buffer for output */ + debug_info_t *debug_info; /* the debug information struct */ + struct debug_view *view; /* used view of debug info */ +} file_private_info_t; + +extern void tod_to_timeval(uint64_t todval, struct timeval *xtime); + +/* internal function prototyes */ + +static int debug_init(void); +static int debug_format_output(debug_info_t * debug_area, char *buf, + int size, struct debug_view *view); +static ssize_t debug_output(struct file *file, char *user_buf, + size_t user_len, loff_t * offset); +static ssize_t debug_input(struct file *file, const char *user_buf, + size_t user_len, loff_t * offset); +static int debug_open(struct inode *inode, struct file *file); +static int debug_close(struct inode *inode, struct file *file); +static struct proc_dir_entry +*debug_create_proc_dir_entry(struct proc_dir_entry *root, + const char *name, mode_t mode, + struct inode_operations *iops, + struct file_operations *fops); +static void debug_delete_proc_dir_entry(struct proc_dir_entry *root, + struct proc_dir_entry *entry); +static debug_info_t* debug_info_create(char *name, int page_order, int nr_areas, int buf_size); +static void debug_info_get(debug_info_t *); +static void debug_info_put(debug_info_t *); +static int debug_prolog_level_fn(debug_info_t * id, + struct debug_view *view, char *out_buf); +static int debug_input_level_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char *user_buf, + size_t user_buf_size, loff_t * offset); +static int debug_hex_ascii_format_fn(debug_info_t * id, struct debug_view *view, + char *out_buf, const char *in_buf); +static int debug_raw_format_fn(debug_info_t * id, + struct debug_view *view, char *out_buf, + const char *in_buf); +static int debug_raw_header_fn(debug_info_t * id, struct debug_view *view, + int area, debug_entry_t * entry, char *out_buf); + +/* globals */ + +struct debug_view debug_raw_view = { + "raw", + NULL, + &debug_raw_header_fn, + &debug_raw_format_fn, + NULL +}; + +struct debug_view debug_hex_ascii_view = { + "hex_ascii", + NULL, + &debug_dflt_header_fn, + &debug_hex_ascii_format_fn, + NULL +}; + +struct debug_view debug_level_view = { + "level", + &debug_prolog_level_fn, + NULL, + NULL, + &debug_input_level_fn +}; + +/* static globals */ + +static debug_info_t *debug_area_first = NULL; +static debug_info_t *debug_area_last = NULL; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) +static struct semaphore debug_lock = MUTEX; +#else +DECLARE_MUTEX(debug_lock); +#endif + +static int initialized = 0; + +static struct file_operations debug_file_ops = { + read: debug_output, + write: debug_input, + open: debug_open, + release: debug_close, +}; + +static struct inode_operations debug_inode_ops = { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) + default_file_ops: &debug_file_ops, /* file ops */ +#endif +}; + + +static struct proc_dir_entry *debug_proc_root_entry; + + +/* functions */ + +/* + * debug_info_create + * - create new debug-info + */ + +static debug_info_t* debug_info_create(char *name, int page_order, + int nr_areas, int buf_size) +{ + debug_info_t* rc; + int i; + + /* alloc everything */ + + rc = (debug_info_t*) kmalloc(sizeof(debug_info_t), GFP_ATOMIC); + if(!rc) + goto fail_malloc_rc; + rc->active_entry = (int*)kmalloc(nr_areas * sizeof(int), GFP_ATOMIC); + if(!rc->active_entry) + goto fail_malloc_active_entry; + memset(rc->active_entry, 0, nr_areas * sizeof(int)); + rc->areas = (debug_entry_t **) kmalloc(nr_areas * + sizeof(debug_entry_t *), + GFP_ATOMIC); + if (!rc->areas) + goto fail_malloc_areas; + for (i = 0; i < nr_areas; i++) { + rc->areas[i] = + (debug_entry_t *) __get_free_pages(GFP_ATOMIC, + page_order); + if (!rc->areas[i]) { + for (i--; i >= 0; i--) { + free_pages((unsigned long) rc->areas[i], + page_order); + } + goto fail_malloc_areas2; + } else { + memset(rc->areas[i], 0, PAGE_SIZE << page_order); + } + } + + /* initialize members */ + + spin_lock_init(&rc->lock); + rc->page_order = page_order; + rc->nr_areas = nr_areas; + rc->active_area = 0; + rc->level = DEBUG_DEFAULT_LEVEL; + rc->buf_size = buf_size; + rc->entry_size = sizeof(debug_entry_t) + buf_size; + strncpy(rc->name, name, MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1))); + rc->name[MIN(strlen(name), (DEBUG_MAX_PROCF_LEN - 1))] = 0; + memset(rc->views, 0, DEBUG_MAX_VIEWS * sizeof(struct debug_view *)); + memset(rc->proc_entries, 0 ,DEBUG_MAX_VIEWS * + sizeof(struct proc_dir_entry*)); + atomic_set(&(rc->ref_count), 0); + rc->proc_root_entry = + debug_create_proc_dir_entry(debug_proc_root_entry, rc->name, + S_IFDIR | S_IRUGO | S_IXUGO | + S_IWUSR | S_IWGRP, NULL, NULL); + + /* append new element to linked list */ + + if(debug_area_first == NULL){ + /* first element in list */ + debug_area_first = rc; + rc->prev = NULL; + } + else{ + /* append element to end of list */ + debug_area_last->next = rc; + rc->prev = debug_area_last; + } + debug_area_last = rc; + rc->next = NULL; + + debug_info_get(rc); + return rc; + +fail_malloc_areas2: + kfree(rc->areas); +fail_malloc_areas: + kfree(rc->active_entry); +fail_malloc_active_entry: + kfree(rc); +fail_malloc_rc: + return NULL; +} + +/* + * debug_info_get + * - increments reference count for debug-info + */ + +static void debug_info_get(debug_info_t * db_info) +{ + if (db_info) + atomic_inc(&db_info->ref_count); +} + +/* + * debug_info_put: + * - decreases reference count for debug-info and frees it if necessary + */ + +static void debug_info_put(debug_info_t *db_info) +{ + int i; + + if (!db_info) + return; + if (atomic_dec_and_test(&db_info->ref_count)) { + printk(KERN_INFO "debug: freeing debug area %p (%s)\n", + db_info, db_info->name); + for (i = 0; i < DEBUG_MAX_VIEWS; i++) { + if (db_info->views[i] != NULL) + debug_delete_proc_dir_entry + (db_info->proc_root_entry, + db_info->proc_entries[i]); + } + debug_delete_proc_dir_entry(debug_proc_root_entry, + db_info->proc_root_entry); + for (i = 0; i < db_info->nr_areas; i++) { + free_pages((unsigned long) db_info->areas[i], + db_info->page_order); + } + kfree(db_info->areas); + kfree(db_info->active_entry); + if(db_info == debug_area_first) + debug_area_first = db_info->next; + if(db_info == debug_area_last) + debug_area_last = db_info->prev; + if(db_info->prev) db_info->prev->next = db_info->next; + if(db_info->next) db_info->next->prev = db_info->prev; + kfree(db_info); + } +} + + +/* + * debug_output: + * - called for user read() + * - copies formated output form private_data of the file + * handle to the user buffer + */ + +static ssize_t debug_output(struct file *file, /* file descriptor */ + char *user_buf, /* user buffer */ + size_t user_len, /* length of buffer */ + loff_t *offset /* offset in the file */ ) +{ + loff_t len; + int rc; + file_private_info_t *p_info; + + p_info = ((file_private_info_t *) file->private_data); + if (*offset >= p_info->len) { + return 0; /* EOF */ + } else { + len = MIN(user_len, (p_info->len - *offset)); + if ((rc = copy_to_user(user_buf, &(p_info->data[*offset]),len))) + return rc;; + (*offset) += len; + return len; /* number of bytes "read" */ + } +} + +/* + * debug_input: + * - called for user write() + * - calls input function of view + */ + +static ssize_t debug_input(struct file *file, + const char *user_buf, size_t length, + loff_t *offset) +{ + int rc = 0; + file_private_info_t *p_info; + + down(&debug_lock); + p_info = ((file_private_info_t *) file->private_data); + if (p_info->view->input_proc) + rc = p_info->view->input_proc(p_info->debug_info, + p_info->view, file, user_buf, + length, offset); + up(&debug_lock); + return rc; /* number of input characters */ +} + +/* + * debug_format_output: + * - calls prolog, header and format functions of view to format output + */ + +static int debug_format_output(debug_info_t * debug_area, char *buf, + int size, struct debug_view *view) +{ + int len = 0; + int i, j; + int nr_of_entries; + debug_entry_t *act_entry; + + /* print prolog */ + if (view->prolog_proc) + len += view->prolog_proc(debug_area, view, buf); + /* print debug records */ + if (!(view->format_proc) && !(view->header_proc)) + goto out; + nr_of_entries = PAGE_SIZE / debug_area->entry_size + << debug_area->page_order; + for (i = 0; i < debug_area->nr_areas; i++) { + act_entry = debug_area->areas[i]; + for (j = 0; j < nr_of_entries; j++) { + if (act_entry->id.fields.used == 0) + break; /* empty entry */ + if (view->header_proc) + len += view->header_proc(debug_area, view, i, + act_entry, buf + len); + if (view->format_proc) + len += view->format_proc(debug_area, view, + buf + len, + DEBUG_DATA(act_entry)); + if (len > size) { + printk(KERN_ERR + "debug: error -- memory exceeded for (%s/%s)\n", + debug_area->name, view->name); + printk(KERN_ERR "debug: fix view %s!!\n", + view->name); + printk(KERN_ERR + "debug: area: %i (0 - %i) entry: %i (0 - %i)\n", + i, debug_area->nr_areas - 1, j, + nr_of_entries - 1); + goto out; + } + act_entry = (debug_entry_t *) (((char *) act_entry) + + debug_area->entry_size); + } + } + out: + return len; +} + + +/* + * debug_open: + * - called for user open() + * - copies formated output to private_data area of the file + * handle + */ + +static int debug_open(struct inode *inode, struct file *file) +{ + int i = 0, size = 0, rc = 0, f_entry_size = 0; + file_private_info_t *p_info; + debug_info_t* debug_info; + +#ifdef DEBUG + printk("debug_open\n"); +#endif + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + down(&debug_lock); + + /* find debug log and view */ + + debug_info = debug_area_first; + while(debug_info != NULL){ + for (i = 0; i < DEBUG_MAX_VIEWS; i++) { + if (debug_info->views[i] == NULL) + continue; + else if (debug_info->proc_entries[i]->low_ino == + file->f_dentry->d_inode->i_ino) { + goto found; /* found view ! */ + } + } + debug_info = debug_info->next; + } + /* no entry found */ + rc = -EINVAL; + goto out; + found: + if ((file->private_data = + kmalloc(sizeof(file_private_info_t), GFP_ATOMIC)) == 0) { + printk(KERN_ERR "debug_open: kmalloc failed\n"); + rc = -ENOMEM; + goto out; + } + p_info = (file_private_info_t *) file->private_data; + + /* + * the size for the formated output is calculated + * with the following formula: + * + * prolog-size + * + + * (record header size + record data field size) + * * number of entries per page + * * number of pages per area + * * number of areas + */ + + if (debug_info->views[i]->prolog_proc) + size += + debug_info->views[i]->prolog_proc(debug_info, + debug_info-> + views[i], NULL); + + if (debug_info->views[i]->header_proc) + f_entry_size = + debug_info->views[i]->header_proc(debug_info, + debug_info-> + views[i], 0, NULL, + NULL); + if (debug_info->views[i]->format_proc) + f_entry_size += + debug_info->views[i]->format_proc(debug_info, + debug_info-> + views[i], NULL, + NULL); + + size += f_entry_size + * (PAGE_SIZE / debug_info->entry_size + << debug_info->page_order) + * debug_info->nr_areas + 1; /* terminating \0 */ +#ifdef DEBUG + printk("debug_open: size: %i\n", size); +#endif + + /* alloc some bytes more to be safe against bad views */ + if ((p_info->data = vmalloc(size + ADD_BUFFER)) == 0) { + printk(KERN_ERR "debug_open: vmalloc failed\n"); + vfree(file->private_data); + rc = -ENOMEM; + goto out; + } + + p_info->size = size; + p_info->debug_info = debug_info; + p_info->view = debug_info->views[i]; + + spin_lock_irq(&debug_info->lock); + + p_info->len = + debug_format_output(debug_info, p_info->data, size, + debug_info->views[i]); +#ifdef DEBUG + { + int ilen = p_info->len; + printk("debug_open: len: %i\n", ilen); + } +#endif + + spin_unlock_irq(&debug_info->lock); + debug_info_get(debug_info); + + out: + up(&debug_lock); +#ifdef MODULE + if (rc != 0) + MOD_DEC_USE_COUNT; +#endif + return rc; +} + +/* + * debug_close: + * - called for user close() + * - deletes private_data area of the file handle + */ + +static int debug_close(struct inode *inode, struct file *file) +{ + file_private_info_t *p_info; +#ifdef DEBUG + printk("debug_close\n"); +#endif + down(&debug_lock); + p_info = (file_private_info_t *) file->private_data; + debug_info_put(p_info->debug_info); + if (p_info->data) { + vfree(p_info->data); + kfree(file->private_data); + } + up(&debug_lock); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; /* success */ +} + +/* + * debug_create_proc_dir_entry: + * - initializes proc-dir-entry and registers it + */ + +static struct proc_dir_entry *debug_create_proc_dir_entry + (struct proc_dir_entry *root, const char *name, mode_t mode, + struct inode_operations *iops, struct file_operations *fops) +{ + struct proc_dir_entry *rc = NULL; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) + const char *fn = name; + int len; + len = strlen(fn); + + rc = (struct proc_dir_entry *) kmalloc(sizeof(struct proc_dir_entry) + + len + 1, GFP_ATOMIC); + if (!rc) + goto out; + + memset(rc, 0, sizeof(struct proc_dir_entry)); + memcpy(((char *) rc) + sizeof(*rc), fn, len + 1); + rc->name = ((char *) rc) + sizeof(*rc); + rc->namelen = len; + rc->low_ino = 0, rc->mode = mode; + rc->nlink = 1; + rc->uid = 0; + rc->gid = 0; + rc->size = 0; + rc->get_info = NULL; + rc->ops = iops; + + proc_register(root, rc); +#else + rc = create_proc_entry(name, mode, root); + if (!rc) + goto out; + if (fops) + rc->proc_fops = fops; +#endif + + out: + return rc; +} + + +/* + * delete_proc_dir_entry: + */ + +static void debug_delete_proc_dir_entry + (struct proc_dir_entry *root, struct proc_dir_entry *proc_entry) +{ + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,98)) + proc_unregister(root, proc_entry->low_ino); + kfree(proc_entry); +#else + remove_proc_entry(proc_entry->name, root); +#endif +} + +/* + * debug_register: + * - creates and initializes debug area for the caller + * - returns handle for debug area + */ + +debug_info_t *debug_register + (char *name, int page_order, int nr_areas, int buf_size) +{ + debug_info_t *rc = NULL; + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + if (!initialized) + debug_init(); + down(&debug_lock); + + /* create new debug_info */ + + rc = debug_info_create(name, page_order, nr_areas, buf_size); + if(!rc) + goto out; + debug_register_view(rc, &debug_level_view); + printk(KERN_INFO + "debug: reserved %d areas of %d pages for debugging %s\n", + nr_areas, 1 << page_order, rc->name); + out: + if (rc == NULL){ + printk(KERN_ERR "debug: debug_register failed for %s\n",name); +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + } + up(&debug_lock); + return rc; +} + +/* + * debug_unregister: + * - give back debug area + */ + +void debug_unregister(debug_info_t * id) +{ + if (!id) + goto out; + down(&debug_lock); + printk(KERN_INFO "debug: unregistering %s\n", id->name); + debug_info_put(id); + up(&debug_lock); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + out: + return; +} + +/* + * debug_set_level: + * - set actual debug level + */ + +void debug_set_level(debug_info_t* id, int new_level) +{ + long flags; + if(!id) + return; + spin_lock_irqsave(&id->lock,flags); + if(new_level == DEBUG_OFF_LEVEL){ + id->level = DEBUG_OFF_LEVEL; + printk(KERN_INFO "debug: %s: switched off\n",id->name); + } else if ((new_level > DEBUG_MAX_LEVEL) || (new_level < 0)) { + printk(KERN_INFO + "debug: %s: level %i is out of range (%i - %i)\n", + id->name, new_level, 0, DEBUG_MAX_LEVEL); + } else { + id->level = new_level; + printk(KERN_INFO + "debug: %s: new level %i\n",id->name,id->level); + } + spin_unlock_irqrestore(&id->lock,flags); +} + + +/* + * proceed_active_entry: + * - set active entry to next in the ring buffer + */ + +static inline void proceed_active_entry(debug_info_t * id) +{ + if ((id->active_entry[id->active_area] += id->entry_size) + > ((PAGE_SIZE << (id->page_order)) - id->entry_size)) + id->active_entry[id->active_area] = 0; +} + +/* + * proceed_active_area: + * - set active area to next in the ring buffer + */ + +static inline void proceed_active_area(debug_info_t * id) +{ + id->active_area++; + id->active_area = id->active_area % id->nr_areas; +} + +/* + * get_active_entry: + */ + +static inline debug_entry_t *get_active_entry(debug_info_t * id) +{ + return (debug_entry_t *) ((char *) id->areas[id->active_area] + + id->active_entry[id->active_area]); +} + +/* + * debug_common: + * - set timestamp, caller address, cpu number etc. + */ + +static inline debug_entry_t *debug_common(debug_info_t * id) +{ + debug_entry_t *active; + + active = get_active_entry(id); + STCK(active->id.stck); + active->id.fields.cpuid = smp_processor_id(); + active->id.fields.used = 1; + active->caller = __builtin_return_address(0); + return active; +} + +/* + * debug_event: + */ + +debug_entry_t *debug_event(debug_info_t * id, int level, void *buf, + int len) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + active->id.fields.exception = 0; + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), buf, MIN(len, id->buf_size)); + proceed_active_entry(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; +} + +/* + * debug_int_event: + */ + +debug_entry_t *debug_int_event(debug_info_t * id, int level, + unsigned int tag) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + active->id.fields.exception = 0; + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), &tag, MIN(sizeof(unsigned int), id->buf_size)); + proceed_active_entry(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; +} + +/* + * debug_text_event: + */ + +debug_entry_t *debug_text_event(debug_info_t * id, int level, + const char *txt) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + memset(DEBUG_DATA(active), 0, id->buf_size); + strncpy(DEBUG_DATA(active), txt, MIN(strlen(txt), id->buf_size)); + active->id.fields.exception = 0; + proceed_active_entry(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; + +} + +/* + * debug_exception: + */ + +debug_entry_t *debug_exception(debug_info_t * id, int level, void *buf, + int len) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + active->id.fields.exception = 1; + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), buf, MIN(len, id->buf_size)); + proceed_active_entry(id); + proceed_active_area(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; +} + +/* + * debug_int_exception: + */ + +debug_entry_t *debug_int_exception(debug_info_t * id, int level, + unsigned int tag) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + active->id.fields.exception = 1; + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), &tag, + MIN(sizeof(unsigned int), id->buf_size)); + proceed_active_entry(id); + proceed_active_area(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; +} + +/* + * debug_text_exception: + */ + +debug_entry_t *debug_text_exception(debug_info_t * id, int level, + const char *txt) +{ + long flags; + debug_entry_t *active = NULL; + + if ((!id) || (level > id->level)) + goto out; + spin_lock_irqsave(&id->lock, flags); + active = debug_common(id); + memset(DEBUG_DATA(active), 0, id->buf_size); + strncpy(DEBUG_DATA(active), txt, MIN(strlen(txt), id->buf_size)); + active->id.fields.exception = 1; + proceed_active_entry(id); + proceed_active_area(id); + spin_unlock_irqrestore(&id->lock, flags); + out: + return active; + +} + +/* + * debug_init: + * - is called exactly once to initialize the debug feature + */ + +int debug_init(void) +{ + int rc = 0; + + down(&debug_lock); + if (!initialized) { + debug_proc_root_entry = + debug_create_proc_dir_entry(&proc_root, DEBUG_DIR_ROOT, + S_IFDIR | S_IRUGO | S_IXUGO + | S_IWUSR | S_IWGRP, NULL, + NULL); + printk(KERN_INFO "debug: Initialization complete\n"); + initialized = 1; + } + up(&debug_lock); + + return rc; +} + +/* + * debug_register_view: + */ + +int debug_register_view(debug_info_t * id, struct debug_view *view) +{ + int rc = 0; + int i; + long flags; + mode_t mode = S_IFREG; + + if (!id) + goto out; + spin_lock_irqsave(&id->lock, flags); + for (i = 0; i < DEBUG_MAX_VIEWS; i++) { + if (id->views[i] == NULL) + break; + } + if (i == DEBUG_MAX_VIEWS) { + printk(KERN_WARNING "debug: cannot register view %s/%s\n", + id->name,view->name); + printk(KERN_WARNING + "debug: maximum number of views reached (%i)!\n", i); + rc = -1; + } + else { + id->views[i] = view; + if (view->prolog_proc || view->format_proc || view->header_proc) + mode |= S_IRUSR; + if (view->input_proc) + mode |= S_IWUSR; + id->proc_entries[i] = + debug_create_proc_dir_entry(id->proc_root_entry, + view->name, mode, + &debug_inode_ops, + &debug_file_ops); + rc = 0; + } + spin_unlock_irqrestore(&id->lock, flags); + out: + return rc; +} + +/* + * debug_unregister_view: + */ + +int debug_unregister_view(debug_info_t * id, struct debug_view *view) +{ + int rc = 0; + int i; + long flags; + + if (!id) + goto out; + spin_lock_irqsave(&id->lock, flags); + for (i = 0; i < DEBUG_MAX_VIEWS; i++) { + if (id->views[i] == view) + break; + } + if (i == DEBUG_MAX_VIEWS) + rc = -1; + else { + debug_delete_proc_dir_entry(id->proc_root_entry, + id->proc_entries[i]); + id->views[i] = NULL; + rc = 0; + } + spin_unlock_irqrestore(&id->lock, flags); + out: + return rc; +} + +/* + * functions for debug-views + *********************************** +*/ + +/* + * prints out actual debug level + */ + +static int debug_prolog_level_fn(debug_info_t * id, + struct debug_view *view, char *out_buf) +{ + int rc = 0; + + if (out_buf == NULL) { + rc = 2; + goto out; + } + if(id->level == -1) rc = sprintf(out_buf,"-\n"); + else rc = sprintf(out_buf, "%i\n", id->level); + out: + return rc; +} + +/* + * reads new debug level + */ + +static int debug_input_level_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char *user_buf, + size_t in_buf_size, loff_t * offset) +{ + char input_buf[1]; + int rc = in_buf_size; + + if (*offset != 0) + goto out; + if ((rc = copy_from_user(input_buf, user_buf, 1))) + goto out; + if (isdigit(input_buf[0])) { + int new_level = ((int) input_buf[0] - (int) '0'); + debug_set_level(id, new_level); + } else if(input_buf[0] == '-') { + debug_set_level(id, DEBUG_OFF_LEVEL); + } else { + printk(KERN_INFO "debug: level `%c` is not valid\n", + input_buf[0]); + } + out: + *offset += in_buf_size; + return rc; /* number of input characters */ +} + +/* + * prints debug header in raw format + */ + +int debug_raw_header_fn(debug_info_t * id, struct debug_view *view, + int area, debug_entry_t * entry, char *out_buf) +{ + int rc; + + rc = sizeof(debug_entry_t); + if (out_buf == NULL) + goto out; + memcpy(out_buf,entry,sizeof(debug_entry_t)); + out: + return rc; +} + +/* + * prints debug data in raw format + */ + +static int debug_raw_format_fn(debug_info_t * id, struct debug_view *view, + char *out_buf, const char *in_buf) +{ + int rc; + + rc = id->buf_size; + if (out_buf == NULL || in_buf == NULL) + goto out; + memcpy(out_buf, in_buf, id->buf_size); + out: + return rc; +} + +/* + * prints debug data in hex/ascii format + */ + +static int debug_hex_ascii_format_fn(debug_info_t * id, struct debug_view *view, + char *out_buf, const char *in_buf) +{ + int i, rc = 0; + + if (out_buf == NULL || in_buf == NULL) { + rc = id->buf_size * 4 + 3; + goto out; + } + for (i = 0; i < id->buf_size; i++) { + rc += sprintf(out_buf + rc, "%02x ", + ((unsigned char *) in_buf)[i]); + } + rc += sprintf(out_buf + rc, "| "); + for (i = 0; i < id->buf_size; i++) { + unsigned char c = in_buf[i]; + if (!isprint(c)) + rc += sprintf(out_buf + rc, "."); + else + rc += sprintf(out_buf + rc, "%c", c); + } + rc += sprintf(out_buf + rc, "\n"); + out: + return rc; +} + +/* + * prints header for debug entry + */ + +int debug_dflt_header_fn(debug_info_t * id, struct debug_view *view, + int area, debug_entry_t * entry, char *out_buf) +{ + struct timeval time_val; + unsigned long long time; + char *except_str; + unsigned long caller; + int rc = 0; + + if (out_buf == NULL) { + rc = DEBUG_PROC_HEADER_SIZE; + goto out; + } + + time = entry->id.stck; + /* adjust todclock to 1970 */ + time -= 0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096); + tod_to_timeval(time, &time_val); + + if (entry->id.fields.exception) + except_str = "*"; + else + except_str = "-"; + caller = (unsigned long) entry->caller; +#if defined(CONFIG_ARCH_S390X) + rc += sprintf(out_buf, "%02i %011lu:%06lu %1s %02i %016lx ", + area, time_val.tv_sec, + time_val.tv_usec, except_str, + entry->id.fields.cpuid, caller); +#else + caller &= 0x7fffffff; + rc += sprintf(out_buf, "%02i %011lu:%06lu %1s %02i %08lx ", + area, time_val.tv_sec, + time_val.tv_usec, except_str, + entry->id.fields.cpuid, caller); +#endif + out: + return rc; +} + +/* + * init_module: + */ + +#ifdef MODULE +int init_module(void) +{ + int rc = 0; +#ifdef DEBUG + printk("debug_module_init: \n"); +#endif + rc = debug_init(); + if (rc) + printk(KERN_INFO "debug: an error occurred with debug_init\n"); + return rc; +} + +/* + * cleanup_module: + */ + +void cleanup_module(void) +{ +#ifdef DEBUG + printk("debug_cleanup_module: \n"); +#endif + debug_delete_proc_dir_entry(&proc_root, debug_proc_root_entry); + return; +} + +#endif /* MODULE */ diff --git a/arch/s390x/kernel/ebcdic.c b/arch/s390x/kernel/ebcdic.c new file mode 100644 index 000000000..fc7740649 --- /dev/null +++ b/arch/s390x/kernel/ebcdic.c @@ -0,0 +1,391 @@ +/* + * arch/s390/kernel/ebcdic.c + * ECBDIC -> ASCII, ASCII -> ECBDIC, + * upper to lower case (EBCDIC) conversion tables. + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> + * Martin Peschke <peschke@fh-brandenburg.de> + */ + +#include <asm/types.h> + +/* + * ASCII (IBM PC 437) -> EBCDIC 037 + */ +__u8 _ascebc[256] = +{ + /*00 NUL SOH STX ETX EOT ENQ ACK BEL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + /* ->NL */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /*18 CAN EM SUB ESC FS GS RS US */ + /* ->IGS ->IRS ->IUS */ + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /*80*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*88*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*90*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*98*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E0 sz */ + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F8*/ + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +/* + * EBCDIC 037 -> ASCII (IBM PC 437) + */ +__u8 _ebcasc[256] = +{ + /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + /* 0x40 SP RSP ä ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + /* 0x48 . < ( + | */ + 0x87, 0xA4, 0x9B, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, + /* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + /* 0x58 ß ! $ * ) ; */ + 0x8D, 0xE1, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAA, + /* 0x60 - / ---- Ä ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + /* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70 ---- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + /* 0x90 ° j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + /* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + /* 0xB0 ^ ---- § ---- */ + 0x5E, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + /* 0xB8 ---- [ ] ---- ---- ---- ---- */ + 0xAB, 0x07, 0x5B, 0x5D, 0x07, 0x07, 0x07, 0x07, + /* 0xC0 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8 H I ---- ö ---- */ + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + /* 0xD0 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8 Q R ---- ü */ + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + /* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8 Y Z ---- Ö ---- ---- ---- */ + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + /* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 +}; + + +/* + * ASCII (IBM PC 437) -> EBCDIC 500 + */ +__u8 _ascebc_500[256] = +{ + /*00 NUL SOH STX ETX EOT ENQ ACK BEL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + /* ->NL */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /*18 CAN EM SUB ESC FS GS RS US */ + /* ->IGS ->IRS ->IUS */ + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x4F, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0x4A, 0xE0, 0x5A, 0x5F, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0xBB, 0xD0, 0xA1, 0x07, + /*80*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*88*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*90*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*98*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E0 sz */ + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F8*/ + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +/* + * EBCDIC 500 -> ASCII (IBM PC 437) + */ +__u8 _ebcasc_500[256] = +{ + /* 0x00 NUL SOH STX ETX *SEL HT *RNL DEL */ + 0x00, 0x01, 0x02, 0x03, 0x07, 0x09, 0x07, 0x7F, + /* 0x08 -GE -SPS -RPT VT FF CR SO SI */ + 0x07, 0x07, 0x07, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /* 0x10 DLE DC1 DC2 DC3 -RES -NL BS -POC + -ENP ->LF */ + 0x10, 0x11, 0x12, 0x13, 0x07, 0x0A, 0x08, 0x07, + /* 0x18 CAN EM -UBS -CU1 -IFS -IGS -IRS -ITB + -IUS */ + 0x18, 0x19, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x20 -DS -SOS FS -WUS -BYP LF ETB ESC + -INP */ + 0x07, 0x07, 0x1C, 0x07, 0x07, 0x0A, 0x17, 0x1B, + /* 0x28 -SA -SFE -SM -CSP -MFA ENQ ACK BEL + -SW */ + 0x07, 0x07, 0x07, 0x07, 0x07, 0x05, 0x06, 0x07, + /* 0x30 ---- ---- SYN -IR -PP -TRN -NBS EOT */ + 0x07, 0x07, 0x16, 0x07, 0x07, 0x07, 0x07, 0x04, + /* 0x38 -SBS -IT -RFF -CU3 DC4 NAK ---- SUB */ + 0x07, 0x07, 0x07, 0x07, 0x14, 0x15, 0x07, 0x1A, + /* 0x40 SP RSP ä ---- */ + 0x20, 0xFF, 0x83, 0x84, 0x85, 0xA0, 0x07, 0x86, + /* 0x48 [ . < ( + ! */ + 0x87, 0xA4, 0x5B, 0x2E, 0x3C, 0x28, 0x2B, 0x21, + /* 0x50 & ---- */ + 0x26, 0x82, 0x88, 0x89, 0x8A, 0xA1, 0x8C, 0x07, + /* 0x58 ß ] $ * ) ; ^ */ + 0x8D, 0xE1, 0x5D, 0x24, 0x2A, 0x29, 0x3B, 0x5E, + /* 0x60 - / ---- Ä ---- ---- ---- */ + 0x2D, 0x2F, 0x07, 0x8E, 0x07, 0x07, 0x07, 0x8F, + /* 0x68 ---- , % _ > ? */ + 0x80, 0xA5, 0x07, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, + /* 0x70 ---- ---- ---- ---- ---- ---- ---- */ + 0x07, 0x90, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + /* 0x78 * ` : # @ ' = " */ + 0x70, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, + /* 0x80 * a b c d e f g */ + 0x07, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + /* 0x88 h i ---- ---- ---- */ + 0x68, 0x69, 0xAE, 0xAF, 0x07, 0x07, 0x07, 0xF1, + /* 0x90 ° j k l m n o p */ + 0xF8, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, + /* 0x98 q r ---- ---- */ + 0x71, 0x72, 0xA6, 0xA7, 0x91, 0x07, 0x92, 0x07, + /* 0xA0 ~ s t u v w x */ + 0xE6, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + /* 0xA8 y z ---- ---- ---- ---- */ + 0x79, 0x7A, 0xAD, 0xAB, 0x07, 0x07, 0x07, 0x07, + /* 0xB0 ---- § ---- */ + 0x9B, 0x9C, 0x9D, 0xFA, 0x07, 0x07, 0x07, 0xAC, + /* 0xB8 ---- | ---- ---- ---- ---- */ + 0xAB, 0x07, 0xAA, 0x7C, 0x07, 0x07, 0x07, 0x07, + /* 0xC0 { A B C D E F G */ + 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + /* 0xC8 H I ---- ö ---- */ + 0x48, 0x49, 0x07, 0x93, 0x94, 0x95, 0xA2, 0x07, + /* 0xD0 } J K L M N O P */ + 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + /* 0xD8 Q R ---- ü */ + 0x51, 0x52, 0x07, 0x96, 0x81, 0x97, 0xA3, 0x98, + /* 0xE0 \ S T U V W X */ + 0x5C, 0xF6, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + /* 0xE8 Y Z ---- Ö ---- ---- ---- */ + 0x59, 0x5A, 0xFD, 0x07, 0x99, 0x07, 0x07, 0x07, + /* 0xF0 0 1 2 3 4 5 6 7 */ + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + /* 0xF8 8 9 ---- ---- Ü ---- ---- ---- */ + 0x38, 0x39, 0x07, 0x07, 0x9A, 0x07, 0x07, 0x07 +}; + + +/* + * EBCDIC 037/500 conversion table: + * from upper to lower case + */ +__u8 _ebc_tolower[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9C, 0x9F, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xAB, 0x8C, 0x8D, 0x8E, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xD0, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xEA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xDB, 0xDC, 0xDD, 0xDE, 0xFF +}; + + +/* + * EBCDIC 037/500 conversion table: + * from lower to upper case + */ +__u8 _ebc_toupper[256] = +{ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, + 0x40, 0x41, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, + 0x50, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, + 0x80, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0x8A, 0x8B, 0xAC, 0xAD, 0xAE, 0x8F, + 0x90, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0x9A, 0x9B, 0x9E, 0x9D, 0x9E, 0x9F, + 0xA0, 0xA1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, + 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + 0xC8, 0xC9, 0xCA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, + 0xD8, 0xD9, 0xDA, 0xFB, 0xFC, 0xFD, 0xFE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, + 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF +}; diff --git a/arch/s390x/kernel/entry.S b/arch/s390x/kernel/entry.S new file mode 100644 index 000000000..c5fbdd61f --- /dev/null +++ b/arch/s390x/kernel/entry.S @@ -0,0 +1,868 @@ +/* + * arch/s390/kernel/entry.S + * S390 low-level entry points. + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Hartmut Penner (hp@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + */ + +#include <linux/sys.h> +#include <linux/linkage.h> +#include <linux/config.h> +#include <asm/lowcore.h> +#include <asm/errno.h> +#define ASSEMBLY +#include <asm/smp.h> +#include <asm/s390-regs-common.h> + + +/* + * stack layout for the system_call stack entry + * Martin please don't modify these back to hard coded values + * You know how bad I'm at mental arithmetic DJB & it gives + * me grief when I modify the pt_regs + */ +SP_PTREGS = STACK_FRAME_OVERHEAD +SP_PSW = SP_PTREGS +SP_R0 = (SP_PSW+PSW_MASK_SIZE+PSW_ADDR_SIZE) +SP_R1 = (SP_R0+GPR_SIZE) +SP_R2 = (SP_R1+GPR_SIZE) +SP_R3 = (SP_R2+GPR_SIZE) +SP_R4 = (SP_R3+GPR_SIZE) +SP_R5 = (SP_R4+GPR_SIZE) +SP_R6 = (SP_R5+GPR_SIZE) +SP_R7 = (SP_R6+GPR_SIZE) +SP_R8 = (SP_R7+GPR_SIZE) +SP_R9 = (SP_R8+GPR_SIZE) +SP_RA = (SP_R9+GPR_SIZE) +SP_RB = (SP_RA+GPR_SIZE) +SP_RC = (SP_RB+GPR_SIZE) +SP_RD = (SP_RC+GPR_SIZE) +SP_RE = (SP_RD+GPR_SIZE) +SP_RF = (SP_RE+GPR_SIZE) +SP_AREGS = (SP_RF+GPR_SIZE) +SP_ORIG_R2 = (SP_AREGS+(NUM_ACRS*ACR_SIZE)) +SP_TRAP = (SP_ORIG_R2+GPR_SIZE) +#if CONFIG_REMOTE_DEBUG +SP_CRREGS = (SP_TRAP+4) +/* fpu registers are saved & restored by the gdb stub itself */ +SP_FPC = (SP_CRREGS+(NUM_CRS*CR_SIZE)) +SP_FPRS = (SP_FPC+FPC_SIZE+FPC_PAD_SIZE) +/* SP_PGM_OLD_ILC etc are not part of pt_regs & they are not + defined in ptrace.h but space is needed for this too */ +SP_PGM_OLD_ILC= (SP_FPRS+(NUM_FPRS*FPR_SIZE)) +#else +SP_PGM_OLD_ILC= (SP_TRAP+4) +#endif +SP_SVC_STEP = (SP_PGM_OLD_ILC+4) +SP_SIZE = (SP_SVC_STEP+4) +/* + * these defines are offsets into the thread_struct + */ +_TSS_PTREGS = 0 +_TSS_FPRS = (_TSS_PTREGS+8) +_TSS_AR2 = (_TSS_FPRS+136) +_TSS_AR4 = (_TSS_AR2+4) +_TSS_KSP = (_TSS_AR4+4) +_TSS_USERSEG = (_TSS_KSP+8) +_TSS_PROT = (_TSS_USERSEG+8) +_TSS_ERROR = (_TSS_PROT+8) +_TSS_TRAP = (_TSS_ERROR+4) +_TSS_PER = (_TSS_TRAP+4) + +/* + * these are offsets into the task-struct. + */ +state = 0 +flags = 8 +sigpending = 16 +need_resched = 32 +tsk_ptrace = 40 +processor = 100 + +/* PSW related defines */ +disable = 0xFC +enable = 0x03 +daton = 0x04 + + +#if 0 +/* some code left lying around in case we need a + * printk for debugging purposes + */ + sysc_printk: .long printk + sysc_msg: .string "<2>r15 %X\n" + .align 4 + +# basr %r13,0 + lg %r0,SP_PSW+8(%r15) + sllg %r0,%r0,1 + chi %r0,0 + jnz sysc_dn + la %r2,sysc_msg-sysc_lit(%r13) + lgr %r3,%r15 + brasl %r14,sysc_printk +sysc_dn: +#endif + +/* + * Register usage in interrupt handlers: + * R9 - pointer to current task structure + * R13 - pointer to literal pool + * R14 - return register for function calls + * R15 - kernel stack pointer + */ + +#define SAVE_ALL(psworg) \ + stmg %r14,%r15,__LC_SAVE_AREA ; \ + stam %a2,%a4,__LC_SAVE_AREA+16 ; \ + tm psworg+1,0x01 ; /* test problem state bit */ \ + jz 0f ; /* skip stack setup save */ \ + lg %r15,__LC_KERNEL_STACK ; /* problem state -> load ksp */ \ + slr %r14,%r14 ; \ + sar %a2,%r14 ; /* set ac.reg. 2 to primary space */ \ + lhi %r14,1 ; \ + sar %a4,%r14 ; /* set access reg. 4 to home space */ \ +0: aghi %r15,-SP_SIZE ; /* make room for registers & psw */ \ + nill %r15,0xfff8 ; /* align stack pointer to 8 */ \ + stmg %r0,%r14,SP_R0(%r15) ; /* store gprs 0-14 to kernel stack */ \ + stg %r2,SP_ORIG_R2(%r15) ; /* store original content of gpr 2 */ \ + mvc SP_RE(16,%r15),__LC_SAVE_AREA ; /* move R15 to stack */ \ + stam %a0,%a15,SP_AREGS(%r15) ; /* store access registers to kst. */\ + mvc SP_AREGS+8(12,%r15),__LC_SAVE_AREA+16 ; /* store ac. regs */ \ + mvc SP_PSW(16,%r15),psworg; /* move user PSW to stack */ \ + lhi %r0,psworg ; /* store trap indication */ \ + st %r0,SP_TRAP(%r15) ; \ + xc 0(8,%r15),0(%r15) ; /* clear back chain */ + +#define RESTORE_ALL \ + mvc __LC_RETURN_PSW(16),SP_PSW(%r15) ; /* move user PSW to lowcore */ \ + lam %a0,%a15,SP_AREGS(%r15) ; /* load the access registers */ \ + lmg %r0,%r15,SP_R0(%r15) ; /* load gprs 0-15 of user */ \ + ni __LC_RETURN_PSW+1,0xfd ; /* clear wait state bit */ \ + lpswe __LC_RETURN_PSW /* back to caller */ + +#define GET_CURRENT /* load pointer to task_struct to R9 */ \ + lghi %r9,-16384 ; \ + ngr %r9,15 + + +/* + * Scheduler resume function, called by switch_to + * grp2 = (thread_struct *) prev->tss + * grp3 = (thread_struct *) next->tss + * Returns: + * gpr2 = prev + */ + .globl resume +resume: + lg %r4,_TSS_PTREGS(%r3) + tm SP_PSW-SP_PTREGS(%r4),0x40 # is the new process using per ? + jz resume_noper # if not we're fine + stctg %r9,%r11,48(%r15) # We are using per stuff + clc _TSS_PER(24,%r3),48(%r15) + je resume_noper # we got away without bashing TLB's + lctlg %c9,%c11,_TSS_PER(%r3) # Nope we didn't +resume_noper: + stmg %r6,%r15,48(%r15) # store resume registers of prev task + stg %r15,_TSS_KSP(%r2) # store kernel stack ptr to prev->tss.ksp + lghi %r0,-16384 + ngr %r0,%r15 + lg %r15,_TSS_KSP(%r3) # load kernel stack ptr from next->tss.ksp + lghi %r1,16383 + ogr %r1,%r15 + aghi %r1,1 + stg %r1,__LC_KERNEL_STACK # __LC_KERNEL_STACK = new kernel stack + stam %a2,%a2,_TSS_AR2(%r2) # store kernel access reg. 2 + stam %a4,%a4,_TSS_AR4(%r2) # store kernel access reg. 4 + lam %a2,%a2,_TSS_AR2(%r3) # load kernel access reg. 2 + lam %a4,%a4,_TSS_AR4(%r3) # load kernel access reg. 4 + lgr %r2,%r0 # return task_struct of last task + lmg %r6,%r15,48(%r15) # load resume registers of next task + br %r14 + +/* + * SVC interrupt handler routine. System calls are synchronous events and + * are executed with interrupts enabled. + */ + + .globl system_call +system_call: + SAVE_ALL(__LC_SVC_OLD_PSW) + xc SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) +pgm_system_call: + larl %r1,sys_call_table + llgc %r8,__LC_SVC_INT_CODE+1 # get svc number from lowcore + stosm 48(%r15),0x03 # reenable interrupts + sll %r8,3 + tm SP_PSW+3(%r15),0x01 # are we running in 31 bit mode ? + jo sysc_noemu + la %r8,4(%r8) # use 31 bit emulation system calls +sysc_noemu: + GET_CURRENT # load pointer to task_struct to R9 + lgf %r8,0(%r8,%r1) # load address of system call routine + tm tsk_ptrace+7(%r9),0x02 # PT_TRACESYS + jnz sysc_tracesys + basr %r14,%r8 # call sys_xxxx + stg %r2,SP_R2(%r15) # store return value (change R2 on stack) + # ATTENTION: check sys_execve_glue before + # changing anything here !! + +sysc_return: + GET_CURRENT # load pointer to task_struct to R9 + tm SP_PSW+1(%r15),0x01 # returning to user ? + jno sysc_leave # no-> skip bottom half, resched & signal +# +# check, if bottom-half has to be done +# + l %r0,__LC_IRQ_STAT # get softirq_active + n %r0,__LC_IRQ_STAT+4 # and it with softirq_mask + jnz sysc_handle_bottom_half +# +# check, if reschedule is needed +# +sysc_return_bh: + lg %r0,need_resched(%r9) # get need_resched from task_struct + ltgr %r0,%r0 + jnz sysc_reschedule + icm %r0,15,sigpending(%r9) # get sigpending from task_struct + jnz sysc_signal_return +sysc_leave: + icm %r0,15,SP_SVC_STEP(%r15) # get sigpending from task_struct + jnz pgm_svcret + stnsm 48(%r15),disable # disable I/O and ext. interrupts + RESTORE_ALL + +# +# call do_signal before return +# +sysc_signal_return: + la %r2,SP_PTREGS(%r15) # load pt_regs + sgr %r3,%r3 # clear *oldset + larl %r14,sysc_leave + jg do_signal # return point is sysc_leave + +# +# call trace before and after sys_call +# +sysc_tracesys: + lghi %r2,-ENOSYS + stg %r2,SP_R2(%r15) # give sysc_trace an -ENOSYS retval + brasl %r14,syscall_trace + lmg %r3,%r6,SP_R3(%r15) + lg %r2,SP_ORIG_R2(%r15) + basr %r14,%r8 # call sys_xxx + stg %r2,SP_R2(%r15) # store return value + larl %r14,sysc_return + jg syscall_trace # return point is sysc_return + + +# +# call do_softirq and return from syscall, if interrupt-level +# is zero +# +sysc_handle_bottom_half: + larl %r14,sysc_return_bh + jg do_softirq # return point is sysc_return_bh + +# +# call schedule with sysc_return as return-address +# +sysc_reschedule: + larl %r14,sysc_return + jg schedule # return point is sysc_return + +# +# a new process exits the kernel with ret_from_fork +# + .globl ret_from_fork +ret_from_fork: + GET_CURRENT # load pointer to task_struct to R9 + stosm 48(%r15),0x03 # reenable interrupts + xc SP_R2(8,%r15),SP_R2(%r15) # child returns 0 +#ifdef CONFIG_SMP + larl %r14,sysc_return + jg schedule_tail # return to sysc_return +#else + j sysc_return +#endif + +# +# clone, fork, vfork, exec and sigreturn need glue, +# because they all expect pt_regs as parameter, +# but are called with different parameter. +# return-address is set up above +# +sys_clone_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + jg sys_clone # branch to sys_clone + +sys_fork_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + jg sys_fork # branch to sys_fork + +sys_vfork_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + jg sys_vfork # branch to sys_vfork + +sys_execve_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + lgr %r12,%r14 # save return address + brasl %r14,sys_execve # call sys_execve + ltgr %r2,%r2 # check if execve failed + bnz 0(%r12) # it did fail -> store result in gpr2 + b 6(%r12) # SKIP STG 2,SP_R2(15) in + # system_call/sysc_tracesys +#ifdef CONFIG_S390_SUPPORT +sys32_execve_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + lgr %r12,%r14 # save return address + brasl %r14,sys32_execve # call sys32_execve + ltgr %r2,%r2 # check if execve failed + bnz 0(%r12) # it did fail -> store result in gpr2 + b 6(%r12) # SKIP STG 2,SP_R2(15) in + # system_call/sysc_tracesys +#endif + +sys_sigreturn_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + jg sys_sigreturn # branch to sys_sigreturn + +#ifdef CONFIG_S390_SUPPORT +sys32_sigreturn_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + jg sys32_sigreturn # branch to sys32_sigreturn +#endif + +sys_rt_sigreturn_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + jg sys_rt_sigreturn # branch to sys_sigreturn + +#ifdef CONFIG_S390_SUPPORT +sys32_rt_sigreturn_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + jg sys32_rt_sigreturn # branch to sys32_sigreturn +#endif + +# +# sigsuspend and rt_sigsuspend need pt_regs as an additional +# parameter and they have to skip the store of %r2 into the +# user register %r2 because the return value was set in +# sigsuspend and rt_sigsuspend already and must not be overwritten! +# + +sys_sigsuspend_glue: + lgr %r5,%r4 # move mask back + lgr %r4,%r3 # move history1 parameter + lgr %r3,%r2 # move history0 parameter + la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter + la %r14,6(%r14) # skip store of return value + jg sys_sigsuspend # branch to sys_sigsuspend + +#ifdef CONFIG_S390_SUPPORT +sys32_sigsuspend_glue: + llgfr %r4,%r4 # unsigned long + lgr %r5,%r4 # move mask back + lgfr %r3,%r3 # int + lgr %r4,%r3 # move history1 parameter + lgfr %r2,%r2 # int + lgr %r3,%r2 # move history0 parameter + la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter + la %r14,6(%r14) # skip store of return value + jg sys32_sigsuspend # branch to sys32_sigsuspend +#endif + +sys_rt_sigsuspend_glue: + lgr %r4,%r3 # move sigsetsize parameter + lgr %r3,%r2 # move unewset parameter + la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter + la %r14,6(%r14) # skip store of return value + jg sys_rt_sigsuspend # branch to sys_rt_sigsuspend + +#ifdef CONFIG_S390_SUPPORT +sys32_rt_sigsuspend_glue: + llgfr %r3,%r3 # size_t + lgr %r4,%r3 # move sigsetsize parameter + llgtr %r2,%r2 # sigset_emu31_t * + lgr %r3,%r2 # move unewset parameter + la %r2,SP_PTREGS(%r15) # load pt_regs as first parameter + la %r14,6(%r14) # skip store of return value + jg sys32_rt_sigsuspend # branch to sys32_rt_sigsuspend +#endif + +sys_sigaltstack_glue: + la %r4,SP_PTREGS(%r15) # load pt_regs as parameter + jg sys_sigaltstack # branch to sys_sigreturn + +#ifdef CONFIG_S390_SUPPORT +sys32_sigaltstack_glue: + la %r4,SP_PTREGS(%r15) # load pt_regs as parameter + jg sys32_sigaltstack_wrapper # branch to sys_sigreturn +#endif + +#ifdef CONFIG_S390_SUPPORT +#define SYSCALL(esame,esa) esame,esa +#else +#define SYSCALL(esame,esa) esame,sys_ni_syscall +#endif + + .globl sys_call_table +sys_call_table: + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 0 */ + .long SYSCALL(sys_exit,sys32_exit_wrapper) + .long SYSCALL(sys_fork_glue,sys_fork_glue) + .long SYSCALL(sys_read,sys32_read_wrapper) + .long SYSCALL(sys_write,sys32_write_wrapper) + .long SYSCALL(sys_open,sys32_open_wrapper) /* 5 */ + .long SYSCALL(sys_close,sys32_close_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old waitpid syscall */ + .long SYSCALL(sys_creat,sys32_creat_wrapper) + .long SYSCALL(sys_link,sys32_link_wrapper) + .long SYSCALL(sys_unlink,sys32_unlink_wrapper) /* 10 */ + .long SYSCALL(sys_execve_glue,sys32_execve_glue) + .long SYSCALL(sys_chdir,sys32_chdir_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_time_wrapper) /* old time syscall */ + .long SYSCALL(sys_mknod,sys32_mknod_wrapper) + .long SYSCALL(sys_chmod,sys32_chmod_wrapper) /* 15 */ + .long SYSCALL(sys_ni_syscall,sys32_lchown16_wrapper) /* old lchown16 syscall*/ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old break syscall */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old stat syscall */ + .long SYSCALL(sys_lseek,sys32_lseek_wrapper) + .long SYSCALL(sys_getpid,sys_getpid) /* 20 */ + .long SYSCALL(sys_mount,sys32_mount_wrapper) + .long SYSCALL(sys_oldumount,sys32_oldumount_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_setuid16_wrapper) /* old setuid16 syscall*/ + .long SYSCALL(sys_ni_syscall,sys32_getuid16) /* old getuid16 syscall*/ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* 25 old stime syscall */ + .long SYSCALL(sys_ptrace,sys32_ptrace_wrapper) + .long SYSCALL(sys_alarm,sys32_alarm_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old fstat syscall */ + .long SYSCALL(sys_pause,sys32_pause) + .long SYSCALL(sys_utime,sys32_utime_wrapper) /* 30 */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old stty syscall */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old gtty syscall */ + .long SYSCALL(sys_access,sys32_access_wrapper) + .long SYSCALL(sys_nice,sys32_nice_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old ftime syscall */ + .long SYSCALL(sys_sync,sys_sync) + .long SYSCALL(sys_kill,sys32_kill_wrapper) + .long SYSCALL(sys_rename,sys32_rename_wrapper) + .long SYSCALL(sys_mkdir,sys32_mkdir_wrapper) + .long SYSCALL(sys_rmdir,sys32_rmdir_wrapper) /* 40 */ + .long SYSCALL(sys_dup,sys32_dup_wrapper) + .long SYSCALL(sys_pipe,sys32_pipe_wrapper) + .long SYSCALL(sys_times,sys32_times_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old prof syscall */ + .long SYSCALL(sys_brk,sys32_brk_wrapper) /* 45 */ + .long SYSCALL(sys_ni_syscall,sys32_setgid16) /* old setgid16 syscall*/ + .long SYSCALL(sys_ni_syscall,sys32_getgid16) /* old getgid16 syscall*/ + .long SYSCALL(sys_signal,sys32_signal_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_geteuid16) /* old geteuid16 syscall */ + .long SYSCALL(sys_ni_syscall,sys32_getegid16) /* old getegid16 syscall */ + .long SYSCALL(sys_acct,sys32_acct_wrapper) + .long SYSCALL(sys_umount,sys32_umount_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old lock syscall */ + .long SYSCALL(sys_ioctl,sys32_ioctl_wrapper) + .long SYSCALL(sys_fcntl,sys32_fcntl_wrapper) /* 55 */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* intel mpx syscall */ + .long SYSCALL(sys_setpgid,sys32_setpgid_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old ulimit syscall */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old uname syscall */ + .long SYSCALL(sys_umask,sys32_umask_wrapper) /* 60 */ + .long SYSCALL(sys_chroot,sys32_chroot_wrapper) + .long SYSCALL(sys_ustat,sys32_ustat_wrapper) + .long SYSCALL(sys_dup2,sys32_dup2_wrapper) + .long SYSCALL(sys_getppid,sys_getppid) + .long SYSCALL(sys_getpgrp,sys_getpgrp) /* 65 */ + .long SYSCALL(sys_setsid,sys_setsid) + .long SYSCALL(sys_sigaction,sys32_sigaction_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old sgetmask syscall*/ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old ssetmask syscall*/ + .long SYSCALL(sys_ni_syscall,sys32_setreuid16_wrapper) /* old setreuid16 syscall */ + .long SYSCALL(sys_ni_syscall,sys32_setregid16_wrapper) /* old setregid16 syscall */ + .long SYSCALL(sys_sigsuspend_glue,sys32_sigsuspend_glue) + .long SYSCALL(sys_sigpending,sys32_sigpending_wrapper) + .long SYSCALL(sys_sethostname,sys32_sethostname_wrapper) + .long SYSCALL(sys_setrlimit,sys32_setrlimit_wrapper) /* 75 */ + .long SYSCALL(sys_getrlimit,sys32_old_getrlimit_wrapper) + .long SYSCALL(sys_getrusage,sys32_getrusage_wrapper) + .long SYSCALL(sys_gettimeofday,sys32_gettimeofday_wrapper) + .long SYSCALL(sys_settimeofday,sys32_settimeofday_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_getgroups16_wrapper) /* old getgroups16 syscall */ + .long SYSCALL(sys_ni_syscall,sys32_setgroups16_wrapper) /* old setgroups16 syscall */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old select syscall */ + .long SYSCALL(sys_symlink,sys32_symlink_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old lstat syscall */ + .long SYSCALL(sys_readlink,sys32_readlink_wrapper) /* 85 */ + .long SYSCALL(sys_uselib,sys32_uselib_wrapper) + .long SYSCALL(sys_swapon,sys32_swapon_wrapper) + .long SYSCALL(sys_reboot,sys32_reboot_wrapper) + .long SYSCALL(sys_ni_syscall,old32_readdir_wrapper) /* old readdir syscall */ + .long SYSCALL(old_mmap,old32_mmap_wrapper) /* 90 */ + .long SYSCALL(sys_munmap,sys32_munmap_wrapper) + .long SYSCALL(sys_truncate,sys32_truncate_wrapper) + .long SYSCALL(sys_ftruncate,sys32_ftruncate_wrapper) + .long SYSCALL(sys_fchmod,sys32_fchmod_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_fchown16_wrapper) /* old fchown16 syscall*/ + .long SYSCALL(sys_getpriority,sys32_getpriority_wrapper) + .long SYSCALL(sys_setpriority,sys32_setpriority_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old profil syscall */ + .long SYSCALL(sys_statfs,sys32_statfs_wrapper) + .long SYSCALL(sys_fstatfs,sys32_fstatfs_wrapper) /* 100 */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) + .long SYSCALL(sys_socketcall,sys32_socketcall_wrapper) + .long SYSCALL(sys_syslog,sys32_syslog_wrapper) + .long SYSCALL(sys_setitimer,sys32_setitimer_wrapper) + .long SYSCALL(sys_getitimer,sys32_getitimer_wrapper) /* 105 */ + .long SYSCALL(sys_newstat,sys32_newstat_wrapper) + .long SYSCALL(sys_newlstat,sys32_newlstat_wrapper) + .long SYSCALL(sys_newfstat,sys32_newfstat_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old uname syscall */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* iopl for i386 */ + .long SYSCALL(sys_vhangup,sys_vhangup) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* old "idle" system call */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* vm86old for i386 */ + .long SYSCALL(sys_wait4,sys32_wait4_wrapper) + .long SYSCALL(sys_swapoff,sys32_swapoff_wrapper) /* 115 */ + .long SYSCALL(sys_sysinfo,sys32_sysinfo_wrapper) + .long SYSCALL(sys_ipc,sys32_ipc_wrapper) + .long SYSCALL(sys_fsync,sys32_fsync_wrapper) + .long SYSCALL(sys_sigreturn_glue,sys32_sigreturn_glue) + .long SYSCALL(sys_clone_glue,sys_clone_glue) /* 120 */ + .long SYSCALL(sys_setdomainname,sys32_setdomainname_wrapper) + .long SYSCALL(sys_newuname,sys32_newuname_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* modify_ldt for i386 */ + .long SYSCALL(sys_adjtimex,sys32_adjtimex_wrapper) + .long SYSCALL(sys_mprotect,sys32_mprotect_wrapper) /* 125 */ + .long SYSCALL(sys_sigprocmask,sys32_sigprocmask_wrapper) + .long SYSCALL(sys_create_module,sys32_create_module_wrapper) + .long SYSCALL(sys_init_module,sys32_init_module_wrapper) + .long SYSCALL(sys_delete_module,sys32_delete_module_wrapper) + .long SYSCALL(sys_get_kernel_syms,sys32_get_kernel_syms_wrapper) /* 130 */ + .long SYSCALL(sys_quotactl,sys32_quotactl_wrapper) + .long SYSCALL(sys_getpgid,sys32_getpgid_wrapper) + .long SYSCALL(sys_fchdir,sys32_fchdir_wrapper) + .long SYSCALL(sys_bdflush,sys32_bdflush_wrapper) + .long SYSCALL(sys_sysfs,sys32_sysfs_wrapper) /* 135 */ + .long SYSCALL(sys_personality,sys32_personality_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* for afs_syscall */ + .long SYSCALL(sys_ni_syscall,sys32_setfsuid16_wrapper) /* old setfsuid16 syscall */ + .long SYSCALL(sys_ni_syscall,sys32_setfsgid16_wrapper) /* old setfsgid16 syscall */ + .long SYSCALL(sys_llseek,sys32_llseek_wrapper) /* 140 */ + .long SYSCALL(sys_getdents,sys32_getdents_wrapper) + .long SYSCALL(sys_select,sys32_select_wrapper) + .long SYSCALL(sys_flock,sys32_flock_wrapper) + .long SYSCALL(sys_msync,sys32_msync_wrapper) + .long SYSCALL(sys_readv,sys32_readv_wrapper) /* 145 */ + .long SYSCALL(sys_writev,sys32_writev_wrapper) + .long SYSCALL(sys_getsid,sys32_getsid_wrapper) + .long SYSCALL(sys_fdatasync,sys32_fdatasync_wrapper) + .long SYSCALL(sys_sysctl,sys_ni_syscall) + .long SYSCALL(sys_mlock,sys32_mlock_wrapper) /* 150 */ + .long SYSCALL(sys_munlock,sys32_munlock_wrapper) + .long SYSCALL(sys_mlockall,sys32_mlockall_wrapper) + .long SYSCALL(sys_munlockall,sys_munlockall) + .long SYSCALL(sys_sched_setparam,sys32_sched_setparam_wrapper) + .long SYSCALL(sys_sched_getparam,sys32_sched_getparam_wrapper) /* 155 */ + .long SYSCALL(sys_sched_setscheduler,sys32_sched_setscheduler_wrapper) + .long SYSCALL(sys_sched_getscheduler,sys32_sched_getscheduler_wrapper) + .long SYSCALL(sys_sched_yield,sys_sched_yield) + .long SYSCALL(sys_sched_get_priority_max,sys32_sched_get_priority_max_wrapper) + .long SYSCALL(sys_sched_get_priority_min,sys32_sched_get_priority_min_wrapper) + .long SYSCALL(sys_sched_rr_get_interval,sys32_sched_rr_get_interval_wrapper) + .long SYSCALL(sys_nanosleep,sys32_nanosleep_wrapper) + .long SYSCALL(sys_mremap,sys32_mremap_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_setresuid16_wrapper) /* old setresuid16 syscall */ + .long SYSCALL(sys_ni_syscall,sys32_getresuid16_wrapper) /* old getresuid16 syscall */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* for vm86 */ + .long SYSCALL(sys_query_module,sys32_query_module_wrapper) + .long SYSCALL(sys_poll,sys32_poll_wrapper) + .long SYSCALL(sys_nfsservctl,sys32_nfsservctl_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_setresgid16_wrapper) /* old setresgid16 syscall */ + .long SYSCALL(sys_ni_syscall,sys32_getresgid16_wrapper) /* old getresgid16 syscall */ + .long SYSCALL(sys_prctl,sys32_prctl_wrapper) + .long SYSCALL(sys_rt_sigreturn_glue,sys32_rt_sigreturn_glue) + .long SYSCALL(sys_rt_sigaction,sys32_rt_sigaction_wrapper) + .long SYSCALL(sys_rt_sigprocmask,sys32_rt_sigprocmask_wrapper) /* 175 */ + .long SYSCALL(sys_rt_sigpending,sys32_rt_sigpending_wrapper) + .long SYSCALL(sys_rt_sigtimedwait,sys32_rt_sigtimedwait_wrapper) + .long SYSCALL(sys_rt_sigqueueinfo,sys32_rt_sigqueueinfo_wrapper) + .long SYSCALL(sys_rt_sigsuspend_glue,sys32_rt_sigsuspend_glue) + .long SYSCALL(sys_pread,sys32_pread_wrapper) /* 180 */ + .long SYSCALL(sys_pwrite,sys32_pwrite_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_chown16_wrapper) /* old chown16 syscall */ + .long SYSCALL(sys_getcwd,sys32_getcwd_wrapper) + .long SYSCALL(sys_capget,sys32_capget_wrapper) + .long SYSCALL(sys_capset,sys32_capset_wrapper) /* 185 */ + .long SYSCALL(sys_sigaltstack_glue,sys32_sigaltstack_glue) + .long SYSCALL(sys_sendfile,sys32_sendfile_wrapper) + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* streams1 */ + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) /* streams2 */ + .long SYSCALL(sys_vfork_glue,sys_vfork_glue) /* 190 */ + .long SYSCALL(sys_getrlimit,sys32_old_getrlimit_wrapper) + .long SYSCALL(sys_mmap2,sys32_mmap2_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_truncate64_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_ftruncate64_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_stat64) /* 195 */ + .long SYSCALL(sys_ni_syscall,sys32_lstat64) + .long SYSCALL(sys_ni_syscall,sys32_fstat64) + .long SYSCALL(sys_lchown,sys32_lchown_wrapper) + .long SYSCALL(sys_getuid,sys_getuid) + .long SYSCALL(sys_getgid,sys_getgid) /* 200 */ + .long SYSCALL(sys_geteuid,sys_geteuid) + .long SYSCALL(sys_getegid,sys_getegid) + .long SYSCALL(sys_setreuid,sys32_setreuid_wrapper) + .long SYSCALL(sys_setregid,sys32_setregid_wrapper) + .long SYSCALL(sys_getgroups,sys32_getgroups_wrapper) /* 205 */ + .long SYSCALL(sys_setgroups,sys32_setgroups_wrapper) + .long SYSCALL(sys_fchown,sys32_fchown_wrapper) + .long SYSCALL(sys_setresuid,sys32_setresuid_wrapper) + .long SYSCALL(sys_getresuid,sys32_getresuid_wrapper) + .long SYSCALL(sys_setresgid,sys32_setresgid_wrapper) /* 210 */ + .long SYSCALL(sys_getresgid,sys32_getresgid_wrapper) + .long SYSCALL(sys_chown,sys32_chown_wrapper) + .long SYSCALL(sys_setuid,sys32_setuid_wrapper) + .long SYSCALL(sys_setgid,sys32_setgid_wrapper) + .long SYSCALL(sys_setfsuid,sys32_setfsuid_wrapper) /* 215 */ + .long SYSCALL(sys_setfsgid,sys32_setfsgid_wrapper) + .long SYSCALL(sys_pivot_root,sys32_pivot_root_wrapper) + .long SYSCALL(sys_mincore,sys32_mincore_wrapper) + .long SYSCALL(sys_madvise,sys32_madvise_wrapper) + .long SYSCALL(sys_ni_syscall,sys32_getdents64_wrapper)/* 220 */ + .long SYSCALL(sys_ni_syscall,sys32_fcntl64_wrapper) + .rept 255-221 + .long SYSCALL(sys_ni_syscall,sys_ni_syscall) + .endr + +/* + * Program check handler routine + */ + + .globl pgm_check_handler +pgm_check_handler: +/* + * First we need to check for a special case: + * Single stepping an instruction that disables the PER event mask will + * cause a PER event AFTER the mask has been set. Example: SVC or LPSW. + * For a single stepped SVC the program check handler gets control after + * the SVC new PSW has been loaded. But we want to execute the SVC first and + * then handle the PER event. Therefore we update the SVC old PSW to point + * to the pgm_check_handler and branch to the SVC handler after we checked + * if we have to load the kernel stack register. + * For every other possible cause for PER event without the PER mask set + * we just ignore the PER event (FIXME: is there anything we have to do + * for LPSW?). + */ + stmg %r14,%r15,__LC_SAVE_AREA + stam %a2,%a4,__LC_SAVE_AREA+16 + tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception + jz pgm_sv # skip if not + tm __LC_PGM_OLD_PSW,0x40 # test if per event recording is on + jnz pgm_sv # skip if it is +# ok its one of the special cases, now we need to find out which one + clc __LC_PGM_OLD_PSW(16),__LC_SVC_NEW_PSW + je pgm_svcper +# no interesting special case, ignore PER event + lm %r13,%r15,__LC_SAVE_AREA + lpsw __LC_PGM_OLD_PSW +# it was a single stepped SVC that is causing all the trouble +pgm_svcper: + tm __LC_SVC_OLD_PSW+1,0x01 # test problem state bit + jz 0f # skip stack & access regs setup + lg %r15,__LC_KERNEL_STACK # problem state -> load ksp + slr %r14,%r14 + sar %a2,%r14 # set ac.reg. 2 to primary space + lhi %r14,1 + sar %a4,%r14 # and access reg. 4 to home space +0: aghi %r15,-SP_SIZE # make room for registers & psw + nill %r15,0xfff8 # align stack pointer to 8 + stmg %r0,%r14,SP_R0(%r15) # store gprs 0-14 to kernel stack + stg %r2,SP_ORIG_R2(%r15) # store original content of gpr 2 + mvc SP_RE(16,%r15),__LC_SAVE_AREA # move R14-R15 to stack + stam %a0,%a15,SP_AREGS(%r15) # store access registers to kst. + mvc SP_AREGS+8(12,%r15),__LC_SAVE_AREA+16 # store ac. regs + mvc SP_PSW(16,%r15),__LC_PGM_OLD_PSW # move user PSW to stack + lhi %r0,__LC_SVC_OLD_PSW # store trap indication + st %r0,SP_TRAP(%r15) + xc 0(8,%r15),0(%r15) # clear back chain + + mvi SP_SVC_STEP(%r15),1 # make SP_SVC_STEP nonzero + mvc SP_PGM_OLD_ILC(4,%r15),__LC_PGM_ILC # save program check information + j pgm_system_call # now do the svc +pgm_svcret: + lhi %r0,__LC_PGM_OLD_PSW # set trap indication back to pgm_chk + st %r0,SP_TRAP(%r15) + llgh %r7,SP_PGM_OLD_ILC(%r15) # get ilc from stack + xc SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) + j pgm_no_sv +pgm_sv: + tm __LC_PGM_OLD_PSW+1,0x01 # test problem state bit + jz 1f # skip stack setup save + lg %r15,__LC_KERNEL_STACK # problem state -> load ksp + slr %r14,%r14 + sar %a2,%r14 # set ac.reg. 2 to primary space + lhi %r14,1 + sar %a4,%r14 # set access reg. 4 to home space +1: aghi %r15,-SP_SIZE # make room for registers & psw + nill %r15,0xfff8 # align stack pointer to 8 + stmg %r0,%r14,SP_R0(%r15) # store gprs 0-14 to kernel stack + stg %r2,SP_ORIG_R2(%r15) # store original content of gpr 2 + mvc SP_RE(16,%r15),__LC_SAVE_AREA # move R14-R15 to stack + stam %a0,%a15,SP_AREGS(%r15) # store access registers to kst. + mvc SP_AREGS+8(12,%r15),__LC_SAVE_AREA+16 # store ac. regs + mvc SP_PSW(16,%r15),__LC_PGM_OLD_PSW # move user PSW to stack + lhi %r0,__LC_PGM_OLD_PSW # store trap indication + st %r0,SP_TRAP(%r15) + xc 0(8,%r15),0(%r15) # clear back chain + xc SP_SVC_STEP(4,%r15),SP_SVC_STEP(%r15) + llgh %r7,__LC_PGM_ILC # load instruction length +pgm_no_sv: + llgh %r8,__LC_PGM_INT_CODE # N.B. saved int code used later KEEP it + stosm 48(%r15),0x03 # reenable interrupts + lghi %r3,0x7f + nr %r3,%r8 # clear per-event-bit & move to r3 + je pgm_dn # none of Martins exceptions occured bypass + sll %r3,3 + larl %r9,pgm_check_table + lg %r9,0(%r3,%r9) # load address of handler routine + srl %r3,3 + la %r2,SP_PTREGS(%r15) # address of register-save area + chi %r3,0x4 # protection-exception ? + jne pgm_go # if not, + lg %r5,SP_PSW+8(15) # load psw addr + slgr %r5,%r7 # substract ilc from psw + stg %r5,SP_PSW+8(15) # store corrected psw addr +pgm_go: basr %r14,%r9 # branch to interrupt-handler +pgm_dn: nill %r8,0x80 # check for per exception + je sysc_return + la %r2,SP_PTREGS(15) # address of register-save area + larl %r14,sysc_return # load adr. of system return + jg handle_per_exception + +/* + * IO interrupt handler routine + */ + .globl io_int_handler +io_int_handler: + SAVE_ALL(__LC_IO_OLD_PSW) + la %r2,SP_PTREGS(%r15) # address of register-save area + llgh %r3,__LC_SUBCHANNEL_NR # load subchannel number + llgf %r4,__LC_IO_INT_PARM # load interruption parm + llgf %r5,__LC_IO_INT_WORD # load interruption word + brasl %r14,do_IRQ # call standard irq handler + +io_return: + GET_CURRENT # load pointer to task_struct to R9 + tm SP_PSW+1(%r15),0x01 # returning to user ? + jz io_leave # no-> skip resched & signal + stosm 48(%r15),0x03 # reenable interrupts +# +# check, if bottom-half has to be done +# + l %r0,__LC_IRQ_STAT # get softirq_active + n %r0,__LC_IRQ_STAT+4 # and it with softirq_mask + jnz io_handle_bottom_half +io_return_bh: +# +# check, if reschedule is needed +# + lg %r0,need_resched(%r9) # get need_resched from task_struct + ltgr %r0,%r0 + jnz io_reschedule + icm %r0,15,sigpending(%r9) # get sigpending from task_struct + jnz io_signal_return +io_leave: + stnsm 48(%r15),disable # disable I/O and ext. interrupts + RESTORE_ALL + +# +# call do_softirq and return from syscall, if interrupt-level +# is zero +# +io_handle_bottom_half: + larl %r14,io_return_bh + jg do_softirq # return point is io_return_bh + +# +# call schedule with io_return as return-address +# +io_reschedule: + larl %r14,io_return + jg schedule # call scheduler, return to io_return + +# +# call do_signal before return +# +io_signal_return: + la %r2,SP_PTREGS(%r15) # load pt_regs + slgr %r3,%r3 # clear *oldset + larl %r14,io_leave + jg do_signal # return point is io_leave + +/* + * External interrupt handler routine + */ + .globl ext_int_handler +ext_int_handler: + SAVE_ALL(__LC_EXT_OLD_PSW) + la %r2,SP_PTREGS(%r15) # address of register-save area + llgh %r3,__LC_EXT_INT_CODE # error code + lgr %r1,%r3 # calculate index = code & 0xff + nill %r1,0xff + sll %r1,3 + larl %r9,ext_int_hash + lg %r9,0(%r1,%r9) # get first list entry for hash value + ltgr %r9,%r9 # == NULL ? + jz io_return # yes, nothing to do, exit +ext_int_loop: + ch %r3,16(%r9) # compare external interrupt code + je ext_int_found + lg %r9,0(%r9) # next list entry + ltgr %r9,%r9 + jnz ext_int_loop + j io_return +ext_int_found: + lg %r9,8(%r9) # get handler address + larl %r14,io_return + br %r9 # branch to ext call handler + +/* + * Machine check handler routines + */ + .globl mcck_int_handler +mcck_int_handler: + SAVE_ALL(__LC_MCK_OLD_PSW) + brasl %r14,s390_do_machine_check +mcck_return: + RESTORE_ALL + +#ifdef CONFIG_SMP +/* + * Restart interruption handler, kick starter for additional CPUs + */ + .globl restart_int_handler +restart_int_handler: + lg %r15,__LC_KERNEL_STACK # load ksp + lctlg %c0,%c15,__LC_CREGS_SAVE_AREA # get new ctl regs + lam %a0,%a15,__LC_AREGS_SAVE_AREA + stosm 0(%r15),daton # now we can turn dat on + lmg %r6,%r15,48(%r15) # load registers from clone + jg start_secondary +#else +/* + * If we do not run with SMP enabled, let the new CPU crash ... + */ + .globl restart_int_handler +restart_int_handler: + basr %r1,0 +restart_base: + lpswe restart_crash-restart_base(%r1) + .align 8 +restart_crash: + .long 0x000a0000,0x00000000,0x00000000,0x00000000 +restart_go: +#endif + diff --git a/arch/s390x/kernel/exec32.c b/arch/s390x/kernel/exec32.c new file mode 100644 index 000000000..3e6f44558 --- /dev/null +++ b/arch/s390x/kernel/exec32.c @@ -0,0 +1,85 @@ +/* + * Support for 32-bit Linux for S390 ELF binaries. + * + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Gerhard Tonn (ton@de.ibm.com) + * + * Seperated from binfmt_elf32.c to reduce exports for module enablement. + * + */ + +#include <linux/config.h> +#include <linux/slab.h> +#include <linux/file.h> +#include <linux/mman.h> +#include <linux/a.out.h> +#include <linux/stat.h> +#include <linux/fcntl.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/pagemap.h> +#include <linux/highmem.h> +#include <linux/spinlock.h> +#define __NO_VERSION__ +#include <linux/module.h> + +#include <asm/uaccess.h> +#include <asm/pgalloc.h> +#include <asm/mmu_context.h> + +#ifdef CONFIG_KMOD +#include <linux/kmod.h> +#endif + + +extern void put_dirty_page(struct task_struct * tsk, struct page *page, unsigned long address); + +#undef STACK_TOP +#define STACK_TOP TASK31_SIZE + +int setup_arg_pages32(struct linux_binprm *bprm) +{ + unsigned long stack_base; + struct vm_area_struct *mpnt; + int i; + + stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE; + + bprm->p += stack_base; + if (bprm->loader) + bprm->loader += stack_base; + bprm->exec += stack_base; + + mpnt = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (!mpnt) + return -ENOMEM; + + down(¤t->mm->mmap_sem); + { + mpnt->vm_mm = current->mm; + mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p; + mpnt->vm_end = STACK_TOP; + mpnt->vm_page_prot = PAGE_COPY; + mpnt->vm_flags = VM_STACK_FLAGS; + mpnt->vm_ops = NULL; + mpnt->vm_pgoff = 0; + mpnt->vm_file = NULL; + mpnt->vm_private_data = (void *) 0; + insert_vm_struct(current->mm, mpnt); + current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT; + } + + for (i = 0 ; i < MAX_ARG_PAGES ; i++) { + struct page *page = bprm->page[i]; + if (page) { + bprm->page[i] = NULL; + current->mm->rss++; + put_dirty_page(current,page,stack_base); + } + stack_base += PAGE_SIZE; + } + up(¤t->mm->mmap_sem); + + return 0; +} + diff --git a/arch/s390x/kernel/gdb-stub.c b/arch/s390x/kernel/gdb-stub.c new file mode 100644 index 000000000..06e3adbb0 --- /dev/null +++ b/arch/s390x/kernel/gdb-stub.c @@ -0,0 +1,575 @@ +/* + * arch/s390/kernel/gdb-stub.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Originally written by Glenn Engel, Lake Stevens Instrument Division + * + * Contributed by HP Systems + * + * Modified for SPARC by Stu Grossman, Cygnus Support. + * + * Modified for Linux/MIPS (and MIPS in general) by Andreas Busse + * Send complaints, suggestions etc. to <andy@waldorf-gmbh.de> + * + * Copyright (C) 1995 Andreas Busse + */ + +/* + * To enable debugger support, two things need to happen. One, a + * call to set_debug_traps() is necessary in order to allow any breakpoints + * or error conditions to be properly intercepted and reported to gdb. + * Two, a breakpoint needs to be generated to begin communication. This + * is most easily accomplished by a call to breakpoint(). Breakpoint() + * simulates a breakpoint by executing a BREAK instruction. + * + * + * The following gdb commands are supported: + * + * command function Return value + * + * g return the value of the CPU registers hex data or ENN + * G set the value of the CPU registers OK or ENN + * + * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN + * + * c Resume at current address SNN ( signal NN) + * cAA..AA Continue at address AA..AA SNN + * + * s Step one instruction SNN + * sAA..AA Step one instruction from AA..AA SNN + * + * k kill + * + * ? What was the last sigval ? SNN (signal NN) + * + * + * All commands and responses are sent with a packet which includes a + * checksum. A packet consists of + * + * $<packet info>#<checksum>. + * + * where + * <packet info> :: <characters representing the command or response> + * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> + * + * When a packet is received, it is first acknowledged with either '+' or '-'. + * '+' indicates a successful transfer. '-' indicates a failed transfer. + * + * Example: + * + * Host: Reply: + * $m0,10#2a +$00010203040506070809101112131415#42 + * + */ + +#include <asm/gdb-stub.h> +#include <linux/string.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <asm/pgtable.h> +#include <asm/system.h> + + +/* + * external low-level support routines + */ + +extern int putDebugChar(char c); /* write a single character */ +extern char getDebugChar(void); /* read and return a single char */ +extern void fltr_set_mem_err(void); +extern void trap_low(void); + +/* + * breakpoint and test functions + */ +extern void breakpoint(void); +extern void breakinst(void); + +/* + * local prototypes + */ + +static void getpacket(char *buffer); +static void putpacket(char *buffer); +static int hex(unsigned char ch); +static int hexToInt(char **ptr, int *intValue); +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault); + + +/* + * BUFMAX defines the maximum number of characters in inbound/outbound buffers + * at least NUMREGBYTES*2 are needed for register packets + */ +#define BUFMAX 2048 + +static char input_buffer[BUFMAX]; +static char output_buffer[BUFMAX]; +int gdb_stub_initialised = FALSE; +static const char hexchars[]="0123456789abcdef"; + + +/* + * Convert ch from a hex digit to an int + */ +static int hex(unsigned char ch) +{ + if (ch >= 'a' && ch <= 'f') + return ch-'a'+10; + if (ch >= '0' && ch <= '9') + return ch-'0'; + if (ch >= 'A' && ch <= 'F') + return ch-'A'+10; + return -1; +} + +/* + * scan for the sequence $<data>#<checksum> + */ +static void getpacket(char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* + * wait around for the start character, + * ignore all other characters + */ + while ((ch = (getDebugChar() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + count = 0; + + /* + * now, read until a # or end of buffer is found + */ + while (count < BUFMAX) { + ch = getDebugChar() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex(getDebugChar() & 0x7f) << 4; + xmitcsum |= hex(getDebugChar() & 0x7f); + + if (checksum != xmitcsum) + putDebugChar('-'); /* failed checksum */ + else { + putDebugChar('+'); /* successful transfer */ + + /* + * if a sequence char is present, + * reply the sequence ID + */ + if (buffer[2] == ':') { + putDebugChar(buffer[0]); + putDebugChar(buffer[1]); + + /* + * remove sequence chars from buffer + */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * send the packet in buffer. + */ +static void putpacket(char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* + * $<packet info>#<checksum>. + */ + + do { + putDebugChar('$'); + checksum = 0; + count = 0; + + while ((ch = buffer[count]) != 0) { + if (!(putDebugChar(ch))) + return; + checksum += ch; + count += 1; + } + + putDebugChar('#'); + putDebugChar(hexchars[checksum >> 4]); + putDebugChar(hexchars[checksum & 0xf]); + + } + while ((getDebugChar() & 0x7f) != '+'); +} + + + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * If MAY_FAULT is non-zero, then we will handle memory faults by returning + * a 0, else treat a fault like any other fault in the stub. + */ +static unsigned char *mem2hex(char *mem, char *buf, int count, int may_fault) +{ + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + while (count-- > 0) { + ch = *(mem++); + if (mem_err) + return 0; + *buf++ = hexchars[ch >> 4]; + *buf++ = hexchars[ch & 0xf]; + } + + *buf = 0; + +/* set_mem_fault_trap(0); */ + + return buf; +} + +/* + * convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + */ +static char *hex2mem(char *buf, char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + +/* set_mem_fault_trap(may_fault); */ + + for (i=0; i<count; i++) + { + ch = hex(*buf++) << 4; + ch |= hex(*buf++); + *(mem++) = ch; + if (mem_err) + return 0; + } + +/* set_mem_fault_trap(0); */ + + return mem; +} + + + +/* + * Set up exception handlers for tracing and breakpoints + */ +void set_debug_traps(void) +{ +// unsigned long flags; + unsigned char c; + +// save_and_cli(flags); + /* + * In case GDB is started before us, ack any packets + * (presumably "$?#xx") sitting there. + */ + while((c = getDebugChar()) != '$'); + while((c = getDebugChar()) != '#'); + c = getDebugChar(); /* eat first csum byte */ + c = getDebugChar(); /* eat second csum byte */ + putDebugChar('+'); /* ack it */ + + gdb_stub_initialised = TRUE; +// restore_flags(flags); +} + + +/* + * Trap handler for memory errors. This just sets mem_err to be non-zero. It + * assumes that %l1 is non-zero. This should be safe, as it is doubtful that + * 0 would ever contain code that could mem fault. This routine will skip + * past the faulting instruction after setting mem_err. + */ +extern void fltr_set_mem_err(void) +{ + /* FIXME: Needs to be written... */ +} + + +/* + * While we find nice hex chars, build an int. + * Return number of chars processed. + */ +static int hexToInt(char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + + (*ptr)++; + } + + return (numChars); +} + +void gdb_stub_get_non_pt_regs(gdb_pt_regs *regs) +{ + s390_fp_regs *fpregs=®s->fp_regs; + int has_ieee=save_fp_regs1(fpregs); + + if(!has_ieee) + { + fpregs->fpc=0; + fpregs->fprs[1].d= + fpregs->fprs[3].d= + fpregs->fprs[5].d= + fpregs->fprs[7].d=0; + memset(&fpregs->fprs[8].d,0,sizeof(freg_t)*8); + } +} + +void gdb_stub_set_non_pt_regs(gdb_pt_regs *regs) +{ + restore_fp_regs1(®s->fp_regs); +} + +void gdb_stub_send_signal(int sigval) +{ + char *ptr; + ptr = output_buffer; + + /* + * Send trap type (converted to signal) + */ + *ptr++ = 'S'; + *ptr++ = hexchars[sigval >> 4]; + *ptr++ = hexchars[sigval & 0xf]; + *ptr++ = 0; + putpacket(output_buffer); /* send it off... */ +} + +/* + * This function does all command processing for interfacing to gdb. It + * returns 1 if you should skip the instruction at the trap address, 0 + * otherwise. + */ +void gdb_stub_handle_exception(gdb_pt_regs *regs,int sigval) +{ + int trap; /* Trap type */ + int addr; + int length; + char *ptr; + unsigned long *stack; + + + /* + * reply to host that an exception has occurred + */ + send_signal(sigval); + + /* + * Wait for input from remote GDB + */ + while (1) { + output_buffer[0] = 0; + getpacket(input_buffer); + + switch (input_buffer[0]) + { + case '?': + send_signal(sigval); + continue; + + case 'd': + /* toggle debug flag */ + break; + + /* + * Return the value of the CPU registers + */ + case 'g': + gdb_stub_get_non_pt_regs(regs); + ptr = output_buffer; + ptr= mem2hex((char *)regs,ptr,sizeof(s390_regs_common),FALSE); + ptr= mem2hex((char *)®s->crs[0],ptr,NUM_CRS*CR_SIZE,FALSE); + ptr = mem2hex((char *)®s->fp_regs, ptr,sizeof(s390_fp_regs)); + break; + + /* + * set the value of the CPU registers - return OK + * FIXME: Needs to be written + */ + case 'G': + ptr=input_buffer; + hex2mem (ptr, (char *)regs,sizeof(s390_regs_common), FALSE); + ptr+=sizeof(s390_regs_common)*2; + hex2mem (ptr, (char *)regs->crs[0],NUM_CRS*CR_SIZE, FALSE); + ptr+=NUM_CRS*CR_SIZE*2; + hex2mem (ptr, (char *)regs->fp_regs,sizeof(s390_fp_regs), FALSE); + gdb_stub_set_non_pt_regs(regs); + strcpy(output_buffer,"OK"); + break; + + /* + * mAA..AA,LLLL Read LLLL bytes at address AA..AA + */ + case 'm': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length)) { + if (mem2hex((char *)addr, output_buffer, length, 1)) + break; + strcpy (output_buffer, "E03"); + } else + strcpy(output_buffer,"E01"); + break; + + /* + * MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK + */ + case 'M': + ptr = &input_buffer[1]; + + if (hexToInt(&ptr, &addr) + && *ptr++ == ',' + && hexToInt(&ptr, &length) + && *ptr++ == ':') { + if (hex2mem(ptr, (char *)addr, length, 1)) + strcpy(output_buffer, "OK"); + else + strcpy(output_buffer, "E03"); + } + else + strcpy(output_buffer, "E02"); + break; + + /* + * cAA..AA Continue at address AA..AA(optional) + */ + case 'c': + /* try to read optional parameter, pc unchanged if no parm */ + + ptr = &input_buffer[1]; + if (hexToInt(&ptr, &addr)) + regs->cp0_epc = addr; + + /* + * Need to flush the instruction cache here, as we may + * have deposited a breakpoint, and the icache probably + * has no way of knowing that a data ref to some location + * may have changed something that is in the instruction + * cache. + * NB: We flush both caches, just to be sure... + */ + + flush_cache_all(); + return; + /* NOTREACHED */ + break; + + + /* + * kill the program + */ + case 'k' : + break; /* do nothing */ + + + /* + * Reset the whole machine (FIXME: system dependent) + */ + case 'r': + break; + + + /* + * Step to next instruction + */ + case 's': + /* + * There is no single step insn in the MIPS ISA, so we + * use breakpoints and continue, instead. + */ + single_step(regs); + flush_cache_all(); + return; + /* NOTREACHED */ + + } + break; + + } /* switch */ + + /* + * reply to the request + */ + + putpacket(output_buffer); + + } /* while */ +} + +/* + * This function will generate a breakpoint exception. It is used at the + * beginning of a program to sync up with a debugger and can be used + * otherwise as a quick means to stop program execution and "break" into + * the debugger. + */ +void breakpoint(void) +{ + if (!gdb_stub_initialised) + return; + __asm__ __volatile__( + ".globl breakinst\n" + "breakinst:\t.word %0\n\t" + : + : "i" (S390_BREAKPOINT_U16) + : + ); +} + + + + + + + diff --git a/arch/s390x/kernel/head.S b/arch/s390x/kernel/head.S new file mode 100644 index 000000000..2b72a62c8 --- /dev/null +++ b/arch/s390x/kernel/head.S @@ -0,0 +1,598 @@ +/* + * arch/s390/kernel/head.S + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com), + * Rob van der Heij (rvdhei@iae.nl) + * + * There are 5 different IPL methods + * 1) load the image directly into ram at address 0 and do an PSW restart + * 2) linload will load the image from address 0x10000 to memory 0x10000 + * and start the code thru LPSW 0x0008000080010000 (VM only, deprecated) + * 3) generate the tape ipl header, store the generated image on a tape + * and ipl from it + * In case of SL tape you need to IPL 5 times to get past VOL1 etc + * 4) generate the vm reader ipl header, move the generated image to the + * VM reader (use option NOH!) and do a ipl from reader (VM only) + * 5) direct call of start by the SALIPL loader + * We use the cpuid to distinguish between VM and native ipl + * params for kernel are pushed to 0x10400 (see setup.h) + + Changes: + Okt 25 2000 <rvdheij@iae.nl> + added code to skip HDR and EOF to allow SL tape IPL (5 retries) + changed first CCW from rewind to backspace block + + */ + +#include <linux/config.h> +#include <asm/setup.h> +#include <asm/lowcore.h> + +#ifndef CONFIG_IPL + .org 0 + .long 0x00080000,0x80000000+startup # Just a restart PSW +#else +#ifdef CONFIG_IPL_TAPE +#define IPL_BS 1024 + .org 0 + .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded + .long 0x27000000,0x60000001 # by ipl to addresses 0-23. + .long 0x02000000,0x20000000+IPL_BS # (a PSW and two CCWs). + .long 0x00000000,0x00000000 # external old psw + .long 0x00000000,0x00000000 # svc old psw + .long 0x00000000,0x00000000 # program check old psw + .long 0x00000000,0x00000000 # machine check old psw + .long 0x00000000,0x00000000 # io old psw + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x000a0000,0x00000058 # external new psw + .long 0x000a0000,0x00000060 # svc new psw + .long 0x000a0000,0x00000068 # program check new psw + .long 0x000a0000,0x00000070 # machine check new psw + .long 0x00080000,0x80000000+.Lioint # io new psw + + .org 0x100 +# +# subroutine for loading from tape +# Paramters: +# R1 = device number +# R2 = load address +.Lloader: + st %r14,.Lldret + la %r3,.Lorbread # r3 = address of orb + la %r5,.Lirb # r5 = address of irb + st %r2,.Lccwread+4 # initialize CCW data addresses + lctl %c6,%c6,.Lcr6 + slr %r2,%r2 +.Lldlp: + la %r6,3 # 3 retries +.Lssch: + ssch 0(%r3) # load chunk of IPL_BS bytes + bnz .Llderr +.Lw4end: + bas %r14,.Lwait4io + tm 8(%r5),0x82 # do we have a problem ? + bnz .Lrecov + slr %r7,%r7 + icm %r7,3,10(%r5) # get residual count + lcr %r7,%r7 + la %r7,IPL_BS(%r7) # IPL_BS-residual=#bytes read + ar %r2,%r7 # add to total size + tm 8(%r5),0x01 # found a tape mark ? + bnz .Ldone + l %r0,.Lccwread+4 # update CCW data addresses + ar %r0,%r7 + st %r0,.Lccwread+4 + b .Lldlp +.Ldone: + l %r14,.Lldret + br %r14 # r2 contains the total size +.Lrecov: + bas %r14,.Lsense # do the sensing + bct %r6,.Lssch # dec. retry count & branch + b .Llderr +# +# Sense subroutine +# +.Lsense: + st %r14,.Lsnsret + la %r7,.Lorbsense + ssch 0(%r7) # start sense command + bnz .Llderr + bas %r14,.Lwait4io + l %r14,.Lsnsret + tm 8(%r5),0x82 # do we have a problem ? + bnz .Llderr + br %r14 +# +# Wait for interrupt subroutine +# +.Lwait4io: + lpsw .Lwaitpsw +.Lioint: + c %r1,0xb8 # compare subchannel number + bne .Lwait4io + tsch 0(%r5) + slr %r0,%r0 + tm 8(%r5),0x82 # do we have a problem ? + bnz .Lwtexit + tm 8(%r5),0x04 # got device end ? + bz .Lwait4io +.Lwtexit: + br %r14 +.Llderr: + lpsw .Lcrash + + .align 8 +.Lorbread: + .long 0x00000000,0x0080ff00,.Lccwread + .align 8 +.Lorbsense: + .long 0x00000000,0x0080ff00,.Lccwsense + .align 8 +.Lccwread: + .long 0x02200000+IPL_BS,0x00000000 +.Lccwsense: + .long 0x04200001,0x00000000 +.Lwaitpsw: + .long 0x020a0000,0x80000000+.Lioint + +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lcr6: .long 0xff000000 + .align 8 +.Lcrash:.long 0x000a0000,0x00000000 +.Lldret:.long 0 +.Lsnsret: .long 0 +#endif /* CONFIG_IPL_TAPE */ + +#ifdef CONFIG_IPL_VM +#define IPL_BS 0x730 + .org 0 + .long 0x00080000,0x80000000+iplstart # The first 24 bytes are loaded + .long 0x02000018,0x60000050 # by ipl to addresses 0-23. + .long 0x02000068,0x60000050 # (a PSW and two CCWs). + .fill 80-24,1,0x40 # bytes 24-79 are discarded !! + .long 0x020000f0,0x60000050 # The next 160 byte are loaded + .long 0x02000140,0x60000050 # to addresses 0x18-0xb7 + .long 0x02000190,0x60000050 # They form the continuation + .long 0x020001e0,0x60000050 # of the CCW program started + .long 0x02000230,0x60000050 # by ipl and load the range + .long 0x02000280,0x60000050 # 0x0f0-0x730 from the image + .long 0x020002d0,0x60000050 # to the range 0x0f0-0x730 + .long 0x02000320,0x60000050 # in memory. At the end of + .long 0x02000370,0x60000050 # the channel program the PSW + .long 0x020003c0,0x60000050 # at location 0 is loaded. + .long 0x02000410,0x60000050 # Initial processing starts + .long 0x02000460,0x60000050 # at 0xf0 = iplstart. + .long 0x020004b0,0x60000050 + .long 0x02000500,0x60000050 + .long 0x02000550,0x60000050 + .long 0x020005a0,0x60000050 + .long 0x020005f0,0x60000050 + .long 0x02000640,0x60000050 + .long 0x02000690,0x60000050 + .long 0x020006e0,0x20000050 + + .org 0xf0 +# +# subroutine for loading cards from the reader +# +.Lloader: + la %r3,.Lorb # r2 = address of orb into r2 + la %r5,.Lirb # r4 = address of irb + la %r6,.Lccws + la %r7,20 +.Linit: + st %r2,4(%r6) # initialize CCW data addresses + la %r2,0x50(%r2) + la %r6,8(%r6) + bct 7,.Linit + + lctl %c6,%c6,.Lcr6 # set IO subclass mask + slr %r2,%r2 +.Lldlp: + ssch 0(%r3) # load chunk of 1600 bytes + bnz .Llderr +.Lwait4irq: + mvc 0x78(8),.Lnewpsw # set up IO interrupt psw + lpsw .Lwaitpsw +.Lioint: + c %r1,0xb8 # compare subchannel number + bne .Lwait4irq + tsch 0(%r5) + + slr %r0,%r0 + ic %r0,8(%r5) # get device status + chi %r0,8 # channel end ? + be .Lcont + chi %r0,12 # channel end + device end ? + be .Lcont + + l %r0,4(%r5) + s %r0,8(%r3) # r0/8 = number of ccws executed + mhi %r0,10 # *10 = number of bytes in ccws + lh %r3,10(%r5) # get residual count + sr %r0,%r3 # #ccws*80-residual=#bytes read + ar %r2,%r0 + + br %r14 # r2 contains the total size + +.Lcont: + ahi %r2,0x640 # add 0x640 to total size + la %r6,.Lccws + la %r7,20 +.Lincr: + l %r0,4(%r6) # update CCW data addresses + ahi %r0,0x640 + st %r0,4(%r6) + ahi %r6,8 + bct 7,.Lincr + + b .Lldlp +.Llderr: + lpsw .Lcrash + + .align 8 +.Lorb: .long 0x00000000,0x0080ff00,.Lccws +.Lirb: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +.Lcr6: .long 0xff000000 +.Lloadp:.long 0,0 + .align 8 +.Lcrash:.long 0x000a0000,0x00000000 +.Lnewpsw: + .long 0x00080000,0x80000000+.Lioint +.Lwaitpsw: + .long 0x020a0000,0x80000000+.Lioint + + .align 8 +.Lccws: .rept 19 + .long 0x02600050,0x00000000 + .endr + .long 0x02200050,0x00000000 +#endif /* CONFIG_IPL_VM */ + +iplstart: + lh %r1,0xb8 # test if subchannel number + bct %r1,.Lnoload # is valid + l %r1,0xb8 # load ipl subchannel number + la %r2,IPL_BS # load start address + bas %r14,.Lloader # load rest of ipl image + st %r1,__LC_IPLDEV # store ipl device number + l %r12,.Lparm # pointer to parameter area + +# +# load parameter file from ipl device +# +.Lagain1: + l %r2,INITRD_START+4-PARMAREA(%r12)# use ramdisk location as temp + bas %r14,.Lloader # load parameter file + ltr %r2,%r2 # got anything ? + bz .Lnopf + chi %r2,895 + bnh .Lnotrunc + la %r2,895 +.Lnotrunc: + l %r4,INITRD_START+4-PARMAREA(%r12) + clc 0(3,%r4),.L_hdr # if it is HDRx + bz .Lagain1 # skip dataset header + clc 0(3,%r4),.L_eof # if it is EOFx + bz .Lagain1 # skip dateset trailer + la %r5,0(%r4,%r2) + lr %r3,%r2 +.Lidebc: + tm 0(%r5),0x80 # high order bit set ? + bo .Ldocv # yes -> convert from EBCDIC + ahi %r5,-1 + bct %r3,.Lidebc + b .Lnocv +.Ldocv: + l %r3,.Lcvtab + tr 0(256,%r4),0(%r3) # convert parameters to ascii + tr 256(256,%r4),0(%r3) + tr 512(256,%r4),0(%r3) + tr 768(122,%r4),0(%r3) +.Lnocv: la %r3,COMMAND_LINE-PARMAREA(%r12) # load adr. of command line + mvc 0(256,%r3),0(%r4) + mvc 256(256,%r3),256(%r4) + mvc 512(256,%r3),512(%r4) + mvc 768(122,%r3),768(%r4) + slr %r0,%r0 + b .Lcntlp +.Ldelspc: + ic %r0,0(%r3) + ic %r0,0(%r2,%r3) + chi %r0,0x20 # is it a space ? + be .Lcntlp + ahi %r2,1 + b .Leolp +.Lcntlp: + brct %r2,.Ldelspc +.Leolp: + slr %r0,%r0 + stc %r0,0(%r2,%r3) # terminate buffer +.Lnopf: + +# +# load ramdisk from ipl device +# +.Lagain2: + l %r2,INITRD_START+4-PARMAREA(%r12)# load adr. of ramdisk + bas %r14,.Lloader # load ramdisk + st %r2,INITRD_SIZE+4-PARMAREA(%r12) # store size of ramdisk + ltr %r2,%r2 + bnz .Lrdcont + st %r2,INITRD_START+4-PARMAREA(%r12)# no ramdisk found, null it +.Lrdcont: + l %r2,INITRD_START-PARMAREA(%r12) + clc 0(3,%r2),.L_hdr # skip HDRx and EOFx + bz .Lagain2 + clc 0(3,%r2),.L_eof + bz .Lagain2 + +#ifdef CONFIG_IPL_VM +# +# reset files in VM reader +# + stidp __LC_CPUID # store cpuid + lh %r0,__LC_CPUID+4 # get cpu version + chi %r0,0x7490 # running on P/390 ? + be start # no -> skip reset + la %r2,.Lreset + lhi %r3,26 + .long 0x83230008 +#endif + +# +# everything loaded, go for it +# +.Lnoload: + l %r1,.Lstartup + br %r1 + +.Lparm: .long PARMAREA +.Lstartup: .long startup +.Lcvtab:.long _ebcasc # ebcdic to ascii table +.Lreset:.byte 0xc3,0xc8,0xc1,0xd5,0xc7,0xc5,0x40,0xd9,0xc4,0xd9,0x40 + .byte 0xc1,0xd3,0xd3,0x40,0xd2,0xc5,0xc5,0xd7,0x40,0xd5,0xd6 + .byte 0xc8,0xd6,0xd3,0xc4 # "change rdr all keep nohold" +.L_eof: .long 0xc5d6c600 /* C'EOF' */ +.L_hdr: .long 0xc8c4d900 /* C'HDR' */ +#endif /* CONFIG_IPL */ + +# +# SALIPL loader support. Based on a patch by Rob van der Heij. +# This entry point is called directly from the SALIPL loader and +# doesn't need a builtin ipl record. +# + .org 0x800 + .globl start +start: + stm %r0,%r15,0x07b0 # store registers + basr %r12,%r0 +.base: + l %r11,.parm + l %r8,.cmd # pointer to command buffer + + ltr %r9,%r9 # do we have SALIPL parameters? + bp .sk8x8 + + mvc 0(64,%r8),0x00b0 # copy saved registers + xc 64(240-64,%r8),0(%r8) # remainder of buffer + tr 0(64,%r8),.lowcase + b .gotr +.sk8x8: + mvc 0(240,%r8),0(%r9) # copy iplparms into buffer +.gotr: + l %r10,.tbl # EBCDIC to ASCII table + tr 0(240,%r8),0(%r10) + stidp __LC_CPUID # Are we running on VM maybe + cli __LC_CPUID,0xff + bnz .test + .long 0x83300060 # diag 3,0,x'0060' - storage size + b .done +.test: + mvc 0x68(8),.pgmnw # set up pgm check handler + l %r2,.fourmeg + lr %r3,%r2 + bctr %r3,%r0 # 4M-1 +.loop: iske %r0,%r3 + ar %r3,%r2 +.pgmx: + sr %r3,%r2 + la %r3,1(%r3) +.done: + st %r3,MEMORY_SIZE-PARMAREA(%r11) + slr %r0,%r0 + st %r0,INITRD_SIZE-PARMAREA(%r11) + st %r0,INITRD_START-PARMAREA(%r11) + j startup # continue with startup +.tbl: .long _ebcasc # translate table +.cmd: .long COMMAND_LINE # address of command line buffer +.parm: .long PARMAREA +.fourmeg: .long 0x00400000 # 4M +.pgmnw: .long 0x00080000,.pgmx +.lowcase: + .byte 0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07 + .byte 0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f + .byte 0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17 + .byte 0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f + .byte 0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27 + .byte 0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f + .byte 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37 + .byte 0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f + .byte 0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47 + .byte 0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f + .byte 0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57 + .byte 0x58,0x59,0x5a,0x5b,0x5c,0x5d,0x5e,0x5f + .byte 0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67 + .byte 0x68,0x69,0x6a,0x6b,0x6c,0x6d,0x6e,0x6f + .byte 0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77 + .byte 0x78,0x79,0x7a,0x7b,0x7c,0x7d,0x7e,0x7f + + .byte 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87 + .byte 0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f + .byte 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97 + .byte 0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f + .byte 0xa0,0xa1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7 + .byte 0xa8,0xa9,0xaa,0xab,0xac,0xad,0xae,0xaf + .byte 0xb0,0xb1,0xb2,0xb3,0xb4,0xb5,0xb6,0xb7 + .byte 0xb8,0xb9,0xba,0xbb,0xbc,0xbd,0xbe,0xbf + .byte 0xc0,0x81,0x82,0x83,0x84,0x85,0x86,0x87 # .abcdefg + .byte 0x88,0x89,0xca,0xcb,0xcc,0xcd,0xce,0xcf # hi + .byte 0xd0,0x91,0x92,0x93,0x94,0x95,0x96,0x97 # .jklmnop + .byte 0x98,0x99,0xda,0xdb,0xdc,0xdd,0xde,0xdf # qr + .byte 0xe0,0xe1,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7 # ..stuvwx + .byte 0xa8,0xa9,0xea,0xeb,0xec,0xed,0xee,0xef # yz + .byte 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7 + .byte 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff + +# +# startup-code at 0x10000, running in real mode +# this is called either by the ipl loader or directly by PSW restart +# or linload or SALIPL +# + .org 0x10000 +startup:basr %r13,0 # get base +.LPG1: n %r13,.Lhighoff-.LPG1(%r13) # remove high order bit + lhi %r1,1 # mode 1 = esame + slr %r0,%r0 # set cpuid to zero + sigp %r1,%r0,0x12 # switch to esame mode + sam64 # switch to 64 bit mode + lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers + lg %r12,.Lparm1-.LPG1(%r13) # pointer to parameter area + +# +# find out memory size. +# + mvc 0x1d0(16),.Lpcmem-.LPG1(%r13) # setup program check handler + lghi %r2,1 + sllg %r2,%r2,17 # test in increments of 128KB + lgr %r1,%r2 + aghi %r1,-8 # test last word in the segment +.Lloop: + lg %r0,0(%r1) # test 128KB segment + stg %r0,0(%r1) + agr %r1,%r2 # add 128KB + bno .Lloop-.LPG1(%r13) # r1 < 0x80000000 -> loop +.Lchkmem: + ng %r1,.L4malign-.LPG1(%r13) # align to multiples of 4M + stg %r1,MEMORY_SIZE-PARMAREA(%r12) # store memory size +# +# find out if we are running under VM +# + stidp __LC_CPUID # store cpuid + tm __LC_CPUID,0xff # running under VM ? + bno .Lnovm-.LPG1(%r13) + oi MACHINE_FLAGS+7-PARMAREA(%r12),1 # set VM flag +.Lnovm: + lh %r0,__LC_CPUID+4 # get cpu version + chi %r0,0x7490 # running on a P/390 ? + bne .Lnop390-.LPG1(%r13) + oi MACHINE_FLAGS+7-PARMAREA(%r12),4 # set P/390 flag +.Lnop390: + + lpswe .Lentry-.LPG1(13) # jump to _stext in primary-space, + # virtual and never return ... + .align 16 +.Lentry:.quad 0x0000000180000000,_stext +.Lctl: .quad 0x04b50002 # cr0: various things + .quad 0 # cr1: primary space segment table + .quad 0 # cr2: access register translation + .quad 0 # cr3: instruction authorization + .quad 0 # cr4: instruction authorization + .quad 0 # cr5: various things + .quad 0 # cr6: I/O interrupts + .quad 0 # cr7: secondary space segment table + .quad 0 # cr8: access registers translation + .quad 0 # cr9: tracing off + .quad 0 # cr10: tracing off + .quad 0 # cr11: tracing off + .quad 0 # cr12: tracing off + .quad 0 # cr13: home space segment table + .quad 0xc0000000 # cr14: machine check handling off + .quad 0 # cr15: linkage stack operations +.Lpcmem:.quad 0x0000000180000000,.Lchkmem +.Lflt0: .double 0 +.Lparm1:.quad PARMAREA +.Lhighoff:.long 0x7fffffff +.L4malign:.quad 0xffffffffffc00000 +.Lbigmem:.quad 0x04000000 +.Lmaxchunk:.quad 0x00ffffff + +# +# params at 10400 (setup.h) +# + .org PARMAREA + .quad 0x0100 # ORIG_ROOT_DEV: ramdisk major/minor + .word 0 # MOUNT_ROOT_RDONLY: no + .quad 0 # MEMORY_SIZE + .quad 0 # MACHINE_FLAGS (bit 0:VM) + .quad RAMDISK_ORIGIN # INITRD_START + .quad RAMDISK_SIZE # INITRD_SIZE + .word 0 # RAMDISK_FLAGS + + .org COMMAND_LINE + .byte "root=/dev/ram0 ro" + .byte 0 + +# +# startup-code, running in virtual mode +# + .org 0x10800 + .globl _stext +_stext: basr %r13,0 # get base +.LPG2: +# +# Setup lowcore +# + l %r1,__LC_IPLDEV # load ipl device number + spx .Lprefix-.LPG2(%r13) # set prefix to linux lowcore + st %r1,__LC_IPLDEV # store ipl device number + lg %r15,.Linittu-.LPG2(%r13) + aghi %r15,16384 # init_task_union + 16384 + stg %r15,__LC_KERNEL_STACK # set end of kernel stack + aghi %r15,-160 + xc 0(8,%r15),0(%r15) # set backchain to zero + lghi %r0,-1 + stg %r0,__LC_KERNEL_LEVEL # set interrupt count to -1 +# +# clear bss memory +# + lg %r2,.Lbss_bgn-.LPG2(%r13) # start of bss + lg %r3,.Lbss_end-.LPG2(%r13) # end of bss + sgr %r3,%r2 # length of bss + sgr %r4,%r4 # + sgr %r5,%r5 # set src,length and pad to zero + mvcle %r2,%r4,0 # clear mem + jo .-4 # branch back, if not finish +# check control registers + stctg %c0,%c15,0(%r15) + oc 6(1,%r15),.Locbits+5-.LPG2(%r13) # enable sigp external ints. + oc 4(1,%r15),.Locbits+4-.LPG2(%r13) # low addresss proctection + lctlg %c0,%c15,0(%r15) + +# + lam 0,15,.Laregs-.LPG2(%r13) # load access regs needed by uaccess + brasl %r14,start_kernel # go to C code +# +# We returned from start_kernel ?!? PANIK +# + basr %r13,0 + lpswe .Ldw-.(%r13) # load disabled wait psw +# +.Lstart: .quad start_kernel + .align 8 +.Lprefix: .long init_S390_lowcore +.Linittu: .quad init_task_union +.Lbss_bgn: .quad __bss_start +.Lbss_end: .quad _end +.Locbits: .quad 0x0102040810204080 + .align 4 +.Laregs: .long 0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 + .align 8 +.Ldw: .quad 0x0002000180000000,0x0000000000000000 + diff --git a/arch/s390x/kernel/ieee.h b/arch/s390x/kernel/ieee.h new file mode 100644 index 000000000..ef7cc29de --- /dev/null +++ b/arch/s390x/kernel/ieee.h @@ -0,0 +1,90 @@ +/* + * arch/s390/kernel/ieee.h + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +#include <linux/sched.h> + +static inline void _adddf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd + + current->tss.fprs[R2].fd; +} + +static inline void _subdf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd - + current->tss.fprs[R2].fd; +} + +static inline void _muldf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd * + current->tss.fprs[R2].fd; +} + +static inline void _divdf(int R1,int R2) +{ + current->tss.fprs[R1].fd = current->tss.fprs[R1].fd / + current->tss.fprs[R2].fd; +} + +static inline void _negdf(int R1,int R2) +{ + current->tss.fprs[R1].fd = -current->tss.fprs[R1].fd; +} + +static inline void _fixdfsi(int R1,int R2) +{ + current->tss.regs->gprs[R1] = (__u32) current->tss.fprs[R2].fd; +} + +static inline void _extendsidf(int R1,int R2) +{ + current->tss.fprs[R1].fd = (double) current->tss.regs->gprs[R2]; +} + + +static inline void _addsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff + + current->tss.fprs[R2].ff; +} + +static inline void _subsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff - + current->tss.fprs[R2].ff; +} + +static inline void _mulsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff * + current->tss.fprs[R2].ff; +} + +static inline void _divsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = current->tss.fprs[R1].ff / + current->tss.fprs[R2].ff; +} + +static inline void _negsf(int R1,int R2) +{ + current->tss.fprs[R1].ff = -current->tss.fprs[R1].ff; +} + +static inline void _fixsfsi(int R1,int R2) +{ + current->tss.regs->gprs[R1] = (__u32) current->tss.fprs[R2].ff; +} + +static inline void _extendsisf(int R1,int R2) +{ + current->tss.fprs[R1].ff = (double) current->tss.regs->gprs[R2]; +} + + diff --git a/arch/s390x/kernel/init_task.c b/arch/s390x/kernel/init_task.c new file mode 100644 index 000000000..74cf730b0 --- /dev/null +++ b/arch/s390x/kernel/init_task.c @@ -0,0 +1,32 @@ +/* + * arch/s390/kernel/init_task.c + * + * S390 version + * + * Derived from "arch/i386/kernel/init_task.c" + */ + +#include <linux/mm.h> +#include <linux/sched.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static struct vm_area_struct init_mmap = INIT_MMAP; +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS; +struct mm_struct init_mm = INIT_MM(init_mm); + +/* + * Initial task structure. + * + * We need to make sure that this is 16384-byte aligned due to the + * way process stacks are handled. This is done by making sure + * the linker maps this in the .text segment right after head.S, + * and making head.S ensure the proper alignment. + * + * The things we do for performance.. + */ +union task_union init_task_union __attribute__((aligned(16384))) = + { INIT_TASK(init_task_union.task) }; diff --git a/arch/s390x/kernel/ioctl32.c b/arch/s390x/kernel/ioctl32.c new file mode 100644 index 000000000..fbc63aac2 --- /dev/null +++ b/arch/s390x/kernel/ioctl32.c @@ -0,0 +1,563 @@ +/* + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Gerhard Tonn (ton@de.ibm.com) + * + * Heavily inspired by the 32-bit Sparc compat code which is + * Copyright (C) 2000 Silicon Graphics, Inc. + * Written by Ulf Carlsson (ulfc@engr.sgi.com) + * + */ + +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/init.h> +#include <linux/file.h> +#include <linux/vt.h> +#include <linux/kd.h> +#include <linux/netdevice.h> +#include <linux/route.h> +#include <linux/ext2_fs.h> +#include <asm/types.h> +#include <asm/uaccess.h> +#include <asm/dasd.h> + +#include "linux32.h" + +long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); + +struct hd_geometry32 { + unsigned char heads; + unsigned char sectors; + unsigned short cylinders; + __u32 start; +}; + +static inline int hd_geometry_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct hd_geometry32 *hg32 = (struct hd_geometry32 *) A(arg); + struct hd_geometry hg; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, cmd, (long)&hg); + set_fs (old_fs); + + if (ret) + return ret; + + ret = put_user (hg.heads, &(hg32->heads)); + ret |= __put_user (hg.sectors, &(hg32->sectors)); + ret |= __put_user (hg.cylinders, &(hg32->cylinders)); + ret |= __put_user (hg.start, &(hg32->start)); + + return ret; +} + +struct timeval32 { + int tv_sec; + int tv_usec; +}; + +#define EXT2_IOC32_GETFLAGS _IOR('f', 1, int) +#define EXT2_IOC32_SETFLAGS _IOW('f', 2, int) +#define EXT2_IOC32_GETVERSION _IOR('v', 1, int) +#define EXT2_IOC32_SETVERSION _IOW('v', 2, int) + +struct ifmap32 { + unsigned int mem_start; + unsigned int mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct ifreq32 { +#define IFHWADDRLEN 6 +#define IFNAMSIZ 16 + union { + char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + } ifr_ifrn; + union { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct ifmap32 ifru_map; + char ifru_slave[IFNAMSIZ]; /* Just fits the size */ + char ifru_newname[IFNAMSIZ]; + __u32 ifru_data; + } ifr_ifru; +}; + +struct ifconf32 { + int ifc_len; /* size of buffer */ + __u32 ifcbuf; +}; + +static int dev_ifname32(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct ireq32 *uir32 = (struct ireq32 *) A(arg); + struct net_device *dev; + struct ifreq32 ifr32; + + if (copy_from_user(&ifr32, uir32, sizeof(struct ifreq32))) + return -EFAULT; + + read_lock(&dev_base_lock); + dev = __dev_get_by_index(ifr32.ifr_ifindex); + if (!dev) { + read_unlock(&dev_base_lock); + return -ENODEV; + } + + strcpy(ifr32.ifr_name, dev->name); + read_unlock(&dev_base_lock); + + if (copy_to_user(uir32, &ifr32, sizeof(struct ifreq32))) + return -EFAULT; + + return 0; +} + +static inline int dev_ifconf(unsigned int fd, unsigned int cmd, + unsigned long arg) +{ + struct ioconf32 *uifc32 = (struct ioconf32 *) A(arg); + struct ifconf32 ifc32; + struct ifconf ifc; + struct ifreq32 *ifr32; + struct ifreq *ifr; + mm_segment_t old_fs; + int len; + int err; + + if (copy_from_user(&ifc32, uifc32, sizeof(struct ifconf32))) + return -EFAULT; + + if(ifc32.ifcbuf == 0) { + ifc32.ifc_len = 0; + ifc.ifc_len = 0; + ifc.ifc_buf = NULL; + } else { + ifc.ifc_len = ((ifc32.ifc_len / sizeof (struct ifreq32))) * + sizeof (struct ifreq); + ifc.ifc_buf = kmalloc (ifc.ifc_len, GFP_KERNEL); + if (!ifc.ifc_buf) + return -ENOMEM; + } + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *) A(ifc32.ifcbuf); + len = ifc32.ifc_len / sizeof (struct ifreq32); + while (len--) { + if (copy_from_user(ifr++, ifr32++, sizeof (struct ifreq32))) { + err = -EFAULT; + goto out; + } + } + + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, SIOCGIFCONF, (unsigned long)&ifc); + set_fs (old_fs); + if (err) + goto out; + + ifr = ifc.ifc_req; + ifr32 = (struct ifreq32 *) A(ifc32.ifcbuf); + len = ifc.ifc_len / sizeof (struct ifreq); + ifc32.ifc_len = len * sizeof (struct ifreq32); + + while (len--) { + if (copy_to_user(ifr32++, ifr++, sizeof (struct ifreq32))) { + err = -EFAULT; + goto out; + } + } + + if (copy_to_user(uifc32, &ifc32, sizeof(struct ifconf32))) { + err = -EFAULT; + goto out; + } +out: + if(ifc.ifc_buf != NULL) + kfree (ifc.ifc_buf); + return err; +} + +static inline int dev_ifsioc(unsigned int fd, unsigned int cmd, + unsigned long arg) +{ + struct ifreq32 *uifr = (struct ifreq32 *) A(arg); + struct ifreq ifr; + mm_segment_t old_fs; + int err; + + switch (cmd) { + case SIOCSIFMAP: + err = copy_from_user(&ifr, uifr, sizeof(ifr.ifr_name)); + err |= __get_user(ifr.ifr_map.mem_start, &(uifr->ifr_ifru.ifru_map.mem_start)); + err |= __get_user(ifr.ifr_map.mem_end, &(uifr->ifr_ifru.ifru_map.mem_end)); + err |= __get_user(ifr.ifr_map.base_addr, &(uifr->ifr_ifru.ifru_map.base_addr)); + err |= __get_user(ifr.ifr_map.irq, &(uifr->ifr_ifru.ifru_map.irq)); + err |= __get_user(ifr.ifr_map.dma, &(uifr->ifr_ifru.ifru_map.dma)); + err |= __get_user(ifr.ifr_map.port, &(uifr->ifr_ifru.ifru_map.port)); + if (err) + return -EFAULT; + break; + default: + if (copy_from_user(&ifr, uifr, sizeof(struct ifreq32))) + return -EFAULT; + break; + } + old_fs = get_fs(); + set_fs (KERNEL_DS); + err = sys_ioctl (fd, cmd, (unsigned long)&ifr); + set_fs (old_fs); + if (!err) { + switch (cmd) { + case SIOCGIFFLAGS: + case SIOCGIFMETRIC: + case SIOCGIFMTU: + case SIOCGIFMEM: + case SIOCGIFHWADDR: + case SIOCGIFINDEX: + case SIOCGIFADDR: + case SIOCGIFBRDADDR: + case SIOCGIFDSTADDR: + case SIOCGIFNETMASK: + case SIOCGIFTXQLEN: + if (copy_to_user(uifr, &ifr, sizeof(struct ifreq32))) + return -EFAULT; + break; + case SIOCGIFMAP: + err = copy_to_user(uifr, &ifr, sizeof(ifr.ifr_name)); + err |= __put_user(ifr.ifr_map.mem_start, &(uifr->ifr_ifru.ifru_map.mem_start)); + err |= __put_user(ifr.ifr_map.mem_end, &(uifr->ifr_ifru.ifru_map.mem_end)); + err |= __put_user(ifr.ifr_map.base_addr, &(uifr->ifr_ifru.ifru_map.base_addr)); + err |= __put_user(ifr.ifr_map.irq, &(uifr->ifr_ifru.ifru_map.irq)); + err |= __put_user(ifr.ifr_map.dma, &(uifr->ifr_ifru.ifru_map.dma)); + err |= __put_user(ifr.ifr_map.port, &(uifr->ifr_ifru.ifru_map.port)); + if (err) + err = -EFAULT; + break; + } + } + return err; +} + +struct rtentry32 +{ + unsigned int rt_pad1; + struct sockaddr rt_dst; /* target address */ + struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */ + struct sockaddr rt_genmask; /* target network mask (IP) */ + unsigned short rt_flags; + short rt_pad2; + unsigned int rt_pad3; + unsigned int rt_pad4; + short rt_metric; /* +1 for binary compatibility! */ + unsigned int rt_dev; /* forcing the device at add */ + unsigned int rt_mtu; /* per route MTU/Window */ +#ifndef __KERNEL__ +#define rt_mss rt_mtu /* Compatibility :-( */ +#endif + unsigned int rt_window; /* Window clamping */ + unsigned short rt_irtt; /* Initial RTT */ +}; + +static inline int routing_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct rtentry32 *ur = (struct rtentry32 *) A(arg); + struct rtentry r; + char devname[16]; + u32 rtdev; + int ret; + mm_segment_t old_fs = get_fs(); + + ret = copy_from_user (&r.rt_dst, &(ur->rt_dst), 3 * sizeof(struct sockaddr)); + ret |= __get_user (r.rt_flags, &(ur->rt_flags)); + ret |= __get_user (r.rt_metric, &(ur->rt_metric)); + ret |= __get_user (r.rt_mtu, &(ur->rt_mtu)); + ret |= __get_user (r.rt_window, &(ur->rt_window)); + ret |= __get_user (r.rt_irtt, &(ur->rt_irtt)); + ret |= __get_user (rtdev, &(ur->rt_dev)); + if (rtdev) { + ret |= copy_from_user (devname, (char *) A(rtdev), 15); + r.rt_dev = devname; devname[15] = 0; + } else + r.rt_dev = 0; + if (ret) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_ioctl (fd, cmd, (long)&r); + set_fs (old_fs); + return ret; +} + +static int do_ext2_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + /* These are just misnamed, they actually get/put from/to user an int */ + switch (cmd) { + case EXT2_IOC32_GETFLAGS: cmd = EXT2_IOC_GETFLAGS; break; + case EXT2_IOC32_SETFLAGS: cmd = EXT2_IOC_SETFLAGS; break; + case EXT2_IOC32_GETVERSION: cmd = EXT2_IOC_GETVERSION; break; + case EXT2_IOC32_SETVERSION: cmd = EXT2_IOC_SETVERSION; break; + } + return sys_ioctl(fd, cmd, arg); +} + +static int w_long(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + mm_segment_t old_fs = get_fs(); + int err; + unsigned long val; + + set_fs (KERNEL_DS); + err = sys_ioctl(fd, cmd, (unsigned long)&val); + set_fs (old_fs); + if (!err && put_user((unsigned int) val, (u32 *)arg)) + return -EFAULT; + return err; +} + +struct ioctl32_handler { + unsigned int cmd; + int (*function)(unsigned int, unsigned int, unsigned long); +}; + +struct ioctl32_list { + struct ioctl32_handler handler; + struct ioctl32_list *next; +}; + +#define IOCTL32_DEFAULT(cmd) { { cmd, (void *) sys_ioctl }, 0 } +#define IOCTL32_HANDLER(cmd, handler) { { cmd, (void *) handler }, 0 } + +static struct ioctl32_list ioctl32_handler_table[] = { + IOCTL32_DEFAULT(FIBMAP), + IOCTL32_DEFAULT(FIGETBSZ), + + IOCTL32_DEFAULT(BIODASDDISABLE), + IOCTL32_DEFAULT(BIODASDENABLE), + IOCTL32_DEFAULT(BIODASDRSRV), + IOCTL32_DEFAULT(BIODASDRLSE), + IOCTL32_DEFAULT(BIODASDSLCK), + IOCTL32_DEFAULT(BIODASDRSID), + IOCTL32_DEFAULT(BIODASDFORMAT), + IOCTL32_DEFAULT(BIODASDRWTB), + + IOCTL32_DEFAULT(BLKRRPART), + + IOCTL32_HANDLER(HDIO_GETGEO, hd_geometry_ioctl), + + IOCTL32_DEFAULT(TCGETA), + IOCTL32_DEFAULT(TCSETA), + IOCTL32_DEFAULT(TCSETAW), + IOCTL32_DEFAULT(TCSETAF), + IOCTL32_DEFAULT(TCSBRK), + IOCTL32_DEFAULT(TCXONC), + IOCTL32_DEFAULT(TCFLSH), + IOCTL32_DEFAULT(TCGETS), + IOCTL32_DEFAULT(TCSETS), + IOCTL32_DEFAULT(TCSETSW), + IOCTL32_DEFAULT(TCSETSF), + IOCTL32_DEFAULT(TIOCLINUX), + + IOCTL32_DEFAULT(TIOCGETD), + IOCTL32_DEFAULT(TIOCSETD), + IOCTL32_DEFAULT(TIOCEXCL), + IOCTL32_DEFAULT(TIOCNXCL), + IOCTL32_DEFAULT(TIOCCONS), + IOCTL32_DEFAULT(TIOCGSOFTCAR), + IOCTL32_DEFAULT(TIOCSSOFTCAR), + IOCTL32_DEFAULT(TIOCSWINSZ), + IOCTL32_DEFAULT(TIOCGWINSZ), + IOCTL32_DEFAULT(TIOCMGET), + IOCTL32_DEFAULT(TIOCMBIC), + IOCTL32_DEFAULT(TIOCMBIS), + IOCTL32_DEFAULT(TIOCMSET), + IOCTL32_DEFAULT(TIOCPKT), + IOCTL32_DEFAULT(TIOCNOTTY), + IOCTL32_DEFAULT(TIOCSTI), + IOCTL32_DEFAULT(TIOCOUTQ), + IOCTL32_DEFAULT(TIOCSPGRP), + IOCTL32_DEFAULT(TIOCGPGRP), + IOCTL32_DEFAULT(TIOCSCTTY), + IOCTL32_DEFAULT(TIOCGPTN), + IOCTL32_DEFAULT(TIOCSPTLCK), + IOCTL32_DEFAULT(TIOCGSERIAL), + IOCTL32_DEFAULT(TIOCSSERIAL), + IOCTL32_DEFAULT(TIOCSERGETLSR), + + IOCTL32_DEFAULT(FIOCLEX), + IOCTL32_DEFAULT(FIONCLEX), + IOCTL32_DEFAULT(FIOASYNC), + IOCTL32_DEFAULT(FIONBIO), + IOCTL32_DEFAULT(FIONREAD), + + IOCTL32_DEFAULT(PIO_FONT), + IOCTL32_DEFAULT(GIO_FONT), + IOCTL32_DEFAULT(KDSIGACCEPT), + IOCTL32_DEFAULT(KDGETKEYCODE), + IOCTL32_DEFAULT(KDSETKEYCODE), + IOCTL32_DEFAULT(KIOCSOUND), + IOCTL32_DEFAULT(KDMKTONE), + IOCTL32_DEFAULT(KDGKBTYPE), + IOCTL32_DEFAULT(KDSETMODE), + IOCTL32_DEFAULT(KDGETMODE), + IOCTL32_DEFAULT(KDSKBMODE), + IOCTL32_DEFAULT(KDGKBMODE), + IOCTL32_DEFAULT(KDSKBMETA), + IOCTL32_DEFAULT(KDGKBMETA), + IOCTL32_DEFAULT(KDGKBENT), + IOCTL32_DEFAULT(KDSKBENT), + IOCTL32_DEFAULT(KDGKBSENT), + IOCTL32_DEFAULT(KDSKBSENT), + IOCTL32_DEFAULT(KDGKBDIACR), + IOCTL32_DEFAULT(KDSKBDIACR), + IOCTL32_DEFAULT(KDGKBLED), + IOCTL32_DEFAULT(KDSKBLED), + IOCTL32_DEFAULT(KDGETLED), + IOCTL32_DEFAULT(KDSETLED), + IOCTL32_DEFAULT(GIO_SCRNMAP), + IOCTL32_DEFAULT(PIO_SCRNMAP), + IOCTL32_DEFAULT(GIO_UNISCRNMAP), + IOCTL32_DEFAULT(PIO_UNISCRNMAP), + IOCTL32_DEFAULT(PIO_FONTRESET), + IOCTL32_DEFAULT(PIO_UNIMAPCLR), + + IOCTL32_DEFAULT(VT_SETMODE), + IOCTL32_DEFAULT(VT_GETMODE), + IOCTL32_DEFAULT(VT_GETSTATE), + IOCTL32_DEFAULT(VT_OPENQRY), + IOCTL32_DEFAULT(VT_ACTIVATE), + IOCTL32_DEFAULT(VT_WAITACTIVE), + IOCTL32_DEFAULT(VT_RELDISP), + IOCTL32_DEFAULT(VT_DISALLOCATE), + IOCTL32_DEFAULT(VT_RESIZE), + IOCTL32_DEFAULT(VT_RESIZEX), + IOCTL32_DEFAULT(VT_LOCKSWITCH), + IOCTL32_DEFAULT(VT_UNLOCKSWITCH), + + IOCTL32_HANDLER(SIOCGIFNAME, dev_ifname32), + IOCTL32_HANDLER(SIOCGIFCONF, dev_ifconf), + IOCTL32_HANDLER(SIOCGIFFLAGS, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFFLAGS, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFMETRIC, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFMETRIC, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFMTU, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFMTU, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFMEM, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFMEM, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFHWADDR, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFHWADDR, dev_ifsioc), + IOCTL32_HANDLER(SIOCADDMULTI, dev_ifsioc), + IOCTL32_HANDLER(SIOCDELMULTI, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFINDEX, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFMAP, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFMAP, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFADDR, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFADDR, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFBRDADDR, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFBRDADDR, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFDSTADDR, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFDSTADDR, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFNETMASK, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFNETMASK, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFPFLAGS, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFPFLAGS, dev_ifsioc), + IOCTL32_HANDLER(SIOCGIFTXQLEN, dev_ifsioc), + IOCTL32_HANDLER(SIOCSIFTXQLEN, dev_ifsioc), + IOCTL32_HANDLER(SIOCADDRT, routing_ioctl), + IOCTL32_HANDLER(SIOCDELRT, routing_ioctl), + + IOCTL32_HANDLER(EXT2_IOC32_GETFLAGS, do_ext2_ioctl), + IOCTL32_HANDLER(EXT2_IOC32_SETFLAGS, do_ext2_ioctl), + IOCTL32_HANDLER(EXT2_IOC32_GETVERSION, do_ext2_ioctl), + IOCTL32_HANDLER(EXT2_IOC32_SETVERSION, do_ext2_ioctl), + + IOCTL32_HANDLER(BLKGETSIZE, w_long) + +}; + +#define NR_IOCTL32_HANDLERS (sizeof(ioctl32_handler_table) / \ + sizeof(ioctl32_handler_table[0])) + +static struct ioctl32_list *ioctl32_hash_table[1024]; + +static inline int ioctl32_hash(unsigned int cmd) +{ + return ((cmd >> 6) ^ (cmd >> 4) ^ cmd) & 0x3ff; +} + +int sys32_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + int (*handler)(unsigned int, unsigned int, unsigned long, struct file * filp); + struct file *filp; + struct ioctl32_list *l; + int error; + + l = ioctl32_hash_table[ioctl32_hash(cmd)]; + + error = -EBADF; + + filp = fget(fd); + if (!filp) + return error; + + if (!filp->f_op || !filp->f_op->ioctl) { + error = sys_ioctl (fd, cmd, arg); + goto out; + } + + while (l && l->handler.cmd != cmd) + l = l->next; + + if (l) { + handler = (void *)l->handler.function; + error = handler(fd, cmd, arg, filp); + } else { + error = -EINVAL; + printk("unknown ioctl: %08x\n", cmd); + } +out: + fput(filp); + return error; +} + +static void ioctl32_insert(struct ioctl32_list *entry) +{ + int hash = ioctl32_hash(entry->handler.cmd); + if (!ioctl32_hash_table[hash]) + ioctl32_hash_table[hash] = entry; + else { + struct ioctl32_list *l; + l = ioctl32_hash_table[hash]; + while (l->next) + l = l->next; + l->next = entry; + entry->next = 0; + } +} + +static int __init init_ioctl32(void) +{ + int i; + for (i = 0; i < NR_IOCTL32_HANDLERS; i++) + ioctl32_insert(&ioctl32_handler_table[i]); + return 0; +} + +__initcall(init_ioctl32); diff --git a/arch/s390x/kernel/irq.c b/arch/s390x/kernel/irq.c new file mode 100644 index 000000000..5177a5ae8 --- /dev/null +++ b/arch/s390x/kernel/irq.c @@ -0,0 +1,423 @@ +/* + * arch/s390/kernel/irq.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ingo Adlung (adlung@de.ibm.com) + * + * Derived from "arch/i386/kernel/irq.c" + * Copyright (C) 1992, 1999 Linus Torvalds, Ingo Molnar + * + * S/390 I/O interrupt processing and I/O request processing is + * implemented in arch/s390/kernel/s390io.c + */ +#include <linux/config.h> +#include <linux/ptrace.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/ioport.h> +#include <linux/interrupt.h> +#include <linux/timex.h> +#include <linux/malloc.h> +#include <linux/string.h> +#include <linux/random.h> +#include <linux/smp.h> +#include <linux/threads.h> +#include <linux/smp_lock.h> +#include <linux/init.h> + +#include <asm/system.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/bitops.h> +#include <asm/smp.h> +#include <asm/pgtable.h> +#include <asm/delay.h> +#include <asm/lowcore.h> + +void s390_init_IRQ ( void ); +void s390_free_irq ( unsigned int irq, void *dev_id); +int s390_request_irq( unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id); + +#if 0 +/* + * The following vectors are part of the Linux architecture, there + * is no hardware IRQ pin equivalent for them, they are triggered + * through the ICC by us (IPIs), via smp_message_pass(): + */ +BUILD_SMP_INTERRUPT(reschedule_interrupt) +BUILD_SMP_INTERRUPT(invalidate_interrupt) +BUILD_SMP_INTERRUPT(stop_cpu_interrupt) +BUILD_SMP_INTERRUPT(mtrr_interrupt) +BUILD_SMP_INTERRUPT(spurious_interrupt) +#endif + +#if 0 +int get_irq_list(char *buf) +{ + int i, j; + struct irqaction * action; + char *p = buf; + + p += sprintf(p, " "); + + for (j=0; j<smp_num_cpus; j++) + p += sprintf(p, "CPU%d ",j); + + *p++ = '\n'; + + for (i = 0 ; i < NR_IRQS ; i++) + { + if (ioinfo[i] == INVALID_STORAGE_AREA) + continue; + + action = ioinfo[i]->irq_desc.action; + + if (!action) + continue; + + p += sprintf(p, "%3d: ",i); +#ifndef CONFIG_SMP + p += sprintf(p, "%10u ", kstat_irqs(i)); +#else + for (j=0; j<smp_num_cpus; j++) + p += sprintf( p, "%10u ", + kstat.irqs[cpu_logical_map(j)][i]); +#endif + p += sprintf(p, " %14s", ioinfo[i]->irq_desc.handler->typename); + p += sprintf(p, " %s", action->name); + + for (action=action->next; action; action = action->next) + { + p += sprintf(p, ", %s", action->name); + + } /* endfor */ + + *p++ = '\n'; + + } /* endfor */ + + p += sprintf(p, "NMI: %10u\n", nmi_counter); +#ifdef CONFIG_SMP + p += sprintf(p, "IPI: %10u\n", atomic_read(&ipi_count)); +#endif + + return p - buf; +} +#endif + +/* + * Global interrupt locks for SMP. Allow interrupts to come in on any + * CPU, yet make cli/sti act globally to protect critical regions.. + */ +#ifdef CONFIG_SMP +atomic_t global_irq_holder = ATOMIC_INIT(NO_PROC_ID); +atomic_t global_irq_lock = ATOMIC_INIT(0); +atomic_t global_irq_count = ATOMIC_INIT(0); +atomic_t global_bh_count; + +/* + * "global_cli()" is a special case, in that it can hold the + * interrupts disabled for a longish time, and also because + * we may be doing TLB invalidates when holding the global + * IRQ lock for historical reasons. Thus we may need to check + * SMP invalidate events specially by hand here (but not in + * any normal spinlocks) + * + * Thankfully we don't need this as we can deliver flush tlbs with + * interrupts disabled DJB :-) + */ +#define check_smp_invalidate(cpu) + +static void show(char * str) +{ + int i; + unsigned long *stack; + int cpu = smp_processor_id(); + + printk("\n%s, CPU %d:\n", str, cpu); + printk("irq: %d [%d]\n", + atomic_read(&global_irq_count),local_irq_count(smp_processor_id())); + printk("bh: %d [%d]\n", + atomic_read(&global_bh_count),local_bh_count(smp_processor_id())); + stack = (unsigned long *) &str; + for (i = 40; i ; i--) { + unsigned long x = *++stack; + if (x > (unsigned long) &init_task_union && x < (unsigned long) &vsprintf) { + printk("<[%08lx]> ", x); + } + } +} + +#define MAXCOUNT 100000000 + +static inline void wait_on_bh(void) +{ + int count = MAXCOUNT; + do { + if (!--count) { + show("wait_on_bh"); + count = ~0; + } + /* nothing .. wait for the other bh's to go away */ + } while (atomic_read(&global_bh_count) != 0); +} + +static inline void wait_on_irq(int cpu) +{ + int count = MAXCOUNT; + + for (;;) { + + /* + * Wait until all interrupts are gone. Wait + * for bottom half handlers unless we're + * already executing in one.. + */ + if (!atomic_read(&global_irq_count)) { + if (local_bh_count(cpu)|| + !atomic_read(&global_bh_count)) + break; + } + + /* Duh, we have to loop. Release the lock to avoid deadlocks */ + atomic_set(&global_irq_lock, 0); + + for (;;) { + if (!--count) { + show("wait_on_irq"); + count = ~0; + } + __sti(); + SYNC_OTHER_CORES(cpu); + __cli(); + check_smp_invalidate(cpu); + if (atomic_read(&global_irq_count)) + continue; + if (atomic_read(&global_irq_lock)) + continue; + if (!local_bh_count(cpu) + && atomic_read(&global_bh_count)) + continue; + if (!atomic_compare_and_swap(0, 1, &global_irq_lock)) + break; + } + } +} + +/* + * This is called when we want to synchronize with + * bottom half handlers. We need to wait until + * no other CPU is executing any bottom half handler. + * + * Don't wait if we're already running in an interrupt + * context or are inside a bh handler. + */ +void synchronize_bh(void) +{ + if (atomic_read(&global_bh_count) && !in_interrupt()) + wait_on_bh(); +} + +/* + * This is called when we want to synchronize with + * interrupts. We may for example tell a device to + * stop sending interrupts: but to make sure there + * are no interrupts that are executing on another + * CPU we need to call this function. + */ +void synchronize_irq(void) +{ + if (atomic_read(&global_irq_count)) { + /* Stupid approach */ + cli(); + sti(); + } +} + +static inline void get_irqlock(int cpu) +{ + if (atomic_compare_and_swap(0,1,&global_irq_lock) != 0) { + /* do we already hold the lock? */ + if ( cpu == atomic_read(&global_irq_holder)) + return; + /* Uhhuh.. Somebody else got it. Wait.. */ + do { + check_smp_invalidate(cpu); + } while (atomic_compare_and_swap(0,1,&global_irq_lock) != 0); + } + /* + * We also to make sure that nobody else is running + * in an interrupt context. + */ + wait_on_irq(cpu); + + /* + * Ok, finally.. + */ + atomic_set(&global_irq_holder,cpu); +} + +#define EFLAGS_I_SHIFT 57 + +/* + * A global "cli()" while in an interrupt context + * turns into just a local cli(). Interrupts + * should use spinlocks for the (very unlikely) + * case that they ever want to protect against + * each other. + * + * If we already have local interrupts disabled, + * this will not turn a local disable into a + * global one (problems with spinlocks: this makes + * save_flags+cli+sti usable inside a spinlock). + */ +void __global_cli(void) +{ + unsigned long flags; + + __save_flags(flags); + if (flags & (1UL << EFLAGS_I_SHIFT)) { + int cpu = smp_processor_id(); + __cli(); + if (!in_irq()) + get_irqlock(cpu); + } +} + +void __global_sti(void) +{ + + if (!in_irq()) + release_irqlock(smp_processor_id()); + __sti(); +} + +/* + * SMP flags value to restore to: + * 0 - global cli + * 1 - global sti + * 2 - local cli + * 3 - local sti + */ +unsigned long __global_save_flags(void) +{ + int retval; + int local_enabled; + unsigned long flags; + + __save_flags(flags); + local_enabled = (flags >> EFLAGS_I_SHIFT) & 1; + /* default to local */ + retval = 2 + local_enabled; + + /* check for global flags if we're not in an interrupt */ + if (!in_irq()) + { + if (local_enabled) + retval = 1; + if (atomic_read(&global_irq_holder)== smp_processor_id()) + retval = 0; + } + return retval; +} + +void __global_restore_flags(unsigned long flags) +{ + switch (flags) { + case 0: + __global_cli(); + break; + case 1: + __global_sti(); + break; + case 2: + __cli(); + break; + case 3: + __sti(); + break; + default: + printk("global_restore_flags: %08lx (%08lx)\n", + flags, (&flags)[-1]); + } +} + +#endif + +/* + * Note : This fuction should be eliminated as it doesn't comply with the + * S/390 irq scheme we have implemented ... + */ +int handle_IRQ_event(unsigned int irq, int cpu, struct pt_regs * regs) +{ + struct irqaction * action; + int status; + + status = 0; + + if ( ioinfo[irq] == INVALID_STORAGE_AREA ) + return( -ENODEV); + + action = ioinfo[irq]->irq_desc.action; + + if (action) + { + status |= 1; + + if (!(action->flags & SA_INTERRUPT)) + __sti(); + + do + { + status |= action->flags; + action->handler(irq, action->dev_id, regs); + action = action->next; + } while (action); + + if (status & SA_SAMPLE_RANDOM) + add_interrupt_randomness(irq); + __cli(); + + } /* endif */ + + return status; +} + +void enable_nop(int irq) +{ +} + +void __init init_IRQ(void) +{ + s390_init_IRQ(); +} + + +void free_irq(unsigned int irq, void *dev_id) +{ + s390_free_irq( irq, dev_id); +} + + +int request_irq( unsigned int irq, + void (*handler)(int, void *, struct pt_regs *), + unsigned long irqflags, + const char *devname, + void *dev_id) +{ + return( s390_request_irq( irq, handler, irqflags, devname, dev_id ) ); + +} + +void init_irq_proc(void) +{ + /* For now, nothing... */ +} + diff --git a/arch/s390x/kernel/irqextras390.c b/arch/s390x/kernel/irqextras390.c new file mode 100644 index 000000000..e1e455813 --- /dev/null +++ b/arch/s390x/kernel/irqextras390.c @@ -0,0 +1,35 @@ +/* + * arch/s390/kernel/irqextras390.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Some channel code by D.J. Barrow + */ + +/* + +*/ +#include<asm/irqextras390.h> +#include<asm/lowcore.h> + +#if 0 +// fixchannelprogram is now obselete +void fixchannelprogram(orb_bits_t *orbptr) +{ + __u32 newAddress=orbptr->ccw_program_address; + fixccws(orbptr->ccw_program_address); + orbptr->ccw_program_address=newAddress; + orbptr->ccw_program_address=(ccw1_t *)(((__u32)orbptr->ccw_program_address)); +} +#endif + +void fixccws(ccw1_bits_t *ccwptr) +{ + for(;;ccwptr++) + { // Just hope nobody starts doing prefixing + if(!ccwptr->cc) + break; + } +} diff --git a/arch/s390x/kernel/linux32.c b/arch/s390x/kernel/linux32.c new file mode 100644 index 000000000..668ac46c4 --- /dev/null +++ b/arch/s390x/kernel/linux32.c @@ -0,0 +1,4326 @@ +/* + * arch/s390x/kernel/linux32.c + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Gerhard Tonn (ton@de.ibm.com) + * + * Conversion between 31bit and 64bit native syscalls. + * + * Heavily inspired by the 32-bit Sparc compat code which is + * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) + * + */ + + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/file.h> +#include <linux/signal.h> +#include <linux/utime.h> +#include <linux/resource.h> +#include <linux/times.h> +#include <linux/utsname.h> +#include <linux/timex.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/sem.h> +#include <linux/msg.h> +#include <linux/shm.h> +#include <linux/malloc.h> +#include <linux/uio.h> +#include <linux/nfs_fs.h> +#include <linux/smb_fs.h> +#include <linux/smb_mount.h> +#include <linux/ncp_fs.h> +#include <linux/quota.h> +#include <linux/module.h> +#include <linux/sunrpc/svc.h> +#include <linux/nfsd/nfsd.h> +#include <linux/nfsd/cache.h> +#include <linux/nfsd/xdr.h> +#include <linux/nfsd/syscall.h> +#include <linux/poll.h> +#include <linux/personality.h> +#include <linux/stat.h> +#include <linux/filter.h> +#include <linux/highmem.h> +#include <linux/highuid.h> +#include <linux/mman.h> +#include <linux/ipv6.h> +#include <linux/in.h> +#include <linux/icmpv6.h> +#include <linux/sysctl.h> + +#include <asm/types.h> +#include <asm/ipc.h> +#include <asm/uaccess.h> +#include <asm/semaphore.h> + +#include <net/scm.h> + +#include "linux32.h" + +extern asmlinkage long sys_chown(const char *, uid_t,gid_t); +extern asmlinkage long sys_lchown(const char *, uid_t,gid_t); +extern asmlinkage long sys_fchown(unsigned int, uid_t,gid_t); +extern asmlinkage long sys_setregid(gid_t, gid_t); +extern asmlinkage long sys_setgid(gid_t); +extern asmlinkage long sys_setreuid(uid_t, uid_t); +extern asmlinkage long sys_setuid(uid_t); +extern asmlinkage long sys_setresuid(uid_t, uid_t, uid_t); +extern asmlinkage long sys_setresgid(gid_t, gid_t, gid_t); +extern asmlinkage long sys_setfsuid(uid_t); +extern asmlinkage long sys_setfsgid(gid_t); + +/* For this source file, we want overflow handling. */ + +#undef high2lowuid +#undef high2lowgid +#undef low2highuid +#undef low2highgid +#undef SET_UID16 +#undef SET_GID16 +#undef NEW_TO_OLD_UID +#undef NEW_TO_OLD_GID +#undef SET_OLDSTAT_UID +#undef SET_OLDSTAT_GID +#undef SET_STAT_UID +#undef SET_STAT_GID + +#define high2lowuid(uid) ((uid) > 65535) ? (u16)overflowuid : (u16)(uid) +#define high2lowgid(gid) ((gid) > 65535) ? (u16)overflowgid : (u16)(gid) +#define low2highuid(uid) ((uid) == (u16)-1) ? (uid_t)-1 : (uid_t)(uid) +#define low2highgid(gid) ((gid) == (u16)-1) ? (gid_t)-1 : (gid_t)(gid) +#define SET_UID16(var, uid) var = high2lowuid(uid) +#define SET_GID16(var, gid) var = high2lowgid(gid) +#define NEW_TO_OLD_UID(uid) high2lowuid(uid) +#define NEW_TO_OLD_GID(gid) high2lowgid(gid) +#define SET_OLDSTAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) +#define SET_OLDSTAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) +#define SET_STAT_UID(stat, uid) (stat).st_uid = high2lowuid(uid) +#define SET_STAT_GID(stat, gid) (stat).st_gid = high2lowgid(gid) + +asmlinkage long sys32_chown16(const char * filename, u16 user, u16 group) +{ + return sys_chown(filename, low2highuid(user), low2highgid(group)); +} + +asmlinkage long sys32_lchown16(const char * filename, u16 user, u16 group) +{ + return sys_lchown(filename, low2highuid(user), low2highgid(group)); +} + +asmlinkage long sys32_fchown16(unsigned int fd, u16 user, u16 group) +{ + return sys_fchown(fd, low2highuid(user), low2highgid(group)); +} + +asmlinkage long sys32_setregid16(u16 rgid, u16 egid) +{ + return sys_setregid(low2highgid(rgid), low2highgid(egid)); +} + +asmlinkage long sys32_setgid16(u16 gid) +{ + return sys_setgid((gid_t)gid); +} + +asmlinkage long sys32_setreuid16(u16 ruid, u16 euid) +{ + return sys_setreuid(low2highuid(ruid), low2highuid(euid)); +} + +asmlinkage long sys32_setuid16(u16 uid) +{ + return sys_setuid((uid_t)uid); +} + +asmlinkage long sys32_setresuid16(u16 ruid, u16 euid, u16 suid) +{ + return sys_setresuid(low2highuid(ruid), low2highuid(euid), + low2highuid(suid)); +} + +asmlinkage long sys32_getresuid16(u16 *ruid, u16 *euid, u16 *suid) +{ + int retval; + + if (!(retval = put_user(high2lowuid(current->uid), ruid)) && + !(retval = put_user(high2lowuid(current->euid), euid))) + retval = put_user(high2lowuid(current->suid), suid); + + return retval; +} + +asmlinkage long sys32_setresgid16(u16 rgid, u16 egid, u16 sgid) +{ + return sys_setresgid(low2highgid(rgid), low2highgid(egid), + low2highgid(sgid)); +} + +asmlinkage long sys32_getresgid16(u16 *rgid, u16 *egid, u16 *sgid) +{ + int retval; + + if (!(retval = put_user(high2lowgid(current->gid), rgid)) && + !(retval = put_user(high2lowgid(current->egid), egid))) + retval = put_user(high2lowgid(current->sgid), sgid); + + return retval; +} + +asmlinkage long sys32_setfsuid16(u16 uid) +{ + return sys_setfsuid((uid_t)uid); +} + +asmlinkage long sys32_setfsgid16(u16 gid) +{ + return sys_setfsgid((gid_t)gid); +} + +asmlinkage long sys32_getgroups16(int gidsetsize, u16 *grouplist) +{ + u16 groups[NGROUPS]; + int i,j; + + if (gidsetsize < 0) + return -EINVAL; + i = current->ngroups; + if (gidsetsize) { + if (i > gidsetsize) + return -EINVAL; + for(j=0;j<i;j++) + groups[j] = current->groups[j]; + if (copy_to_user(grouplist, groups, sizeof(u16)*i)) + return -EFAULT; + } + return i; +} + +asmlinkage long sys32_setgroups16(int gidsetsize, u16 *grouplist) +{ + u16 groups[NGROUPS]; + int i; + + if (!capable(CAP_SETGID)) + return -EPERM; + if ((unsigned) gidsetsize > NGROUPS) + return -EINVAL; + if (copy_from_user(groups, grouplist, gidsetsize * sizeof(u16))) + return -EFAULT; + for (i = 0 ; i < gidsetsize ; i++) + current->groups[i] = (gid_t)groups[i]; + current->ngroups = gidsetsize; + return 0; +} + +asmlinkage long sys32_getuid16(void) +{ + return high2lowuid(current->uid); +} + +asmlinkage long sys32_geteuid16(void) +{ + return high2lowuid(current->euid); +} + +asmlinkage long sys32_getgid16(void) +{ + return high2lowgid(current->gid); +} + +asmlinkage long sys32_getegid16(void) +{ + return high2lowgid(current->egid); +} + +/* 32-bit timeval and related flotsam. */ + +struct timeval32 +{ + int tv_sec, tv_usec; +}; + +struct itimerval32 +{ + struct timeval32 it_interval; + struct timeval32 it_value; +}; + +static inline long get_tv32(struct timeval *o, struct timeval32 *i) +{ + return (!access_ok(VERIFY_READ, tv32, sizeof(*tv32)) || + (__get_user(o->tv_sec, &i->tv_sec) | + __get_user(o->tv_usec, &i->tv_usec))); +} + +static inline long put_tv32(struct timeval32 *o, struct timeval *i) +{ + return (!access_ok(VERIFY_WRITE, o, sizeof(*o)) || + (__put_user(i->tv_sec, &o->tv_sec) | + __put_user(i->tv_usec, &o->tv_usec))); +} + +static inline long get_it32(struct itimerval *o, struct itimerval32 *i) +{ + return (!access_ok(VERIFY_READ, i32, sizeof(*i32)) || + (__get_user(o->it_interval.tv_sec, &i->it_interval.tv_sec) | + __get_user(o->it_interval.tv_usec, &i->it_interval.tv_usec) | + __get_user(o->it_value.tv_sec, &i->it_value.tv_sec) | + __get_user(o->it_value.tv_usec, &i->it_value.tv_usec))); +} + +static inline long put_it32(struct itimerval32 *o, struct itimerval *i) +{ + return (!access_ok(VERIFY_WRITE, i32, sizeof(*i32)) || + (__put_user(i->it_interval.tv_sec, &o->it_interval.tv_sec) | + __put_user(i->it_interval.tv_usec, &o->it_interval.tv_usec) | + __put_user(i->it_value.tv_sec, &o->it_value.tv_sec) | + __put_user(i->it_value.tv_usec, &o->it_value.tv_usec))); +} + +struct msgbuf32 { s32 mtype; char mtext[1]; }; + +struct ipc_perm32 +{ + key_t key; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_uid_t32 cuid; + __kernel_gid_t32 cgid; + __kernel_mode_t32 mode; + unsigned short seq; +}; + +struct semid_ds32 { + struct ipc_perm32 sem_perm; /* permissions .. see ipc.h */ + __kernel_time_t32 sem_otime; /* last semop time */ + __kernel_time_t32 sem_ctime; /* last change time */ + u32 sem_base; /* ptr to first semaphore in array */ + u32 sem_pending; /* pending operations to be processed */ + u32 sem_pending_last; /* last pending operation */ + u32 undo; /* undo requests on this array */ + unsigned short sem_nsems; /* no. of semaphores in array */ +}; + +struct semid64_ds32 { + struct ipc64_perm sem_perm; /* this structure is the same on sparc32 and sparc64 */ + unsigned int __pad1; + __kernel_time_t32 sem_otime; + unsigned int __pad2; + __kernel_time_t32 sem_ctime; + u32 sem_nsems; + u32 __unused1; + u32 __unused2; +}; + +struct msqid_ds32 +{ + struct ipc_perm32 msg_perm; + u32 msg_first; + u32 msg_last; + __kernel_time_t32 msg_stime; + __kernel_time_t32 msg_rtime; + __kernel_time_t32 msg_ctime; + u32 wwait; + u32 rwait; + unsigned short msg_cbytes; + unsigned short msg_qnum; + unsigned short msg_qbytes; + __kernel_ipc_pid_t32 msg_lspid; + __kernel_ipc_pid_t32 msg_lrpid; +}; + +struct msqid64_ds32 { + struct ipc64_perm msg_perm; + unsigned int __pad1; + __kernel_time_t32 msg_stime; + unsigned int __pad2; + __kernel_time_t32 msg_rtime; + unsigned int __pad3; + __kernel_time_t32 msg_ctime; + unsigned int msg_cbytes; + unsigned int msg_qnum; + unsigned int msg_qbytes; + __kernel_pid_t32 msg_lspid; + __kernel_pid_t32 msg_lrpid; + unsigned int __unused1; + unsigned int __unused2; +}; + + +struct shmid_ds32 { + struct ipc_perm32 shm_perm; + int shm_segsz; + __kernel_time_t32 shm_atime; + __kernel_time_t32 shm_dtime; + __kernel_time_t32 shm_ctime; + __kernel_ipc_pid_t32 shm_cpid; + __kernel_ipc_pid_t32 shm_lpid; + unsigned short shm_nattch; +}; + +struct shmid64_ds32 { + struct ipc64_perm shm_perm; + unsigned int __pad1; + __kernel_time_t32 shm_atime; + unsigned int __pad2; + __kernel_time_t32 shm_dtime; + unsigned int __pad3; + __kernel_time_t32 shm_ctime; + __kernel_size_t32 shm_segsz; + __kernel_pid_t32 shm_cpid; + __kernel_pid_t32 shm_lpid; + unsigned int shm_nattch; + unsigned int __unused1; + unsigned int __unused2; +}; + + +/* + * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation.. + * + * This is really horribly ugly. + */ +#define IPCOP_MASK(__x) (1UL << (__x)) +static int do_sys32_semctl(int first, int second, int third, void *uptr) +{ + union semun fourth; + u32 pad; + int err = -EINVAL; + + if (!uptr) + goto out; + err = -EFAULT; + if (get_user (pad, (u32 *)uptr)) + goto out; + if(third == SETVAL) + fourth.val = (int)pad; + else + fourth.__pad = (void *)A(pad); + if (IPCOP_MASK (third) & + (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SEM_INFO) | IPCOP_MASK (GETVAL) | + IPCOP_MASK (GETPID) | IPCOP_MASK (GETNCNT) | IPCOP_MASK (GETZCNT) | + IPCOP_MASK (GETALL) | IPCOP_MASK (SETALL) | IPCOP_MASK (IPC_RMID))) { + err = sys_semctl (first, second, third, fourth); + } else if (third & IPC_64) { + struct semid64_ds s; + struct semid64_ds32 *usp = (struct semid64_ds32 *)A(pad); + mm_segment_t old_fs; + int need_back_translation; + + if (third == (IPC_SET|IPC_64)) { + err = get_user (s.sem_perm.uid, &usp->sem_perm.uid); + err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid); + err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode); + if (err) + goto out; + fourth.__pad = &s; + } + need_back_translation = + (IPCOP_MASK (third) & + (IPCOP_MASK (SEM_STAT) | IPCOP_MASK (IPC_STAT))) != 0; + if (need_back_translation) + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + if (need_back_translation) { + int err2 = put_user (s.sem_perm.key, &usp->sem_perm.key); + err2 |= __put_user (high2lowuid(s.sem_perm.uid), &usp->sem_perm.uid); + err2 |= __put_user (high2lowgid(s.sem_perm.gid), &usp->sem_perm.gid); + err2 |= __put_user (high2lowuid(s.sem_perm.cuid), &usp->sem_perm.cuid); + err2 |= __put_user (high2lowgid(s.sem_perm.cgid), &usp->sem_perm.cgid); + err2 |= __put_user (s.sem_perm.mode, &usp->sem_perm.mode); + err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq); + err2 |= __put_user (s.sem_otime, &usp->sem_otime); + err2 |= __put_user (s.sem_ctime, &usp->sem_ctime); + err2 |= __put_user (s.sem_nsems, &usp->sem_nsems); + if (err2) err = -EFAULT; + } + } else { + struct semid_ds s; + struct semid_ds32 *usp = (struct semid_ds32 *)A(pad); + mm_segment_t old_fs; + int need_back_translation; + + if (third == IPC_SET) { + err = get_user (s.sem_perm.uid, &usp->sem_perm.uid); + err |= __get_user (s.sem_perm.gid, &usp->sem_perm.gid); + err |= __get_user (s.sem_perm.mode, &usp->sem_perm.mode); + if (err) + goto out; + fourth.__pad = &s; + } + need_back_translation = + (IPCOP_MASK (third) & + (IPCOP_MASK (SEM_STAT) | IPCOP_MASK (IPC_STAT))) != 0; + if (need_back_translation) + fourth.__pad = &s; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_semctl (first, second, third, fourth); + set_fs (old_fs); + if (need_back_translation) { + int err2 = put_user (s.sem_perm.key, &usp->sem_perm.key); + err2 |= __put_user (high2lowuid(s.sem_perm.uid), &usp->sem_perm.uid); + err2 |= __put_user (high2lowgid(s.sem_perm.gid), &usp->sem_perm.gid); + err2 |= __put_user (high2lowuid(s.sem_perm.cuid), &usp->sem_perm.cuid); + err2 |= __put_user (high2lowgid(s.sem_perm.cgid), &usp->sem_perm.cgid); + err2 |= __put_user (s.sem_perm.mode, &usp->sem_perm.mode); + err2 |= __put_user (s.sem_perm.seq, &usp->sem_perm.seq); + err2 |= __put_user (s.sem_otime, &usp->sem_otime); + err2 |= __put_user (s.sem_ctime, &usp->sem_ctime); + err2 |= __put_user (s.sem_nsems, &usp->sem_nsems); + if (err2) err = -EFAULT; + } + } +out: + return err; +} + +static int do_sys32_msgsnd (int first, int second, int third, void *uptr) +{ + struct msgbuf *p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + struct msgbuf32 *up = (struct msgbuf32 *)uptr; + mm_segment_t old_fs; + int err; + + if (!p) + return -ENOMEM; + err = get_user (p->mtype, &up->mtype); + err |= __copy_from_user (p->mtext, &up->mtext, second); + if (err) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgsnd (first, p, second, third); + set_fs (old_fs); +out: + kfree (p); + return err; +} + +static int do_sys32_msgrcv (int first, int second, int msgtyp, int third, + int version, void *uptr) +{ + struct msgbuf32 *up; + struct msgbuf *p; + mm_segment_t old_fs; + int err; + + if (!version) { + struct ipc_kludge_32 *uipck = (struct ipc_kludge_32 *)uptr; + struct ipc_kludge_32 ipck; + + err = -EINVAL; + if (!uptr) + goto out; + err = -EFAULT; + if (copy_from_user (&ipck, uipck, sizeof (struct ipc_kludge_32))) + goto out; + uptr = (void *)A(ipck.msgp); + msgtyp = ipck.msgtyp; + } + err = -ENOMEM; + p = kmalloc (second + sizeof (struct msgbuf) + 4, GFP_USER); + if (!p) + goto out; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgrcv (first, p, second + 4, msgtyp, third); + set_fs (old_fs); + if (err < 0) + goto free_then_out; + up = (struct msgbuf32 *)uptr; + if (put_user (p->mtype, &up->mtype) || + __copy_to_user (&up->mtext, p->mtext, err)) + err = -EFAULT; +free_then_out: + kfree (p); +out: + return err; +} + +static int do_sys32_msgctl (int first, int second, void *uptr) +{ + int err; + + if (IPCOP_MASK (second) & + (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (MSG_INFO) | + IPCOP_MASK (IPC_RMID))) { + err = sys_msgctl (first, second, (struct msqid_ds *)uptr); + } else if (second & IPC_64) { + struct msqid64_ds m; + struct msqid64_ds32 *up = (struct msqid64_ds32 *)uptr; + mm_segment_t old_fs; + + if (second == (IPC_SET|IPC_64)) { + err = get_user (m.msg_perm.uid, &up->msg_perm.uid); + err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); + err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); + err |= __get_user (m.msg_qbytes, &up->msg_qbytes); + if (err) + goto out; + } + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, (struct msqid_ds *)&m); + set_fs (old_fs); + if (IPCOP_MASK (second) & + (IPCOP_MASK (MSG_STAT) | IPCOP_MASK (IPC_STAT))) { + int err2 = put_user (m.msg_perm.key, &up->msg_perm.key); + err2 |= __put_user (high2lowuid(m.msg_perm.uid), &up->msg_perm.uid); + err2 |= __put_user (high2lowgid(m.msg_perm.gid), &up->msg_perm.gid); + err2 |= __put_user (high2lowuid(m.msg_perm.cuid), &up->msg_perm.cuid); + err2 |= __put_user (high2lowgid(m.msg_perm.cgid), &up->msg_perm.cgid); + err2 |= __put_user (m.msg_perm.mode, &up->msg_perm.mode); + err2 |= __put_user (m.msg_perm.seq, &up->msg_perm.seq); + err2 |= __put_user (m.msg_stime, &up->msg_stime); + err2 |= __put_user (m.msg_rtime, &up->msg_rtime); + err2 |= __put_user (m.msg_ctime, &up->msg_ctime); + err2 |= __put_user (m.msg_cbytes, &up->msg_cbytes); + err2 |= __put_user (m.msg_qnum, &up->msg_qnum); + err2 |= __put_user (m.msg_qbytes, &up->msg_qbytes); + err2 |= __put_user (m.msg_lspid, &up->msg_lspid); + err2 |= __put_user (m.msg_lrpid, &up->msg_lrpid); + if (err2) + err = -EFAULT; + } + } else { + struct msqid_ds m; + struct msqid_ds32 *up = (struct msqid_ds32 *)uptr; + mm_segment_t old_fs; + + if (second == IPC_SET) { + err = get_user (m.msg_perm.uid, &up->msg_perm.uid); + err |= __get_user (m.msg_perm.gid, &up->msg_perm.gid); + err |= __get_user (m.msg_perm.mode, &up->msg_perm.mode); + err |= __get_user (m.msg_qbytes, &up->msg_qbytes); + if (err) + goto out; + } + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_msgctl (first, second, &m); + set_fs (old_fs); + if (IPCOP_MASK (second) & + (IPCOP_MASK (MSG_STAT) | IPCOP_MASK (IPC_STAT))) { + int err2 = put_user (m.msg_perm.key, &up->msg_perm.key); + err2 |= __put_user (high2lowuid(m.msg_perm.uid), &up->msg_perm.uid); + err2 |= __put_user (high2lowgid(m.msg_perm.gid), &up->msg_perm.gid); + err2 |= __put_user (high2lowuid(m.msg_perm.cuid), &up->msg_perm.cuid); + err2 |= __put_user (high2lowgid(m.msg_perm.cgid), &up->msg_perm.cgid); + err2 |= __put_user (m.msg_perm.mode, &up->msg_perm.mode); + err2 |= __put_user (m.msg_perm.seq, &up->msg_perm.seq); + err2 |= __put_user (m.msg_stime, &up->msg_stime); + err2 |= __put_user (m.msg_rtime, &up->msg_rtime); + err2 |= __put_user (m.msg_ctime, &up->msg_ctime); + err2 |= __put_user (m.msg_cbytes, &up->msg_cbytes); + err2 |= __put_user (m.msg_qnum, &up->msg_qnum); + err2 |= __put_user (m.msg_qbytes, &up->msg_qbytes); + err2 |= __put_user (m.msg_lspid, &up->msg_lspid); + err2 |= __put_user (m.msg_lrpid, &up->msg_lrpid); + if (err2) + err = -EFAULT; + } + } + +out: + return err; +} + +static int do_sys32_shmat (int first, int second, int third, int version, void *uptr) +{ + unsigned long raddr; + u32 *uaddr = (u32 *)A((u32)third); + int err = -EINVAL; + + if (version == 1) + goto out; + err = sys_shmat (first, uptr, second, &raddr); + if (err) + goto out; + err = put_user (raddr, uaddr); +out: + return err; +} + +static int do_sys32_shmctl (int first, int second, void *uptr) +{ + int err; + + if (IPCOP_MASK (second) & + (IPCOP_MASK (IPC_INFO) | IPCOP_MASK (SHM_LOCK) | IPCOP_MASK (SHM_UNLOCK) | + IPCOP_MASK (IPC_RMID))) { + if (second == (IPC_INFO|IPC_64)) + second = IPC_INFO; /* So that we don't have to translate it */ + err = sys_shmctl (first, second, (struct shmid_ds *)uptr); + } else if ((second & IPC_64) && second != (SHM_INFO|IPC_64)) { + struct shmid64_ds s; + struct shmid64_ds32 *up = (struct shmid64_ds32 *)uptr; + mm_segment_t old_fs; + + if (second == (IPC_SET|IPC_64)) { + err = get_user (s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); + if (err) + goto out; + } + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, (struct shmid_ds *)&s); + set_fs (old_fs); + if (err < 0) + goto out; + + /* Mask it even in this case so it becomes a CSE. */ + if (IPCOP_MASK (second) & + (IPCOP_MASK (SHM_STAT) | IPCOP_MASK (IPC_STAT))) { + int err2 = put_user (s.shm_perm.key, &up->shm_perm.key); + err2 |= __put_user (high2lowuid(s.shm_perm.uid), &up->shm_perm.uid); + err2 |= __put_user (high2lowgid(s.shm_perm.gid), &up->shm_perm.gid); + err2 |= __put_user (high2lowuid(s.shm_perm.cuid), &up->shm_perm.cuid); + err2 |= __put_user (high2lowgid(s.shm_perm.cgid), &up->shm_perm.cgid); + err2 |= __put_user (s.shm_perm.mode, &up->shm_perm.mode); + err2 |= __put_user (s.shm_perm.seq, &up->shm_perm.seq); + err2 |= __put_user (s.shm_atime, &up->shm_atime); + err2 |= __put_user (s.shm_dtime, &up->shm_dtime); + err2 |= __put_user (s.shm_ctime, &up->shm_ctime); + err2 |= __put_user (s.shm_segsz, &up->shm_segsz); + err2 |= __put_user (s.shm_nattch, &up->shm_nattch); + err2 |= __put_user (s.shm_cpid, &up->shm_cpid); + err2 |= __put_user (s.shm_lpid, &up->shm_lpid); + if (err2) + err = -EFAULT; + } + } else { + struct shmid_ds s; + struct shmid_ds32 *up = (struct shmid_ds32 *)uptr; + mm_segment_t old_fs; + + second &= ~IPC_64; + if (second == IPC_SET) { + err = get_user (s.shm_perm.uid, &up->shm_perm.uid); + err |= __get_user (s.shm_perm.gid, &up->shm_perm.gid); + err |= __get_user (s.shm_perm.mode, &up->shm_perm.mode); + if (err) + goto out; + } + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_shmctl (first, second, &s); + set_fs (old_fs); + if (err < 0) + goto out; + + /* Mask it even in this case so it becomes a CSE. */ + if (second == SHM_INFO) { + struct shm_info32 { + int used_ids; + u32 shm_tot, shm_rss, shm_swp; + u32 swap_attempts, swap_successes; + } *uip = (struct shm_info32 *)uptr; + struct shm_info *kp = (struct shm_info *)&s; + int err2 = put_user (kp->used_ids, &uip->used_ids); + err2 |= __put_user (kp->shm_tot, &uip->shm_tot); + err2 |= __put_user (kp->shm_rss, &uip->shm_rss); + err2 |= __put_user (kp->shm_swp, &uip->shm_swp); + err2 |= __put_user (kp->swap_attempts, &uip->swap_attempts); + err2 |= __put_user (kp->swap_successes, &uip->swap_successes); + if (err2) + err = -EFAULT; + } else if (IPCOP_MASK (second) & + (IPCOP_MASK (SHM_STAT) | IPCOP_MASK (IPC_STAT))) { + int err2 = put_user (s.shm_perm.key, &up->shm_perm.key); + err2 |= __put_user (high2lowuid(s.shm_perm.uid), &up->shm_perm.uid); + err2 |= __put_user (high2lowgid(s.shm_perm.gid), &up->shm_perm.gid); + err2 |= __put_user (high2lowuid(s.shm_perm.cuid), &up->shm_perm.cuid); + err2 |= __put_user (high2lowgid(s.shm_perm.cgid), &up->shm_perm.cgid); + err2 |= __put_user (s.shm_perm.mode, &up->shm_perm.mode); + err2 |= __put_user (s.shm_perm.seq, &up->shm_perm.seq); + err2 |= __put_user (s.shm_atime, &up->shm_atime); + err2 |= __put_user (s.shm_dtime, &up->shm_dtime); + err2 |= __put_user (s.shm_ctime, &up->shm_ctime); + err2 |= __put_user (s.shm_segsz, &up->shm_segsz); + err2 |= __put_user (s.shm_nattch, &up->shm_nattch); + err2 |= __put_user (s.shm_cpid, &up->shm_cpid); + err2 |= __put_user (s.shm_lpid, &up->shm_lpid); + if (err2) + err = -EFAULT; + } + } +out: + return err; +} + +asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth) +{ + int version, err; + + version = call >> 16; /* hack for backward compatibility */ + call &= 0xffff; + + if(version) + return -EINVAL; + + if (call <= SEMCTL) + switch (call) { + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + err = sys_semop (first, (struct sembuf *)AA(ptr), second); + goto out; + case SEMGET: + err = sys_semget (first, second, third); + goto out; + case SEMCTL: + err = do_sys32_semctl (first, second, third, (void *)AA(ptr)); + goto out; + default: + err = -EINVAL; + goto out; + }; + if (call <= MSGCTL) + switch (call) { + case MSGSND: + err = do_sys32_msgsnd (first, second, third, (void *)AA(ptr)); + goto out; + case MSGRCV: + err = do_sys32_msgrcv (first, second, 0, third, + version, (void *)AA(ptr)); + goto out; + case MSGGET: + err = sys_msgget ((key_t) first, second); + goto out; + case MSGCTL: + err = do_sys32_msgctl (first, second, (void *)AA(ptr)); + goto out; + default: + err = -EINVAL; + goto out; + } + if (call <= SHMCTL) + switch (call) { + case SHMAT: + err = do_sys32_shmat (first, second, third, + version, (void *)AA(ptr)); + goto out; + case SHMDT: + err = sys_shmdt ((char *)AA(ptr)); + goto out; + case SHMGET: + err = sys_shmget (first, second, third); + goto out; + case SHMCTL: + err = do_sys32_shmctl (first, second, (void *)AA(ptr)); + goto out; + default: + err = -EINVAL; + goto out; + } + + err = -EINVAL; + +out: + return err; +} + +static inline int get_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = get_user(kfl->l_type, &ufl->l_type); + err |= __get_user(kfl->l_whence, &ufl->l_whence); + err |= __get_user(kfl->l_start, &ufl->l_start); + err |= __get_user(kfl->l_len, &ufl->l_len); + err |= __get_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +static inline int put_flock(struct flock *kfl, struct flock32 *ufl) +{ + int err; + + err = __put_user(kfl->l_type, &ufl->l_type); + err |= __put_user(kfl->l_whence, &ufl->l_whence); + err |= __put_user(kfl->l_start, &ufl->l_start); + err |= __put_user(kfl->l_len, &ufl->l_len); + err |= __put_user(kfl->l_pid, &ufl->l_pid); + return err; +} + +extern asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg); + +asmlinkage long sys32_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case F_GETLK: + case F_SETLK: + case F_SETLKW: + { + struct flock f; + mm_segment_t old_fs; + long ret; + + if(get_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + old_fs = get_fs(); set_fs (KERNEL_DS); + ret = sys_fcntl(fd, cmd, (unsigned long)&f); + set_fs (old_fs); + if (ret) return ret; + if (f.l_start >= 0x7fffffffUL || + f.l_len >= 0x7fffffffUL || + f.l_start + f.l_len >= 0x7fffffffUL) + return -EOVERFLOW; + if(put_flock(&f, (struct flock32 *)arg)) + return -EFAULT; + return 0; + } + default: + return sys_fcntl(fd, cmd, (unsigned long)arg); + } +} + +asmlinkage long sys32_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + if (cmd >= F_GETLK64 && cmd <= F_SETLKW64) + return sys_fcntl(fd, cmd + F_GETLK - F_GETLK64, arg); + return sys32_fcntl(fd, cmd, arg); +} + +struct dqblk32 { + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u32 dqb_curblocks; + __u32 dqb_ihardlimit; + __u32 dqb_isoftlimit; + __u32 dqb_curinodes; + __kernel_time_t32 dqb_btime; + __kernel_time_t32 dqb_itime; +}; + +extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); + +asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) +{ + int cmds = cmd >> SUBCMDSHIFT; + int err; + struct mem_dqblk d; + mm_segment_t old_fs; + char *spec; + + switch (cmds) { + case Q_GETQUOTA: + break; + case Q_SETQUOTA: + case Q_SETUSE: + case Q_SETQLIM: + if (copy_from_user (&d, (struct dqblk32 *)addr, + sizeof (struct dqblk32))) + return -EFAULT; + d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + break; + default: + return sys_quotactl(cmd, special, + id, (caddr_t)addr); + } + spec = getname (special); + err = PTR_ERR(spec); + if (IS_ERR(spec)) return err; + old_fs = get_fs (); + set_fs (KERNEL_DS); + err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + set_fs (old_fs); + putname (spec); + if (cmds == Q_GETQUOTA) { + __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; + ((struct dqblk32 *)&d)->dqb_itime = i; + ((struct dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct dqblk32 *)addr, &d, + sizeof (struct dqblk32))) + return -EFAULT; + } + return err; +} + +static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) +{ + int err; + + err = put_user (kbuf->f_type, &ubuf->f_type); + err |= __put_user (kbuf->f_bsize, &ubuf->f_bsize); + err |= __put_user (kbuf->f_blocks, &ubuf->f_blocks); + err |= __put_user (kbuf->f_bfree, &ubuf->f_bfree); + err |= __put_user (kbuf->f_bavail, &ubuf->f_bavail); + err |= __put_user (kbuf->f_files, &ubuf->f_files); + err |= __put_user (kbuf->f_ffree, &ubuf->f_ffree); + err |= __put_user (kbuf->f_namelen, &ubuf->f_namelen); + err |= __put_user (kbuf->f_fsid.val[0], &ubuf->f_fsid.val[0]); + err |= __put_user (kbuf->f_fsid.val[1], &ubuf->f_fsid.val[1]); + return err; +} + +extern asmlinkage int sys_statfs(const char * path, struct statfs * buf); + +asmlinkage int sys32_statfs(const char * path, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + char *pth; + + pth = getname (path); + ret = PTR_ERR(pth); + if (!IS_ERR(pth)) { + set_fs (KERNEL_DS); + ret = sys_statfs((const char *)pth, &s); + set_fs (old_fs); + putname (pth); + if (put_statfs(buf, &s)) + return -EFAULT; + } + return ret; +} + +extern asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf); + +asmlinkage int sys32_fstatfs(unsigned int fd, struct statfs32 *buf) +{ + int ret; + struct statfs s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_fstatfs(fd, &s); + set_fs (old_fs); + if (put_statfs(buf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_truncate(const char * path, unsigned long length); +extern asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length); + +asmlinkage int sys32_truncate64(const char * path, unsigned long high, unsigned long low) +{ + if ((int)high < 0) + return -EINVAL; + else + return sys_truncate(path, (high << 32) | low); +} + +asmlinkage int sys32_ftruncate64(unsigned int fd, unsigned long high, unsigned long low) +{ + if ((int)high < 0) + return -EINVAL; + else + return sys_ftruncate(fd, (high << 32) | low); +} + +extern asmlinkage int sys_utime(char * filename, struct utimbuf * times); + +struct utimbuf32 { + __kernel_time_t32 actime, modtime; +}; + +asmlinkage int sys32_utime(char * filename, struct utimbuf32 *times) +{ + struct utimbuf t; + mm_segment_t old_fs; + int ret; + char *filenam; + + if (!times) + return sys_utime(filename, NULL); + if (get_user (t.actime, ×->actime) || + __get_user (t.modtime, ×->modtime)) + return -EFAULT; + filenam = getname (filename); + ret = PTR_ERR(filenam); + if (!IS_ERR(filenam)) { + old_fs = get_fs(); + set_fs (KERNEL_DS); + ret = sys_utime(filenam, &t); + set_fs (old_fs); + putname (filenam); + } + return ret; +} + +struct iovec32 { u32 iov_base; __kernel_size_t32 iov_len; }; + +typedef ssize_t (*io_fn_t)(struct file *, char *, size_t, loff_t *); +typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *); + +static long do_readv_writev32(int type, struct file *file, + const struct iovec32 *vector, u32 count) +{ + unsigned long tot_len; + struct iovec iovstack[UIO_FASTIOV]; + struct iovec *iov=iovstack, *ivp; + struct inode *inode; + long retval, i; + io_fn_t fn; + iov_fn_t fnv; + + /* First get the "struct iovec" from user memory and + * verify all the pointers + */ + if (!count) + return 0; + if (verify_area(VERIFY_READ, vector, sizeof(struct iovec32)*count)) + return -EFAULT; + if (count > UIO_MAXIOV) + return -EINVAL; + if (count > UIO_FASTIOV) { + iov = kmalloc(count*sizeof(struct iovec), GFP_KERNEL); + if (!iov) + return -ENOMEM; + } + + tot_len = 0; + i = count; + ivp = iov; + while(i > 0) { + u32 len; + u32 buf; + + __get_user(len, &vector->iov_len); + __get_user(buf, &vector->iov_base); + tot_len += len; + ivp->iov_base = (void *)A(buf); + ivp->iov_len = (__kernel_size_t) len; + vector++; + ivp++; + i--; + } + + inode = file->f_dentry->d_inode; + /* VERIFY_WRITE actually means a read, as we write to user space */ + retval = locks_verify_area((type == VERIFY_WRITE + ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE), + inode, file, file->f_pos, tot_len); + if (retval) + goto out; + + /* VERIFY_WRITE actually means a read, as we write to user space */ + fnv = (type == VERIFY_WRITE ? file->f_op->readv : file->f_op->writev); + if (fnv) { + retval = fnv(file, iov, count, &file->f_pos); + goto out; + } + + fn = (type == VERIFY_WRITE ? file->f_op->read : + (io_fn_t) file->f_op->write); + + ivp = iov; + while (count > 0) { + void * base; + int len, nr; + + base = ivp->iov_base; + len = ivp->iov_len; + ivp++; + count--; + nr = fn(file, base, len, &file->f_pos); + if (nr < 0) { + if (!retval) + retval = nr; + break; + } + retval += nr; + if (nr != len) + break; + } +out: + if (iov != iovstack) + kfree(iov); + + return retval; +} + +asmlinkage long sys32_readv(int fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + long ret = -EBADF; + + file = fget(fd); + if(!file) + goto bad_file; + + if (file->f_op && (file->f_mode & FMODE_READ) && + (file->f_op->readv || file->f_op->read)) + ret = do_readv_writev32(VERIFY_WRITE, file, vector, count); + fput(file); + +bad_file: + return ret; +} + +asmlinkage long sys32_writev(int fd, struct iovec32 *vector, u32 count) +{ + struct file *file; + int ret = -EBADF; + + file = fget(fd); + if(!file) + goto bad_file; + if (file->f_op && (file->f_mode & FMODE_WRITE) && + (file->f_op->writev || file->f_op->write)) + ret = do_readv_writev32(VERIFY_READ, file, vector, count); + fput(file); + +bad_file: + return ret; +} + +/* readdir & getdents */ + +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+sizeof(u32)-1) & ~(sizeof(u32)-1)) + +struct old_linux_dirent32 { + u32 d_ino; + u32 d_offset; + unsigned short d_namlen; + char d_name[1]; +}; + +struct readdir_callback32 { + struct old_linux_dirent32 * dirent; + int count; +}; + +static int fillonedir(void * __buf, const char * name, int namlen, + off_t offset, ino_t ino, unsigned int d_type) +{ + struct readdir_callback32 * buf = (struct readdir_callback32 *) __buf; + struct old_linux_dirent32 * dirent; + + if (buf->count) + return -EINVAL; + buf->count++; + dirent = buf->dirent; + put_user(ino, &dirent->d_ino); + put_user(offset, &dirent->d_offset); + put_user(namlen, &dirent->d_namlen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + return 0; +} + +asmlinkage int old32_readdir(unsigned int fd, struct old_linux_dirent32 *dirent, unsigned int count) +{ + int error = -EBADF; + struct file * file; + struct readdir_callback32 buf; + + file = fget(fd); + if (!file) + goto out; + + buf.count = 0; + buf.dirent = dirent; + + error = vfs_readdir(file, fillonedir, &buf); + if (error < 0) + goto out_putf; + error = buf.count; + +out_putf: + fput(file); +out: + return error; +} + +struct linux_dirent32 { + u32 d_ino; + u32 d_off; + unsigned short d_reclen; + char d_name[1]; +}; + +struct getdents_callback32 { + struct linux_dirent32 * current_dir; + struct linux_dirent32 * previous; + int count; + int error; +}; + +static int filldir(void * __buf, const char * name, int namlen, off_t offset, ino_t ino, + unsigned int d_type) +{ + struct linux_dirent32 * dirent; + struct getdents_callback32 * buf = (struct getdents_callback32 *) __buf; + int reclen = ROUND_UP(NAME_OFFSET(dirent) + namlen + 1); + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) + put_user(offset, &dirent->d_off); + dirent = buf->current_dir; + buf->previous = dirent; + put_user(ino, &dirent->d_ino); + put_user(reclen, &dirent->d_reclen); + copy_to_user(dirent->d_name, name, namlen); + put_user(0, dirent->d_name + namlen); + ((char *) dirent) += reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} + +asmlinkage int sys32_getdents(unsigned int fd, struct linux_dirent32 *dirent, unsigned int count) +{ + struct file * file; + struct linux_dirent32 * lastdirent; + struct getdents_callback32 buf; + int error = -EBADF; + + file = fget(fd); + if (!file) + goto out; + + buf.current_dir = dirent; + buf.previous = NULL; + buf.count = count; + buf.error = 0; + + error = vfs_readdir(file, filldir, &buf); + if (error < 0) + goto out_putf; + lastdirent = buf.previous; + error = buf.error; + if(lastdirent) { + put_user(file->f_pos, &lastdirent->d_off); + error = count - buf.count; + } +out_putf: + fput(file); +out: + return error; +} + +/* end of readdir & getdents */ + +/* + * Ooo, nasty. We need here to frob 32-bit unsigned longs to + * 64-bit unsigned longs. + */ + +static inline int +get_fd_set32(unsigned long n, unsigned long *fdset, u32 *ufdset) +{ + if (ufdset) { + unsigned long odd; + + if (verify_area(VERIFY_WRITE, ufdset, n*sizeof(u32))) + return -EFAULT; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + __get_user(l, ufdset); + __get_user(h, ufdset+1); + ufdset += 2; + *fdset++ = h << 32 | l; + n -= 2; + } + if (odd) + __get_user(*fdset, ufdset); + } else { + /* Tricky, must clear full unsigned long in the + * kernel fdset at the end, this makes sure that + * actually happens. + */ + memset(fdset, 0, ((n + 1) & ~1)*sizeof(u32)); + } + return 0; +} + +static inline void +set_fd_set32(unsigned long n, u32 *ufdset, unsigned long *fdset) +{ + unsigned long odd; + + if (!ufdset) + return; + + odd = n & 1UL; + n &= ~1UL; + while (n) { + unsigned long h, l; + l = *fdset++; + h = l >> 32; + __put_user(l, ufdset); + __put_user(h, ufdset+1); + ufdset += 2; + n -= 2; + } + if (odd) + __put_user(*fdset, ufdset); +} + +#define MAX_SELECT_SECONDS \ + ((unsigned long) (MAX_SCHEDULE_TIMEOUT / HZ)-1) + +asmlinkage int sys32_select(int n, u32 *inp, u32 *outp, u32 *exp, u32 tvp_x) +{ + fd_set_bits fds; + struct timeval32 *tvp = (struct timeval32 *)AA(tvp_x); + char *bits; + unsigned long nn; + long timeout; + int ret, size; + + timeout = MAX_SCHEDULE_TIMEOUT; + if (tvp) { + time_t sec, usec; + + if ((ret = verify_area(VERIFY_READ, tvp, sizeof(*tvp))) + || (ret = __get_user(sec, &tvp->tv_sec)) + || (ret = __get_user(usec, &tvp->tv_usec))) + goto out_nofds; + + ret = -EINVAL; + if(sec < 0 || usec < 0) + goto out_nofds; + + if ((unsigned long) sec < MAX_SELECT_SECONDS) { + timeout = (usec + 1000000/HZ - 1) / (1000000/HZ); + timeout += sec * (unsigned long) HZ; + } + } + + ret = -EINVAL; + if (n < 0) + goto out_nofds; + if (n > current->files->max_fdset) + n = current->files->max_fdset; + + /* + * We need 6 bitmaps (in/out/ex for both incoming and outgoing), + * since we used fdset we need to allocate memory in units of + * long-words. + */ + ret = -ENOMEM; + size = FDS_BYTES(n); + bits = kmalloc(6 * size, GFP_KERNEL); + if (!bits) + goto out_nofds; + fds.in = (unsigned long *) bits; + fds.out = (unsigned long *) (bits + size); + fds.ex = (unsigned long *) (bits + 2*size); + fds.res_in = (unsigned long *) (bits + 3*size); + fds.res_out = (unsigned long *) (bits + 4*size); + fds.res_ex = (unsigned long *) (bits + 5*size); + + nn = (n + 8*sizeof(u32) - 1) / (8*sizeof(u32)); + if ((ret = get_fd_set32(nn, fds.in, inp)) || + (ret = get_fd_set32(nn, fds.out, outp)) || + (ret = get_fd_set32(nn, fds.ex, exp))) + goto out; + zero_fd_set(n, fds.res_in); + zero_fd_set(n, fds.res_out); + zero_fd_set(n, fds.res_ex); + + ret = do_select(n, &fds, &timeout); + + if (tvp && !(current->personality & STICKY_TIMEOUTS)) { + time_t sec = 0, usec = 0; + if (timeout) { + sec = timeout / HZ; + usec = timeout % HZ; + usec *= (1000000/HZ); + } + put_user(sec, &tvp->tv_sec); + put_user(usec, &tvp->tv_usec); + } + + if (ret < 0) + goto out; + if (!ret) { + ret = -ERESTARTNOHAND; + if (signal_pending(current)) + goto out; + ret = 0; + } + + set_fd_set32(nn, inp, fds.res_in); + set_fd_set32(nn, outp, fds.res_out); + set_fd_set32(nn, exp, fds.res_ex); + +out: + kfree(bits); +out_nofds: + return ret; +} + +static int cp_new_stat32(struct inode *inode, struct stat32 *statbuf) +{ + unsigned long ino, blksize, blocks; + kdev_t dev, rdev; + umode_t mode; + nlink_t nlink; + uid_t uid; + gid_t gid; + off_t size; + time_t atime, mtime, ctime; + int err; + + /* Stream the loads of inode data into the load buffer, + * then we push it all into the store buffer below. This + * should give optimal cache performance. + */ + ino = inode->i_ino; + dev = inode->i_dev; + mode = inode->i_mode; + nlink = inode->i_nlink; + uid = inode->i_uid; + gid = inode->i_gid; + rdev = inode->i_rdev; + size = inode->i_size; + atime = inode->i_atime; + mtime = inode->i_mtime; + ctime = inode->i_ctime; + blksize = inode->i_blksize; + blocks = inode->i_blocks; + + err = put_user(kdev_t_to_nr(dev), &statbuf->st_dev); + err |= put_user(ino, &statbuf->st_ino); + err |= put_user(mode, &statbuf->st_mode); + err |= put_user(nlink, &statbuf->st_nlink); + err |= put_user(high2lowuid(uid), &statbuf->st_uid); + err |= put_user(high2lowgid(gid), &statbuf->st_gid); + err |= put_user(kdev_t_to_nr(rdev), &statbuf->st_rdev); + err |= put_user(size, &statbuf->st_size); + err |= put_user(atime, &statbuf->st_atime); + err |= put_user(0, &statbuf->__unused1); + err |= put_user(mtime, &statbuf->st_mtime); + err |= put_user(0, &statbuf->__unused2); + err |= put_user(ctime, &statbuf->st_ctime); + err |= put_user(0, &statbuf->__unused3); + if (blksize) { + err |= put_user(blksize, &statbuf->st_blksize); + err |= put_user(blocks, &statbuf->st_blocks); + } else { + unsigned int tmp_blocks; + +#define D_B 7 +#define I_B (BLOCK_SIZE / sizeof(unsigned short)) + tmp_blocks = (size + BLOCK_SIZE - 1) / BLOCK_SIZE; + if (tmp_blocks > D_B) { + unsigned int indirect; + + indirect = (tmp_blocks - D_B + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) { + indirect = (indirect - 1 + I_B - 1) / I_B; + tmp_blocks += indirect; + if (indirect > 1) + tmp_blocks++; + } + } + err |= put_user(BLOCK_SIZE, &statbuf->st_blksize); + err |= put_user((BLOCK_SIZE / 512) * tmp_blocks, &statbuf->st_blocks); +#undef D_B +#undef I_B + } +/* fixme + err |= put_user(0, &statbuf->__unused4[0]); + err |= put_user(0, &statbuf->__unused4[1]); +*/ + + return err; +} + +/* Perhaps this belongs in fs.h or similar. -DaveM */ +static __inline__ int +do_revalidate(struct dentry *dentry) +{ + struct inode * inode = dentry->d_inode; + if (inode->i_op && inode->i_op->revalidate) + return inode->i_op->revalidate(dentry); + return 0; +} + +asmlinkage int sys32_newstat(char * filename, struct stat32 *statbuf) +{ + struct nameidata nd; + int error; + + error = user_path_walk(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_new_stat32(nd.dentry->d_inode, statbuf); + path_release(&nd); + } + return error; +} + +asmlinkage int sys32_newlstat(char * filename, struct stat32 *statbuf) +{ + struct nameidata nd; + int error; + + error = user_path_walk_link(filename, &nd); + if (!error) { + error = do_revalidate(nd.dentry); + if (!error) + error = cp_new_stat32(nd.dentry->d_inode, statbuf); + + path_release(&nd); + } + return error; +} + +asmlinkage int sys32_newfstat(unsigned int fd, struct stat32 *statbuf) +{ + struct file *f; + int err = -EBADF; + + f = fget(fd); + if (f) { + struct dentry * dentry = f->f_dentry; + + err = do_revalidate(dentry); + if (!err) + err = cp_new_stat32(dentry->d_inode, statbuf); + fput(f); + } + return err; +} + +extern asmlinkage int sys_sysfs(int option, unsigned long arg1, unsigned long arg2); + +asmlinkage int sys32_sysfs(int option, u32 arg1, u32 arg2) +{ + return sys_sysfs(option, arg1, arg2); +} + +struct ncp_mount_data32 { + int version; + unsigned int ncp_fd; + __kernel_uid_t32 mounted_uid; + __kernel_pid_t32 wdog_pid; + unsigned char mounted_vol[NCP_VOLNAME_LEN + 1]; + unsigned int time_out; + unsigned int retry_count; + unsigned int flags; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +static void *do_ncp_super_data_conv(void *raw_data) +{ + struct ncp_mount_data *n = (struct ncp_mount_data *)raw_data; + struct ncp_mount_data32 *n32 = (struct ncp_mount_data32 *)raw_data; + + n->dir_mode = n32->dir_mode; + n->file_mode = n32->file_mode; + n->gid = low2highgid(n32->gid); + n->uid = low2highuid(n32->uid); + memmove (n->mounted_vol, n32->mounted_vol, (sizeof (n32->mounted_vol) + 3 * sizeof (unsigned int))); + n->wdog_pid = n32->wdog_pid; + n->mounted_uid = low2highuid(n32->mounted_uid); + return raw_data; +} + +struct smb_mount_data32 { + int version; + __kernel_uid_t32 mounted_uid; + __kernel_uid_t32 uid; + __kernel_gid_t32 gid; + __kernel_mode_t32 file_mode; + __kernel_mode_t32 dir_mode; +}; + +static void *do_smb_super_data_conv(void *raw_data) +{ + struct smb_mount_data *s = (struct smb_mount_data *)raw_data; + struct smb_mount_data32 *s32 = (struct smb_mount_data32 *)raw_data; + + s->version = s32->version; + s->mounted_uid = low2highuid(s32->mounted_uid); + s->uid = low2highuid(s32->uid); + s->gid = low2highgid(s32->gid); + s->file_mode = s32->file_mode; + s->dir_mode = s32->dir_mode; + return raw_data; +} + +static int copy_mount_stuff_to_kernel(const void *user, unsigned long *kernel) +{ + int i; + unsigned long page; + struct vm_area_struct *vma; + + *kernel = 0; + if(!user) + return 0; + vma = find_vma(current->mm, (unsigned long)user); + if(!vma || (unsigned long)user < vma->vm_start) + return -EFAULT; + if(!(vma->vm_flags & VM_READ)) + return -EFAULT; + i = vma->vm_end - (unsigned long) user; + if(PAGE_SIZE <= (unsigned long) i) + i = PAGE_SIZE - 1; + if(!(page = __get_free_page(GFP_KERNEL))) + return -ENOMEM; + if(copy_from_user((void *) page, user, i)) { + free_page(page); + return -EFAULT; + } + *kernel = page; + return 0; +} + +#define SMBFS_NAME "smbfs" +#define NCPFS_NAME "ncpfs" + +asmlinkage int sys32_mount(char *dev_name, char *dir_name, char *type, unsigned long new_flags, u32 data) +{ + unsigned long type_page = 0; + unsigned long data_page = 0; + unsigned long dev_page = 0; + unsigned long dir_page = 0; + int err, is_smb, is_ncp; + + is_smb = is_ncp = 0; + + err = copy_mount_stuff_to_kernel((const void *)type, &type_page); + if (err) + goto out; + + if (!type_page) { + err = -EINVAL; + goto out; + } + + is_smb = !strcmp((char *)type_page, SMBFS_NAME); + is_ncp = !strcmp((char *)type_page, NCPFS_NAME); + + err = copy_mount_stuff_to_kernel((const void *)AA(data), &data_page); + if (err) + goto type_out; + + err = copy_mount_stuff_to_kernel(dev_name, &dev_page); + if (err) + goto data_out; + + err = copy_mount_stuff_to_kernel(dir_name, &dir_page); + if (err) + goto dev_out; + + if (!is_smb && !is_ncp) { + lock_kernel(); + err = do_mount((char*)dev_page, (char*)dir_page, + (char*)type_page, new_flags, (char*)data_page); + unlock_kernel(); + } else { + if (is_ncp) + do_ncp_super_data_conv((void *)data_page); + else + do_smb_super_data_conv((void *)data_page); + + lock_kernel(); + err = do_mount((char*)dev_page, (char*)dir_page, + (char*)type_page, new_flags, (char*)data_page); + unlock_kernel(); + } + free_page(dir_page); + +dev_out: + free_page(dev_page); + +data_out: + free_page(data_page); + +type_out: + free_page(type_page); + +out: + return err; +} + +struct rusage32 { + struct timeval32 ru_utime; + struct timeval32 ru_stime; + s32 ru_maxrss; + s32 ru_ixrss; + s32 ru_idrss; + s32 ru_isrss; + s32 ru_minflt; + s32 ru_majflt; + s32 ru_nswap; + s32 ru_inblock; + s32 ru_oublock; + s32 ru_msgsnd; + s32 ru_msgrcv; + s32 ru_nsignals; + s32 ru_nvcsw; + s32 ru_nivcsw; +}; + +static int put_rusage (struct rusage32 *ru, struct rusage *r) +{ + int err; + + err = put_user (r->ru_utime.tv_sec, &ru->ru_utime.tv_sec); + err |= __put_user (r->ru_utime.tv_usec, &ru->ru_utime.tv_usec); + err |= __put_user (r->ru_stime.tv_sec, &ru->ru_stime.tv_sec); + err |= __put_user (r->ru_stime.tv_usec, &ru->ru_stime.tv_usec); + err |= __put_user (r->ru_maxrss, &ru->ru_maxrss); + err |= __put_user (r->ru_ixrss, &ru->ru_ixrss); + err |= __put_user (r->ru_idrss, &ru->ru_idrss); + err |= __put_user (r->ru_isrss, &ru->ru_isrss); + err |= __put_user (r->ru_minflt, &ru->ru_minflt); + err |= __put_user (r->ru_majflt, &ru->ru_majflt); + err |= __put_user (r->ru_nswap, &ru->ru_nswap); + err |= __put_user (r->ru_inblock, &ru->ru_inblock); + err |= __put_user (r->ru_oublock, &ru->ru_oublock); + err |= __put_user (r->ru_msgsnd, &ru->ru_msgsnd); + err |= __put_user (r->ru_msgrcv, &ru->ru_msgrcv); + err |= __put_user (r->ru_nsignals, &ru->ru_nsignals); + err |= __put_user (r->ru_nvcsw, &ru->ru_nvcsw); + err |= __put_user (r->ru_nivcsw, &ru->ru_nivcsw); + return err; +} + +asmlinkage int sys32_wait4(__kernel_pid_t32 pid, unsigned int *stat_addr, int options, struct rusage32 *ru) +{ + if (!ru) + return sys_wait4(pid, stat_addr, options, NULL); + else { + struct rusage r; + int ret; + unsigned int status; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_wait4(pid, stat_addr ? &status : NULL, options, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + if (stat_addr && put_user (status, stat_addr)) + return -EFAULT; + return ret; + } +} + +struct sysinfo32 { + s32 uptime; + u32 loads[3]; + u32 totalram; + u32 freeram; + u32 sharedram; + u32 bufferram; + u32 totalswap; + u32 freeswap; + unsigned short procs; + char _f[22]; +}; + +extern asmlinkage int sys_sysinfo(struct sysinfo *info); + +asmlinkage int sys32_sysinfo(struct sysinfo32 *info) +{ + struct sysinfo s; + int ret, err; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sysinfo(&s); + set_fs (old_fs); + err = put_user (s.uptime, &info->uptime); + err |= __put_user (s.loads[0], &info->loads[0]); + err |= __put_user (s.loads[1], &info->loads[1]); + err |= __put_user (s.loads[2], &info->loads[2]); + err |= __put_user (s.totalram, &info->totalram); + err |= __put_user (s.freeram, &info->freeram); + err |= __put_user (s.sharedram, &info->sharedram); + err |= __put_user (s.bufferram, &info->bufferram); + err |= __put_user (s.totalswap, &info->totalswap); + err |= __put_user (s.freeswap, &info->freeswap); + err |= __put_user (s.procs, &info->procs); + if (err) + return -EFAULT; + return ret; +} + +struct timespec32 { + s32 tv_sec; + s32 tv_nsec; +}; + +extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval); + +asmlinkage int sys32_sched_rr_get_interval(__kernel_pid_t32 pid, struct timespec32 *interval) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_sched_rr_get_interval(pid, &t); + set_fs (old_fs); + if (put_user (t.tv_sec, &interval->tv_sec) || + __put_user (t.tv_nsec, &interval->tv_nsec)) + return -EFAULT; + return ret; +} + +extern asmlinkage int sys_nanosleep(struct timespec *rqtp, struct timespec *rmtp); + +asmlinkage int sys32_nanosleep(struct timespec32 *rqtp, struct timespec32 *rmtp) +{ + struct timespec t; + int ret; + mm_segment_t old_fs = get_fs (); + + if (get_user (t.tv_sec, &rqtp->tv_sec) || + __get_user (t.tv_nsec, &rqtp->tv_nsec)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_nanosleep(&t, rmtp ? &t : NULL); + set_fs (old_fs); + if (rmtp && ret == -EINTR) { + if (__put_user (t.tv_sec, &rmtp->tv_sec) || + __put_user (t.tv_nsec, &rmtp->tv_nsec)) + return -EFAULT; + } + return ret; +} + +extern asmlinkage int sys_sigprocmask(int how, old_sigset_t *set, old_sigset_t *oset); + +asmlinkage int sys32_sigprocmask(int how, old_sigset_t32 *set, old_sigset_t32 *oset) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set && get_user (s, set)) return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL); + set_fs (old_fs); + if (ret) return ret; + if (oset && put_user (s, oset)) return -EFAULT; + return 0; +} + +extern asmlinkage int sys_rt_sigprocmask(int how, sigset_t *set, sigset_t *oset, size_t sigsetsize); + +asmlinkage int sys32_rt_sigprocmask(int how, sigset_t32 *set, sigset_t32 *oset, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset_t32 s32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set) { + if (copy_from_user (&s32, set, sizeof(sigset_t32))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: s.sig[3] = s32.sig[6] | (((long)s32.sig[7]) << 32); + case 3: s.sig[2] = s32.sig[4] | (((long)s32.sig[5]) << 32); + case 2: s.sig[1] = s32.sig[2] | (((long)s32.sig[3]) << 32); + case 1: s.sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32); + } + } + set_fs (KERNEL_DS); + ret = sys_rt_sigprocmask(how, set ? &s : NULL, oset ? &s : NULL, sigsetsize); + set_fs (old_fs); + if (ret) return ret; + if (oset) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (oset, &s32, sizeof(sigset_t32))) + return -EFAULT; + } + return 0; +} + +extern asmlinkage int sys_sigpending(old_sigset_t *set); + +asmlinkage int sys32_sigpending(old_sigset_t32 *set) +{ + old_sigset_t s; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_sigpending(&s); + set_fs (old_fs); + if (put_user (s, set)) return -EFAULT; + return ret; +} + +extern asmlinkage int sys_rt_sigpending(sigset_t *set, size_t sigsetsize); + +asmlinkage int sys32_rt_sigpending(sigset_t32 *set, __kernel_size_t32 sigsetsize) +{ + sigset_t s; + sigset_t32 s32; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_rt_sigpending(&s, sigsetsize); + set_fs (old_fs); + if (!ret) { + switch (_NSIG_WORDS) { + case 4: s32.sig[7] = (s.sig[3] >> 32); s32.sig[6] = s.sig[3]; + case 3: s32.sig[5] = (s.sig[2] >> 32); s32.sig[4] = s.sig[2]; + case 2: s32.sig[3] = (s.sig[1] >> 32); s32.sig[2] = s.sig[1]; + case 1: s32.sig[1] = (s.sig[0] >> 32); s32.sig[0] = s.sig[0]; + } + if (copy_to_user (set, &s32, sizeof(sigset_t32))) + return -EFAULT; + } + return ret; +} + +extern int +copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from); + +asmlinkage int +sys32_rt_sigtimedwait(sigset_t32 *uthese, siginfo_t32 *uinfo, + struct timespec32 *uts, __kernel_size_t32 sigsetsize) +{ + int ret, sig; + sigset_t these; + sigset_t32 these32; + struct timespec ts; + siginfo_t info; + long timeout = 0; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user (&these32, uthese, sizeof(sigset_t32))) + return -EFAULT; + + switch (_NSIG_WORDS) { + case 4: these.sig[3] = these32.sig[6] | (((long)these32.sig[7]) << 32); + case 3: these.sig[2] = these32.sig[4] | (((long)these32.sig[5]) << 32); + case 2: these.sig[1] = these32.sig[2] | (((long)these32.sig[3]) << 32); + case 1: these.sig[0] = these32.sig[0] | (((long)these32.sig[1]) << 32); + } + + /* + * Invert the set of allowed signals to get those we + * want to block. + */ + sigdelsetmask(&these, sigmask(SIGKILL)|sigmask(SIGSTOP)); + signotset(&these); + + if (uts) { + if (get_user (ts.tv_sec, &uts->tv_sec) || + get_user (ts.tv_nsec, &uts->tv_nsec)) + return -EINVAL; + if (ts.tv_nsec >= 1000000000L || ts.tv_nsec < 0 + || ts.tv_sec < 0) + return -EINVAL; + } + + spin_lock_irq(¤t->sigmask_lock); + sig = dequeue_signal(&these, &info); + if (!sig) { + /* None ready -- temporarily unblock those we're interested + in so that we'll be awakened when they arrive. */ + sigset_t oldblocked = current->blocked; + sigandsets(¤t->blocked, ¤t->blocked, &these); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + + timeout = MAX_SCHEDULE_TIMEOUT; + if (uts) + timeout = (timespec_to_jiffies(&ts) + + (ts.tv_sec || ts.tv_nsec)); + + current->state = TASK_INTERRUPTIBLE; + timeout = schedule_timeout(timeout); + + spin_lock_irq(¤t->sigmask_lock); + sig = dequeue_signal(&these, &info); + current->blocked = oldblocked; + recalc_sigpending(current); + } + spin_unlock_irq(¤t->sigmask_lock); + + if (sig) { + ret = sig; + if (uinfo) { + if (copy_siginfo_to_user32(uinfo, &info)) + ret = -EFAULT; + } + } else { + ret = -EAGAIN; + if (timeout) + ret = -EINTR; + } + + return ret; +} + +extern asmlinkage int +sys_rt_sigqueueinfo(int pid, int sig, siginfo_t *uinfo); + +asmlinkage int +sys32_rt_sigqueueinfo(int pid, int sig, siginfo_t32 *uinfo) +{ + siginfo_t info; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_from_user (&info, uinfo, 3*sizeof(int)) || + copy_from_user (info._sifields._pad, uinfo->_sifields._pad, SI_PAD_SIZE)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo(pid, sig, &info); + set_fs (old_fs); + return ret; +} + +struct tms32 { + __kernel_clock_t32 tms_utime; + __kernel_clock_t32 tms_stime; + __kernel_clock_t32 tms_cutime; + __kernel_clock_t32 tms_cstime; +}; + +extern asmlinkage long sys_times(struct tms * tbuf); + +asmlinkage long sys32_times(struct tms32 *tbuf) +{ + struct tms t; + long ret; + mm_segment_t old_fs = get_fs (); + int err; + + set_fs (KERNEL_DS); + ret = sys_times(tbuf ? &t : NULL); + set_fs (old_fs); + if (tbuf) { + err = put_user (t.tms_utime, &tbuf->tms_utime); + err |= __put_user (t.tms_stime, &tbuf->tms_stime); + err |= __put_user (t.tms_cutime, &tbuf->tms_cutime); + err |= __put_user (t.tms_cstime, &tbuf->tms_cstime); + if (err) + ret = -EFAULT; + } + return ret; +} + +#define RLIM_INFINITY32 0x7fffffff +#define RESOURCE32(x) ((x > RLIM_INFINITY32) ? RLIM_INFINITY32 : x) + +struct rlimit32 { + u32 rlim_cur; + u32 rlim_max; +}; + +extern asmlinkage long sys_old_getrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage int sys32_old_getrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + set_fs (KERNEL_DS); + ret = sys_old_getrlimit(resource, &r); + set_fs (old_fs); + if (!ret) { + ret = put_user (RESOURCE32(r.rlim_cur), &rlim->rlim_cur); + ret |= __put_user (RESOURCE32(r.rlim_max), &rlim->rlim_max); + } + return ret; +} + +extern asmlinkage int sys_setrlimit(unsigned int resource, struct rlimit *rlim); + +asmlinkage int sys32_setrlimit(unsigned int resource, struct rlimit32 *rlim) +{ + struct rlimit r; + int ret; + mm_segment_t old_fs = get_fs (); + + if (resource >= RLIM_NLIMITS) return -EINVAL; + if (get_user (r.rlim_cur, &rlim->rlim_cur) || + __get_user (r.rlim_max, &rlim->rlim_max)) + return -EFAULT; + if (r.rlim_cur == RLIM_INFINITY32) + r.rlim_cur = RLIM_INFINITY; + if (r.rlim_max == RLIM_INFINITY32) + r.rlim_max = RLIM_INFINITY; + set_fs (KERNEL_DS); + ret = sys_setrlimit(resource, &r); + set_fs (old_fs); + return ret; +} + +extern asmlinkage int sys_getrusage(int who, struct rusage *ru); + +asmlinkage int sys32_getrusage(int who, struct rusage32 *ru) +{ + struct rusage r; + int ret; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_getrusage(who, &r); + set_fs (old_fs); + if (put_rusage (ru, &r)) return -EFAULT; + return ret; +} + +/* XXX This really belongs in some header file... -DaveM */ +#define MAX_SOCK_ADDR 128 /* 108 for Unix domain - + 16 for IP, 16 for IPX, + 24 for IPv6, + about 80 for AX.25 */ + +extern struct socket *sockfd_lookup(int fd, int *err); + +/* XXX This as well... */ +extern __inline__ void sockfd_put(struct socket *sock) +{ + fput(sock->file); +} + +struct msghdr32 { + u32 msg_name; + int msg_namelen; + u32 msg_iov; + __kernel_size_t32 msg_iovlen; + u32 msg_control; + __kernel_size_t32 msg_controllen; + unsigned msg_flags; +}; + +struct cmsghdr32 { + __kernel_size_t32 cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +/* Bleech... */ +#define __CMSG32_NXTHDR(ctl, len, cmsg, cmsglen) __cmsg32_nxthdr((ctl),(len),(cmsg),(cmsglen)) +#define CMSG32_NXTHDR(mhdr, cmsg, cmsglen) cmsg32_nxthdr((mhdr), (cmsg), (cmsglen)) + +#define CMSG32_ALIGN(len) ( ((len)+sizeof(int)-1) & ~(sizeof(int)-1) ) + +#define CMSG32_DATA(cmsg) ((void *)((char *)(cmsg) + CMSG32_ALIGN(sizeof(struct cmsghdr32)))) +#define CMSG32_SPACE(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + CMSG32_ALIGN(len)) +#define CMSG32_LEN(len) (CMSG32_ALIGN(sizeof(struct cmsghdr32)) + (len)) + +#define __CMSG32_FIRSTHDR(ctl,len) ((len) >= sizeof(struct cmsghdr32) ? \ + (struct cmsghdr32 *)(ctl) : \ + (struct cmsghdr32 *)NULL) +#define CMSG32_FIRSTHDR(msg) __CMSG32_FIRSTHDR((msg)->msg_control, (msg)->msg_controllen) + +__inline__ struct cmsghdr32 *__cmsg32_nxthdr(void *__ctl, __kernel_size_t __size, + struct cmsghdr32 *__cmsg, int __cmsg_len) +{ + struct cmsghdr32 * __ptr; + + __ptr = (struct cmsghdr32 *)(((unsigned char *) __cmsg) + + CMSG32_ALIGN(__cmsg_len)); + if ((unsigned long)((char*)(__ptr+1) - (char *) __ctl) > __size) + return NULL; + + return __ptr; +} + +__inline__ struct cmsghdr32 *cmsg32_nxthdr (struct msghdr *__msg, + struct cmsghdr32 *__cmsg, + int __cmsg_len) +{ + return __cmsg32_nxthdr(__msg->msg_control, __msg->msg_controllen, + __cmsg, __cmsg_len); +} + +static inline int iov_from_user32_to_kern(struct iovec *kiov, + struct iovec32 *uiov32, + int niov) +{ + int tot_len = 0; + + while(niov > 0) { + u32 len, buf; + + if(get_user(len, &uiov32->iov_len) || + get_user(buf, &uiov32->iov_base)) { + tot_len = -EFAULT; + break; + } + tot_len += len; + kiov->iov_base = (void *)A(buf); + kiov->iov_len = (__kernel_size_t) len; + uiov32++; + kiov++; + niov--; + } + return tot_len; +} + +static inline int msghdr_from_user32_to_kern(struct msghdr *kmsg, + struct msghdr32 *umsg) +{ + u32 tmp1, tmp2, tmp3; + int err; + + err = get_user(tmp1, &umsg->msg_name); + err |= __get_user(tmp2, &umsg->msg_iov); + err |= __get_user(tmp3, &umsg->msg_control); + if (err) + return -EFAULT; + + kmsg->msg_name = (void *)A(tmp1); + kmsg->msg_iov = (struct iovec *)A(tmp2); + kmsg->msg_control = (void *)A(tmp3); + + err = get_user(kmsg->msg_namelen, &umsg->msg_namelen); + err |= get_user(kmsg->msg_iovlen, &umsg->msg_iovlen); + err |= get_user(kmsg->msg_controllen, &umsg->msg_controllen); + err |= get_user(kmsg->msg_flags, &umsg->msg_flags); + + return err; +} + +/* I've named the args so it is easy to tell whose space the pointers are in. */ +static int verify_iovec32(struct msghdr *kern_msg, struct iovec *kern_iov, + char *kern_address, int mode) +{ + int tot_len; + + if(kern_msg->msg_namelen) { + if(mode==VERIFY_READ) { + int err = move_addr_to_kernel(kern_msg->msg_name, + kern_msg->msg_namelen, + kern_address); + if(err < 0) + return err; + } + kern_msg->msg_name = kern_address; + } else + kern_msg->msg_name = NULL; + + if(kern_msg->msg_iovlen > UIO_FASTIOV) { + kern_iov = kmalloc(kern_msg->msg_iovlen * sizeof(struct iovec), + GFP_KERNEL); + if(!kern_iov) + return -ENOMEM; + } + + tot_len = iov_from_user32_to_kern(kern_iov, + (struct iovec32 *)kern_msg->msg_iov, + kern_msg->msg_iovlen); + if(tot_len >= 0) + kern_msg->msg_iov = kern_iov; + else if(kern_msg->msg_iovlen > UIO_FASTIOV) + kfree(kern_iov); + + return tot_len; +} + +/* There is a lot of hair here because the alignment rules (and + * thus placement) of cmsg headers and length are different for + * 32-bit apps. -DaveM + */ +static int cmsghdr_from_user32_to_kern(struct msghdr *kmsg, + unsigned char *stackbuf, int stackbuf_size) +{ + struct cmsghdr32 *ucmsg; + struct cmsghdr *kcmsg, *kcmsg_base; + __kernel_size_t32 ucmlen; + __kernel_size_t kcmlen, tmp; + + kcmlen = 0; + kcmsg_base = kcmsg = (struct cmsghdr *)stackbuf; + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + if(get_user(ucmlen, &ucmsg->cmsg_len)) + return -EFAULT; + + /* Catch bogons. */ + if(CMSG32_ALIGN(ucmlen) < + CMSG32_ALIGN(sizeof(struct cmsghdr32))) + return -EINVAL; + if((unsigned long)(((char *)ucmsg - (char *)kmsg->msg_control) + + ucmlen) > kmsg->msg_controllen) + return -EINVAL; + + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmlen += tmp; + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + if(kcmlen == 0) + return -EINVAL; + + /* The kcmlen holds the 64-bit version of the control length. + * It may not be modified as we do not stick it into the kmsg + * until we have successfully copied over all of the data + * from the user. + */ + if(kcmlen > stackbuf_size) + kcmsg_base = kcmsg = kmalloc(kcmlen, GFP_KERNEL); + if(kcmsg == NULL) + return -ENOBUFS; + + /* Now copy them over neatly. */ + memset(kcmsg, 0, kcmlen); + ucmsg = CMSG32_FIRSTHDR(kmsg); + while(ucmsg != NULL) { + __get_user(ucmlen, &ucmsg->cmsg_len); + tmp = ((ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))) + + CMSG_ALIGN(sizeof(struct cmsghdr))); + kcmsg->cmsg_len = tmp; + __get_user(kcmsg->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg->cmsg_type, &ucmsg->cmsg_type); + + /* Copy over the data. */ + if(copy_from_user(CMSG_DATA(kcmsg), + CMSG32_DATA(ucmsg), + (ucmlen - CMSG32_ALIGN(sizeof(*ucmsg))))) + goto out_free_efault; + + /* Advance. */ + kcmsg = (struct cmsghdr *)((char *)kcmsg + CMSG_ALIGN(tmp)); + ucmsg = CMSG32_NXTHDR(kmsg, ucmsg, ucmlen); + } + + /* Ok, looks like we made it. Hook it up and return success. */ + kmsg->msg_control = kcmsg_base; + kmsg->msg_controllen = kcmlen; + return 0; + +out_free_efault: + if(kcmsg_base != (struct cmsghdr *)stackbuf) + kfree(kcmsg_base); + return -EFAULT; +} + +static void put_cmsg32(struct msghdr *kmsg, int level, int type, + int len, void *data) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + struct cmsghdr32 cmhdr; + int cmlen = CMSG32_LEN(len); + + if(cm == NULL || kmsg->msg_controllen < sizeof(*cm)) { + kmsg->msg_flags |= MSG_CTRUNC; + return; + } + + if(kmsg->msg_controllen < cmlen) { + kmsg->msg_flags |= MSG_CTRUNC; + cmlen = kmsg->msg_controllen; + } + cmhdr.cmsg_level = level; + cmhdr.cmsg_type = type; + cmhdr.cmsg_len = cmlen; + + if(copy_to_user(cm, &cmhdr, sizeof cmhdr)) + return; + if(copy_to_user(CMSG32_DATA(cm), data, cmlen - sizeof(struct cmsghdr32))) + return; + cmlen = CMSG32_SPACE(len); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; +} + +static void scm_detach_fds32(struct msghdr *kmsg, struct scm_cookie *scm) +{ + struct cmsghdr32 *cm = (struct cmsghdr32 *) kmsg->msg_control; + int fdmax = (kmsg->msg_controllen - sizeof(struct cmsghdr32)) / sizeof(int); + int fdnum = scm->fp->count; + struct file **fp = scm->fp->fp; + int *cmfptr; + int err = 0, i; + + if (fdnum < fdmax) + fdmax = fdnum; + + for (i = 0, cmfptr = (int *) CMSG32_DATA(cm); i < fdmax; i++, cmfptr++) { + int new_fd; + err = get_unused_fd(); + if (err < 0) + break; + new_fd = err; + err = put_user(new_fd, cmfptr); + if (err) { + put_unused_fd(new_fd); + break; + } + /* Bump the usage count and install the file. */ + get_file(fp[i]); + fd_install(new_fd, fp[i]); + } + + if (i > 0) { + int cmlen = CMSG32_LEN(i * sizeof(int)); + if (!err) + err = put_user(SOL_SOCKET, &cm->cmsg_level); + if (!err) + err = put_user(SCM_RIGHTS, &cm->cmsg_type); + if (!err) + err = put_user(cmlen, &cm->cmsg_len); + if (!err) { + cmlen = CMSG32_SPACE(i * sizeof(int)); + kmsg->msg_control += cmlen; + kmsg->msg_controllen -= cmlen; + } + } + if (i < fdnum) + kmsg->msg_flags |= MSG_CTRUNC; + + /* + * All of the files that fit in the message have had their + * usage counts incremented, so we just free the list. + */ + __scm_destroy(scm); +} + +/* In these cases we (currently) can just copy to data over verbatim + * because all CMSGs created by the kernel have well defined types which + * have the same layout in both the 32-bit and 64-bit API. One must add + * some special cased conversions here if we start sending control messages + * with incompatible types. + * + * SCM_RIGHTS and SCM_CREDENTIALS are done by hand in recvmsg32 right after + * we do our work. The remaining cases are: + * + * SOL_IP IP_PKTINFO struct in_pktinfo 32-bit clean + * IP_TTL int 32-bit clean + * IP_TOS __u8 32-bit clean + * IP_RECVOPTS variable length 32-bit clean + * IP_RETOPTS variable length 32-bit clean + * (these last two are clean because the types are defined + * by the IPv4 protocol) + * IP_RECVERR struct sock_extended_err + + * struct sockaddr_in 32-bit clean + * SOL_IPV6 IPV6_RECVERR struct sock_extended_err + + * struct sockaddr_in6 32-bit clean + * IPV6_PKTINFO struct in6_pktinfo 32-bit clean + * IPV6_HOPLIMIT int 32-bit clean + * IPV6_FLOWINFO u32 32-bit clean + * IPV6_HOPOPTS ipv6 hop exthdr 32-bit clean + * IPV6_DSTOPTS ipv6 dst exthdr(s) 32-bit clean + * IPV6_RTHDR ipv6 routing exthdr 32-bit clean + * IPV6_AUTHHDR ipv6 auth exthdr 32-bit clean + */ +static void cmsg32_recvmsg_fixup(struct msghdr *kmsg, unsigned long orig_cmsg_uptr) +{ + unsigned char *workbuf, *wp; + unsigned long bufsz, space_avail; + struct cmsghdr *ucmsg; + + bufsz = ((unsigned long)kmsg->msg_control) - orig_cmsg_uptr; + space_avail = kmsg->msg_controllen + bufsz; + wp = workbuf = kmalloc(bufsz, GFP_KERNEL); + if(workbuf == NULL) + goto fail; + + /* To make this more sane we assume the kernel sends back properly + * formatted control messages. Because of how the kernel will truncate + * the cmsg_len for MSG_TRUNC cases, we need not check that case either. + */ + ucmsg = (struct cmsghdr *) orig_cmsg_uptr; + while(((unsigned long)ucmsg) <= + (((unsigned long)kmsg->msg_control) - sizeof(struct cmsghdr))) { + struct cmsghdr32 *kcmsg32 = (struct cmsghdr32 *) wp; + int clen64, clen32; + + /* UCMSG is the 64-bit format CMSG entry in user-space. + * KCMSG32 is within the kernel space temporary buffer + * we use to convert into a 32-bit style CMSG. + */ + __get_user(kcmsg32->cmsg_len, &ucmsg->cmsg_len); + __get_user(kcmsg32->cmsg_level, &ucmsg->cmsg_level); + __get_user(kcmsg32->cmsg_type, &ucmsg->cmsg_type); + + clen64 = kcmsg32->cmsg_len; + copy_from_user(CMSG32_DATA(kcmsg32), CMSG_DATA(ucmsg), + clen64 - CMSG_ALIGN(sizeof(*ucmsg))); + clen32 = ((clen64 - CMSG_ALIGN(sizeof(*ucmsg))) + + CMSG32_ALIGN(sizeof(struct cmsghdr32))); + kcmsg32->cmsg_len = clen32; + + ucmsg = (struct cmsghdr *) (((char *)ucmsg) + CMSG_ALIGN(clen64)); + wp = (((char *)kcmsg32) + CMSG32_ALIGN(clen32)); + } + + /* Copy back fixed up data, and adjust pointers. */ + bufsz = (wp - workbuf); + copy_to_user((void *)orig_cmsg_uptr, workbuf, bufsz); + + kmsg->msg_control = (struct cmsghdr *) + (((char *)orig_cmsg_uptr) + bufsz); + kmsg->msg_controllen = space_avail - bufsz; + + kfree(workbuf); + return; + +fail: + /* If we leave the 64-bit format CMSG chunks in there, + * the application could get confused and crash. So to + * ensure greater recovery, we report no CMSGs. + */ + kmsg->msg_controllen += bufsz; + kmsg->msg_control = (void *) orig_cmsg_uptr; +} + +asmlinkage int sys32_sendmsg(int fd, struct msghdr32 *user_msg, unsigned user_flags) +{ + struct socket *sock; + char address[MAX_SOCK_ADDR]; + struct iovec iov[UIO_FASTIOV]; + unsigned char ctl[sizeof(struct cmsghdr) + 20]; + unsigned char *ctl_buf = ctl; + struct msghdr kern_msg; + int err, total_len; + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + err = verify_iovec32(&kern_msg, iov, address, VERIFY_READ); + if (err < 0) + goto out; + total_len = err; + + if(kern_msg.msg_controllen) { + err = cmsghdr_from_user32_to_kern(&kern_msg, ctl, sizeof(ctl)); + if(err) + goto out_freeiov; + ctl_buf = kern_msg.msg_control; + } + kern_msg.msg_flags = user_flags; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + if (sock->file->f_flags & O_NONBLOCK) + kern_msg.msg_flags |= MSG_DONTWAIT; + err = sock_sendmsg(sock, &kern_msg, total_len); + sockfd_put(sock); + } + + /* N.B. Use kfree here, as kern_msg.msg_controllen might change? */ + if(ctl_buf != ctl) + kfree(ctl_buf); +out_freeiov: + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + return err; +} + +asmlinkage int sys32_recvmsg(int fd, struct msghdr32 *user_msg, unsigned int user_flags) +{ + struct iovec iovstack[UIO_FASTIOV]; + struct msghdr kern_msg; + char addr[MAX_SOCK_ADDR]; + struct socket *sock; + struct iovec *iov = iovstack; + struct sockaddr *uaddr; + int *uaddr_len; + unsigned long cmsg_ptr; + int err, total_len, len = 0; + + if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) + return -EFAULT; + if(kern_msg.msg_iovlen > UIO_MAXIOV) + return -EINVAL; + + uaddr = kern_msg.msg_name; + uaddr_len = &user_msg->msg_namelen; + err = verify_iovec32(&kern_msg, iov, addr, VERIFY_WRITE); + if (err < 0) + goto out; + total_len = err; + + cmsg_ptr = (unsigned long) kern_msg.msg_control; + kern_msg.msg_flags = 0; + + sock = sockfd_lookup(fd, &err); + if (sock != NULL) { + struct scm_cookie scm; + + if (sock->file->f_flags & O_NONBLOCK) + user_flags |= MSG_DONTWAIT; + memset(&scm, 0, sizeof(scm)); + err = sock->ops->recvmsg(sock, &kern_msg, total_len, + user_flags, &scm); + if(err >= 0) { + len = err; + if(!kern_msg.msg_control) { + if(sock->passcred || scm.fp) + kern_msg.msg_flags |= MSG_CTRUNC; + if(scm.fp) + __scm_destroy(&scm); + } else { + /* If recvmsg processing itself placed some + * control messages into user space, it's is + * using 64-bit CMSG processing, so we need + * to fix it up before we tack on more stuff. + */ + if((unsigned long) kern_msg.msg_control != cmsg_ptr) + cmsg32_recvmsg_fixup(&kern_msg, cmsg_ptr); + + /* Wheee... */ + if(sock->passcred) + put_cmsg32(&kern_msg, + SOL_SOCKET, SCM_CREDENTIALS, + sizeof(scm.creds), &scm.creds); + if(scm.fp != NULL) + scm_detach_fds32(&kern_msg, &scm); + } + } + sockfd_put(sock); + } + + if(uaddr != NULL && err >= 0) + err = move_addr_to_user(addr, kern_msg.msg_namelen, uaddr, uaddr_len); + if(cmsg_ptr != 0 && err >= 0) { + unsigned long ucmsg_ptr = ((unsigned long)kern_msg.msg_control); + __kernel_size_t32 uclen = (__kernel_size_t32) (ucmsg_ptr - cmsg_ptr); + err |= __put_user(uclen, &user_msg->msg_controllen); + } + if(err >= 0) + err = __put_user(kern_msg.msg_flags, &user_msg->msg_flags); + if(kern_msg.msg_iov != iov) + kfree(kern_msg.msg_iov); +out: + if(err < 0) + return err; + return len; +} + +extern asmlinkage int sys_setsockopt(int fd, int level, int optname, + char *optval, int optlen); + +static int do_set_attach_filter(int fd, int level, int optname, + char *optval, int optlen) +{ + struct sock_fprog32 { + __u16 len; + __u32 filter; + } *fprog32 = (struct sock_fprog32 *)optval; + struct sock_fprog kfprog; + struct sock_filter *kfilter; + unsigned int fsize; + mm_segment_t old_fs; + __u32 uptr; + int ret; + + if (get_user(kfprog.len, &fprog32->len) || + __get_user(uptr, &fprog32->filter)) + return -EFAULT; + + kfprog.filter = (struct sock_filter *)A(uptr); + fsize = kfprog.len * sizeof(struct sock_filter); + + kfilter = (struct sock_filter *)kmalloc(fsize, GFP_KERNEL); + if (kfilter == NULL) + return -ENOMEM; + + if (copy_from_user(kfilter, kfprog.filter, fsize)) { + kfree(kfilter); + return -EFAULT; + } + + kfprog.filter = kfilter; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *)&kfprog, sizeof(kfprog)); + set_fs(old_fs); + + kfree(kfilter); + + return ret; +} + +static int do_set_icmpv6_filter(int fd, int level, int optname, + char *optval, int optlen) +{ + struct icmp6_filter kfilter; + mm_segment_t old_fs; + int ret, i; + + if (copy_from_user(&kfilter, optval, sizeof(kfilter))) + return -EFAULT; + + + for (i = 0; i < 8; i += 2) { + u32 tmp = kfilter.data[i]; + + kfilter.data[i] = kfilter.data[i + 1]; + kfilter.data[i + 1] = tmp; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_setsockopt(fd, level, optname, + (char *) &kfilter, sizeof(kfilter)); + set_fs(old_fs); + + return ret; +} + +asmlinkage int sys32_setsockopt(int fd, int level, int optname, + char *optval, int optlen) +{ + if (optname == SO_ATTACH_FILTER) + return do_set_attach_filter(fd, level, optname, + optval, optlen); + if (level == SOL_ICMPV6 && optname == ICMPV6_FILTER) + return do_set_icmpv6_filter(fd, level, optname, + optval, optlen); + + return sys_setsockopt(fd, level, optname, optval, optlen); +} + +extern void check_pending(int signum); + +/* + * count32() counts the number of arguments/envelopes + */ +static int count32(u32 * argv) +{ + int i = 0; + + if (argv != NULL) { + for (;;) { + u32 p; int error; + + error = get_user(p,argv); + if (error) return error; + if (!p) break; + argv++; i++; + } + } + return i; +} + +/* + * 'copy_string32()' copies argument/envelope strings from user + * memory to free pages in kernel mem. These are in a format ready + * to be put directly into the top of new user memory. + */ +static int copy_strings32(int argc, u32 * argv, struct linux_binprm *bprm) +{ + while (argc-- > 0) { + u32 str; + int len; + unsigned long pos; + + if (get_user(str, argv + argc) || + !str || + !(len = strnlen_user((char *)A(str), bprm->p))) + return -EFAULT; + + if (bprm->p < len) + return -E2BIG; + + bprm->p -= len; + + pos = bprm->p; + while (len) { + char *kaddr; + struct page *page; + int offset, bytes_to_copy, new, err; + + offset = pos % PAGE_SIZE; + page = bprm->page[pos / PAGE_SIZE]; + new = 0; + if (!page) { + page = alloc_page(GFP_USER); + bprm->page[pos / PAGE_SIZE] = page; + if (!page) + return -ENOMEM; + new = 1; + } + kaddr = (char *)kmap(page); + + if (new && offset) + memset(kaddr, 0, offset); + bytes_to_copy = PAGE_SIZE - offset; + if (bytes_to_copy > len) { + bytes_to_copy = len; + if (new) + memset(kaddr+offset+len, 0, + PAGE_SIZE-offset-len); + } + + err = copy_from_user(kaddr + offset, (char *)A(str), + bytes_to_copy); + flush_page_to_ram(page); + kunmap((unsigned long)kaddr); + + if (err) + return -EFAULT; + + pos += bytes_to_copy; + str += bytes_to_copy; + len -= bytes_to_copy; + } + } + return 0; +} + +/* + * sys32_execve() executes a new program. + */ +static inline int +do_execve32(char * filename, u32 * argv, u32 * envp, struct pt_regs * regs) +{ + struct linux_binprm bprm; + struct file * file; + int retval; + int i; + + bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *); + memset(bprm.page, 0, MAX_ARG_PAGES * sizeof(bprm.page[0])); + + file = open_exec(filename); + + retval = PTR_ERR(file); + if (IS_ERR(file)) + return retval; + + bprm.file = file; + bprm.filename = filename; + bprm.sh_bang = 0; + bprm.loader = 0; + bprm.exec = 0; + if ((bprm.argc = count32(argv)) < 0) { + allow_write_access(file); + fput(file); + return bprm.argc; + } + if ((bprm.envc = count32(envp)) < 0) { + allow_write_access(file); + fput(file); + return bprm.envc; + } + + retval = prepare_binprm(&bprm); + if (retval < 0) + goto out; + + retval = copy_strings_kernel(1, &bprm.filename, &bprm); + if (retval < 0) + goto out; + + bprm.exec = bprm.p; + retval = copy_strings32(bprm.envc, envp, &bprm); + if (retval < 0) + goto out; + + retval = copy_strings32(bprm.argc, argv, &bprm); + if (retval < 0) + goto out; + + retval = search_binary_handler(&bprm, regs); + if (retval >= 0) + /* execve success */ + return retval; + +out: + /* Something went wrong, return the inode and free the argument pages*/ + allow_write_access(bprm.file); + if (bprm.file) + fput(bprm.file); + + for (i=0 ; i<MAX_ARG_PAGES ; i++) + if (bprm.page[i]) + __free_page(bprm.page[i]); + + return retval; +} + +/* + * sys32_execve() executes a new program after the asm stub has set + * things up for us. This should basically do what I want it to. + */ +asmlinkage int +sys32_execve(struct pt_regs regs) +{ + int error; + char * filename; + + filename = getname((char *)A(regs.orig_gpr2)); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve32(filename, (u32 *)A(regs.gprs[3]), (u32 *)A(regs.gprs[4]), ®s); + if (error == 0) + { + current->ptrace &= ~PT_DTRACE; + current->thread.fp_regs.fpc=0; + __asm__ __volatile__ + ("sr 0,0\n\t" + "sfpc 0,0\n\t" + : : :"0"); + } + putname(filename); +out: + return error; +} + + +#ifdef CONFIG_MODULES + +extern asmlinkage unsigned long sys_create_module(const char *name_user, size_t size); + +asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size) +{ + return sys_create_module(name_user, (size_t)size); +} + +extern asmlinkage int sys_init_module(const char *name_user, struct module *mod_user); + +/* Hey, when you're trying to init module, take time and prepare us a nice 64bit + * module structure, even if from 32bit modutils... Why to pollute kernel... :)) + */ +asmlinkage int sys32_init_module(const char *name_user, struct module *mod_user) +{ + return sys_init_module(name_user, mod_user); +} + +extern asmlinkage int sys_delete_module(const char *name_user); + +asmlinkage int sys32_delete_module(const char *name_user) +{ + return sys_delete_module(name_user); +} + +struct module_info32 { + u32 addr; + u32 size; + u32 flags; + s32 usecount; +}; + +/* Query various bits about modules. */ + +static inline long +get_mod_name(const char *user_name, char **buf) +{ + unsigned long page; + long retval; + + if ((unsigned long)user_name >= TASK_SIZE + && !segment_eq(get_fs (), KERNEL_DS)) + return -EFAULT; + + page = __get_free_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + retval = strncpy_from_user((char *)page, user_name, PAGE_SIZE); + if (retval > 0) { + if (retval < PAGE_SIZE) { + *buf = (char *)page; + return retval; + } + retval = -ENAMETOOLONG; + } else if (!retval) + retval = -EINVAL; + + free_page(page); + return retval; +} + +static inline void +put_mod_name(char *buf) +{ + free_page((unsigned long)buf); +} + +static __inline__ struct module *find_module(const char *name) +{ + struct module *mod; + + for (mod = module_list; mod ; mod = mod->next) { + if (mod->flags & MOD_DELETED) + continue; + if (!strcmp(mod->name, name)) + break; + } + + return mod; +} + +static int +qm_modules(char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + struct module *mod; + size_t nmod, space, len; + + nmod = space = 0; + + for (mod = module_list; mod->next != NULL; mod = mod->next, ++nmod) { + len = strlen(mod->name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, mod->name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nmod, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((mod = mod->next)->next != NULL) + space += strlen(mod->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_deps(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t i, space, len; + + if (mod->next == NULL) + return -EINVAL; + if (!MOD_CAN_QUERY(mod)) + return put_user(0, ret); + + space = 0; + for (i = 0; i < mod->ndeps; ++i) { + const char *dep_name = mod->deps[i].dep->name; + + len = strlen(dep_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, dep_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + return put_user(i, ret); + +calc_space_needed: + space += len; + while (++i < mod->ndeps) + space += strlen(mod->deps[i].dep->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static int +qm_refs(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t nrefs, space, len; + struct module_ref *ref; + + if (mod->next == NULL) + return -EINVAL; + if (!MOD_CAN_QUERY(mod)) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = 0; + for (nrefs = 0, ref = mod->refs; ref ; ++nrefs, ref = ref->next_ref) { + const char *ref_name = ref->ref->name; + + len = strlen(ref_name)+1; + if (len > bufsize) + goto calc_space_needed; + if (copy_to_user(buf, ref_name, len)) + return -EFAULT; + buf += len; + bufsize -= len; + space += len; + } + + if (put_user(nrefs, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + space += len; + while ((ref = ref->next_ref) != NULL) + space += strlen(ref->ref->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_symbols(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + size_t i, space, len; + struct module_symbol *s; + char *strings; + unsigned *vals; + + if (!MOD_CAN_QUERY(mod)) + if (put_user(0, ret)) + return -EFAULT; + else + return 0; + + space = mod->nsyms * 2*sizeof(u32); + + i = len = 0; + s = mod->syms; + + if (space > bufsize) + goto calc_space_needed; + + if (!access_ok(VERIFY_WRITE, buf, space)) + return -EFAULT; + + bufsize -= space; + vals = (unsigned *)buf; + strings = buf+space; + + for (; i < mod->nsyms ; ++i, ++s, vals += 2) { + len = strlen(s->name)+1; + if (len > bufsize) + goto calc_space_needed; + + if (copy_to_user(strings, s->name, len) + || __put_user(s->value, vals+0) + || __put_user(space, vals+1)) + return -EFAULT; + + strings += len; + bufsize -= len; + space += len; + } + + if (put_user(i, ret)) + return -EFAULT; + else + return 0; + +calc_space_needed: + for (; i < mod->nsyms; ++i, ++s) + space += strlen(s->name)+1; + + if (put_user(space, ret)) + return -EFAULT; + else + return -ENOSPC; +} + +static inline int +qm_info(struct module *mod, char *buf, size_t bufsize, __kernel_size_t32 *ret) +{ + int error = 0; + + if (mod->next == NULL) + return -EINVAL; + + if (sizeof(struct module_info32) <= bufsize) { + struct module_info32 info; + info.addr = (unsigned long)mod; + info.size = mod->size; + info.flags = mod->flags; + info.usecount = + ((mod_member_present(mod, can_unload) + && mod->can_unload) + ? -1 : atomic_read(&mod->uc.usecount)); + + if (copy_to_user(buf, &info, sizeof(struct module_info32))) + return -EFAULT; + } else + error = -ENOSPC; + + if (put_user(sizeof(struct module_info32), ret)) + return -EFAULT; + + return error; +} + +asmlinkage int sys32_query_module(char *name_user, int which, char *buf, __kernel_size_t32 bufsize, u32 ret) +{ + struct module *mod; + int err; + + lock_kernel(); + if (name_user == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; mod->next != NULL; mod = mod->next) + ; + } else { + long namelen; + char *name; + + if ((namelen = get_mod_name(name_user, &name)) < 0) { + err = namelen; + goto out; + } + err = -ENOENT; + if (namelen == 0) { + /* This finds "kernel_module" which is not exported. */ + for(mod = module_list; mod->next != NULL; mod = mod->next) + ; + } else if ((mod = find_module(name)) == NULL) { + put_mod_name(name); + goto out; + } + put_mod_name(name); + } + + switch (which) + { + case 0: + err = 0; + break; + case QM_MODULES: + err = qm_modules(buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_DEPS: + err = qm_deps(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_REFS: + err = qm_refs(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_SYMBOLS: + err = qm_symbols(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + case QM_INFO: + err = qm_info(mod, buf, bufsize, (__kernel_size_t32 *)AA(ret)); + break; + default: + err = -EINVAL; + break; + } +out: + unlock_kernel(); + return err; +} + +struct kernel_sym32 { + u32 value; + char name[60]; +}; + +extern asmlinkage int sys_get_kernel_syms(struct kernel_sym *table); + +asmlinkage int sys32_get_kernel_syms(struct kernel_sym32 *table) +{ + int len, i; + struct kernel_sym *tbl; + mm_segment_t old_fs; + + len = sys_get_kernel_syms(NULL); + if (!table) return len; + tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); + if (!tbl) return -ENOMEM; + old_fs = get_fs(); + set_fs (KERNEL_DS); + sys_get_kernel_syms(tbl); + set_fs (old_fs); + for (i = 0; i < len; i++, table += sizeof (struct kernel_sym32)) { + if (put_user (tbl[i].value, &table->value) || + copy_to_user (table->name, tbl[i].name, 60)) + break; + } + kfree (tbl); + return i; +} + +#else /* CONFIG_MODULES */ + +asmlinkage unsigned long +sys32_create_module(const char *name_user, size_t size) +{ + return -ENOSYS; +} + +asmlinkage int +sys32_init_module(const char *name_user, struct module *mod_user) +{ + return -ENOSYS; +} + +asmlinkage int +sys32_delete_module(const char *name_user) +{ + return -ENOSYS; +} + +asmlinkage int +sys32_query_module(const char *name_user, int which, char *buf, size_t bufsize, + size_t *ret) +{ + /* Let the program know about the new interface. Not that + it'll do them much good. */ + if (which == 0) + return 0; + + return -ENOSYS; +} + +asmlinkage int +sys32_get_kernel_syms(struct kernel_sym *table) +{ + return -ENOSYS; +} + +#endif /* CONFIG_MODULES */ + +/* Stuff for NFS server syscalls... */ +struct nfsctl_svc32 { + u16 svc32_port; + s32 svc32_nthreads; +}; + +struct nfsctl_client32 { + s8 cl32_ident[NFSCLNT_IDMAX+1]; + s32 cl32_naddr; + struct in_addr cl32_addrlist[NFSCLNT_ADDRMAX]; + s32 cl32_fhkeytype; + s32 cl32_fhkeylen; + u8 cl32_fhkey[NFSCLNT_KEYMAX]; +}; + +struct nfsctl_export32 { + s8 ex32_client[NFSCLNT_IDMAX+1]; + s8 ex32_path[NFS_MAXPATHLEN+1]; + __kernel_dev_t32 ex32_dev; + __kernel_ino_t32 ex32_ino; + s32 ex32_flags; + __kernel_uid_t32 ex32_anon_uid; + __kernel_gid_t32 ex32_anon_gid; +}; + +struct nfsctl_uidmap32 { + u32 ug32_ident; /* char * */ + __kernel_uid_t32 ug32_uidbase; + s32 ug32_uidlen; + u32 ug32_udimap; /* uid_t * */ + __kernel_uid_t32 ug32_gidbase; + s32 ug32_gidlen; + u32 ug32_gdimap; /* gid_t * */ +}; + +struct nfsctl_fhparm32 { + struct sockaddr gf32_addr; + __kernel_dev_t32 gf32_dev; + __kernel_ino_t32 gf32_ino; + s32 gf32_version; +}; + +struct nfsctl_fdparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_version; +}; + +struct nfsctl_fsparm32 { + struct sockaddr gd32_addr; + s8 gd32_path[NFS_MAXPATHLEN+1]; + s32 gd32_maxlen; +}; + +struct nfsctl_arg32 { + s32 ca32_version; /* safeguard */ + union { + struct nfsctl_svc32 u32_svc; + struct nfsctl_client32 u32_client; + struct nfsctl_export32 u32_export; + struct nfsctl_uidmap32 u32_umap; + struct nfsctl_fhparm32 u32_getfh; + struct nfsctl_fdparm32 u32_getfd; + struct nfsctl_fsparm32 u32_getfs; + } u; +#define ca32_svc u.u32_svc +#define ca32_client u.u32_client +#define ca32_export u.u32_export +#define ca32_umap u.u32_umap +#define ca32_getfh u.u32_getfh +#define ca32_getfd u.u32_getfd +#define ca32_getfs u.u32_getfs +#define ca32_authd u.u32_authd +}; + +union nfsctl_res32 { + __u8 cr32_getfh[NFS_FHSIZE]; + struct knfsd_fh cr32_getfs; +}; + +static int nfs_svc32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= __get_user(karg->ca_svc.svc_port, &arg32->ca32_svc.svc32_port); + err |= __get_user(karg->ca_svc.svc_nthreads, &arg32->ca32_svc.svc32_nthreads); + return err; +} + +static int nfs_clnt32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_client.cl_ident[0], + &arg32->ca32_client.cl32_ident[0], + NFSCLNT_IDMAX); + err |= __get_user(karg->ca_client.cl_naddr, &arg32->ca32_client.cl32_naddr); + err |= copy_from_user(&karg->ca_client.cl_addrlist[0], + &arg32->ca32_client.cl32_addrlist[0], + (sizeof(struct in_addr) * NFSCLNT_ADDRMAX)); + err |= __get_user(karg->ca_client.cl_fhkeytype, + &arg32->ca32_client.cl32_fhkeytype); + err |= __get_user(karg->ca_client.cl_fhkeylen, + &arg32->ca32_client.cl32_fhkeylen); + err |= copy_from_user(&karg->ca_client.cl_fhkey[0], + &arg32->ca32_client.cl32_fhkey[0], + NFSCLNT_KEYMAX); + return err; +} + +static int nfs_exp32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_export.ex_client[0], + &arg32->ca32_export.ex32_client[0], + NFSCLNT_IDMAX); + err |= copy_from_user(&karg->ca_export.ex_path[0], + &arg32->ca32_export.ex32_path[0], + NFS_MAXPATHLEN); + err |= __get_user(karg->ca_export.ex_dev, + &arg32->ca32_export.ex32_dev); + err |= __get_user(karg->ca_export.ex_ino, + &arg32->ca32_export.ex32_ino); + err |= __get_user(karg->ca_export.ex_flags, + &arg32->ca32_export.ex32_flags); + err |= __get_user(karg->ca_export.ex_anon_uid, + &arg32->ca32_export.ex32_anon_uid); + err |= __get_user(karg->ca_export.ex_anon_gid, + &arg32->ca32_export.ex32_anon_gid); + karg->ca_export.ex_anon_uid = high2lowuid(karg->ca_export.ex_anon_uid); + karg->ca_export.ex_anon_gid = high2lowgid(karg->ca_export.ex_anon_gid); + return err; +} + +static int nfs_uud32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + u32 uaddr; + int i; + int err; + + memset(karg, 0, sizeof(*karg)); + if(__get_user(karg->ca_version, &arg32->ca32_version)) + return -EFAULT; + karg->ca_umap.ug_ident = (char *)get_free_page(GFP_USER); + if(!karg->ca_umap.ug_ident) + return -ENOMEM; + err = __get_user(uaddr, &arg32->ca32_umap.ug32_ident); + if(strncpy_from_user(karg->ca_umap.ug_ident, + (char *)A(uaddr), PAGE_SIZE) <= 0) + return -EFAULT; + err |= __get_user(karg->ca_umap.ug_uidbase, + &arg32->ca32_umap.ug32_uidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_uidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_udimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_udimap = kmalloc((sizeof(uid_t) * karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_udimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_uidlen; i++) + err |= __get_user(karg->ca_umap.ug_udimap[i], + &(((__kernel_uid_t32 *)A(uaddr))[i])); + err |= __get_user(karg->ca_umap.ug_gidbase, + &arg32->ca32_umap.ug32_gidbase); + err |= __get_user(karg->ca_umap.ug_uidlen, + &arg32->ca32_umap.ug32_gidlen); + err |= __get_user(uaddr, &arg32->ca32_umap.ug32_gdimap); + if (err) + return -EFAULT; + karg->ca_umap.ug_gdimap = kmalloc((sizeof(gid_t) * karg->ca_umap.ug_uidlen), + GFP_USER); + if(!karg->ca_umap.ug_gdimap) + return -ENOMEM; + for(i = 0; i < karg->ca_umap.ug_gidlen; i++) + err |= __get_user(karg->ca_umap.ug_gdimap[i], + &(((__kernel_gid_t32 *)A(uaddr))[i])); + + return err; +} + +static int nfs_getfh32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfh.gf_addr, + &arg32->ca32_getfh.gf32_addr, + (sizeof(struct sockaddr))); + err |= __get_user(karg->ca_getfh.gf_dev, + &arg32->ca32_getfh.gf32_dev); + err |= __get_user(karg->ca_getfh.gf_ino, + &arg32->ca32_getfh.gf32_ino); + err |= __get_user(karg->ca_getfh.gf_version, + &arg32->ca32_getfh.gf32_version); + return err; +} + +static int nfs_getfd32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfd.gd_addr, + &arg32->ca32_getfd.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfd.gd_path, + &arg32->ca32_getfd.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfd.gd_version, + &arg32->ca32_getfd.gd32_version); + return err; +} + +static int nfs_getfs32_trans(struct nfsctl_arg *karg, struct nfsctl_arg32 *arg32) +{ + int err; + + err = __get_user(karg->ca_version, &arg32->ca32_version); + err |= copy_from_user(&karg->ca_getfs.gd_addr, + &arg32->ca32_getfs.gd32_addr, + (sizeof(struct sockaddr))); + err |= copy_from_user(&karg->ca_getfs.gd_path, + &arg32->ca32_getfs.gd32_path, + (NFS_MAXPATHLEN+1)); + err |= __get_user(karg->ca_getfs.gd_maxlen, + &arg32->ca32_getfs.gd32_maxlen); + return err; +} + +/* This really doesn't need translations, we are only passing + * back a union which contains opaque nfs file handle data. + */ +static int nfs_getfh32_res_trans(union nfsctl_res *kres, union nfsctl_res32 *res32) +{ + return copy_to_user(res32, kres, sizeof(*res32)); +} + +/* +asmlinkage long sys_ni_syscall(void); +*/ + +int asmlinkage sys32_nfsservctl(int cmd, struct nfsctl_arg32 *arg32, union nfsctl_res32 *res32) +{ + struct nfsctl_arg *karg = NULL; + union nfsctl_res *kres = NULL; + mm_segment_t oldfs; + int err; + + karg = kmalloc(sizeof(*karg), GFP_USER); + if(!karg) + return -ENOMEM; + if(res32) { + kres = kmalloc(sizeof(*kres), GFP_USER); + if(!kres) { + kfree(karg); + return -ENOMEM; + } + } + switch(cmd) { + case NFSCTL_SVC: + err = nfs_svc32_trans(karg, arg32); + break; + case NFSCTL_ADDCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_DELCLIENT: + err = nfs_clnt32_trans(karg, arg32); + break; + case NFSCTL_EXPORT: + case NFSCTL_UNEXPORT: + err = nfs_exp32_trans(karg, arg32); + break; + /* This one is unimplemented, be we're ready for it. */ + case NFSCTL_UGIDUPDATE: + err = nfs_uud32_trans(karg, arg32); + break; + case NFSCTL_GETFH: + err = nfs_getfh32_trans(karg, arg32); + break; + case NFSCTL_GETFD: + err = nfs_getfd32_trans(karg, arg32); + break; + case NFSCTL_GETFS: + err = nfs_getfs32_trans(karg, arg32); + break; + default: + err = -EINVAL; + break; + } + if(err) + goto done; + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = sys_nfsservctl(cmd, karg, kres); + set_fs(oldfs); + + if (err) + goto done; + + if((cmd == NFSCTL_GETFH) || + (cmd == NFSCTL_GETFD) || + (cmd == NFSCTL_GETFS)) + err = nfs_getfh32_res_trans(kres, res32); + +done: + if(karg) { + if(cmd == NFSCTL_UGIDUPDATE) { + if(karg->ca_umap.ug_ident) + kfree(karg->ca_umap.ug_ident); + if(karg->ca_umap.ug_udimap) + kfree(karg->ca_umap.ug_udimap); + if(karg->ca_umap.ug_gdimap) + kfree(karg->ca_umap.ug_gdimap); + } + kfree(karg); + } + if(kres) + kfree(kres); + return err; +} + +/* Translations due to time_t size differences. Which affects all + sorts of things, like timeval and itimerval. */ + +extern struct timezone sys_tz; +extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz); + +asmlinkage int sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) +{ + if (tv) { + struct timeval ktv; + do_gettimeofday(&ktv); + if (put_tv32(tv, &ktv)) + return -EFAULT; + } + if (tz) { + if (copy_to_user(tz, &sys_tz, sizeof(sys_tz))) + return -EFAULT; + } + return 0; +} + +asmlinkage int sys32_settimeofday(struct timeval32 *tv, struct timezone *tz) +{ + struct timeval ktv; + struct timezone ktz; + + if (tv) { + if (get_tv32(&ktv, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &ktv : NULL, tz ? &ktz : NULL); +} + +extern int do_getitimer(int which, struct itimerval *value); + +asmlinkage int sys32_getitimer(int which, struct itimerval32 *it) +{ + struct itimerval kit; + int error; + + error = do_getitimer(which, &kit); + if (!error && put_it32(it, &kit)) + error = -EFAULT; + + return error; +} + +extern int do_setitimer(int which, struct itimerval *, struct itimerval *); + +asmlinkage int sys32_setitimer(int which, struct itimerval32 *in, struct itimerval32 *out) +{ + struct itimerval kin, kout; + int error; + + if (in) { + if (get_it32(&kin, in)) + return -EFAULT; + } else + memset(&kin, 0, sizeof(kin)); + + error = do_setitimer(which, &kin, out ? &kout : NULL); + if (error || !out) + return error; + if (put_it32(out, &kout)) + return -EFAULT; + + return 0; + +} + +asmlinkage int sys_utimes(char *, struct timeval *); + +asmlinkage int sys32_utimes(char *filename, struct timeval32 *tvs) +{ + char *kfilename; + struct timeval ktvs[2]; + mm_segment_t old_fs; + int ret; + + kfilename = getname(filename); + ret = PTR_ERR(kfilename); + if (!IS_ERR(kfilename)) { + if (tvs) { + if (get_tv32(&ktvs[0], tvs) || + get_tv32(&ktvs[1], 1+tvs)) + return -EFAULT; + } + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_utimes(kfilename, &ktvs[0]); + set_fs(old_fs); + + putname(kfilename); + } + return ret; +} + +/* These are here just in case some old sparc32 binary calls it. */ +asmlinkage int sys32_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +extern asmlinkage int sys_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5); + +asmlinkage int sys32_prctl(int option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) +{ + return sys_prctl(option, + (unsigned long) arg2, + (unsigned long) arg3, + (unsigned long) arg4, + (unsigned long) arg5); +} + + +extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, + size_t count, loff_t pos); + +extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, + size_t count, loff_t pos); + +typedef __kernel_ssize_t32 ssize_t32; + +asmlinkage ssize_t32 sys32_pread(unsigned int fd, char *ubuf, + __kernel_size_t32 count, u32 poshi, u32 poslo) +{ + return sys_pread(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + +asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, char *ubuf, + __kernel_size_t32 count, u32 poshi, u32 poslo) +{ + return sys_pwrite(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + + +extern asmlinkage ssize_t sys_sendfile(int out_fd, int in_fd, off_t *offset, size_t count); + +asmlinkage int sys32_sendfile(int out_fd, int in_fd, __kernel_off_t32 *offset, s32 count) +{ + mm_segment_t old_fs = get_fs(); + int ret; + off_t of; + + if (offset && get_user(of, offset)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_sendfile(out_fd, in_fd, offset ? &of : NULL, count); + set_fs(old_fs); + + if (!ret && offset && put_user(of, offset)) + return -EFAULT; + + return ret; +} + +/* Handle adjtimex compatability. */ + +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct timeval32 time; + s32 tick; + s32 ppsfreq, jitter, shift, stabil; + s32 jitcnt, calcnt, errcnt, stbcnt; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; + s32 :32; s32 :32; s32 :32; s32 :32; +}; + +extern int do_adjtimex(struct timex *); + +asmlinkage int sys32_adjtimex(struct timex32 *utp) +{ + struct timex txc; + int ret; + + memset(&txc, 0, sizeof(struct timex)); + + if(get_user(txc.modes, &utp->modes) || + __get_user(txc.offset, &utp->offset) || + __get_user(txc.freq, &utp->freq) || + __get_user(txc.maxerror, &utp->maxerror) || + __get_user(txc.esterror, &utp->esterror) || + __get_user(txc.status, &utp->status) || + __get_user(txc.constant, &utp->constant) || + __get_user(txc.precision, &utp->precision) || + __get_user(txc.tolerance, &utp->tolerance) || + __get_user(txc.time.tv_sec, &utp->time.tv_sec) || + __get_user(txc.time.tv_usec, &utp->time.tv_usec) || + __get_user(txc.tick, &utp->tick) || + __get_user(txc.ppsfreq, &utp->ppsfreq) || + __get_user(txc.jitter, &utp->jitter) || + __get_user(txc.shift, &utp->shift) || + __get_user(txc.stabil, &utp->stabil) || + __get_user(txc.jitcnt, &utp->jitcnt) || + __get_user(txc.calcnt, &utp->calcnt) || + __get_user(txc.errcnt, &utp->errcnt) || + __get_user(txc.stbcnt, &utp->stbcnt)) + return -EFAULT; + + ret = do_adjtimex(&txc); + + if(put_user(txc.modes, &utp->modes) || + __put_user(txc.offset, &utp->offset) || + __put_user(txc.freq, &utp->freq) || + __put_user(txc.maxerror, &utp->maxerror) || + __put_user(txc.esterror, &utp->esterror) || + __put_user(txc.status, &utp->status) || + __put_user(txc.constant, &utp->constant) || + __put_user(txc.precision, &utp->precision) || + __put_user(txc.tolerance, &utp->tolerance) || + __put_user(txc.time.tv_sec, &utp->time.tv_sec) || + __put_user(txc.time.tv_usec, &utp->time.tv_usec) || + __put_user(txc.tick, &utp->tick) || + __put_user(txc.ppsfreq, &utp->ppsfreq) || + __put_user(txc.jitter, &utp->jitter) || + __put_user(txc.shift, &utp->shift) || + __put_user(txc.stabil, &utp->stabil) || + __put_user(txc.jitcnt, &utp->jitcnt) || + __put_user(txc.calcnt, &utp->calcnt) || + __put_user(txc.errcnt, &utp->errcnt) || + __put_user(txc.stbcnt, &utp->stbcnt)) + ret = -EFAULT; + + return ret; +} + +extern asmlinkage long sys_setpriority(int which, int who, int niceval); + +asmlinkage int sys_setpriority32(u32 which, u32 who, u32 niceval) +{ + return sys_setpriority((int) which, + (int) who, + (int) niceval); +} + +struct __sysctl_args32 { + u32 name; + int nlen; + u32 oldval; + u32 oldlenp; + u32 newval; + u32 newlen; + u32 __unused[4]; +}; + +extern asmlinkage long sys32_sysctl(struct __sysctl_args32 *args) +{ + struct __sysctl_args32 tmp; + int error; + size_t oldlen, *oldlenp = NULL; + unsigned long addr = (((long)&args->__unused[0]) + 7) & ~7; + + if (copy_from_user(&tmp, args, sizeof(tmp))) + return -EFAULT; + + if (tmp.oldval && tmp.oldlenp) { + /* Duh, this is ugly and might not work if sysctl_args + is in read-only memory, but do_sysctl does indirectly + a lot of uaccess in both directions and we'd have to + basically copy the whole sysctl.c here, and + glibc's __sysctl uses rw memory for the structure + anyway. */ + if (get_user(oldlen, (u32 *)A(tmp.oldlenp)) || + put_user(oldlen, (size_t *)addr)) + return -EFAULT; + oldlenp = (size_t *)addr; + } + + lock_kernel(); + error = do_sysctl((int *)A(tmp.name), tmp.nlen, (void *)A(tmp.oldval), + oldlenp, (void *)A(tmp.newval), tmp.newlen); + unlock_kernel(); + if (oldlenp) { + if (!error) { + if (get_user(oldlen, (size_t *)addr) || + put_user(oldlen, (u32 *)A(tmp.oldlenp))) + error = -EFAULT; + } + copy_to_user(args->__unused, tmp.__unused, sizeof(tmp.__unused)); + } + return error; +} + +struct stat64_emu31 { + unsigned short st_dev; + unsigned char __pad0[6]; + + long long st_ino; + unsigned int st_mode; + unsigned int st_nlink; + + __u32 st_uid; + __u32 st_gid; + + unsigned short st_rdev; + unsigned char __pad3[10]; + + long long st_size; + __u32 st_blksize; + + __u32 st_blocks; /* Number 512-byte blocks allocated. */ + __u32 __pad4; /* future possible st_blocks high bits */ + + __u32 st_atime; + __u32 __pad5; + + __u32 st_mtime; + __u32 __pad6; + + __u32 st_ctime; + __u32 __pad7; /* will be high 32 bits of ctime someday */ + + __u32 __unused1; + __u32 __unused2; +}; + +static inline int +putstat64 (struct stat64_emu31 *ubuf, struct stat *kbuf) +{ + int err; + + err = put_user (kbuf->st_dev, &ubuf->st_dev); + err |= __put_user (kbuf->st_ino, &ubuf->st_ino); + err |= __put_user (kbuf->st_mode, &ubuf->st_mode); + err |= __put_user (kbuf->st_nlink, &ubuf->st_nlink); + err |= __put_user (kbuf->st_uid, &ubuf->st_uid); + err |= __put_user (kbuf->st_gid, &ubuf->st_gid); + err |= __put_user (kbuf->st_rdev, &ubuf->st_rdev); + err |= __put_user (kbuf->st_size, &ubuf->st_size); + err |= __put_user (kbuf->st_blksize, &ubuf->st_blksize); + err |= __put_user (kbuf->st_blocks, &ubuf->st_blocks); + err |= __put_user (kbuf->st_atime, &ubuf->st_atime); + err |= __put_user (kbuf->st_mtime, &ubuf->st_mtime); + err |= __put_user (kbuf->st_ctime, &ubuf->st_ctime); + return err; +} + +extern asmlinkage long sys_newstat(char * filename, struct stat * statbuf); + +asmlinkage long sys32_stat64(char * filename, struct stat64_emu31 * statbuf, long flags) +{ + int ret; + struct stat s; + char * tmp; + int err; + mm_segment_t old_fs = get_fs(); + + tmp = getname(filename); + err = PTR_ERR(tmp); + if (IS_ERR(tmp)) + return err; + + set_fs (KERNEL_DS); + ret = sys_newstat(tmp, &s); + set_fs (old_fs); + putname(tmp); + if (putstat64 (statbuf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_newlstat(char * filename, struct stat * statbuf); + +asmlinkage long sys32_lstat64(char * filename, struct stat64_emu31 * statbuf, long flags) +{ + int ret; + struct stat s; + char * tmp; + int err; + mm_segment_t old_fs = get_fs(); + + tmp = getname(filename); + err = PTR_ERR(tmp); + if (IS_ERR(tmp)) + return err; + + set_fs (KERNEL_DS); + ret = sys_newstat(tmp, &s); + set_fs (old_fs); + putname(tmp); + if (putstat64 (statbuf, &s)) + return -EFAULT; + return ret; +} + +extern asmlinkage long sys_newfstat(unsigned int fd, struct stat * statbuf); + +asmlinkage long sys32_fstat64(unsigned long fd, struct stat64_emu31 * statbuf, long flags) +{ + int ret; + struct stat s; + mm_segment_t old_fs = get_fs(); + + set_fs (KERNEL_DS); + ret = sys_newfstat(fd, &s); + set_fs (old_fs); + if (putstat64 (statbuf, &s)) + return -EFAULT; + return ret; +} + +/* + * Linux/i386 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_emu31 { + u32 addr; + u32 len; + u32 prot; + u32 flags; + u32 fd; + u32 offset; +}; + +/* 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(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + + +asmlinkage unsigned long +old32_mmap(struct mmap_arg_struct_emu31 *arg) +{ + struct mmap_arg_struct_emu31 a; + int error = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + + error = -EINVAL; + if (a.offset & ~PAGE_MASK) + goto out; + + error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT); +out: + return error; +} + +asmlinkage long +sys32_mmap2(struct mmap_arg_struct_emu31 *arg) +{ + struct mmap_arg_struct_emu31 a; + int error = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset); +out: + return error; +} + +extern asmlinkage long sys_socket(int family, int type, int protocol); +extern asmlinkage long sys_bind(int fd, struct sockaddr *umyaddr, int addrlen); +extern asmlinkage long sys_connect(int fd, struct sockaddr *uservaddr, int addrlen); +extern asmlinkage long sys_listen(int fd, int backlog); +extern asmlinkage long sys_accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen); +extern asmlinkage long sys_getsockname(int fd, struct sockaddr *usockaddr, int *usockaddr_len); +extern asmlinkage long sys_getpeername(int fd, struct sockaddr *usockaddr, int *usockaddr_len); +extern asmlinkage long sys_socketpair(int family, int type, int protocol, int usockvec[2]); +extern asmlinkage long sys_send(int fd, void * buff, size_t len, unsigned flags); +extern asmlinkage long sys_sendto(int fd, void * buff, size_t len, unsigned flags, + struct sockaddr *addr, int addr_len); +extern asmlinkage long sys_recv(int fd, void * ubuf, size_t size, unsigned flags); +extern asmlinkage long sys_recvfrom(int fd, void * ubuf, size_t size, unsigned flags, + struct sockaddr *addr, int *addr_len); +extern asmlinkage long sys_shutdown(int fd, int how); +extern asmlinkage long sys_getsockopt(int fd, int level, int optname, char *optval, int * optlen); + +/* Argument list sizes for sys_socketcall */ +#define AL(x) ((x) * sizeof(u32)) +static unsigned char nas[18] = {AL(0),AL(3),AL(3),AL(3),AL(2),AL(3), + AL(3),AL(3),AL(4),AL(4),AL(4),AL(6), + AL(6),AL(2),AL(5),AL(5),AL(3),AL(3)}; +#undef AL + +asmlinkage long sys32_socketcall(int call, u32 *args) +{ + int ret; + u32 a[6]; + + if (call < SYS_SOCKET || call > SYS_RECVMSG) + return -EINVAL; + if (copy_from_user(a, args, nas[call])) + return -EFAULT; + switch(call) { + case SYS_SOCKET: + ret = sys_socket(a[0], a[1], a[2]); + break; + case SYS_BIND: + ret = sys_bind(a[0], (struct sockaddr *) A(a[1]), a[2]); + break; + case SYS_CONNECT: + ret = sys_connect(a[0], (struct sockaddr *) A(a[1]), a[2]); + break; + case SYS_LISTEN: + ret = sys_listen(a[0], a[1]); + break; + case SYS_ACCEPT: + ret = sys_accept(a[0], (struct sockaddr *) A(a[1]), + (int *) A(a[2])); + break; + case SYS_GETSOCKNAME: + ret = sys_getsockname(a[0], (struct sockaddr *) A(a[1]), + (int *) A(a[2])); + break; + case SYS_GETPEERNAME: + ret = sys_getpeername(a[0], (struct sockaddr *) A(a[1]), + (int *) A(a[2])); + break; + case SYS_SOCKETPAIR: + ret = sys_socketpair(a[0], a[1], a[2], (int *) A(a[3])); + break; + case SYS_SEND: + ret = sys_send(a[0], (void *) A(a[1]), a[2], a[3]); + break; + case SYS_SENDTO: + ret = sys_sendto(a[0], (void*) A(a[1]), a[2], a[3], (struct sockaddr *) A(a[4]), a[5]); + break; + case SYS_RECV: + ret = sys_recv(a[0], (void *) A(a[1]), a[2], a[3]); + break; + case SYS_RECVFROM: + ret = sys_recvfrom(a[0], (void *) A(a[1]), a[2], a[3], (struct sockaddr *) A(a[4]), (int *) A(a[5]) ); + break; + case SYS_SHUTDOWN: + ret = sys_shutdown(a[0], a[1]); + break; + case SYS_SETSOCKOPT: + ret = sys32_setsockopt(a[0], a[1], a[2], (char *) A(a[3]), + a[4]); + break; + case SYS_GETSOCKOPT: + ret = sys_getsockopt(a[0], a[1], a[2], (char *) A(a[3]), (int *) A(a[4]) ); + break; + case SYS_SENDMSG: + ret = sys32_sendmsg(a[0], (struct msghdr32 *) A(a[1]), + a[2]); + break; + case SYS_RECVMSG: + ret = sys32_recvmsg(a[0], (struct msghdr32 *) A(a[1]), + a[2]); + break; + default: + ret = EINVAL; + break; + } + return ret; +} + diff --git a/arch/s390x/kernel/linux32.h b/arch/s390x/kernel/linux32.h new file mode 100644 index 000000000..c3641b0a0 --- /dev/null +++ b/arch/s390x/kernel/linux32.h @@ -0,0 +1,246 @@ +#ifndef _ASM_S390X_S390_H +#define _ASM_S390X_S390_H + +#include <linux/config.h> +#include <linux/socket.h> +#include <linux/nfs_fs.h> +#include <linux/sunrpc/svc.h> +#include <linux/nfsd/nfsd.h> +#include <linux/nfsd/export.h> + +#ifdef CONFIG_S390_SUPPORT + +/* Macro that masks the high order bit of an 32 bit pointer and converts it*/ +/* to a 64 bit pointer */ +#define A(__x) ((unsigned long)((__x) & 0x7FFFFFFFUL)) +#define AA(__x) \ + ((unsigned long)(__x)) + +/* Now 32bit compatibility types */ +typedef unsigned int __kernel_size_t32; +typedef int __kernel_ssize_t32; +typedef int __kernel_ptrdiff_t32; +typedef int __kernel_time_t32; +typedef int __kernel_clock_t32; +typedef int __kernel_pid_t32; +typedef unsigned short __kernel_ipc_pid_t32; +typedef unsigned short __kernel_uid_t32; +typedef unsigned short __kernel_gid_t32; +typedef unsigned short __kernel_dev_t32; +typedef unsigned int __kernel_ino_t32; +typedef unsigned short __kernel_mode_t32; +typedef unsigned short __kernel_umode_t32; +typedef short __kernel_nlink_t32; +typedef int __kernel_daddr_t32; +typedef int __kernel_off_t32; +typedef unsigned int __kernel_caddr_t32; +typedef long __kernel_loff_t32; +typedef __kernel_fsid_t __kernel_fsid_t32; + +struct ipc_kludge_32 { + __u32 msgp; /* pointer */ + __s32 msgtyp; +}; + +#define F_GETLK64 12 +#define F_SETLK64 13 +#define F_SETLKW64 14 + +struct flock32 { + short l_type; + short l_whence; + __kernel_off_t32 l_start; + __kernel_off_t32 l_len; + __kernel_pid_t32 l_pid; + short __unused; +}; + +struct stat32 { + unsigned short st_dev; + unsigned short __pad1; + __u32 st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + __u32 st_size; + __u32 st_blksize; + __u32 st_blocks; + __u32 st_atime; + __u32 __unused1; + __u32 st_mtime; + __u32 __unused2; + __u32 st_ctime; + __u32 __unused3; + __u32 __unused4; + __u32 __unused5; +}; + +struct statfs32 { + __s32 f_type; + __s32 f_bsize; + __s32 f_blocks; + __s32 f_bfree; + __s32 f_bavail; + __s32 f_files; + __s32 f_ffree; + __kernel_fsid_t f_fsid; + __s32 f_namelen; + __s32 f_spare[6]; +}; + +typedef __u32 old_sigset_t32; /* at least 32 bits */ + +struct old_sigaction32 { + __u32 sa_handler; /* Really a pointer, but need to deal with 32 bits */ + old_sigset_t32 sa_mask; /* A 32 bit mask */ + __u32 sa_flags; + __u32 sa_restorer; /* Another 32 bit pointer */ +}; + +#define _SIGCONTEXT_NSIG_WORDS32 2 +typedef struct { + __u32 sig[_SIGCONTEXT_NSIG_WORDS32]; +} sigset_t32; + +typedef union sigval32 { + int sival_int; + __u32 sival_ptr; +} sigval_t32; + +typedef struct siginfo32 { + int si_signo; + int si_errno; + int si_code; + + union { + int _pad[((128/sizeof(int)) - 3)]; + + /* kill() */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + } _kill; + + /* POSIX.1b timers */ + struct { + unsigned int _timer1; + unsigned int _timer2; + + } _timer; + + /* POSIX.1b signals */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + sigval_t32 _sigval; + } _rt; + + /* SIGCHLD */ + struct { + pid_t _pid; /* which child */ + uid_t _uid; /* sender's uid */ + int _status;/* exit code */ + __kernel_clock_t32 _utime; + __kernel_clock_t32 _stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + __u32 _addr; /* faulting insn/memory ref. - pointer */ + } _sigfault; + + /* SIGPOLL */ + struct { + int _band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int _fd; + } _sigpoll; + } _sifields; +} siginfo_t32; + +/* + * How these fields are to be accessed. + */ +#define si_pid _sifields._kill._pid +#define si_uid _sifields._kill._uid +#define si_status _sifields._sigchld._status +#define si_utime _sifields._sigchld._utime +#define si_stime _sifields._sigchld._stime +#define si_value _sifields._rt._sigval +#define si_int _sifields._rt._sigval.sival_int +#define si_ptr _sifields._rt._sigval.sival_ptr +#define si_addr _sifields._sigfault._addr +#define si_band _sifields._sigpoll._band +#define si_fd _sifields._sigpoll._fd + +/* asm/sigcontext.h */ +typedef union +{ + __u64 d; + __u32 f; +} freg_t32; + +typedef struct +{ + unsigned int fpc; + freg_t32 fprs[__NUM_FPRS]; +} _s390_fp_regs32; + +typedef struct +{ + __u32 mask; + __u32 addr; +} _psw_t32 __attribute__ ((aligned(8))); + +typedef struct +{ + _psw_t32 psw; + __u32 gprs[__NUM_GPRS]; + __u32 acrs[__NUM_ACRS]; +} _s390_regs_common32; + +typedef struct +{ + _s390_regs_common32 regs; + _s390_fp_regs32 fpregs; +} _sigregs32; + +#define _SIGCONTEXT_NSIG32 64 +#define _SIGCONTEXT_NSIG_BPW32 32 +#define __SIGNAL_FRAMESIZE32 96 +#define _SIGMASK_COPY_SIZE32 (sizeof(u32)*2) + +struct sigcontext32 +{ + __u32 oldmask[_SIGCONTEXT_NSIG_WORDS32]; + __u32 sregs; /* pointer */ +}; + +/* asm/signal.h */ +struct sigaction32 { + __u32 sa_handler; /* pointer */ + __u32 sa_flags; + __u32 sa_restorer; /* pointer */ + sigset_t32 sa_mask; /* mask last for extensibility */ +}; + +typedef struct { + __u32 ss_sp; /* pointer */ + int ss_flags; + __kernel_size_t32 ss_size; +} stack_t32; + +/* asm/ucontext.h */ +struct ucontext32 { + __u32 uc_flags; + __u32 uc_link; /* pointer */ + stack_t32 uc_stack; + sigset_t32 uc_sigmask; /* mask last for extensibility */ + __u32 sc; /* pointer */ +}; + +#endif /* !CONFIG_S390_SUPPORT */ + +#endif /* _ASM_S390X_S390_H */ diff --git a/arch/s390x/kernel/lowcore.S b/arch/s390x/kernel/lowcore.S new file mode 100644 index 000000000..4cf3bf8f2 --- /dev/null +++ b/arch/s390x/kernel/lowcore.S @@ -0,0 +1,28 @@ +/* + * arch/s390/kernel/lowcore.S + * S390 lowcore definition. + * + * S390 64 bit Version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hpenner@de.ibm.com) + * Martin Schwidefsky (schwidefsky@de.ibm.com), + */ +#include <asm/lowcore.h> + + .align 8192 + .globl init_S390_lowcore +init_S390_lowcore: + .fill 0x1a0-0x000,1,0 + .quad _RESTART_PSW_MASK + .quad restart_int_handler + .quad _EXT_PSW_MASK + .quad ext_int_handler + .quad _SVC_PSW_MASK + .quad system_call + .quad _PGM_PSW_MASK + .quad pgm_check_handler + .quad _MCCK_PSW_MASK + .quad mcck_int_handler +EXT_PSW: .quad _IO_PSW_MASK + .quad io_int_handler + .fill 0x2000-0x200,1,0 diff --git a/arch/s390x/kernel/mathemu.c b/arch/s390x/kernel/mathemu.c new file mode 100644 index 000000000..db6dc9431 --- /dev/null +++ b/arch/s390x/kernel/mathemu.c @@ -0,0 +1,920 @@ +/* + * arch/s390/kernel/mathemu.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * 'mathemu.c' handles IEEE instructions on a S390 processor + * that does not have the IEEE fpu + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/ptrace.h> + +#include <asm/uaccess.h> +#include <asm/mathemu.h> + +#ifdef CONFIG_SYSCTL +int sysctl_ieee_emulation_warnings=1; +#endif + +static void display_emulation_not_implemented(char *instr) +{ + struct pt_regs *regs; + __u16 *location; + +#if CONFIG_SYSCTL + if(sysctl_ieee_emulation_warnings) +#endif + { + regs=current->thread.regs; + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + printk("%s ieee fpu instruction not emulated process name: %s pid: %d \n", + instr, + current->comm, current->pid); + printk("%s's PSW: %08lx %08lx\n",instr, + (unsigned long) regs->psw.mask, + (unsigned long) location); + } +} + + +static void set_CC_df(__u64 val1,__u64 val2) { + int rc; + rc = __cmpdf2(val1,val2); + current->thread.regs->psw.mask &= 0xFFFFCFFFFFFFFFFFL; + switch (rc) { + case -1: + current->thread.regs->psw.mask |= 0x0000100000000000L; + break; + case 1: + current->thread.regs->psw.mask |= 0x0000200000000000L; + break; + } +} + +static void set_CC_sf(__u32 val1,__u32 val2) { + int rc; + rc = __cmpsf2(val1,val2); + current->thread.regs->psw.mask &= 0xFFFFCFFFFFFFFFFF; + switch (rc) { + case -1: + current->thread.regs->psw.mask |= 0x0000100000000000L; + break; + case 1: + current->thread.regs->psw.mask |= 0x0000200000000000L; + break; + } +} + + +static void emu_adb (int rx, __u64 val) { + current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d,val); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_adbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __adddf3(current->thread.fp_regs.fprs[rx].d, + current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_aeb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f,val); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_aebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __addsf3(current->thread.fp_regs.fprs[rx].f, + current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_axbr (int rx, int ry) { + display_emulation_not_implemented("axbr"); +} + +static void emu_cdb (int rx, __u64 val) { + set_CC_df(current->thread.fp_regs.fprs[rx].d,val); +} + +static void emu_cdbr (int rx, int ry) { + set_CC_df(current->thread.fp_regs.fprs[rx].d,current->thread.fp_regs.fprs[ry].d); +} + +static void emu_cdfbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = + __floatsidf(current->thread.regs->gprs[ry]); +} + +static void emu_ceb (int rx, __u32 val) { + set_CC_sf(current->thread.fp_regs.fprs[rx].f,val); +} + +static void emu_cebr (int rx, int ry) { + set_CC_sf(current->thread.fp_regs.fprs[rx].f,current->thread.fp_regs.fprs[ry].f); +} + +static void emu_cefbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = + __floatsisf(current->thread.regs->gprs[ry]); +} + +static void emu_cfdbr (int rx, int ry, int mask) { + current->thread.regs->gprs[rx] = + __fixdfsi(current->thread.fp_regs.fprs[ry].d); +} + +static void emu_cfebr (int rx, int ry, int mask) { + current->thread.regs->gprs[rx] = + __fixsfsi(current->thread.fp_regs.fprs[ry].f); +} + +static void emu_cfxbr (int rx, int ry, int mask) { + display_emulation_not_implemented("cfxbr"); +} + +static void emu_cxbr (int rx, int ry) { + display_emulation_not_implemented("cxbr"); +} + +static void emu_cxfbr (int rx, int ry) { + display_emulation_not_implemented("cxfbr"); +} + +static void emu_ddb (int rx, __u64 val) { + current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d,val); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_ddbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __divdf3(current->thread.fp_regs.fprs[rx].d, + current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_deb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f,val); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_debr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __divsf3(current->thread.fp_regs.fprs[rx].f, + current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_didbr (int rx, int ry, int mask) { + display_emulation_not_implemented("didbr"); +} + +static void emu_diebr (int rx, int ry, int mask) { + display_emulation_not_implemented("diebr"); +} + +static void emu_dxbr (int rx, int ry) { + display_emulation_not_implemented("dxbr"); +} + +static void emu_efpc (int rx, int ry) { + display_emulation_not_implemented("efpc"); +} + +static void emu_fidbr (int rx, int ry, int mask) { + display_emulation_not_implemented("fidbr"); +} + +static void emu_fiebr (int rx, int ry, int mask) { + display_emulation_not_implemented("fiebr"); +} + +static void emu_fixbr (int rx, int ry, int mask) { + display_emulation_not_implemented("fixbr"); +} + +static void emu_kdb (int rx, __u64 val) { + display_emulation_not_implemented("kdb"); +} + +static void emu_kdbr (int rx, int ry) { + display_emulation_not_implemented("kdbr"); +} + +static void emu_keb (int rx, __u32 val) { + display_emulation_not_implemented("keb"); +} + +static void emu_kebr (int rx, int ry) { + display_emulation_not_implemented("kebr"); +} + +static void emu_kxbr (int rx, int ry) { + display_emulation_not_implemented("kxbr"); +} + +static void emu_lcdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = + __negdf2(current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_lcebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = + __negsf2(current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_lcxbr (int rx, int ry) { + display_emulation_not_implemented("lcxbr"); +} + +static void emu_ldeb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].d = __extendsfdf2(val); +} + +static void emu_ldebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = + __extendsfdf2(current->thread.fp_regs.fprs[ry].f); +} + +static void emu_ldxbr (int rx, int ry) { + display_emulation_not_implemented("ldxbr"); +} + +static void emu_ledbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __truncdfsf2(current->thread.fp_regs.fprs[ry].d); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_lexbr (int rx, int ry) { + display_emulation_not_implemented("lexbr"); +} + +static void emu_lndbr (int rx, int ry) { + display_emulation_not_implemented("lndbr"); +} + +static void emu_lnebr (int rx, int ry) { + display_emulation_not_implemented("lnebr"); +} + +static void emu_lnxbr (int rx, int ry) { + display_emulation_not_implemented("lnxbr"); +} + +static void emu_lpdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __absdf2(current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0); +} + +static void emu_lpebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __abssf2(current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_lpxbr (int rx, int ry) { + display_emulation_not_implemented("lpxbr"); +} + +static void emu_ltdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = current->thread.fp_regs.fprs[ry].d; + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_ltebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = current->thread.fp_regs.fprs[ry].f; + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_ltxbr (int rx, int ry) { + display_emulation_not_implemented("ltxbr"); +} + +static void emu_lxdb (int rx, __u64 val) { + display_emulation_not_implemented("lxdb"); +} + +static void emu_lxdbr (int rx, int ry) { + display_emulation_not_implemented("lxdbr"); +} + +static void emu_lxeb (int rx, __u32 val) { + display_emulation_not_implemented("lxeb"); +} + +static void emu_lxebr (int rx, int ry) { + display_emulation_not_implemented("lxebr"); +} + +static void emu_madb (int rx, __u64 val, int mask) { + display_emulation_not_implemented("madb"); +} + +static void emu_madbr (int rx, int ry, int mask) { + display_emulation_not_implemented("madbr"); +} + +static void emu_maeb (int rx, __u32 val, int mask) { + display_emulation_not_implemented("maeb"); +} + +static void emu_maebr (int rx, int ry, int mask) { + display_emulation_not_implemented("maebr"); +} + +static void emu_mdb (int rx, __u64 val) { + current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d,val); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_mdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __muldf3(current->thread.fp_regs.fprs[rx].d, + current->thread.fp_regs.fprs[ry].d); + set_CC_df(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_mdeb (int rx, __u32 val) { + display_emulation_not_implemented("mdeb"); +} + +static void emu_mdebr (int rx, int ry) { + display_emulation_not_implemented("mdebr"); +} + +static void emu_meeb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f, + val); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_meebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __mulsf3(current->thread.fp_regs.fprs[rx].f, + current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_msdb (int rx, __u64 val, int mask) { + display_emulation_not_implemented("msdb"); +} + +static void emu_msdbr (int rx, int ry, int mask) { + display_emulation_not_implemented("msdbr"); +} + +static void emu_mseb (int rx, __u32 val, int mask) { + display_emulation_not_implemented("mseb"); +} + +static void emu_msebr (int rx, int ry, int mask) { + display_emulation_not_implemented("msebr"); +} + +static void emu_mxbr (int rx, int ry) { + display_emulation_not_implemented("mxbr"); +} + +static void emu_mxdb (int rx, __u64 val) { + display_emulation_not_implemented("mxdb"); +} + +static void emu_mxdbr (int rx, int ry) { + display_emulation_not_implemented("mxdbr"); +} + +static void emu_sdb (int rx, __u64 val) { + current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d, + val); + set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_sdbr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].d = __subdf3(current->thread.fp_regs.fprs[rx].d, + current->thread.fp_regs.fprs[ry].d); + set_CC_sf(current->thread.fp_regs.fprs[rx].d,0ULL); +} + +static void emu_seb (int rx, __u32 val) { + current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f, + val); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_sebr (int rx, int ry) { + current->thread.fp_regs.fprs[rx].f = __subsf3(current->thread.fp_regs.fprs[rx].f, + current->thread.fp_regs.fprs[ry].f); + set_CC_sf(current->thread.fp_regs.fprs[rx].f,0); +} + +static void emu_sfpc (int rx, int ry) { + display_emulation_not_implemented("sfpc"); +} + +static void emu_sqdb (int rx, __u64 val) { + display_emulation_not_implemented("sqdb"); +} + +static void emu_sqdbr (int rx, int ry) { + display_emulation_not_implemented("sqdbr"); +} + +static void emu_sqeb (int rx, __u32 val) { + display_emulation_not_implemented("sqeb"); +} + +static void emu_sqebr (int rx, int ry) { + display_emulation_not_implemented("sqebr"); +} + +static void emu_sqxbr (int rx, int ry) { + display_emulation_not_implemented("sqxbr"); +} + +static void emu_sxbr (int rx, int ry) { + display_emulation_not_implemented("sxbr"); +} + +static void emu_tcdb (int rx, __u64 val) { + display_emulation_not_implemented("tcdb"); +} + +static void emu_tceb (int rx, __u32 val) { + display_emulation_not_implemented("tceb"); +} + +static void emu_tcxb (int rx, __u64 val) { + display_emulation_not_implemented("tcxb"); +} + + +static inline void emu_load_regd(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */ + " bras 1,0f\n" + " ld 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].d) + : "1" ); + } +} + +static inline void emu_load_rege(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* load reg from fp_regs.fprs[reg] */ + " bras 1,0f\n" + " le 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].f) + : "1" ); + } +} + +static inline void emu_store_regd(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */ + " bras 1,0f\n" + " std 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].d) + : "1" ); + } +} + + +static inline void emu_store_rege(int reg) { + if ((reg&9) == 0) { /* test if reg in {0,2,4,6} */ + __asm__ __volatile ( /* store reg to fp_regs.fprs[reg] */ + " bras 1,0f\n" + " ste 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (reg<<4), "a" (¤t->thread.fp_regs.fprs[reg].f) + : "1" ); + } +} + +int math_emu_b3(__u8 *opcode, struct pt_regs * regs) { + static const __u8 format_table[] = { + 2, 2, 2, 2, 9, 1, 2, 1, 2, 2, 2, 2, 9, 2, 4, 4, + 1, 1, 1, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1,10, 1, 1, 3, 1, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, + 0, 0, 0, 0, 5, 6, 6, 0, 7, 8, 8, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const void *jump_table[]= { + emu_lpebr, emu_lnebr, emu_ltebr, emu_lcebr, + emu_ldebr, emu_lxdbr, emu_lxebr, emu_mxdbr, + emu_kebr, emu_cebr, emu_aebr, emu_sebr, + emu_mdebr, emu_debr, emu_maebr, emu_msebr, + emu_lpdbr, emu_lndbr, emu_ltdbr, emu_lcdbr, + emu_sqebr, emu_sqdbr, emu_sqxbr, emu_meebr, + emu_kdbr, emu_cdbr, emu_adbr, emu_sdbr, + emu_mdbr, emu_ddbr, emu_madbr, emu_msdbr, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_lpxbr, emu_lnxbr, emu_ltxbr, emu_lcxbr, + emu_ledbr, emu_ldxbr, emu_lexbr, emu_fixbr, + emu_kxbr, emu_cxbr, emu_axbr, emu_sxbr, + emu_mxbr, emu_dxbr, NULL, NULL, + NULL, NULL, NULL, emu_diebr, + NULL, NULL, NULL, emu_fiebr, + NULL, NULL, NULL, emu_didbr, + NULL, NULL, NULL, emu_fidbr, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_sfpc, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_efpc, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + emu_cefbr, emu_cdfbr, emu_cxfbr, NULL, + emu_cfebr, emu_cfdbr, emu_cfxbr + }; + + switch (format_table[opcode[1]]) { + case 1: /* RRE format, double operation */ + emu_store_regd((opcode[3]>>4)&15); + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_regd((opcode[3]>>4)&15); + emu_load_regd(opcode[3]&15); + return 0; + case 2: /* RRE format, float operation */ + emu_store_rege((opcode[3]>>4)&15); + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_rege((opcode[3]>>4)&15); + emu_load_rege(opcode[3]&15); + return 0; + case 3: /* RRF format, double operation */ + emu_store_regd((opcode[3]>>4)&15); + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + emu_load_regd((opcode[3]>>4)&15); + emu_load_regd(opcode[3]&15); + return 0; + case 4: /* RRF format, float operation */ + emu_store_rege((opcode[3]>>4)&15); + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + emu_load_rege((opcode[3]>>4)&15); + emu_load_rege(opcode[3]&15); + return 0; + case 5: /* RRE format, cefbr instruction */ + emu_store_rege((opcode[3]>>4)&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_rege((opcode[3]>>4)&15); + return 0; + case 6: /* RRE format, cdfbr & cxfbr instruction */ + emu_store_regd((opcode[3]>>4)&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_regd((opcode[3]>>4)&15); + return 0; + /* FIXME !! */ + return 0; + case 7: /* RRF format, cfebr instruction */ + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + return 0; + case 8: /* RRF format, cfdbr & cfxbr instruction */ + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15,opcode[2]>>4); + return 0; + case 9: /* RRE format, ldebr & mdebr instruction */ + /* float store but double load */ + emu_store_rege((opcode[3]>>4)&15); + emu_store_rege(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_regd((opcode[3]>>4)&15); + return 0; + case 10: /* RRE format, ledbr instruction */ + /* double store but float load */ + emu_store_regd((opcode[3]>>4)&15); + emu_store_regd(opcode[3]&15); + /* call the emulation function */ + ((void (*)(int, int))jump_table[opcode[1]]) + (opcode[3]>>4,opcode[3]&15); + emu_load_rege((opcode[3]>>4)&15); + return 0; + default: + return 1; + } +} + +static void* calc_addr(struct pt_regs *regs,int rx,int rb,int disp) +{ + rx &= 0xf; + rb &= 0xf; + disp &= 0xfff; + return (void*) ((rx != 0 ? regs->gprs[rx] : 0) + /* index */ + (rb != 0 ? regs->gprs[rb] : 0) + /* base */ + disp); +} + +int math_emu_ed(__u8 *opcode, struct pt_regs * regs) { + static const __u8 format_table[] = { + 0, 0, 0, 0, 5, 1, 2, 1, 2, 2, 2, 2, 5, 2, 4, 4, + 2, 1, 1, 0, 2, 1, 0, 2, 1, 1, 1, 1, 1, 1, 3, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + static const void *jump_table[]= { + NULL, NULL, NULL, NULL, + emu_ldeb, emu_lxdb, emu_lxeb, emu_mxdb, + emu_keb, emu_ceb, emu_aeb, emu_seb, + emu_mdeb, emu_deb, emu_maeb, emu_mseb, + emu_tceb, emu_tcdb, emu_tcxb, NULL, + emu_sqeb, emu_sqdb, NULL, emu_meeb, + emu_kdb, emu_cdb, emu_adb, emu_sdb, + emu_mdb, emu_ddb, emu_madb, emu_msdb + }; + + switch (format_table[opcode[5]]) { + case 1: /* RXE format, __u64 constant */ { + __u64 *dxb, temp; + __u32 opc; + + emu_store_regd((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_from_user fails ? */ + copy_from_user(&temp, dxb, 8); + /* call the emulation function */ + ((void (*)(int, __u64))jump_table[opcode[5]]) + (opcode[1]>>4,temp); + emu_load_regd((opcode[1]>>4)&15); + return 0; + } + case 2: /* RXE format, __u32 constant */ { + __u32 *dxb, temp; + __u32 opc; + + emu_store_rege((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + get_user(temp, dxb); + /* call the emulation function */ + ((void (*)(int, __u32))jump_table[opcode[5]]) + (opcode[1]>>4,temp); + emu_load_rege((opcode[1]>>4)&15); + return 0; + } + case 3: /* RXF format, __u64 constant */ { + __u32 *dxb, temp; + __u32 opc; + + emu_store_regd((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_from_user fails ? */ + copy_from_user(&temp, dxb, 8); + /* call the emulation function */ + ((void (*)(int, __u32, int))jump_table[opcode[5]]) + (opcode[1]>>4,temp,opcode[4]>>4); + emu_load_regd((opcode[1]>>4)&15); + return 0; + } + case 4: /* RXF format, __u32 constant */ { + __u32 *dxb, temp; + __u32 opc; + + emu_store_rege((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + get_user(temp, dxb); + /* call the emulation function */ + ((void (*)(int, __u32, int))jump_table[opcode[5]]) + (opcode[1]>>4,temp,opcode[4]>>4); + emu_load_rege((opcode[1]>>4)&15); + return 0; + } + case 5: /* RXE format, __u32 constant */ + /* store_rege and load_regd */ + { + __u32 *dxb, temp; + __u32 opc; + emu_store_rege((opcode[1]>>4)&15); + opc = *((__u32 *) opcode); + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + get_user(temp, dxb); + /* call the emulation function */ + ((void (*)(int, __u32))jump_table[opcode[5]]) + (opcode[1]>>4,temp); + emu_load_regd((opcode[1]>>4)&15); + return 0; + } + default: + return 1; + } +} + +/* + * Emulate LDR Rx,Ry with Rx or Ry not in {0, 2, 4, 6} + */ +void math_emu_ldr(__u8 *opcode) { + __u16 opc = *((__u16 *) opcode); + + if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ + /* we got an exception therfore ry can't be in {0,2,4,6} */ + __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */ + " bras 1,0f\n" + " ld 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (opc&0x00f0), + "a" (¤t->thread.fp_regs.fprs[opc&0x000f].d) + : "1" ); + } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */ + __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */ + " bras 1,0f\n" + " std 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" ((opc&0x000f)<<4), + "a" (¤t->thread.fp_regs.fprs[(opc&0x00f0)>>4].d) + : "1" ); + } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */ + current->thread.fp_regs.fprs[(opc&0x00f0)>>4] = + current->thread.fp_regs.fprs[opc&0x000f]; + } +} + +/* + * Emulate LER Rx,Ry with Rx or Ry not in {0, 2, 4, 6} + */ +void math_emu_ler(__u8 *opcode) { + __u16 opc = *((__u16 *) opcode); + + if ((opc & 0x0090) == 0) { /* test if rx in {0,2,4,6} */ + /* we got an exception therfore ry can't be in {0,2,4,6} */ + __asm__ __volatile ( /* load rx from fp_regs.fprs[ry] */ + " bras 1,0f\n" + " le 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" (opc&0x00f0), + "a" (¤t->thread.fp_regs.fprs[opc&0x000f].f) + : "1" ); + } else if ((opc & 0x0009) == 0) { /* test if ry in {0,2,4,6} */ + __asm__ __volatile ( /* store ry to fp_regs.fprs[rx] */ + " bras 1,0f\n" + " ste 0,0(%1)\n" + "0: ex %0,0(1)" + : /* no output */ + : "a" ((opc&0x000f)<<4), + "a" (¤t->thread.fp_regs.fprs[(opc&0x00f0)>>4].f) + : "1" ); + } else { /* move fp_regs.fprs[ry] to fp_regs.fprs[rx] */ + current->thread.fp_regs.fprs[(opc&0x00f0)>>4] = + current->thread.fp_regs.fprs[opc&0x000f]; + } +} + +/* + * Emulate LD R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_ld(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u64 *dxb; + + dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_from_user fails ? */ + copy_from_user(¤t->thread.fp_regs.fprs[(opc>>20)&15].d, dxb, 8); +} + +/* + * Emulate LE R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_le(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u32 *mem, *dxb; + + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if get_user fails ? */ + mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f); + get_user(mem[0], dxb); +} + +/* + * Emulate STD R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_std(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u64 *dxb; + dxb = (__u64 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if copy_to_user fails ? */ + copy_to_user(dxb, ¤t->thread.fp_regs.fprs[(opc>>20)&15].d, 8); +} + +/* + * Emulate STE R,D(X,B) with R not in {0, 2, 4, 6} + */ +void math_emu_ste(__u8 *opcode, struct pt_regs * regs) { + __u32 opc = *((__u32 *) opcode); + __u32 *mem, *dxb; + dxb = (__u32 *) calc_addr(regs,opc>>16,opc>>12,opc); + /* FIXME: how to react if put_user fails ? */ + mem = (__u32 *) (¤t->thread.fp_regs.fprs[(opc>>20)&15].f); + put_user(mem[0], dxb); +} + +/* + * Emulate LFPC D(B) + */ +int math_emu_lfpc(__u8 *opcode, struct pt_regs *regs) { + /* FIXME: how to do that ?!? */ + return 0; +} + +/* + * Emulate STFPC D(B) + */ +int math_emu_stfpc(__u8 *opcode, struct pt_regs *regs) { + /* FIXME: how to do that ?!? */ + return 0; +} + +/* + * Emulate SRNM D(B) + */ +int math_emu_srnm(__u8 *opcode, struct pt_regs *regs) { + /* FIXME: how to do that ?!? */ + return 0; +} + + + + + + + + + + + + + + + + diff --git a/arch/s390x/kernel/process.c b/arch/s390x/kernel/process.c new file mode 100644 index 000000000..4533ad528 --- /dev/null +++ b/arch/s390x/kernel/process.c @@ -0,0 +1,516 @@ +/* + * arch/s390/kernel/process.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Hartmut Penner (hp@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Derived from "arch/i386/kernel/process.c" + * Copyright (C) 1995, Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of process handling.. + */ + +#define __KERNEL_SYSCALLS__ +#include <stdarg.h> + +#include <linux/config.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/malloc.h> +#include <linux/vmalloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/reboot.h> +#include <linux/init.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/misc390.h> +#include <asm/irq.h> + +spinlock_t semaphore_wake_lock = SPIN_LOCK_UNLOCKED; + +asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); + +/* + * The idle loop on a S390... + */ + +static psw_t wait_psw; + +int cpu_idle(void *unused) +{ + /* endless idle loop with no priority at all */ + init_idle(); + current->nice = 20; + current->counter = -100; + wait_psw.mask = _WAIT_PSW_MASK; + wait_psw.addr = (unsigned long) &&idle_wakeup; + while(1) { + if (softirq_active(smp_processor_id()) & + softirq_mask(smp_processor_id())) { + do_softirq(); + __sti(); + if (!current->need_resched) + continue; + } + if (current->need_resched) { + schedule(); + check_pgt_cache(); + continue; + } + + /* load wait psw */ + asm volatile ( + "lpswe %0" + : : "m" (wait_psw) ); +idle_wakeup: + } +} + +/* + As all the register will only be made displayable to the root + user ( via printk ) or checking if the uid of the user is 0 from + the /proc filesystem please god this will be secure enough DJB. + The lines are given one at a time so as not to chew stack space in + printk on a crash & also for the proc filesystem when you get + 0 returned you know you've got all the lines + */ + +static int sprintf_regs(int line, char *buff, struct task_struct *task, struct pt_regs *regs) +{ + int linelen=0; + int regno,chaincnt; + u64 backchain,prev_backchain,endchain; + u64 ksp = 0; + char *mode = "???"; + + enum + { + sp_linefeed, + sp_psw, + sp_ksp, + sp_gprs, + sp_gprs1, + sp_gprs2, + sp_gprs3, + sp_gprs4, + sp_gprs5, + sp_gprs6, + sp_gprs7, + sp_gprs8, + sp_acrs, + sp_acrs1, + sp_acrs2, + sp_acrs3, + sp_acrs4, + sp_kern_backchain, + sp_kern_backchain1 + }; + + if (task) + ksp = task->thread.ksp; + if (regs && !(regs->psw.mask & PSW_PROBLEM_STATE)) + ksp = regs->gprs[15]; + + if (regs) + mode = (regs->psw.mask & PSW_PROBLEM_STATE)? + "User" : "Kernel"; + + switch(line) + { + case sp_linefeed: + linelen=sprintf(buff,"\n"); + break; + case sp_psw: + if(regs) + linelen=sprintf(buff, "%s PSW: %016lx %016lx\n", mode, + (unsigned long) regs->psw.mask, + (unsigned long) regs->psw.addr); + else + linelen=sprintf(buff,"pt_regs=NULL some info unavailable\n"); + break; + case sp_ksp: + linelen=sprintf(&buff[linelen], + "task: %016lx ksp: %016lx pt_regs: %016lx\n", + (addr_t)task, (addr_t)ksp, (addr_t)regs); + break; + case sp_gprs: + if(regs) + linelen=sprintf(buff, "%s GPRS:\n", mode); + break; + case sp_gprs1 ... sp_gprs8: + if(regs) + { + regno=(line-sp_gprs1)*2; + linelen = sprintf(buff,"%016lx %016lx\n", + regs->gprs[regno], + regs->gprs[regno+1]); + } + break; + case sp_acrs: + if(regs) + linelen=sprintf(buff, "%s ACRS:\n", mode); + break; + case sp_acrs1 ... sp_acrs4: + if(regs) + { + regno=(line-sp_acrs1)*4; + linelen=sprintf(buff,"%08x %08x %08x %08x\n", + regs->acrs[regno], + regs->acrs[regno+1], + regs->acrs[regno+2], + regs->acrs[regno+3]); + } + break; + case sp_kern_backchain: + if (regs && (regs->psw.mask & PSW_PROBLEM_STATE)) + break; + if (ksp) + linelen=sprintf(buff, "Kernel BackChain CallChain\n"); + break; + default: + if (ksp) + { + + backchain=ksp&PSW_ADDR_MASK; + endchain=((backchain&(-THREAD_SIZE))+THREAD_SIZE); + prev_backchain=backchain-1; + line-=sp_kern_backchain1; + for(chaincnt=0;;chaincnt++) + { + if((backchain==0)||(backchain>=endchain) + ||(chaincnt>=8)||(prev_backchain>=backchain)) + break; + if(chaincnt==line) + { + linelen+=sprintf(&buff[linelen]," %016lx [<%016lx>]\n", + backchain, + *(u64 *)(backchain+112)&PSW_ADDR_MASK); + break; + } + prev_backchain=backchain; + backchain=(*((u64 *)backchain))&PSW_ADDR_MASK; + } + } + } + return(linelen); +} + +void show_regs(struct pt_regs *regs) +{ + char buff[80]; + int line; + + printk("CPU: %d\n",smp_processor_id()); + printk("Process %s (pid: %d, stackpage=%016lX)\n", + current->comm, current->pid, 4096+(addr_t)current); + + for (line = 0; sprintf_regs(line, buff, current, regs); line++) + printk(buff); +} + +char *task_show_regs(struct task_struct *task, char *buffer) +{ + int line, len; + + for (line = 0; ; line++) + { + len = sprintf_regs(line, buffer, task, NULL); + if (!len) break; + buffer += len; + } + return buffer; +} + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + int clone_arg = flags | CLONE_VM; + int retval; + + __asm__ __volatile__( + " slgr 2,2\n" + " lgr 3,%1\n" + " lg 4,%6\n" /* load kernel stack ptr of parent */ + " svc %b2\n" /* Linux system call*/ + " clg 4,%6\n" /* compare ksp's: child or parent ? */ + " je 0f\n" /* parent - jump*/ + " lg 15,%6\n" /* fix kernel stack pointer*/ + " aghi 15,%7\n" + " xc 0(160,15),0(15)\n" /* clear save area */ + " lgr 2,%4\n" /* load argument*/ + " basr 14,%5\n" /* call fn*/ + " svc %b3\n" /* Linux system call*/ + "0: lgr %0,2" + : "=a" (retval) + : "d" (clone_arg), "i" (__NR_clone), "i" (__NR_exit), + "d" (arg), "a" (fn), "i" (__LC_KERNEL_STACK) , + "i" (-STACK_FRAME_OVERHEAD) + : "2", "3", "4" ); + return retval; +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ +} + +void flush_thread(void) +{ + + current->used_math = 0; + current->flags &= ~PF_USEDFPU; +} + +void release_thread(struct task_struct *dead_task) +{ +} + +int copy_thread(int nr, unsigned long clone_flags, unsigned long new_stackp, + unsigned long unused, + struct task_struct * p, struct pt_regs * regs) +{ + struct stack_frame + { + unsigned long back_chain; + unsigned long eos; + unsigned long glue1; + unsigned long glue2; + unsigned long scratch[2]; + unsigned long gprs[10]; /* gprs 6 -15 */ + unsigned long fprs[2]; /* fpr 4 and 6 */ + unsigned long empty[2]; +#if CONFIG_REMOTE_DEBUG + gdb_pt_regs childregs; +#else + pt_regs childregs; +#endif + __u32 pgm_old_ilc; /* single step magic from entry.S */ + __u32 pgm_svc_step; + } *frame; + + frame = (struct stack_frame *) (4*PAGE_SIZE + (unsigned long) p) -1; + frame = (struct stack_frame *) (((unsigned long) frame)&-8L); + p->thread.regs = &frame->childregs; + p->thread.ksp = (unsigned long) frame; + frame->childregs = *regs; + frame->childregs.gprs[15] = new_stackp; + frame->eos = 0; + + /* new return point is ret_from_sys_call */ + frame->gprs[8] = (unsigned long) &ret_from_fork; + + /* fake return stack for resume(), don't go back to schedule */ + frame->gprs[9] = (unsigned long) frame; + frame->pgm_svc_step = 0; /* Nope we aren't single stepping an svc */ + /* save fprs, if used in last task */ + save_fp_regs(&p->thread.fp_regs); + p->thread.user_seg = __pa((unsigned long) p->mm->pgd) | _REGION_TABLE; + /* Don't copy debug registers */ + memset(&p->thread.per_info,0,sizeof(p->thread.per_info)); + return 0; +} + +asmlinkage int sys_fork(struct pt_regs regs) +{ + int ret; + + lock_kernel(); + ret = do_fork(SIGCHLD, regs.gprs[15], ®s, 0); + unlock_kernel(); + return ret; +} + +asmlinkage int sys_clone(struct pt_regs regs) +{ + unsigned long clone_flags; + unsigned long newsp; + int ret; + + lock_kernel(); + clone_flags = regs.gprs[3]; + newsp = regs.orig_gpr2; + if (!newsp) + newsp = regs.gprs[15]; + ret = do_fork(clone_flags, newsp, ®s, 0); + unlock_kernel(); + return ret; +} + +/* + * This is trivial, and on the face of it looks like it + * could equally well be done in user mode. + * + * Not so, for quite unobvious reasons - register pressure. + * In user mode vfork() cannot have a stack frame, and if + * done by calling the "clone()" system call directly, you + * do not have enough call-clobbered registers to hold all + * the information you need. + */ +asmlinkage int sys_vfork(struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, + regs.gprs[15], ®s, 0); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage int sys_execve(struct pt_regs regs) +{ + int error; + char * filename; + + filename = getname((char *) regs.orig_gpr2); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, (char **) regs.gprs[3], (char **) regs.gprs[4], ®s); + if (error == 0) + { + current->ptrace &= ~PT_DTRACE; + current->thread.fp_regs.fpc=0; + __asm__ __volatile__ + ("sr 0,0\n\t" + "sfpc 0,0\n\t" + : : :"0"); + } + putname(filename); +out: + return error; +} + + +/* + * fill in the FPU structure for a core dump. + */ +int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs) +{ + save_fp_regs(fpregs); + return 1; +} + +/* + * fill in the user structure for a core dump.. + */ +void dump_thread(struct pt_regs * regs, struct user * dump) +{ + +/* changed the size calculations - should hopefully work better. lbt */ + dump->magic = CMAGIC; + dump->start_code = 0; + dump->start_stack = regs->gprs[15] & ~(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; + memcpy(&dump->regs.gprs[0],regs,sizeof(s390_regs)); + dump_fpu (regs, &dump->regs.fp_regs); + memcpy(&dump->regs.per_info,¤t->thread.per_info,sizeof(per_struct)); +} + +/* + * These bracket the sleeping functions.. + */ +extern void scheduling_functions_start_here(void); +extern void scheduling_functions_end_here(void); +#define first_sched ((unsigned long) scheduling_functions_start_here) +#define last_sched ((unsigned long) scheduling_functions_end_here) + +unsigned long get_wchan(struct task_struct *p) +{ + unsigned long r14, r15, bc; + unsigned long stack_page; + int count = 0; + if (!p || p == current || p->state == TASK_RUNNING) + return 0; + stack_page = (unsigned long) p; + r15 = p->thread.ksp; + if (!stack_page || r15 < stack_page || r15 >= 16380+stack_page) + return 0; + bc = *(unsigned long *) r15; + do { + if (bc < stack_page || bc >= 16380+stack_page) + return 0; + r14 = *(unsigned long *) (bc+112); + if (r14 < first_sched || r14 >= last_sched) + return r14; + bc = *(unsigned long *) bc; + } while (count++ < 16); + return 0; +} +#undef last_sched +#undef first_sched + +/* + * This should be safe even if called from tq_scheduler + * A typical mask would be sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM) or 0. + * + */ +void s390_daemonize(char *name,unsigned long mask,int use_init_fs) +{ + struct fs_struct *fs; + extern struct task_struct *child_reaper; + struct task_struct *this_process=current; + + /* + * If we were started as result of loading a module, close all of the + * user space pages. We don't need them, and if we didn't close them + * they would be locked into memory. + */ + exit_mm(current); + + this_process->session = 1; + this_process->pgrp = 1; + if(name) + { + strncpy(current->comm,name,15); + current->comm[15]=0; + } + else + current->comm[0]=0; + /* set signal mask to what we want to respond */ + siginitsetinv(¤t->blocked,mask); + /* exit_signal isn't set up */ + /* if we inherit from cpu idle */ + this_process->exit_signal=SIGCHLD; + /* if priority=0 schedule can go into a tight loop */ + this_process->policy= SCHED_OTHER; + /* nice goes priority=20-nice; */ + this_process->nice=10; + if(use_init_fs) + { + exit_fs(this_process); /* current->fs->count--; */ + fs = init_task.fs; + current->fs = fs; + atomic_inc(&fs->count); + exit_files(current); + } + write_lock_irq(&tasklist_lock); + /* We want init as our parent */ + REMOVE_LINKS(this_process); + this_process->p_opptr=this_process->p_pptr=child_reaper; + SET_LINKS(this_process); + write_unlock_irq(&tasklist_lock); +} diff --git a/arch/s390x/kernel/ptrace.c b/arch/s390x/kernel/ptrace.c new file mode 100644 index 000000000..d70b76d2b --- /dev/null +++ b/arch/s390x/kernel/ptrace.c @@ -0,0 +1,613 @@ +/* + * arch/s390/kernel/ptrace.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Based on PowerPC version + * Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org) + * + * Derived from "arch/m68k/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 + * + * Modified by Cort Dougan (cort@cs.nmt.edu) + * + * + * 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. + */ + +#include <stddef.h> +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> + +#include <asm/segment.h> +#include <asm/page.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/system.h> +#include <asm/uaccess.h> + +void FixPerRegisters(struct task_struct *task) +{ + struct pt_regs *regs = task->thread.regs; + per_struct *per_info= + (per_struct *)&task->thread.per_info; + + per_info->control_regs.bits.em_instruction_fetch = + per_info->single_step | per_info->instruction_fetch; + + if (per_info->single_step) { + per_info->control_regs.bits.starting_addr=0; +#ifdef CONFIG_S390_SUPPORT + if (current->thread.flags & S390_FLAG_31BIT) { + per_info->control_regs.bits.ending_addr=0x7fffffffUL; + } + else +#endif + { + per_info->control_regs.bits.ending_addr=-1L; + } + } else { + per_info->control_regs.bits.starting_addr= + per_info->starting_addr; + per_info->control_regs.bits.ending_addr= + per_info->ending_addr; + } + /* if any of the control reg tracing bits are on + we switch on per in the psw */ + if (per_info->control_regs.words.cr[0] & PER_EM_MASK) + regs->psw.mask |= PSW_PER_MASK; + else + regs->psw.mask &= ~PSW_PER_MASK; + if (per_info->control_regs.bits.storage_alt_space_ctl) + task->thread.user_seg |= USER_STD_MASK; + else + task->thread.user_seg &= ~USER_STD_MASK; +} + +void set_single_step(struct task_struct *task) +{ + per_struct *per_info= (per_struct *) &task->thread.per_info; + + per_info->single_step = 1; /* Single step */ + FixPerRegisters (task); +} + +void clear_single_step(struct task_struct *task) +{ + per_struct *per_info= (per_struct *) &task->thread.per_info; + + per_info->single_step = 0; + FixPerRegisters (task); +} + +int ptrace_usercopy(addr_t realuseraddr, addr_t copyaddr, int len, + int tofromuser, int writeuser, unsigned long mask) +{ + unsigned long *realuserptr, *copyptr; + unsigned long tempuser; + int retval; + + retval = 0; + realuserptr = (unsigned long *) realuseraddr; + copyptr = (unsigned long *) copyaddr; + + if (writeuser && realuserptr == NULL) + return 0; + + if (mask != -1L) { + tempuser = *realuserptr; + if (!writeuser) { + tempuser &= mask; + realuserptr = &tempuser; + } + } + if (tofromuser) { + if (writeuser) { + retval = copy_from_user(realuserptr, copyptr, len); + } else { + if (realuserptr == NULL) + retval = clear_user(copyptr, len); + else + retval = copy_to_user(copyptr,realuserptr,len); + retval = (retval == -EFAULT) ? -EIO : 0; + } + } else { + if (writeuser) + memcpy(realuserptr, copyptr, len); + else + memcpy(copyptr, realuserptr, len); + } + if (mask != -1L && writeuser) + *realuserptr = (*realuserptr & mask) | (tempuser & ~mask); + return retval; +} + +int copy_user(struct task_struct *task,saddr_t useraddr, addr_t copyaddr, + int len, int tofromuser, int writingtouser) +{ + int copylen=0,copymax; + addr_t realuseraddr; + saddr_t enduseraddr; + + unsigned long mask; + +#ifdef CONFIG_S390_SUPPORT + if (current->thread.flags & S390_FLAG_31BIT) { + /* adjust user offsets to 64 bit structure */ + if (useraddr < PT_PSWADDR / 2) + useraddr = 2 * useraddr; + else if(useraddr < PT_ACR0 / 2) + useraddr = 2 * useraddr + sizeof(addr_t) / 2; + else if(useraddr < PT_ACR0 / 2 + (PT_ORIGGPR2 - PT_ACR0)) + useraddr = useraddr + PT_ACR0 / 2; + else if(useraddr < PT_ACR0 / 2 + (sizeof(user_regs_struct) - sizeof(addr_t) / 2 - PT_ACR0)) + useraddr = useraddr + PT_ACR0 / 2 + sizeof(addr_t) / 2; + } +#endif + + enduseraddr=useraddr+len; + + if (useraddr < 0 || enduseraddr > sizeof(struct user)|| + (useraddr < PT_ENDREGS && (useraddr&3))|| + (enduseraddr < PT_ENDREGS && (enduseraddr&3))) + return (-EIO); + while(len>0) + { + mask=PSW_ADDR_MASK; + if(useraddr<PT_FPC) + { + realuseraddr=(addr_t)&(((u8 *)task->thread.regs)[useraddr]); + if(useraddr<PT_PSWMASK) + { + copymax=PT_PSWMASK; + } + else if(useraddr<(PT_PSWMASK+PSW_MASK_SIZE)) + { + copymax=(PT_PSWMASK+PSW_MASK_SIZE); + if(writingtouser) + mask=PSW_MASK_DEBUGCHANGE; + } + else if(useraddr<(PT_PSWADDR+PSW_ADDR_SIZE)) + { + copymax=PT_PSWADDR+PSW_ADDR_SIZE; + mask=PSW_ADDR_DEBUGCHANGE; + } + else + copymax=PT_FPC; + + } + else if(useraddr<(PT_FPR15+sizeof(freg_t))) + { + copymax=(PT_FPR15+sizeof(freg_t)); + realuseraddr=(addr_t)&(((u8 *)&task->thread.fp_regs)[useraddr-PT_FPC]); + } + else if(useraddr<sizeof(user_regs_struct)) + { + copymax=sizeof(user_regs_struct); + realuseraddr=(addr_t)&(((u8 *)&task->thread.per_info)[useraddr-PT_CR_9]); + } + else + { + copymax=sizeof(struct user); + realuseraddr=(addr_t)NULL; + } + copylen=copymax-useraddr; + copylen=(copylen>len ? len:copylen); + if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask)) + return (-EIO); + copyaddr+=copylen; + len-=copylen; + useraddr+=copylen; + } + FixPerRegisters(task); + return(0); +} + +asmlinkage int sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret = -EPERM; + unsigned long flags; + unsigned long tmp; + int copied; + ptrace_area parea; + + lock_kernel(); + 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); + read_unlock(&tasklist_lock); + if (!child) + goto out; + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out; + if (request == PTRACE_ATTACH) + { + if (child == current) + goto out; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid)) && !capable(CAP_SYS_PTRACE)) + goto out; + /* the same process cannot be attached many times */ + if (child->ptrace & PT_PTRACED) + goto out; + child->ptrace |= PT_PTRACED; + + write_lock_irqsave(&tasklist_lock, flags); + if (child->p_pptr != current) + { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irqrestore(&tasklist_lock, flags); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out; + } + ret = -ESRCH; + // printk("child=%lX child->flags=%lX",child,child->flags); + /* I added child!=current line so we can get the */ + /* ieee_instruction_pointer from the user structure DJB */ + if(child!=current) + { + if (!(child->ptrace & PT_PTRACED)) + goto out; + if (child->state != TASK_STOPPED) + { + if (request != PTRACE_KILL) + goto out; + } + if (child->p_pptr != current) + goto out; + } + switch (request) + { + /* If I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + goto out; + ret = put_user(tmp,(unsigned long *) data); + goto out; + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: + ret=copy_user(child,addr,data,sizeof(unsigned long),1,0); + break; + + /* If 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)) + goto out; + ret = -EIO; + goto out; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret=copy_user(child,addr,(addr_t)&data,sizeof(unsigned long),0,1); + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + clear_single_step(child); + wake_up_process(child); + /* make sure the single step bit is not set. */ + break; + + case PTRACE_SINGLESTEP: /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + set_single_step(child); + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); + child->exit_code = data; + write_lock_irqsave(&tasklist_lock, flags); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + case PTRACE_PEEKUSR_AREA: + case PTRACE_POKEUSR_AREA: + if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0) + ret=copy_user(child,parea.kernel_addr,parea.process_addr, + parea.len,1,(request==PTRACE_POKEUSR_AREA)); + break; + default: + ret = -EIO; + break; + } + out: + unlock_kernel(); + return ret; +} + +typedef struct +{ +__u32 len; +__u32 kernel_addr; +__u32 process_addr; +} ptrace_area_emu31; + +asmlinkage int sys32_ptrace(long request, long pid, long addr, s32 data) +{ + struct task_struct *child; + int ret = -EPERM; + unsigned long flags; + u32 tmp; + int copied; + ptrace_area parea; + + lock_kernel(); + 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); + read_unlock(&tasklist_lock); + if (!child) + goto out; + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + goto out; + if (request == PTRACE_ATTACH) + { + if (child == current) + goto out; + if ((!child->dumpable || + (current->uid != child->euid) || + (current->uid != child->suid) || + (current->uid != child->uid) || + (current->gid != child->egid) || + (current->gid != child->sgid)) && !capable(CAP_SYS_PTRACE)) + goto out; + /* the same process cannot be attached many times */ + if (child->ptrace & PT_PTRACED) + goto out; + child->ptrace |= PT_PTRACED; + + write_lock_irqsave(&tasklist_lock, flags); + if (child->p_pptr != current) + { + REMOVE_LINKS(child); + child->p_pptr = current; + SET_LINKS(child); + } + write_unlock_irqrestore(&tasklist_lock, flags); + + send_sig(SIGSTOP, child, 1); + ret = 0; + goto out; + } + ret = -ESRCH; + // printk("child=%lX child->flags=%lX",child,child->flags); + /* I added child!=current line so we can get the */ + /* ieee_instruction_pointer from the user structure DJB */ + if(child!=current) + { + if (!(child->ptrace & PT_PTRACED)) + goto out; + if (child->state != TASK_STOPPED) + { + if (request != PTRACE_KILL) + goto out; + } + if (child->p_pptr != current) + goto out; + } + switch (request) + { + /* If I and D space are separate, these will need to be fixed. */ + case PTRACE_PEEKTEXT: /* read word at location addr. */ + case PTRACE_PEEKDATA: + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + ret = -EIO; + if (copied != sizeof(tmp)) + goto out; + ret = put_user(tmp,(u32 *)(unsigned long)data); + goto out; + + /* read the word at location addr in the USER area. */ + case PTRACE_PEEKUSR: + ret=copy_user(child,addr,data,sizeof(u32),1,0); + break; + + /* If 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)) + goto out; + ret = -EIO; + goto out; + break; + + case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ + ret=copy_user(child,addr,(addr_t)&data,sizeof(u32),0,1); + break; + + case PTRACE_SYSCALL: /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: /* restart after signal. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + if (request == PTRACE_SYSCALL) + child->ptrace |= PT_TRACESYS; + else + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + +/* + * make the child exit. Best I can do is send it a sigkill. + * perhaps it should be put in the status that it wants to + * exit. + */ + case PTRACE_KILL: + ret = 0; + if (child->state == TASK_ZOMBIE) /* already dead */ + break; + child->exit_code = SIGKILL; + clear_single_step(child); + wake_up_process(child); + /* make sure the single step bit is not set. */ + break; + + case PTRACE_SINGLESTEP: /* set the trap flag. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + child->ptrace &= ~PT_TRACESYS; + child->exit_code = data; + set_single_step(child); + /* give it a chance to run. */ + wake_up_process(child); + ret = 0; + break; + + case PTRACE_DETACH: /* detach a process that was attached. */ + ret = -EIO; + if ((unsigned long) data >= _NSIG) + break; + child->ptrace &= ~(PT_PTRACED|PT_TRACESYS); + child->exit_code = data; + write_lock_irqsave(&tasklist_lock, flags); + REMOVE_LINKS(child); + child->p_pptr = child->p_opptr; + SET_LINKS(child); + write_unlock_irqrestore(&tasklist_lock, flags); + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + ret = 0; + break; + case PTRACE_PEEKUSR_AREA: + case PTRACE_POKEUSR_AREA: + { + ptrace_area_emu31 * parea31 = (void *)addr; + if (!access_ok(VERIFY_READ, parea31, sizeof(*parea31))) + return(-EFAULT); + ret = __get_user(parea.len, &parea31->len); + ret |= __get_user(parea.kernel_addr, &parea31->kernel_addr); + ret |= __get_user(parea.process_addr, &parea31->process_addr); + if(ret==0) + ret=copy_user(child,parea.kernel_addr,parea.process_addr, + parea.len,1,(request==PTRACE_POKEUSR_AREA)); + break; + } + default: + ret = -EIO; + break; + } + out: + unlock_kernel(); + return ret; +} + +asmlinkage void syscall_trace(void) +{ + lock_kernel(); + if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) + != (PT_PTRACED|PT_TRACESYS)) + goto out; + current->exit_code = SIGTRAP; + set_current_state(TASK_STOPPED); + notify_parent(current, SIGCHLD); + schedule(); + /* + * 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; + } + out: + unlock_kernel(); +} diff --git a/arch/s390x/kernel/reipl.S b/arch/s390x/kernel/reipl.S new file mode 100644 index 000000000..d8af95ce1 --- /dev/null +++ b/arch/s390x/kernel/reipl.S @@ -0,0 +1,94 @@ +/* + * arch/s390/kernel/reipl.S + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Holger Smolinski (Holger.Smolinski@de.ibm.com) + Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + */ + +#include <asm/lowcore.h> + .globl do_reipl +do_reipl: basr %r13,0 +.Lpg0: lpswe .Lnewpsw-.Lpg0(%r13) +.Lpg1: lctlg %c6,%c6,.Lall-.Lpg0(%r13) + stctg %c0,%c0,.Lctlsave-.Lpg0(%r13) + ni .Lctlsave+4-.Lpg0(%r13),0xef + lctlg %c0,%c0,.Lctlsave-.Lpg0(%r13) + lgr %r1,%r2 + mvc __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13) + stsch .Lschib-.Lpg0(%r13) + oi .Lschib+5-.Lpg0(%r13),0x84 +.Lecs: xi .Lschib+27-.Lpg0(%r13),0x01 + msch .Lschib-.Lpg0(%r13) + ssch .Liplorb-.Lpg0(%r13) + jz .L001 + bas %r14,.Ldisab-.Lpg0(%r13) +.L001: mvc __LC_IO_NEW_PSW(16),.Lionew-.Lpg0(%r13) +.Ltpi: lpswe .Lwaitpsw-.Lpg0(%r13) +.Lcont: c %r1,__LC_SUBCHANNEL_ID + jnz .Ltpi + clc __LC_IO_INT_PARM(4),.Liplorb-.Lpg0(%r13) + jnz .Ltpi + tsch .Liplirb-.Lpg0(%r13) + tm .Liplirb+9-.Lpg0(%r13),0xbf + jz .L002 + bas %r14,.Ldisab-.Lpg0(%r13) +.L002: tm .Liplirb+8-.Lpg0(%r13),0xf3 + jz .L003 + bas %r14,.Ldisab-.Lpg0(%r13) +.L003: spx .Lnull-.Lpg0(%r13) + st %r1,__LC_SUBCHANNEL_ID + lhi %r1,0 # mode 0 = esa + slr %r0,%r0 # set cpuid to zero + sigp %r1,%r0,0x12 # switch to esa mode + lpsw 0 +.Ldisab: sll %r14,1 + srl %r14,1 # need to kill hi bit to avoid specification exceptions. + st %r14,.Ldispsw+12-.Lpg0(%r13) + lpswe .Ldispsw-.Lpg0(%r13) + .align 8 +.Lall: .quad 0x00000000ff000000 +.Lctlsave: .quad 0x0000000000000000 +.Lnull: .long 0x0000000000000000 + .align 16 +/* + * These addresses have to be 31 bit otherwise + * the sigp will throw a specifcation exception + * when switching to ESA mode as bit 31 be set + * in the ESA psw. + * Bit 31 of the addresses has to be 0 for the + * 31bit lpswe instruction a fact they appear to have + * ommited from the pop. + */ +.Lnewpsw: .quad 0x0000000080000000 + .quad .Lpg1 +.Lpcnew: .quad 0x0000000080000000 + .quad .Lecs +.Lionew: .quad 0x0000000080000000 + .quad .Lcont +.Lwaitpsw: .quad 0x0202000080000000 + .quad .Ltpi +.Ldispsw: .quad 0x0002000080000000 + .quad 0x0000000000000000 +.Liplccws: .long 0x02000000,0x60000018 + .long 0x08000008,0x20000001 +.Liplorb: .long 0x0049504c,0x0000ff80 + .long 0x00000000+.Liplccws +.Lschib: .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 +.Liplirb: .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + .long 0x00000000,0x00000000 + + + diff --git a/arch/s390x/kernel/s390_ext.c b/arch/s390x/kernel/s390_ext.c new file mode 100644 index 000000000..6a7be9496 --- /dev/null +++ b/arch/s390x/kernel/s390_ext.c @@ -0,0 +1,77 @@ +/* + * arch/s390/kernel/s390_ext.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Holger Smolinski (Holger.Smolinski@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) + */ + +#include <linux/kernel.h> +#include <linux/malloc.h> +#include <asm/lowcore.h> +#include <asm/s390_ext.h> + +/* + * Simple hash strategy: index = code & 0xff; + * ext_int_hash[index] is the start of the list for all external interrupts + * that hash to this index. With the current set of external interrupts + * (0x1202 external call, 0x1004 cpu timer, 0x2401 hwc console and 0x4000 + * iucv) this is always the first element. + */ +ext_int_info_t *ext_int_hash[256] = { 0, }; +ext_int_info_t ext_int_info_timer; +ext_int_info_t ext_int_info_hwc; + +int register_external_interrupt(__u16 code, ext_int_handler_t handler) { + ext_int_info_t *p; + int index; + + index = code & 0xff; + p = ext_int_hash[index]; + while (p != NULL) { + if (p->code == code) + return -EBUSY; + p = p->next; + } + if (code == 0x1004) /* time_init is done before kmalloc works :-/ */ + p = &ext_int_info_timer; + else if (code == 0x2401) /* hwc_init is done too early too */ + p = &ext_int_info_hwc; + else + p = (ext_int_info_t *) + kmalloc(sizeof(ext_int_info_t), GFP_ATOMIC); + if (p == NULL) + return -ENOMEM; + p->code = code; + p->handler = handler; + p->next = ext_int_hash[index]; + ext_int_hash[index] = p; + return 0; +} + +int unregister_external_interrupt(__u16 code, ext_int_handler_t handler) { + ext_int_info_t *p, *q; + int index; + + index = code & 0xff; + q = NULL; + p = ext_int_hash[index]; + while (p != NULL) { + if (p->code == code && p->handler == handler) + break; + q = p; + p = p->next; + } + if (p == NULL) + return -ENOENT; + if (q != NULL) + q->next = p->next; + else + ext_int_hash[index] = p->next; + if (code != 0x1004 && code != 0x2401) + kfree(p); + return 0; +} + + diff --git a/arch/s390x/kernel/s390_ksyms.c b/arch/s390x/kernel/s390_ksyms.c new file mode 100644 index 000000000..5ab122488 --- /dev/null +++ b/arch/s390x/kernel/s390_ksyms.c @@ -0,0 +1,157 @@ +/* + * arch/s390/kernel/s390_ksyms.c + * + * S390 version + */ +#include <linux/config.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/highuid.h> +#include <asm/ccwcache.h> +#include <asm/debug.h> +#include <asm/irq.h> +#include <asm/s390_ext.h> +#include <asm/s390dyn.h> +#include <asm/ebcdic.h> +#include <asm/checksum.h> +#include <asm/delay.h> +#include <asm/pgalloc.h> +#include <asm/idals.h> +#if CONFIG_CHANDEV +#include <asm/chandev.h> +#endif +#if CONFIG_IP_MULTICAST +#include <net/arp.h> +#endif + +/* + * I/O subsystem + */ +EXPORT_SYMBOL(halt_IO); +EXPORT_SYMBOL(clear_IO); +EXPORT_SYMBOL(do_IO); +EXPORT_SYMBOL(resume_IO); +EXPORT_SYMBOL(ioinfo); +EXPORT_SYMBOL(get_dev_info_by_irq); +EXPORT_SYMBOL(get_dev_info_by_devno); +EXPORT_SYMBOL(get_irq_by_devno); +EXPORT_SYMBOL(get_devno_by_irq); +EXPORT_SYMBOL(get_irq_first); +EXPORT_SYMBOL(get_irq_next); +EXPORT_SYMBOL(read_conf_data); +EXPORT_SYMBOL(read_dev_chars); +EXPORT_SYMBOL(s390_request_irq_special); +EXPORT_SYMBOL(s390_device_register); +EXPORT_SYMBOL(s390_device_unregister); + +EXPORT_SYMBOL(ccw_alloc_request); +EXPORT_SYMBOL(ccw_free_request); + +EXPORT_SYMBOL(register_external_interrupt); +EXPORT_SYMBOL(unregister_external_interrupt); + +/* + * debug feature + */ +EXPORT_SYMBOL(debug_register); +EXPORT_SYMBOL(debug_unregister); +EXPORT_SYMBOL(debug_set_level); +EXPORT_SYMBOL(debug_register_view); +EXPORT_SYMBOL(debug_unregister_view); +EXPORT_SYMBOL(debug_event); +EXPORT_SYMBOL(debug_int_event); +EXPORT_SYMBOL(debug_text_event); +EXPORT_SYMBOL(debug_exception); +EXPORT_SYMBOL(debug_int_exception); +EXPORT_SYMBOL(debug_text_exception); +EXPORT_SYMBOL(debug_hex_ascii_view); +EXPORT_SYMBOL(debug_raw_view); +EXPORT_SYMBOL(debug_dflt_header_fn); + +/* + * memory management + */ +EXPORT_SYMBOL(_oi_bitmap); +EXPORT_SYMBOL(_ni_bitmap); +EXPORT_SYMBOL(_zb_findmap); +EXPORT_SYMBOL(__copy_from_user_fixup); +EXPORT_SYMBOL(__copy_to_user_fixup); + +/* + * semaphore ops + */ +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_interruptible); +EXPORT_SYMBOL(__down_trylock); + +/* + * string functions + */ +EXPORT_SYMBOL_NOVERS(memcmp); +EXPORT_SYMBOL_NOVERS(memset); +EXPORT_SYMBOL_NOVERS(memmove); +EXPORT_SYMBOL_NOVERS(strlen); +EXPORT_SYMBOL_NOVERS(strchr); +EXPORT_SYMBOL_NOVERS(strcmp); +EXPORT_SYMBOL_NOVERS(strncat); +EXPORT_SYMBOL_NOVERS(strncmp); +EXPORT_SYMBOL_NOVERS(strncpy); +EXPORT_SYMBOL_NOVERS(strnlen); +EXPORT_SYMBOL_NOVERS(strrchr); +EXPORT_SYMBOL_NOVERS(strtok); +EXPORT_SYMBOL_NOVERS(strpbrk); + +EXPORT_SYMBOL_NOVERS(_ascebc_500); +EXPORT_SYMBOL_NOVERS(_ebcasc_500); +EXPORT_SYMBOL_NOVERS(_ascebc); +EXPORT_SYMBOL_NOVERS(_ebcasc); +EXPORT_SYMBOL_NOVERS(_ebc_tolower); +EXPORT_SYMBOL_NOVERS(_ebc_toupper); + +/* + * binfmt_elf loader + */ +EXPORT_SYMBOL(get_pte_slow); +EXPORT_SYMBOL(get_pmd_slow); +extern int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs); +EXPORT_SYMBOL(dump_fpu); +#ifdef CONFIG_S390_SUPPORT +extern int setup_arg_pages32(struct linux_binprm *bprm); +EXPORT_SYMBOL(setup_arg_pages32); +#endif +EXPORT_SYMBOL(overflowuid); +EXPORT_SYMBOL(overflowgid); + +/* + * misc. + */ +EXPORT_SYMBOL(module_list); +EXPORT_SYMBOL(__udelay); +#ifdef CONFIG_SMP +#include <asm/smplock.h> +EXPORT_SYMBOL(__global_cli); +EXPORT_SYMBOL(__global_sti); +EXPORT_SYMBOL(__global_save_flags); +EXPORT_SYMBOL(__global_restore_flags); +EXPORT_SYMBOL(lowcore_ptr); +EXPORT_SYMBOL(global_bh_lock); +EXPORT_SYMBOL(kernel_flag); +EXPORT_SYMBOL(smp_ctl_set_bit); +EXPORT_SYMBOL(smp_ctl_clear_bit); +#endif +EXPORT_SYMBOL(kernel_thread); +#if CONFIG_CHANDEV +EXPORT_SYMBOL(chandev_register_and_probe); +EXPORT_SYMBOL(chandev_request_irq); +EXPORT_SYMBOL(chandev_unregister); +EXPORT_SYMBOL(chandev_initdevice); +EXPORT_SYMBOL(chandev_initnetdevice); +#endif +#if CONFIG_IP_MULTICAST +/* Required for lcs gigibit ethernet multicast support */ +EXPORT_SYMBOL(arp_mc_map); +#endif +EXPORT_SYMBOL(s390_daemonize); +EXPORT_SYMBOL (set_normalized_cda); + diff --git a/arch/s390x/kernel/s390fpu.c b/arch/s390x/kernel/s390fpu.c new file mode 100644 index 000000000..7dfea3fba --- /dev/null +++ b/arch/s390x/kernel/s390fpu.c @@ -0,0 +1,87 @@ +/* + * arch/s390/kernel/s390fpu.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * + * s390fpu.h functions for saving & restoring the fpu state. + * + * I couldn't inline these as linux/sched.h included half the world + * & was required to at the task structure. + * & the functions were too complex to make macros from. + * ( & as usual I didn't feel like debugging inline code ). + */ + +#include <linux/sched.h> + +void save_fp_regs(s390_fp_regs *fpregs) +{ +/* + * I don't think we can use STE here as this would load + * fp registers 0 & 2 into memory locations 0 & 1 etc. + */ + asm volatile ("STFPC 0(%0)\n\t" + "STD 0,8(%0)\n\t" + "STD 1,16(%0)\n\t" + "STD 2,24(%0)\n\t" + "STD 3,32(%0)\n\t" + "STD 4,40(%0)\n\t" + "STD 5,48(%0)\n\t" + "STD 6,56(%0)\n\t" + "STD 7,64(%0)\n\t" + "STD 8,72(%0)\n\t" + "STD 9,80(%0)\n\t" + "STD 10,88(%0)\n\t" + "STD 11,96(%0)\n\t" + "STD 12,104(%0)\n\t" + "STD 13,112(%0)\n\t" + "STD 14,120(%0)\n\t" + "STD 15,128(%0)\n\t" + : + : "a" (fpregs) + : "memory" + ); +} + +void restore_fp_regs(s390_fp_regs *fpregs) +{ + /* If we don't mask with the FPC_VALID_MASK here + * we've got a very quick shutdown -h now command + * via a kernel specification exception. + */ + fpregs->fpc&=FPC_VALID_MASK; + asm volatile ("LFPC 0(%0)\n\t" + "LD 0,8(%0)\n\t" + "LD 1,16(%0)\n\t" + "LD 2,24(%0)\n\t" + "LD 3,32(%0)\n\t" + "LD 4,40(%0)\n\t" + "LD 5,48(%0)\n\t" + "LD 6,56(%0)\n\t" + "LD 7,64(%0)\n\t" + "LD 8,72(%0)\n\t" + "LD 9,80(%0)\n\t" + "LD 10,88(%0)\n\t" + "LD 11,96(%0)\n\t" + "LD 12,104(%0)\n\t" + "LD 13,112(%0)\n\t" + "LD 14,120(%0)\n\t" + "LD 15,128(%0)\n\t" + : + : "a" (fpregs) + : "memory" + ); +} + + + + + + + + + + + + diff --git a/arch/s390x/kernel/semaphore.c b/arch/s390x/kernel/semaphore.c new file mode 100644 index 000000000..8af6d8277 --- /dev/null +++ b/arch/s390x/kernel/semaphore.c @@ -0,0 +1,302 @@ +/* + * linux/arch/S390/kernel/semaphore.c + * + * S390 version + * Copyright (C) 1998-2000 IBM Corporation + * Author(s): Martin Schwidefsky + * + * Derived from "linux/arch/i386/kernel/semaphore.c + * Copyright (C) 1999, Linus Torvalds + * + */ +#include <linux/sched.h> + +#include <asm/semaphore.h> + +/* + * Semaphores are implemented using a two-way counter: + * The "count" variable is decremented for each process + * that tries to acquire the semaphore, while the "sleeping" + * variable is a count of such acquires. + * + * Notably, the inline "up()" and "down()" functions can + * efficiently test if they need to do any extra work (up + * needs to do something only if count was negative before + * the increment operation. + * + * "sleeping" and the contention routine ordering is + * protected by the semaphore spinlock. + * + * Note that these functions are only called when there is + * contention on the lock, and as such all this is the + * "non-critical" part of the whole semaphore business. The + * critical part is the inline stuff in <asm/semaphore.h> + * where we want to avoid any extra jumps and calls. + */ + +/* + * Logic: + * - only on a boundary condition do we need to care. When we go + * from a negative count to a non-negative, we wake people up. + * - when we go from a non-negative count to a negative do we + * (a) synchronize with the "sleeper" count and (b) make sure + * that we're on the wakeup list before we synchronize so that + * we cannot lose wakeup events. + */ + +void __up(struct semaphore *sem) +{ + wake_up(&sem->wait); +} + +static spinlock_t semaphore_lock = SPIN_LOCK_UNLOCKED; + +void __down(struct semaphore * sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_UNINTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_UNINTERRUPTIBLE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; + wake_up(&sem->wait); +} + +int __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + tsk->state = TASK_INTERRUPTIBLE; + add_wait_queue_exclusive(&sem->wait, &wait); + + spin_lock_irq(&semaphore_lock); + sem->sleepers ++; + for (;;) { + int sleepers = sem->sleepers; + + /* + * With signals pending, this turns into + * the trylock failure case - we won't be + * sleeping, and we* can't get the lock as + * it has contention. Just correct the count + * and exit. + */ + if (signal_pending(current)) { + retval = -EINTR; + sem->sleepers = 0; + atomic_add(sleepers, &sem->count); + break; + } + + /* + * Add "everybody else" into it. They aren't + * playing, because we own the spinlock. The + * "-1" is because we're still hoping to get + * the lock. + */ + if (!atomic_add_negative(sleepers - 1, &sem->count)) { + sem->sleepers = 0; + break; + } + sem->sleepers = 1; /* us - see -1 above */ + spin_unlock_irq(&semaphore_lock); + + schedule(); + tsk->state = TASK_INTERRUPTIBLE; + spin_lock_irq(&semaphore_lock); + } + spin_unlock_irq(&semaphore_lock); + tsk->state = TASK_RUNNING; + remove_wait_queue(&sem->wait, &wait); + wake_up(&sem->wait); + return retval; +} + +/* + * Trylock failed - make sure we correct for + * having decremented the count. + */ +int __down_trylock(struct semaphore * sem) +{ + unsigned long flags; + int sleepers; + + spin_lock_irqsave(&semaphore_lock, flags); + sleepers = sem->sleepers + 1; + sem->sleepers = 0; + + /* + * Add "everybody else" and us into it. They aren't + * playing, because we own the spinlock. + */ + if (!atomic_add_negative(sleepers, &sem->count)) + wake_up(&sem->wait); + + spin_unlock_irqrestore(&semaphore_lock, flags); + return 1; +} + +void down_read_failed_biased(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue(&sem->wait, &wait); /* put ourselves at the head of the list */ + + for (;;) { + if (sem->read_bias_granted && xchg(&sem->read_bias_granted, 0)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (!sem->read_bias_granted) + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; +} + +void down_write_failed_biased(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + add_wait_queue_exclusive(&sem->write_bias_wait, &wait); /* put ourselves at the end of the list */ + + for (;;) { + if (sem->write_bias_granted && xchg(&sem->write_bias_granted, 0)) + break; + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (!sem->write_bias_granted) + schedule(); + } + + remove_wait_queue(&sem->write_bias_wait, &wait); + tsk->state = TASK_RUNNING; + + /* if the lock is currently unbiased, awaken the sleepers + * FIXME: this wakes up the readers early in a bit of a + * stampede -> bad! + */ + if (atomic_read(&sem->count) >= 0) + wake_up(&sem->wait); +} + +/* Wait for the lock to become unbiased. Readers + * are non-exclusive. =) + */ +void down_read_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + up_read(sem); /* this takes care of granting the lock */ + + add_wait_queue(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (atomic_read(&sem->count) >= 0) + break; + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; +} + +/* Wait for the lock to become unbiased. Since we're + * a writer, we'll make ourselves exclusive. + */ +void down_write_failed(struct rw_semaphore *sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + up_write(sem); /* this takes care of granting the lock */ + + add_wait_queue_exclusive(&sem->wait, &wait); + + while (atomic_read(&sem->count) < 0) { + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + if (atomic_read(&sem->count) >= 0) + break; /* we must attempt to acquire or bias the lock */ + schedule(); + } + + remove_wait_queue(&sem->wait, &wait); + tsk->state = TASK_RUNNING; +} + +/* Called when someone has done an up that transitioned from + * negative to non-negative, meaning that the lock has been + * granted to whomever owned the bias. + */ +void rwsem_wake_readers(struct rw_semaphore *sem) +{ + if (xchg(&sem->read_bias_granted, 1)) + BUG(); + wake_up(&sem->wait); +} + +void rwsem_wake_writers(struct rw_semaphore *sem) +{ + if (xchg(&sem->write_bias_granted, 1)) + BUG(); + wake_up(&sem->write_bias_wait); +} + +void __down_read_failed(int count, struct rw_semaphore *sem) +{ + do { + if (count == -1) { + down_read_failed_biased(sem); + break; + } + down_read_failed(sem); + count = atomic_dec_return(&sem->count); + } while (count != 0); +} + +void __down_write_failed(int count, struct rw_semaphore *sem) +{ + do { + if (count < 0 && count > -RW_LOCK_BIAS) { + down_write_failed_biased(sem); + break; + } + down_write_failed(sem); + count = atomic_add_return(-RW_LOCK_BIAS, &sem->count); + } while (count != 0); +} + +void __rwsem_wake(int count, struct rw_semaphore *sem) +{ + if (count == 0) + rwsem_wake_readers(sem); + else + rwsem_wake_writers(sem); +} + diff --git a/arch/s390x/kernel/setup.c b/arch/s390x/kernel/setup.c new file mode 100644 index 000000000..528793cfc --- /dev/null +++ b/arch/s390x/kernel/setup.c @@ -0,0 +1,380 @@ +/* + * arch/s390/kernel/setup.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * Derived from "arch/i386/kernel/setup.c" + * Copyright (C) 1995, Linus Torvalds + */ + +/* + * This file handles the architecture-dependent parts of initialization + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/config.h> +#include <linux/init.h> +#ifdef CONFIG_BLK_DEV_RAM +#include <linux/blk.h> +#endif +#include <linux/bootmem.h> +#include <linux/console.h> +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/smp.h> +#include <asm/mmu_context.h> + +/* + * Machine setup.. + */ +__u16 boot_cpu_addr; +int cpus_initialized = 0; +unsigned long cpu_initialized = 0; +volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ + +/* + * Setup options + */ + +#ifdef CONFIG_BLK_DEV_RAM +extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */ +extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt*/ +extern int rd_image_start; /* starting block # of image */ +#endif + +extern int root_mountflags; +extern int _text,_etext, _edata, _end; + + +/* + * This is set up by the setup-routine at boot-time + * for S390 need to find out, what we have to setup + * using address 0x10400 ... + */ + +#include <asm/setup.h> + +static char command_line[COMMAND_LINE_SIZE] = { 0, }; + char saved_command_line[COMMAND_LINE_SIZE]; + +static struct resource code_resource = { "Kernel code", 0x100000, 0 }; +static struct resource data_resource = { "Kernel data", 0, 0 }; + +/* + * cpu_init() initializes state that is per-CPU. + */ +void __init cpu_init (void) +{ + int nr = smp_processor_id(); + int addr = hard_smp_processor_id(); + + if (test_and_set_bit(nr,&cpu_initialized)) { + printk("CPU#%d ALREADY INITIALIZED!!!!!!!!!\n", nr); + for (;;) __sti(); + } + cpus_initialized++; + + /* + * Store processor id in lowcore (used e.g. in timer_interrupt) + */ + asm volatile ("stidp %0": "=m" (S390_lowcore.cpu_data.cpu_id)); + S390_lowcore.cpu_data.cpu_addr = addr; + S390_lowcore.cpu_data.cpu_nr = nr; + + /* + * Force FPU initialization: + */ + current->flags &= ~PF_USEDFPU; + current->used_math = 0; + + /* Setup active_mm for idle_task */ + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + if (current->mm) + BUG(); + enter_lazy_tlb(&init_mm, current, nr); +} + +/* + * VM halt and poweroff setup routines + */ +char vmhalt_cmd[128] = ""; +char vmpoff_cmd[128] = ""; + +static inline void strncpy_skip_quote(char *dst, char *src, int n) +{ + int sx, dx; + + dx = 0; + for (sx = 0; src[sx] != 0; sx++) { + if (src[sx] == '"') continue; + dst[dx++] = src[sx]; + if (dx >= n) break; + } +} + +static int __init vmhalt_setup(char *str) +{ + strncpy_skip_quote(vmhalt_cmd, str, 127); + vmhalt_cmd[127] = 0; + return 1; +} + +__setup("vmhalt=", vmhalt_setup); + +static int __init vmpoff_setup(char *str) +{ + strncpy_skip_quote(vmpoff_cmd, str, 127); + vmpoff_cmd[127] = 0; + return 1; +} + +__setup("vmpoff=", vmpoff_setup); + +/* + * Reboot, halt and power_off routines for non SMP. + */ +#ifndef CONFIG_SMP +void machine_restart(char * __unused) +{ + reipl(S390_lowcore.ipl_device); +} + +void machine_halt(void) +{ + if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) + cpcmd(vmhalt_cmd, NULL, 0); + signal_processor(smp_processor_id(), sigp_stop_and_store_status); +} + +void machine_power_off(void) +{ + if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) + cpcmd(vmpoff_cmd, NULL, 0); + signal_processor(smp_processor_id(), sigp_stop_and_store_status); +} +#endif + +/* + * Setup function called from init/main.c just after the banner + * was printed. + */ +void __init setup_arch(char **cmdline_p) +{ + unsigned long bootmap_size; + unsigned long memory_start, memory_end; + char c = ' ', cn, *to = command_line, *from = COMMAND_LINE; + struct resource *res; + unsigned long start_pfn, end_pfn; + static unsigned int smptrap=0; + unsigned long delay = 0; + int len = 0; + + if (smptrap) + return; + smptrap=1; + + printk("Command line is: %s\n", COMMAND_LINE); + + /* + * Setup lowcore information for boot cpu + */ + cpu_init(); + boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + __cpu_logical_map[0] = boot_cpu_addr; + + /* + * print what head.S has found out about the machine + */ + printk((MACHINE_IS_VM) ? + "We are running under VM\n" : + "We are running native\n"); + + ROOT_DEV = to_kdev_t(ORIG_ROOT_DEV); +#ifdef CONFIG_BLK_DEV_RAM + rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; + rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0); + rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0); +#endif + /* nasty stuff with PARMAREAs. we use head.S or parameterline + if (!MOUNT_ROOT_RDONLY) + root_mountflags &= ~MS_RDONLY; + */ + memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ + memory_end = MEMORY_SIZE; /* detected in head.s */ + init_mm.start_code = PAGE_OFFSET; + init_mm.end_code = (unsigned long) &_etext; + init_mm.end_data = (unsigned long) &_edata; + init_mm.brk = (unsigned long) &_end; + + code_resource.start = (unsigned long) &_text; + code_resource.end = (unsigned long) &_etext - 1; + data_resource.start = (unsigned long) &_etext; + data_resource.end = (unsigned long) &_edata - 1; + + /* Save unparsed command line copy for /proc/cmdline */ + memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); + saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; + + for (;;) { + /* + * "mem=XXX[kKmM]" sets memsize + */ + if (c == ' ' && strncmp(from, "mem=", 4) == 0) { + if (to != command_line) to--; + memory_end = simple_strtoul(from+4, &from, 0); + if ( *from == 'K' || *from == 'k' ) { + memory_end = memory_end << 10; + from++; + } else if ( *from == 'M' || *from == 'm' ) { + memory_end = memory_end << 20; + from++; + } + } + /* + * "ipldelay=XXX[sm]" sets ipl delay in seconds or minutes + */ + if (c == ' ' && strncmp(from, "ipldelay=", 9) == 0) { + if (to != command_line) to--; + delay = simple_strtoul(from+9, &from, 0); + if (*from == 's' || *from == 'S') { + delay = delay*1000000; + from++; + } else if (*from == 'm' || *from == 'M') { + delay = delay*60*1000000; + from++; + } + /* now wait for the requestion amount of time */ + udelay(delay); + } + cn = *(from++); + if (!cn) + break; + if (cn == '\n') + cn = ' '; /* replace newlines with space */ + if (cn == ' ' && c == ' ') + continue; /* remove additional spaces */ + c = cn; + if (COMMAND_LINE_SIZE <= ++len) + break; + *(to++) = c; + } + if (c == ' ' && to > command_line) to--; + *to = '\0'; + *cmdline_p = command_line; + + /* + * partially used pages are not usable - thus + * we are rounding upwards: + */ + start_pfn = (__pa(&_end) + PAGE_SIZE - 1) >> PAGE_SHIFT; + end_pfn = memory_end >> PAGE_SHIFT; + + /* + * Initialize the boot-time allocator + */ + bootmap_size = init_bootmem(start_pfn, end_pfn); + + /* + * Register RAM pages with the bootmem allocator. + */ + free_bootmem(start_pfn << PAGE_SHIFT, + (end_pfn - start_pfn) << PAGE_SHIFT); + + /* + * Reserve the bootmem bitmap itself as well. We do this in two + * steps (first step was init_bootmem()) because this catches + * the (very unlikely) case of us accidentally initializing the + * bootmem allocator with an invalid RAM area. + */ + reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size); + + + + +#ifdef CONFIG_BLK_DEV_INITRD + if (INITRD_START) { + if (INITRD_START + INITRD_SIZE <= memory_end) { + reserve_bootmem(INITRD_START, INITRD_SIZE); + initrd_start = INITRD_START; + initrd_end = initrd_start + INITRD_SIZE; + } else { + printk("initrd extends beyond end of memory " + "(0x%08lx > 0x%08lx)\ndisabling initrd\n", + initrd_start + INITRD_SIZE, memory_end); + initrd_start = initrd_end = 0; + } + } +#endif + + paging_init(); + + res = alloc_bootmem_low(sizeof(struct resource)); + res->start = 0; + res->end = memory_end; + res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + request_resource(&iomem_resource, res); + request_resource(res, &code_resource); + request_resource(res, &data_resource); +} + +void print_cpu_info(struct cpuinfo_S390 *cpuinfo) +{ + printk("cpu %d " +#ifdef CONFIG_SMP + "phys_idx=%d " +#endif + "vers=%02X ident=%06X machine=%04X unused=%04X\n", + cpuinfo->cpu_nr, +#ifdef CONFIG_SMP + cpuinfo->cpu_addr, +#endif + cpuinfo->cpu_id.version, + cpuinfo->cpu_id.ident, + cpuinfo->cpu_id.machine, + cpuinfo->cpu_id.unused); +} + +/* + * Get CPU information for use by the procfs. + */ + +int get_cpuinfo(char * buffer) +{ + struct cpuinfo_S390 *cpuinfo; + char *p = buffer; + int i; + + p += sprintf(p,"vendor_id : IBM/S390\n" + "# processors : %i\n" + "bogomips per cpu: %lu.%02lu\n", + smp_num_cpus, loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ))%100); + for (i = 0; i < smp_num_cpus; i++) { + cpuinfo = &safe_get_cpu_lowcore(i).cpu_data; + p += sprintf(p,"processor %i: " + "version = %02X, " + "identification = %06X, " + "machine = %04X\n", + i, cpuinfo->cpu_id.version, + cpuinfo->cpu_id.ident, + cpuinfo->cpu_id.machine); + } + return p - buffer; +} + diff --git a/arch/s390x/kernel/signal.c b/arch/s390x/kernel/signal.c new file mode 100644 index 000000000..4728336c4 --- /dev/null +++ b/arch/s390x/kernel/signal.c @@ -0,0 +1,595 @@ +/* + * arch/s390/kernel/signal.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * + * Based on Intel version + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* pretcode & sig are used to store the return addr on Intel + & the signal no as the first parameter we do this differently + using gpr14 & gpr2. */ + +#define SIGFRAME_COMMON \ +__u8 callee_used_stack[__SIGNAL_FRAMESIZE]; \ +struct sigcontext sc; \ +_sigregs sregs; \ +__u8 retcode[S390_SYSCALL_SIZE]; + +typedef struct +{ + SIGFRAME_COMMON +} sigframe; + +typedef struct +{ + SIGFRAME_COMMON + struct siginfo info; + struct ucontext uc; +} rt_sigframe; + +asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset)); + +int copy_siginfo_to_user(siginfo_t *to, siginfo_t *from) +{ + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t))) + return -EFAULT; + if (from->si_code < 0) + return __copy_to_user(to, from, sizeof(siginfo_t)); + else { + int err; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + /* First 32bits of unions are always present. */ + err |= __put_user(from->si_pid, &to->si_pid); + switch (from->si_code >> 16) { + case __SI_FAULT >> 16: + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + default: + err |= __put_user(from->si_uid, &to->si_uid); + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + return err; + } +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int +sys_sigsuspend(struct pt_regs * regs,int history0, int history1, old_sigset_t mask) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + regs->gprs[2] = -EINTR; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage int +sys_rt_sigsuspend(struct pt_regs * regs,sigset_t *unewset, size_t sigsetsize) +{ + 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->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + regs->gprs[2] = -EINTR; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (do_signal(regs, &saveset)) + 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 (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +asmlinkage int +sys_sigaltstack(const stack_t *uss, stack_t *uoss, struct pt_regs *regs) +{ + return do_sigaltstack(uss, uoss, regs->gprs[15]); +} + + + + +static int save_sigregs(struct pt_regs *regs,_sigregs *sregs) +{ + int err; + s390_fp_regs fpregs; + + err = __copy_to_user(&sregs->regs,regs,sizeof(s390_regs_common)); + if(!err) + { + save_fp_regs(&fpregs); + err=__copy_to_user(&sregs->fpregs,&fpregs,sizeof(fpregs)); + } + return(err); + +} + +static int restore_sigregs(struct pt_regs *regs,_sigregs *sregs) +{ + int err; + s390_fp_regs fpregs; + psw_t saved_psw=regs->psw; + err=__copy_from_user(regs,&sregs->regs,sizeof(s390_regs_common)); + if(!err) + { + regs->orig_gpr2 = -1; /* disable syscall checks */ + regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| + (regs->psw.mask&PSW_MASK_DEBUGCHANGE); + regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| + (regs->psw.addr&PSW_ADDR_DEBUGCHANGE); + err=__copy_from_user(&fpregs,&sregs->fpregs,sizeof(fpregs)); + if(!err) + restore_fp_regs(&fpregs); + } + return(err); +} + +static int +restore_sigcontext(struct sigcontext *sc, pt_regs *regs, + _sigregs *sregs,sigset_t *set) +{ + unsigned int err; + + err=restore_sigregs(regs,sregs); + if(!err) + err=__copy_from_user(&set->sig,&sc->oldmask,_SIGMASK_COPY_SIZE); + return(err); +} + +int sigreturn_common(struct pt_regs *regs,int framesize) +{ + sigframe *frame = (sigframe *)regs->gprs[15]; + sigset_t set; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + return -1; + if (restore_sigcontext(&frame->sc,regs,&frame->sregs,&set)) + return -1; + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + return 0; +} + +asmlinkage long sys_sigreturn(struct pt_regs *regs) +{ + + if (sigreturn_common(regs,sizeof(sigframe))) + goto badframe; + return regs->gprs[2]; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) +{ + rt_sigframe *frame = (rt_sigframe *)regs->gprs[15]; + + if (sigreturn_common(regs,sizeof(rt_sigframe))) + goto badframe; + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&frame->uc.uc_stack, NULL, regs->gprs[15]); + return regs->gprs[2]; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long sp; + + /* Default to using normal stack */ + sp = regs->gprs[15]; + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + /* This is the legacy signal stack switching. */ + else if (!user_mode(regs) && + !(ka->sa.sa_flags & SA_RESTORER) && + ka->sa.sa_restorer) { + sp = (unsigned long) ka->sa.sa_restorer; + } + + return (void *)((sp - frame_size) & -8ul); +} + +static void *setup_frame_common(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs, + int frame_size,u16 retcode) +{ + sigframe *frame; + int err; + + frame = get_sigframe(ka, regs,frame_size); + if (!access_ok(VERIFY_WRITE, frame,frame_size)) + return 0; + err = save_sigregs(regs,&frame->sregs); + if(!err) + err=__put_user(&frame->sregs,&frame->sc.sregs); + if(!err) + + err=__copy_to_user(&frame->sc.oldmask,&set->sig,_SIGMASK_COPY_SIZE); + if(!err) + { + regs->gprs[2]=(current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig); + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + } + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + } else { + regs->gprs[14] = FIX_PSW(frame->retcode); + err |= __put_user(retcode, (u16 *)(frame->retcode)); + } + return(err ? 0:frame); +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + sigframe *frame; + + if((frame=setup_frame_common(sig,ka,set,regs,sizeof(sigframe), + (S390_SYSCALL_OPCODE|__NR_sigreturn)))==0) + goto give_sigsegv; +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); +#endif + /* Martin wants this for pthreads */ + regs->gprs[3] = (addr_t)&frame->sc; + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + rt_sigframe *frame; + addr_t orig_sp=regs->gprs[15]; + int err; + + if((frame=setup_frame_common(sig,ka,set,regs,sizeof(rt_sigframe), + (S390_SYSCALL_OPCODE|__NR_rt_sigreturn)))==0) + goto give_sigsegv; + + 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(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(orig_sp), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= __put_user(&frame->sc,&frame->uc.sc); + regs->gprs[3] = (addr_t)&frame->info; + regs->gprs[4] = (addr_t)&frame->uc; + + if (err) + goto give_sigsegv; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); +#endif + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +/* + * OK, we're invoking a handler + */ + +static void +handle_signal(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +{ + /* Are we from a system call? */ + if (regs->orig_gpr2 >= 0) { + /* If so, check system call restarting.. */ + switch (regs->gprs[2]) { + case -ERESTARTNOHAND: + regs->gprs[2] = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->gprs[2] = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->gprs[2] = regs->orig_gpr2; + regs->psw.addr -= 2; + } + } + + /* 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->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ +int do_signal(struct pt_regs *regs, sigset_t *oldset) +{ + siginfo_t info; + struct k_sigaction *ka; + + /* + * 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; +#ifdef CONFIG_S390_SUPPORT + if (current->thread.flags & S390_FLAG_31BIT) { + extern asmlinkage int do_signal32(struct pt_regs *regs, sigset_t *oldset); + return do_signal32(regs, oldset); + } +#endif + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + set_current_state(TASK_STOPPED); + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + set_current_state(TASK_STOPPED); + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + lock_kernel(); + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Whee! Actually deliver the signal. */ + handle_signal(signr, ka, &info, oldset, regs); + return 1; + } + + /* Did we come from a system call? */ + if ( regs->trap == __LC_SVC_OLD_PSW /* System Call! */ ) { + /* Restart the system call - no handlers present */ + if (regs->gprs[2] == -ERESTARTNOHAND || + regs->gprs[2] == -ERESTARTSYS || + regs->gprs[2] == -ERESTARTNOINTR) { + regs->gprs[2] = regs->orig_gpr2; + regs->psw.addr -= 2; + } + } + return 0; +} diff --git a/arch/s390x/kernel/signal32.c b/arch/s390x/kernel/signal32.c new file mode 100644 index 000000000..81e5417ba --- /dev/null +++ b/arch/s390x/kernel/signal32.c @@ -0,0 +1,725 @@ +/* + * arch/s390/kernel/signal32.c + * + * S390 version + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * Gerhard Tonn (ton@de.ibm.com) + * + * Copyright (C) 1991, 1992 Linus Torvalds + * + * 1997-11-28 Modified for POSIX.1b signals by Richard Henderson + */ + +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/kernel.h> +#include <linux/signal.h> +#include <linux/errno.h> +#include <linux/wait.h> +#include <linux/ptrace.h> +#include <linux/unistd.h> +#include <linux/stddef.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> +#include "linux32.h" + +#define DEBUG_SIG 0 + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +/* pretcode & sig are used to store the return addr on Intel + & the signal no as the first parameter we do this differently + using gpr14 & gpr2. */ + +#define SIGFRAME_COMMON32 \ +__u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; \ +struct sigcontext32 sc; \ +_sigregs32 sregs; \ +__u8 retcode[S390_SYSCALL_SIZE]; + +typedef struct +{ + SIGFRAME_COMMON32 +} sigframe32; + +typedef struct +{ + SIGFRAME_COMMON32 + struct siginfo32 info; + struct ucontext32 uc; +} rt_sigframe32; + +asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset)); + +int do_signal32(struct pt_regs *regs, sigset_t *oldset); + +int copy_siginfo_to_user32(siginfo_t32 *to, siginfo_t *from) +{ + int err; + + if (!access_ok (VERIFY_WRITE, to, sizeof(siginfo_t32))) + return -EFAULT; + + /* If you change siginfo_t structure, please be sure + this code is fixed accordingly. + It should never copy any pad contained in the structure + to avoid security leaks, but must copy the generic + 3 ints plus the relevant union member. + This routine must convert siginfo from 64bit to 32bit as well + at the same time. */ + err = __put_user(from->si_signo, &to->si_signo); + err |= __put_user(from->si_errno, &to->si_errno); + err |= __put_user((short)from->si_code, &to->si_code); + if (from->si_code < 0) + err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE); + else { + switch (from->si_code >> 16) { + case __SI_KILL >> 16: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + break; + case __SI_CHLD >> 16: + err |= __put_user(from->si_pid, &to->si_pid); + err |= __put_user(from->si_uid, &to->si_uid); + err |= __put_user(from->si_utime, &to->si_utime); + err |= __put_user(from->si_stime, &to->si_stime); + err |= __put_user(from->si_status, &to->si_status); + break; + case __SI_FAULT >> 16: + err |= __put_user(from->si_addr, &to->si_addr); + break; + case __SI_POLL >> 16: + case __SI_TIMER >> 16: + err |= __put_user(from->si_band, &to->si_band); + err |= __put_user(from->si_fd, &to->si_fd); + break; + default: + break; + /* case __SI_RT: This is not generated by the kernel as of now. */ + } + } + return err; +} + +/* + * Atomically swap in the new signal mask, and wait for a signal. + */ +asmlinkage int +sys32_sigsuspend(struct pt_regs * regs,int history0, int history1, old_sigset_t mask) +{ + sigset_t saveset; + + mask &= _BLOCKABLE; + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + regs->gprs[2] = -EINTR; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (do_signal32(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage int +sys32_rt_sigsuspend(struct pt_regs * regs,sigset_t32 *unewset, size_t sigsetsize) +{ + sigset_t saveset, newset; + sigset_t32 set32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t)) + return -EINVAL; + + if (copy_from_user(&set32, unewset, sizeof(set32))) + return -EFAULT; + switch (_NSIG_WORDS) { + case 4: newset.sig[3] = set32.sig[6] + (((long)set32.sig[7]) << 32); + case 3: newset.sig[2] = set32.sig[4] + (((long)set32.sig[5]) << 32); + case 2: newset.sig[1] = set32.sig[2] + (((long)set32.sig[3]) << 32); + case 1: newset.sig[0] = set32.sig[0] + (((long)set32.sig[1]) << 32); + } + sigdelsetmask(&newset, ~_BLOCKABLE); + + spin_lock_irq(¤t->sigmask_lock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + regs->gprs[2] = -EINTR; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (do_signal32(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage int +sys32_sigaction(int sig, const struct old_sigaction32 *act, + struct old_sigaction32 *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t32 mask; + if (verify_area(VERIFY_READ, act, sizeof(*act)) || + __get_user((unsigned long)new_ka.sa.sa_handler, &act->sa_handler) || + __get_user((unsigned long)new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (verify_area(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user((unsigned long)old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user((unsigned long)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; +} + +int +do_sigaction(int sig, const struct k_sigaction *act, struct k_sigaction *oact); + +asmlinkage long +sys32_rt_sigaction(int sig, const struct sigaction32 *act, + struct sigaction32 *oact, size_t sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + int ret; + sigset_t32 set32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(sigset_t32)) + return -EINVAL; + + if (act) { + ret = get_user((unsigned long)new_ka.sa.sa_handler, &act->sa_handler); + ret |= __copy_from_user(&set32, &act->sa_mask, + sizeof(sigset_t32)); + switch (_NSIG_WORDS) { + case 4: new_ka.sa.sa_mask.sig[3] = set32.sig[6] + | (((long)set32.sig[7]) << 32); + case 3: new_ka.sa.sa_mask.sig[2] = set32.sig[4] + | (((long)set32.sig[5]) << 32); + case 2: new_ka.sa.sa_mask.sig[1] = set32.sig[2] + | (((long)set32.sig[3]) << 32); + case 1: new_ka.sa.sa_mask.sig[0] = set32.sig[0] + | (((long)set32.sig[1]) << 32); + } + ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags); + + if (ret) + return -EFAULT; + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + switch (_NSIG_WORDS) { + case 4: + set32.sig[7] = (old_ka.sa.sa_mask.sig[3] >> 32); + set32.sig[6] = old_ka.sa.sa_mask.sig[3]; + case 3: + set32.sig[5] = (old_ka.sa.sa_mask.sig[2] >> 32); + set32.sig[4] = old_ka.sa.sa_mask.sig[2]; + case 2: + set32.sig[3] = (old_ka.sa.sa_mask.sig[1] >> 32); + set32.sig[2] = old_ka.sa.sa_mask.sig[1]; + case 1: + set32.sig[1] = (old_ka.sa.sa_mask.sig[0] >> 32); + set32.sig[0] = old_ka.sa.sa_mask.sig[0]; + } + ret = put_user((unsigned long)old_ka.sa.sa_handler, &oact->sa_handler); + ret |= __copy_to_user(&oact->sa_mask, &set32, + sizeof(sigset_t32)); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + } + + return ret; +} + +asmlinkage int +sys32_sigaltstack(const stack_t32 *uss, stack_t32 *uoss, struct pt_regs *regs) +{ + stack_t kss, koss; + int ret, err = 0; + mm_segment_t old_fs = get_fs(); + + if (uss) { + if (!access_ok(VERIFY_READ, uss, sizeof(*uss))) + return -EFAULT; + err |= __get_user(kss.ss_sp, &uss->ss_sp); + err |= __get_user(kss.ss_size, &uss->ss_size); + err |= __get_user(kss.ss_flags, &uss->ss_flags); + if (err) + return -EFAULT; + } + + set_fs (KERNEL_DS); + ret = do_sigaltstack(uss ? &kss : NULL , uoss ? &koss : NULL, regs->gprs[15]); + set_fs (old_fs); + + if (!ret && uoss) { + if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss))) + return -EFAULT; + err |= __put_user(koss.ss_sp, &uoss->ss_sp); + err |= __put_user(koss.ss_size, &uoss->ss_size); + err |= __put_user(koss.ss_flags, &uoss->ss_flags); + if (err) + return -EFAULT; + } + return ret; +} + +static int save_sigregs32(struct pt_regs *regs,_sigregs32 *sregs) +{ + int err = 0; + s390_fp_regs fpregs; + int i; + + for(i=0; i<NUM_GPRS; i++) + err |= __put_user(regs->gprs[i], &sregs->regs.gprs[i]); + for(i=0; i<NUM_ACRS; i++) + err |= __put_user(regs->acrs[i], &sregs->regs.acrs[i]); + err |= __copy_to_user(&sregs->regs.psw.mask, ®s->psw.mask, 4); + err |= __copy_to_user(&sregs->regs.psw.addr, ((char*)®s->psw.addr)+4, 4); + if(!err) + { + save_fp_regs(&fpregs); + __put_user(fpregs.fpc, &sregs->fpregs.fpc); + for(i=0; i<NUM_FPRS; i++) + err |= __put_user(fpregs.fprs[i].d, &sregs->fpregs.fprs[i].d); + } + return(err); + +} + +static int restore_sigregs32(struct pt_regs *regs,_sigregs32 *sregs) +{ + int err = 0; + s390_fp_regs fpregs; + psw_t saved_psw=regs->psw; + int i; + + for(i=0; i<NUM_GPRS; i++) + err |= __get_user(regs->gprs[i], &sregs->regs.gprs[i]); + for(i=0; i<NUM_ACRS; i++) + err |= __get_user(regs->acrs[i], &sregs->regs.acrs[i]); + err |= __copy_from_user(®s->psw.mask, &sregs->regs.psw.mask, 4); + err |= __copy_from_user(((char*)®s->psw.addr)+4, &sregs->regs.psw.addr, 4); + + if(!err) + { + regs->orig_gpr2 = -1; /* disable syscall checks */ + regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)| + (regs->psw.mask&PSW_MASK_DEBUGCHANGE); + regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)| + (regs->psw.addr&PSW_ADDR_DEBUGCHANGE); + __get_user(fpregs.fpc, &sregs->fpregs.fpc); + for(i=0; i<NUM_FPRS; i++) + err |= __get_user(fpregs.fprs[i].d, &sregs->fpregs.fprs[i].d); + if(!err) + restore_fp_regs(&fpregs); + } + return(err); +} + +static int +restore_sigcontext32(struct sigcontext32 *sc, pt_regs *regs, + _sigregs32 *sregs,sigset_t *set) +{ + unsigned int err; + + err=restore_sigregs32(regs,sregs); + if(!err) + err=__copy_from_user(&set->sig,&sc->oldmask,_SIGMASK_COPY_SIZE32); + return(err); +} + +int sigreturn_common32(struct pt_regs *regs) +{ + sigframe32 *frame = (sigframe32 *)regs->gprs[15]; + sigset_t set; + + if (verify_area(VERIFY_READ, frame, sizeof(*frame))) + return -1; + if (restore_sigcontext32(&frame->sc,regs,&frame->sregs,&set)) + return -1; + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sigmask_lock); + current->blocked = set; + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + return 0; +} + +asmlinkage long sys32_sigreturn(struct pt_regs *regs) +{ + + if (sigreturn_common32(regs)) + goto badframe; + return regs->gprs[2]; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs) +{ + rt_sigframe32 *frame = (rt_sigframe32 *)regs->gprs[15]; + stack_t st; + int err; + mm_segment_t old_fs = get_fs(); + + if (sigreturn_common32(regs)) + goto badframe; + + err = __get_user(st.ss_sp, &frame->uc.uc_stack.ss_sp); + st.ss_sp = (void *) A((unsigned long)st.ss_sp); + err |= __get_user(st.ss_size, &frame->uc.uc_stack.ss_size); + err |= __get_user(st.ss_flags, &frame->uc.uc_stack.ss_flags); + if (err) + goto badframe; + set_fs (KERNEL_DS); + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + do_sigaltstack(&st, NULL, regs->gprs[15]); + set_fs (old_fs); + + return regs->gprs[2]; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +/* + * Set up a signal frame. + */ + + +/* + * Determine which stack to use.. + */ +static inline void * +get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size) +{ + unsigned long sp; + + /* Default to using normal stack */ + sp = (unsigned long) A(regs->gprs[15]); + + /* This is the X/Open sanctioned signal stack switching. */ + if (ka->sa.sa_flags & SA_ONSTACK) { + if (! on_sig_stack(sp)) + sp = current->sas_ss_sp + current->sas_ss_size; + } + + /* This is the legacy signal stack switching. */ + else if (!user_mode(regs) && + !(ka->sa.sa_flags & SA_RESTORER) && + ka->sa.sa_restorer) { + sp = (unsigned long) ka->sa.sa_restorer; + } + + return (void *)((sp - frame_size) & -8ul); +} + +static void *setup_frame_common32(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs, + int frame_size,u16 retcode) +{ + sigframe32 *frame; + int err; + + frame = get_sigframe(ka, regs,frame_size); + if (!access_ok(VERIFY_WRITE, frame,frame_size)) + return 0; + err = save_sigregs32(regs,&frame->sregs); + if(!err) + err=__put_user(&frame->sregs,&frame->sc.sregs); + if(!err) + + err=__copy_to_user(&frame->sc.oldmask,&set->sig,_SIGMASK_COPY_SIZE32); + if(!err) + { + regs->gprs[2]=(current->exec_domain + && current->exec_domain->signal_invmap + && sig < 32 + ? current->exec_domain->signal_invmap[sig] + : sig); + /* Set up registers for signal handler */ + regs->gprs[15] = (addr_t)frame; + regs->psw.addr = FIX_PSW(ka->sa.sa_handler); + } + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer); + } else { + regs->gprs[14] = FIX_PSW(frame->retcode); + err |= __put_user(retcode, (u16 *)(frame->retcode)); + } + return(err ? 0:frame); +} + +static void setup_frame32(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + sigframe32 *frame; + + if((frame=setup_frame_common32(sig,ka,set,regs,sizeof(sigframe32), + (S390_SYSCALL_OPCODE|__NR_sigreturn)))==0) + goto give_sigsegv; +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); +#endif + /* Martin wants this for pthreads */ + regs->gprs[3] = (addr_t)&frame->sc; + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +static void setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + rt_sigframe32 *frame; + addr_t orig_sp=regs->gprs[15]; + int err; + + if((frame=setup_frame_common32(sig,ka,set,regs,sizeof(rt_sigframe32), + (S390_SYSCALL_OPCODE|__NR_rt_sigreturn)))==0) + goto give_sigsegv; + + err = copy_siginfo_to_user32(&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(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(orig_sp), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + regs->gprs[3] = (addr_t)&frame->info; + regs->gprs[4] = (addr_t)&frame->uc; + + if (err) + goto give_sigsegv; + +#if DEBUG_SIG + printk("SIG deliver (%s:%d): sp=%p pc=%p ra=%p\n", + current->comm, current->pid, frame, regs->eip, frame->pretcode); +#endif + return; + +give_sigsegv: + if (sig == SIGSEGV) + ka->sa.sa_handler = SIG_DFL; + force_sig(SIGSEGV, current); +} + +/* + * OK, we're invoking a handler + */ + +static void +handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +{ + /* Are we from a system call? */ + if (regs->orig_gpr2 >= 0) { + /* If so, check system call restarting.. */ + switch (regs->gprs[2]) { + case -ERESTARTNOHAND: + regs->gprs[2] = -EINTR; + break; + + case -ERESTARTSYS: + if (!(ka->sa.sa_flags & SA_RESTART)) { + regs->gprs[2] = -EINTR; + break; + } + /* fallthrough */ + case -ERESTARTNOINTR: + regs->gprs[2] = regs->orig_gpr2; + regs->psw.addr -= 2; + } + } + + /* Set up the stack frame */ + if (ka->sa.sa_flags & SA_SIGINFO) + setup_rt_frame32(sig, ka, info, oldset, regs); + else + setup_frame32(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->sigmask_lock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(current); + spin_unlock_irq(¤t->sigmask_lock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * Note that we go through the signals twice: once to check the signals that + * the kernel can handle, and then we build all the user-level signal handling + * stack-frames in one go after that. + */ +int do_signal32(struct pt_regs *regs, sigset_t *oldset) +{ + siginfo_t info; + struct k_sigaction *ka; + + /* + * 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; + + for (;;) { + unsigned long signr; + + spin_lock_irq(¤t->sigmask_lock); + signr = dequeue_signal(¤t->blocked, &info); + spin_unlock_irq(¤t->sigmask_lock); + + if (!signr) + break; + + if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) { + /* Let the debugger run. */ + current->exit_code = signr; + set_current_state(TASK_STOPPED); + notify_parent(current, SIGCHLD); + schedule(); + + /* We're back. Did the debugger cancel the sig? */ + if (!(signr = current->exit_code)) + continue; + current->exit_code = 0; + + /* The debugger continued. Ignore SIGSTOP. */ + if (signr == SIGSTOP) + continue; + + /* Update the siginfo structure. Is this good? */ + if (signr != info.si_signo) { + info.si_signo = signr; + info.si_errno = 0; + info.si_code = SI_USER; + info.si_pid = current->p_pptr->pid; + info.si_uid = current->p_pptr->uid; + } + + /* If the (new) signal is now blocked, requeue it. */ + if (sigismember(¤t->blocked, signr)) { + send_sig_info(signr, &info, current); + continue; + } + } + + ka = ¤t->sig->action[signr-1]; + if (ka->sa.sa_handler == SIG_IGN) { + if (signr != SIGCHLD) + continue; + /* Check for SIGCHLD: it's special. */ + while (sys_wait4(-1, NULL, WNOHANG, NULL) > 0) + /* nothing */; + continue; + } + + if (ka->sa.sa_handler == SIG_DFL) { + int exit_code = signr; + + /* Init gets no signals it doesn't want. */ + if (current->pid == 1) + continue; + + switch (signr) { + case SIGCONT: case SIGCHLD: case SIGWINCH: + continue; + + case SIGTSTP: case SIGTTIN: case SIGTTOU: + if (is_orphaned_pgrp(current->pgrp)) + continue; + /* FALLTHRU */ + + case SIGSTOP: + set_current_state(TASK_STOPPED); + current->exit_code = signr; + if (!(current->p_pptr->sig->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDSTOP)) + notify_parent(current, SIGCHLD); + schedule(); + continue; + + case SIGQUIT: case SIGILL: case SIGTRAP: + case SIGABRT: case SIGFPE: case SIGSEGV: + if (do_coredump(signr, regs)) + exit_code |= 0x80; + /* FALLTHRU */ + + default: + lock_kernel(); + sigaddset(¤t->pending.signal, signr); + recalc_sigpending(current); + current->flags |= PF_SIGNALED; + do_exit(exit_code); + /* NOTREACHED */ + } + } + + /* Whee! Actually deliver the signal. */ + handle_signal32(signr, ka, &info, oldset, regs); + return 1; + } + + /* Did we come from a system call? */ + if ( regs->trap == __LC_SVC_OLD_PSW /* System Call! */ ) { + /* Restart the system call - no handlers present */ + if (regs->gprs[2] == -ERESTARTNOHAND || + regs->gprs[2] == -ERESTARTSYS || + regs->gprs[2] == -ERESTARTNOINTR) { + regs->gprs[2] = regs->orig_gpr2; + regs->psw.addr -= 2; + } + } + return 0; +} diff --git a/arch/s390x/kernel/smp.c b/arch/s390x/kernel/smp.c new file mode 100644 index 000000000..19d8e3dad --- /dev/null +++ b/arch/s390x/kernel/smp.c @@ -0,0 +1,760 @@ +/* + * arch/s390/kernel/smp.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * based on other smp stuff by + * (c) 1995 Alan Cox, CymruNET Ltd <alan@cymru.net> + * (c) 1998 Ingo Molnar + * + * We work with logical cpu numbering everywhere we can. The only + * functions using the real cpu address (got from STAP) are the sigp + * functions. For all other functions we use the identity mapping. + * That means that cpu_number_map[i] == i for every cpu. cpu_number_map is + * used e.g. to find the idle task belonging to a logical cpu. Every array + * in the kernel is sorted by the logical cpu number and not by the physical + * one which is causing all the confusion with __cpu_logical_map and + * cpu_number_map in other architectures. + */ + +#include <linux/init.h> + +#include <linux/mm.h> +#include <linux/spinlock.h> +#include <linux/kernel_stat.h> +#include <linux/smp_lock.h> + +#include <linux/delay.h> + +#include <asm/sigp.h> +#include <asm/pgalloc.h> +#include <asm/irq.h> +#include <asm/s390_ext.h> + +#include "cpcmd.h" + +/* prototypes */ +extern int cpu_idle(void * unused); + +extern __u16 boot_cpu_addr; +extern volatile int __cpu_logical_map[]; + +/* + * An array with a pointer the lowcore of every CPU. + */ +static int max_cpus = NR_CPUS; /* Setup configured maximum number of CPUs to activate */ +int smp_num_cpus; +struct _lowcore *lowcore_ptr[NR_CPUS]; +unsigned int prof_multiplier[NR_CPUS]; +unsigned int prof_old_multiplier[NR_CPUS]; +unsigned int prof_counter[NR_CPUS]; +cycles_t cacheflush_time=0; +int smp_threads_ready=0; /* Set when the idlers are all forked. */ +static atomic_t smp_commenced = ATOMIC_INIT(0); + +spinlock_t kernel_flag = SPIN_LOCK_UNLOCKED; + +/* + * Setup routine for controlling SMP activation + * + * Command-line option of "nosmp" or "maxcpus=0" will disable SMP + * activation entirely (the MPS table probe still happens, though). + * + * Command-line option of "maxcpus=<NUM>", where <NUM> is an integer + * greater than 0, limits the maximum number of CPUs activated in + * SMP mode to <NUM>. + */ + +static int __init nosmp(char *str) +{ + max_cpus = 0; + return 1; +} + +__setup("nosmp", nosmp); + +static int __init maxcpus(char *str) +{ + get_option(&str, &max_cpus); + return 1; +} + +__setup("maxcpus=", maxcpus); + +/* + * Reboot, halt and power_off routines for SMP. + */ +extern char vmhalt_cmd[]; +extern char vmpoff_cmd[]; + +extern void reipl(unsigned long devno); + +void do_machine_restart(void) +{ + smp_send_stop(); + reipl(S390_lowcore.ipl_device); +} + +void machine_restart(char * __unused) +{ + if (smp_processor_id() != 0) { + smp_ext_bitcall(0, ec_restart); + for (;;); + } else + do_machine_restart(); +} + +void do_machine_halt(void) +{ + smp_send_stop(); + if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) + cpcmd(vmhalt_cmd, NULL, 0); + signal_processor(smp_processor_id(), sigp_stop_and_store_status); +} + +void machine_halt(void) +{ + if (smp_processor_id() != 0) { + smp_ext_bitcall(0, ec_halt); + for (;;); + } else + do_machine_halt(); +} + +void do_machine_power_off(void) +{ + smp_send_stop(); + if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) + cpcmd(vmpoff_cmd, NULL, 0); + signal_processor(smp_processor_id(), sigp_stop_and_store_status); +} + +void machine_power_off(void) +{ + if (smp_processor_id() != 0) { + smp_ext_bitcall(0, ec_power_off); + for (;;); + } else + do_machine_power_off(); +} + +/* + * This is the main routine where commands issued by other + * cpus are handled. + */ + +void do_ext_call_interrupt(struct pt_regs *regs, __u16 code) +{ + ec_ext_call *ec, *next; + unsigned long bits; + + /* + * handle bit signal external calls + * + * For the ec_schedule signal we have to do nothing. All the work + * is done automatically when we return from the interrupt. + * For the ec_restart, ec_halt and ec_power_off we call the + * appropriate routine. + */ + bits = xchg(&S390_lowcore.ext_call_fast, 0); + + if (test_bit(ec_restart, &bits)) + do_machine_restart(); + if (test_bit(ec_halt, &bits)) + do_machine_halt(); + if (test_bit(ec_power_off, &bits)) + do_machine_power_off(); + + /* + * Handle external call commands with a parameter area + */ + ec = (ec_ext_call *) xchg(&S390_lowcore.ext_call_queue, 0); + if (ec == NULL) + return; /* no command signals */ + + /* Make a fifo out of the lifo */ + next = ec->next; + ec->next = NULL; + while (next != NULL) { + ec_ext_call *tmp = next->next; + next->next = ec; + ec = next; + next = tmp; + } + + /* Execute every sigp command on the queue */ + while (ec != NULL) { + switch (ec->cmd) { + case ec_callback_async: { + void (*func)(void *info); + void *info; + + func = ec->func; + info = ec->info; + atomic_set(&ec->status,ec_executing); + (func)(info); + return; + } + case ec_callback_sync: + atomic_set(&ec->status,ec_executing); + (ec->func)(ec->info); + atomic_set(&ec->status,ec_done); + return; + default: + } + ec = ec->next; + } +} + +/* + * Swap in a new request to external call queue + */ +static inline void smp_add_ext_call(ec_ext_call *ec, struct _lowcore *lowcore) +{ + int success; + + while (1) { + ec->next = (ec_ext_call*) lowcore->ext_call_queue; + __asm__ __volatile__ ( + " lgr 0,%2\n" + " csg 0,%3,%1\n" + " ipm %0\n" + " srl %0,28\n" + : "=d" (success), "+m" (lowcore->ext_call_queue) + : "d" (ec->next), "d" (ec) + : "cc", "0" ); + if (success == 0) break; + } +} + +/* + * Send an external call sigp to another cpu and wait for its completion. + */ +sigp_ccode +smp_ext_call(int cpu, void (*func)(void *info), void *info, int wait) +{ + sigp_ccode ccode; + ec_ext_call ec; + + ec.cmd = wait ? ec_callback_sync:ec_callback_async; + atomic_set(&ec.status, ec_pending); + ec.func = func; + ec.info = info; + /* swap in new request to external call queue */ + smp_add_ext_call(&ec, &get_cpu_lowcore(cpu)); + /* + * We try once to deliver the signal. There are four possible + * return codes: + * 0) Order code accepted - can't show up on an external call + * 1) Status stored - fine, wait for completion. + * 2) Busy - there is another signal pending. Thats fine too, because + * do_ext_call from the pending signal will execute all signals on + * the queue. We wait for completion. + * 3) Not operational - something very bad has happened to the cpu. + * do not wait for completion. + */ + ccode = signal_processor(cpu, sigp_external_call); + + if (ccode != sigp_not_operational) + /* wait for completion, FIXME: possible seed of a deadlock */ + while (atomic_read(&ec.status) != (wait?ec_done:ec_executing)); + + return ccode; +} + +/* + * Send a callback sigp to every other cpu in the system. + */ +void smp_ext_call_others(void (*func)(void *info), void *info, int wait) +{ + ec_ext_call ec[NR_CPUS]; + sigp_ccode ccode; + int i; + + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() == i) + continue; + ec[i].cmd = wait ? ec_callback_sync : ec_callback_async; + atomic_set(&ec[i].status, ec_pending); + ec[i].func = func; + ec[i].info = info; + smp_add_ext_call(ec+i, &get_cpu_lowcore(i)); + ccode = signal_processor(i, sigp_external_call); + } + + /* wait for completion, FIXME: possible seed of a deadlock */ + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() == i) + continue; + while (atomic_read(&ec[i].status) != + (wait ? ec_done:ec_executing)); + } +} + +/* + * Send an external call sigp to another cpu and return without waiting + * for its completion. + */ +sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig) +{ + sigp_ccode ccode; + + /* + * Set signaling bit in lowcore of target cpu and kick it + */ + set_bit(sig, &(get_cpu_lowcore(cpu).ext_call_fast)); + ccode = signal_processor(cpu, sigp_external_call); + return ccode; +} + +/* + * Send an external call sigp to every other cpu in the system and + * return without waiting for its completion. + */ +void smp_ext_bitcall_others(ec_bit_sig sig) +{ + sigp_ccode ccode; + int i; + + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() == i) + continue; + /* + * Set signaling bit in lowcore of target cpu and kick it + */ + set_bit(sig, &(get_cpu_lowcore(i).ext_call_fast)); + ccode = signal_processor(i, sigp_external_call); + } +} + +/* + * cycles through all the cpus, + * returns early if info is not NULL & the processor has something + * of intrest to report in the info structure. + * it returns the next cpu to check if it returns early. + * i.e. it should be used as follows if you wish to receive info. + * next_cpu=0; + * do + * { + * info->cpu=next_cpu; + * next_cpu=smp_signal_others(order_code,parameter,1,info); + * ... check info here + * } while(next_cpu<=smp_num_cpus) + * + * if you are lazy just use it like + * smp_signal_others(order_code,parameter,0,1,NULL); + */ +int smp_signal_others(sigp_order_code order_code, u32 parameter, + int spin, sigp_info *info) +{ + sigp_ccode ccode; + u32 dummy; + u16 i; + + if (info) + info->intresting = 0; + for (i = (info ? info->cpu : 0); i < smp_num_cpus; i++) { + if (smp_processor_id() != i) { + do { + ccode = signal_processor_ps( + (info ? &info->status : &dummy), + parameter, i, order_code); + } while(spin && ccode == sigp_busy); + if (info && ccode != sigp_order_code_accepted) { + info->intresting = 1; + info->cpu = i; + info->ccode = ccode; + i++; + break; + } + } + } + return i; +} + +/* + * this function sends a 'stop' sigp to all other CPUs in the system. + * it goes straight through. + */ + +void smp_send_stop(void) +{ + int i; + u32 dummy; + unsigned long low_core_addr; + + /* write magic number to zero page (absolute 0) */ + + get_cpu_lowcore(smp_processor_id()).panic_magic = __PANIC_MAGIC; + + /* stop all processors */ + + smp_signal_others(sigp_stop, 0, TRUE, NULL); + + /* store status of all processors in their lowcores (real 0) */ + + for (i = 0; i < smp_num_cpus; i++) { + if (smp_processor_id() != i) { + int ccode; + low_core_addr = (unsigned long)&get_cpu_lowcore(i); + do { + ccode = signal_processor_ps( + &dummy, + low_core_addr, + i, + sigp_store_status_at_address); + } while(ccode == sigp_busy); + } + } +} + +/* + * this function sends a 'reschedule' IPI to another CPU. + * it goes straight through and wastes no time serializing + * anything. Worst case is that we lose a reschedule ... + */ + +void smp_send_reschedule(int cpu) +{ + smp_ext_bitcall(cpu, ec_schedule); +} + +/* + * parameter area for the set/clear control bit callbacks + */ +typedef struct +{ + __u16 start_ctl; + __u16 end_ctl; + __u64 orvals[16]; + __u64 andvals[16]; +} ec_creg_mask_parms; + +/* + * callback for setting/clearing control bits + */ +void smp_ctl_bit_callback(void *info) { + ec_creg_mask_parms *pp; + u64 cregs[16]; + int i; + + pp = (ec_creg_mask_parms *) info; + asm volatile (" bras 1,0f\n" + " stctg 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); + for (i = pp->start_ctl; i <= pp->end_ctl; i++) + cregs[i] = (cregs[i] & pp->andvals[i]) | pp->orvals[i]; + asm volatile (" bras 1,0f\n" + " lctlg 0,0,0(%0)\n" + "0: ex %1,0(1)\n" + : : "a" (cregs+pp->start_ctl), + "a" ((pp->start_ctl<<4) + pp->end_ctl) + : "memory", "1" ); +} + +/* + * Set a bit in a control register of all cpus + */ +void smp_ctl_set_bit(int cr, int bit) { + ec_creg_mask_parms parms; + + if (atomic_read(&smp_commenced) != 0) { + parms.start_ctl = cr; + parms.end_ctl = cr; + parms.orvals[cr] = 1 << bit; + parms.andvals[cr] = -1L; + smp_ext_call_others(smp_ctl_bit_callback, &parms, 1); + } + __ctl_set_bit(cr, bit); +} + +/* + * Clear a bit in a control register of all cpus + */ +void smp_ctl_clear_bit(int cr, int bit) { + ec_creg_mask_parms parms; + + if (atomic_read(&smp_commenced) != 0) { + parms.start_ctl = cr; + parms.end_ctl = cr; + parms.orvals[cr] = 0; + parms.andvals[cr] = ~(1L << bit); + smp_ext_call_others(smp_ctl_bit_callback, &parms, 1); + } + __ctl_clear_bit(cr, bit); +} + +/* + * Call a function on all other processors + */ + +int +smp_call_function(void (*func)(void *info), void *info, int retry, int wait) +/* + * [SUMMARY] Run a function on all other CPUs. + * <func> The function to run. This must be fast and non-blocking. + * <info> An arbitrary pointer to pass to the function. + * <retry> currently unused. + * <wait> If true, wait (atomically) until function has completed on other CPUs. + * [RETURNS] 0 on success, else a negative status code. Does not return until + * remote CPUs are nearly ready to execute <<func>> or are or have executed. + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler, you may call it from a bottom half handler. + */ +{ + if (atomic_read(&smp_commenced) != 0) + smp_ext_call_others(func, info, 1); + (func)(info); + return 0; +} + +/* + * Lets check how many CPUs we have. + */ + +void smp_count_cpus(void) +{ + int curr_cpu; + + current->processor = 0; + smp_num_cpus = 1; + for (curr_cpu = 0; + curr_cpu <= 65535 && smp_num_cpus < max_cpus; curr_cpu++) { + if ((__u16) curr_cpu == boot_cpu_addr) + continue; + __cpu_logical_map[smp_num_cpus] = (__u16) curr_cpu; + if (signal_processor(smp_num_cpus, sigp_sense) == + sigp_not_operational) + continue; + smp_num_cpus++; + } + printk("Detected %d CPU's\n",(int) smp_num_cpus); + printk("Boot cpu address %2X\n", boot_cpu_addr); +} + + +/* + * Activate a secondary processor. + */ +extern void init_100hz_timer(void); + +int __init start_secondary(void *cpuvoid) +{ + /* Setup the cpu */ + cpu_init(); + /* Print info about this processor */ + print_cpu_info(&safe_get_cpu_lowcore(smp_processor_id()).cpu_data); + /* Wait for completion of smp startup */ + while (!atomic_read(&smp_commenced)) + /* nothing */ ; + /* init per CPU 100 hz timer */ + init_100hz_timer(); + /* cpu_idle will call schedule for us */ + return cpu_idle(NULL); +} + +/* + * The restart interrupt handler jumps to start_secondary directly + * without the detour over initialize_secondary. We defined it here + * so that the linker doesn't complain. + */ +void __init initialize_secondary(void) +{ +} + +static int __init fork_by_hand(void) +{ + struct pt_regs regs; + /* don't care about the psw and regs settings since we'll never + reschedule the forked task. */ + memset(®s,0,sizeof(pt_regs)); + return do_fork(CLONE_VM|CLONE_PID, 0, ®s, 0); +} + +static void __init do_boot_cpu(int cpu) +{ + struct task_struct *idle; + struct _lowcore *cpu_lowcore; + + /* We can't use kernel_thread since we must _avoid_ to reschedule + the child. */ + if (fork_by_hand() < 0) + panic("failed fork for CPU %d", cpu); + + /* + * We remove it from the pidhash and the runqueue + * once we got the process: + */ + idle = init_task.prev_task; + if (!idle) + panic("No idle process for CPU %d",cpu); + idle->processor = cpu; + idle->has_cpu = 1; /* we schedule the first task manually */ + + del_from_runqueue(idle); + unhash_process(idle); + init_tasks[cpu] = idle; + + cpu_lowcore=&get_cpu_lowcore(cpu); + cpu_lowcore->kernel_stack=idle->thread.ksp; + __asm__ __volatile__("stctg 0,15,%0\n\t" + "stam 0,15,%1" + : "=m" (cpu_lowcore->cregs_save_area[0]), + "=m" (cpu_lowcore->access_regs_save_area[0]) + : : "memory"); + + eieio(); + signal_processor(cpu,sigp_restart); +} + +/* + * Architecture specific routine called by the kernel just before init is + * fired off. This allows the BP to have everything in order [we hope]. + * At the end of this all the APs will hit the system scheduling and off + * we go. Each AP will load the system gdt's and jump through the kernel + * init into idle(). At this point the scheduler will one day take over + * and give them jobs to do. smp_callin is a standard routine + * we use to track CPUs as they power up. + */ + +void __init smp_commence(void) +{ + /* + * Lets the callins below out of their loop. + */ + atomic_set(&smp_commenced,1); +} + +/* + * Cycle through the processors sending restart sigps to boot each. + */ + +void __init smp_boot_cpus(void) +{ + struct _lowcore *curr_lowcore; + sigp_ccode ccode; + int i; + + /* request the 0x1202 external interrupt */ + if (register_external_interrupt(0x1202, do_ext_call_interrupt) != 0) + panic("Couldn't request external interrupt 0x1202"); + smp_count_cpus(); + memset(lowcore_ptr,0,sizeof(lowcore_ptr)); + + /* + * Initialize the logical to physical CPU number mapping + * and the per-CPU profiling counter/multiplier + */ + + for (i = 0; i < NR_CPUS; i++) { + prof_counter[i] = 1; + prof_old_multiplier[i] = 1; + prof_multiplier[i] = 1; + } + + print_cpu_info(&safe_get_cpu_lowcore(0).cpu_data); + + for(i = 0; i < smp_num_cpus; i++) + { + curr_lowcore = (struct _lowcore *) + __get_free_pages(GFP_KERNEL|GFP_DMA, 1); + if (curr_lowcore == NULL) { + printk("smp_boot_cpus failed to allocate prefix memory\n"); + break; + } + lowcore_ptr[i] = curr_lowcore; + memcpy(curr_lowcore, &S390_lowcore, sizeof(struct _lowcore)); + /* + * Most of the parameters are set up when the cpu is + * started up. + */ + if (smp_processor_id() == i) + set_prefix((u32)(u64)curr_lowcore); + else { + ccode = signal_processor_p((u64)(curr_lowcore), + i, sigp_set_prefix); + if(ccode) { + /* if this gets troublesome I'll have to do + * something about it. */ + printk("ccode %d for cpu %d returned when " + "setting prefix in smp_boot_cpus not good.\n", + (int) ccode, (int) i); + } + else + do_boot_cpu(i); + } + } +} + +/* + * the frequency of the profiling timer can be changed + * by writing a multiplier value into /proc/profile. + * + * usually you want to run this on all CPUs ;) + */ +int setup_profiling_timer(unsigned int multiplier) +{ + return 0; +} + +/* + * Local timer interrupt handler. It does both profiling and + * process statistics/rescheduling. + * + * We do profiling in every local tick, statistics/rescheduling + * happen only every 'profiling multiplier' ticks. The default + * multiplier is 1 and it can be changed by writing the new multiplier + * value into /proc/profile. + */ + +void smp_local_timer_interrupt(struct pt_regs * regs) +{ + int user = (user_mode(regs) != 0); + int cpu = smp_processor_id(); + + /* + * The profiling function is SMP safe. (nothing can mess + * around with "current", and the profiling counters are + * updated with atomic operations). This is especially + * useful with a profiling multiplier != 1 + */ + if (!user_mode(regs)) + s390_do_profile(regs->psw.addr); + + if (!--prof_counter[cpu]) { + int system = 1-user; + struct task_struct * p = current; + + /* + * The multiplier may have changed since the last time we got + * to this point as a result of the user writing to + * /proc/profile. In this case we need to adjust the APIC + * timer accordingly. + * + * Interrupts are already masked off at this point. + */ + prof_counter[cpu] = prof_multiplier[cpu]; + if (prof_counter[cpu] != prof_old_multiplier[cpu]) { + prof_old_multiplier[cpu] = prof_counter[cpu]; + } + + /* + * After doing the above, we need to make like + * a normal interrupt - otherwise timer interrupts + * ignore the global interrupt lock, which is the + * WrongThing (tm) to do. + */ + + irq_enter(cpu, 0); + update_process_times(user); + irq_exit(cpu, 0); + } +} + diff --git a/arch/s390x/kernel/sys_s390.c b/arch/s390x/kernel/sys_s390.c new file mode 100644 index 000000000..4c06bd326 --- /dev/null +++ b/arch/s390x/kernel/sys_s390.c @@ -0,0 +1,205 @@ +/* + * arch/s390/kernel/sys_s390.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * Derived from "arch/i386/kernel/sys_i386.c" + * + * This file contains various random system calls that + * have a non-standard calling sequence on the Linux/s390 + * 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/mman.h> +#include <linux/file.h> +#include <linux/utsname.h> + +#include <asm/uaccess.h> +#include <asm/ipc.h> + +/* + * sys_pipe() is the normal C calling standard for creating + * a pipe. It's not the way Unix traditionally does this, though. + */ +asmlinkage long sys_pipe(unsigned long * 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) +{ + long error = -EBADF; + struct file * file = NULL; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * calls. Linux/i386 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 long sys_mmap2(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + int error = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset); +out: + return error; +} + +asmlinkage long old_mmap(struct mmap_arg_struct *arg) +{ + struct mmap_arg_struct a; + long error = -EFAULT; + + if (copy_from_user(&a, arg, sizeof(a))) + goto out; + + error = -EINVAL; + if (a.offset & ~PAGE_MASK) + goto out; + + error = do_mmap2(a.addr, a.len, a.prot, a.flags, a.fd, a.offset >> PAGE_SHIFT); +out: + return error; +} + +extern asmlinkage int sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); + +/* + * 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, + unsigned long third, void *ptr) +{ + struct ipc_kludge tmp; + int ret; + + switch (call) { + case SEMOP: + return sys_semop (first, (struct sembuf *)ptr, second); + case SEMGET: + return sys_semget (first, second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void **) ptr)) + return -EFAULT; + return sys_semctl (first, second, third, fourth); + } + case MSGSND: + return sys_msgsnd (first, (struct msgbuf *) ptr, + second, third); + break; + case MSGRCV: + if (!ptr) + return -EINVAL; + if (copy_from_user (&tmp, (struct ipc_kludge *) ptr, + sizeof (struct ipc_kludge))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, + second, tmp.msgtyp, third); + case MSGGET: + return sys_msgget ((key_t) first, second); + case MSGCTL: + return sys_msgctl (first, second, (struct msqid_ds *) ptr); + + case SHMAT: { + ulong raddr; + ret = sys_shmat (first, (char *) ptr, second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong *) third); + break; + } + case SHMDT: + return sys_shmdt ((char *)ptr); + case SHMGET: + return sys_shmget (first, second, third); + case SHMCTL: + return sys_shmctl (first, second, + (struct shmid_ds *) ptr); + default: + return -EINVAL; + + } + + return -EINVAL; +} + +/* + * Old cruft + */ +asmlinkage int sys_uname(struct old_utsname * name) +{ + int err; + if (!name) + return -EFAULT; + down_read(&uts_sem); + err=copy_to_user(name, &system_utsname, sizeof (*name)); + up_read(&uts_sem); + return err?-EFAULT:0; +} + +asmlinkage int sys_pause(void) +{ + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + return -ERESTARTNOHAND; +} + diff --git a/arch/s390x/kernel/time.c b/arch/s390x/kernel/time.c new file mode 100644 index 000000000..d4e91842e --- /dev/null +++ b/arch/s390x/kernel/time.c @@ -0,0 +1,258 @@ +/* + * arch/s390/kernel/time.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com), + * Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) + * + * Derived from "arch/i386/kernel/time.c" + * Copyright (C) 1991, 1992, 1995 Linus Torvalds + */ + +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/types.h> + +#include <asm/uaccess.h> +#include <asm/delay.h> + +#include <linux/timex.h> +#include <linux/config.h> + +#include <asm/irq.h> +#include <asm/s390_ext.h> + + +/* change this if you have some constant time drift */ +#define USECS_PER_JIFFY ((signed long)1000000/HZ) +#define CLK_TICKS_PER_JIFFY ((signed long)USECS_PER_JIFFY<<12) + +#define TICK_SIZE tick + +static uint64_t init_timer_cc, last_timer_cc; + +extern rwlock_t xtime_lock; +extern unsigned long wall_jiffies; + +void tod_to_timeval(uint64_t todval, struct timeval *xtime) +{ +#if 0 + const int high_bit = 0x80000000L; + const int c_f4240 = 0xf4240L; + const int c_7a120 = 0x7a120; + /* We have to divide the 64 bit value todval by 4096 + * (because the 2^12 bit is the one that changes every + * microsecond) and then split it into seconds and + * microseconds. A value of max (2^52-1) divided by + * the value 0xF4240 can yield a max result of approx + * (2^32.068). Thats to big to fit into a signed int + * ... hacking time! + */ + asm volatile ("L 2,%1\n\t" + "LR 3,2\n\t" + "SRL 2,12\n\t" + "SLL 3,20\n\t" + "L 4,%O1+4(%R1)\n\t" + "SRL 4,12\n\t" + "OR 3,4\n\t" /* now R2/R3 contain (todval >> 12) */ + "SR 4,4\n\t" + "CL 2,%2\n\t" + "JL .+12\n\t" + "S 2,%2\n\t" + "L 4,%3\n\t" + "D 2,%4\n\t" + "OR 3,4\n\t" + "ST 2,%O0+4(%R0)\n\t" + "ST 3,%0" + : "=m" (*xtime) : "m" (todval), + "m" (c_7a120), "m" (high_bit), "m" (c_f4240) + : "cc", "memory", "2", "3", "4" ); +#else + todval >>= 12; + xtime->tv_sec = todval / 1000000; + xtime->tv_usec = todval % 1000000; +#endif +} + +unsigned long do_gettimeoffset(void) +{ + __u64 timer_cc; + + asm volatile ("STCK %0" : "=m" (timer_cc)); + /* We require the offset from the previous interrupt */ + return ((unsigned long)((timer_cc - last_timer_cc)>>12)); +} + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long usec, sec; + unsigned long lost_ticks = jiffies - wall_jiffies; + + read_lock_irqsave(&xtime_lock, flags); + usec = do_gettimeoffset(); + if (lost_ticks) + usec +=(USECS_PER_JIFFY*lost_ticks); + sec = xtime.tv_sec; + usec += xtime.tv_usec; + read_unlock_irqrestore(&xtime_lock, flags); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +void do_settimeofday(struct timeval *tv) +{ + + write_lock_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! + */ + tv->tv_usec -= do_gettimeoffset(); + + while (tv->tv_usec < 0) { + tv->tv_usec += 1000000; + tv->tv_sec--; + } + + xtime = *tv; + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_unlock_irq(&xtime_lock); +} + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ + +#ifdef CONFIG_SMP +extern __u16 boot_cpu_addr; +#endif + +void do_timer_interrupt(struct pt_regs *regs,int error_code) +{ + unsigned long flags; + + /* + * reset timer to 10ms minus time already elapsed + * since timer-interrupt pending + */ + + save_flags(flags); + cli(); +#ifdef CONFIG_SMP + if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) { + write_lock(&xtime_lock); + last_timer_cc = S390_lowcore.jiffy_timer_cc; + } +#else + last_timer_cc = S390_lowcore.jiffy_timer_cc; +#endif + /* set clock comparator */ + S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY; + asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc)); + +/* + * In the SMP case we use the local timer interrupt to do the + * profiling, except when we simulate SMP mode on a uniprocessor + * system, in that case we have to call the local interrupt handler. + */ +#ifdef CONFIG_SMP + /* when SMP, do smp_local_timer_interrupt for *all* CPUs, + but only do the rest for the boot CPU */ + smp_local_timer_interrupt(regs); +#else + if (!user_mode(regs)) + s390_do_profile(regs->psw.addr); +#endif + +#ifdef CONFIG_SMP + if(S390_lowcore.cpu_data.cpu_addr==boot_cpu_addr) +#endif + { + do_timer(regs); +#ifdef CONFIG_SMP + write_unlock(&xtime_lock); +#endif + } + restore_flags(flags); + +} + +/* + * Start the clock comparator on the current CPU + */ +static unsigned long cr0 __attribute__ ((aligned (8))); + +void init_100hz_timer(void) +{ + /* allow clock comparator timer interrupt */ + asm volatile ("STCTG 0,0,%0" : "=m" (cr0) : : "memory"); + cr0 |= 0x800; + asm volatile ("LCTLG 0,0,%0" : : "m" (cr0) : "memory"); + /* set clock comparator */ + /* read the TOD clock */ + asm volatile ("STCK %0" : "=m" (S390_lowcore.jiffy_timer_cc)); + S390_lowcore.jiffy_timer_cc += CLK_TICKS_PER_JIFFY; + asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer_cc)); +} + +/* + * Initialize the TOD clock and the CPU timer of + * the boot cpu. + */ +void __init time_init(void) +{ + int cc; + + /* kick the TOD clock */ + asm volatile ("STCK %1\n\t" + "IPM %0\n\t" + "SRL %0,28" : "=r" (cc), "=m" (init_timer_cc)); + switch (cc) { + case 0: /* clock in set state: all is fine */ + break; + case 1: /* clock in non-set state: FIXME */ + printk("time_init: TOD clock in non-set state\n"); + break; + case 2: /* clock in error state: FIXME */ + printk("time_init: TOD clock in error state\n"); + break; + case 3: /* clock in stopped or not-operational state: FIXME */ + printk("time_init: TOD clock stopped/non-operational\n"); + break; + } + /* request the 0x1004 external interrupt */ + if (register_external_interrupt(0x1004, do_timer_interrupt) != 0) + panic("Couldn't request external interrupts 0x1004"); + init_100hz_timer(); + init_timer_cc = S390_lowcore.jiffy_timer_cc; + init_timer_cc -= 0x8126d60e46000000LL - + (0x3c26700LL*1000000*4096); + tod_to_timeval(init_timer_cc, &xtime); +} diff --git a/arch/s390x/kernel/traps.c b/arch/s390x/kernel/traps.c new file mode 100644 index 000000000..3301ddc18 --- /dev/null +++ b/arch/s390x/kernel/traps.c @@ -0,0 +1,248 @@ +/* + * arch/s390/kernel/traps.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * Derived from "arch/i386/kernel/traps.c" + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +/* + * 'Traps.c' handles hardware traps and faults after we have saved some + * state in 'asm.s'. + */ +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/timer.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/mathemu.h> +#if CONFIG_REMOTE_DEBUG +#include <asm/gdb-stub.h> +#endif + +/* Called from entry.S only */ +extern void handle_per_exception(struct pt_regs *regs); + +typedef void pgm_check_handler_t(struct pt_regs *, long); +pgm_check_handler_t *pgm_check_table[128]; + +#ifdef CONFIG_SYSCTL +#ifdef CONFIG_PROCESS_DEBUG +int sysctl_userprocess_debug = 1; +#else +int sysctl_userprocess_debug = 0; +#endif +#endif + +extern pgm_check_handler_t do_page_fault; + +spinlock_t die_lock; + +void die(const char * str, struct pt_regs * regs, long err) +{ + console_verbose(); + spin_lock_irq(&die_lock); + printk("%s: %04lx\n", str, err & 0xffff); + show_regs(regs); + spin_unlock_irq(&die_lock); + do_exit(SIGSEGV); +} + +#define DO_ERROR(signr, str, name) \ +asmlinkage void name(struct pt_regs * regs, long interruption_code) \ +{ \ + do_trap(interruption_code, signr, str, regs, NULL); \ +} + +#define DO_ERROR_INFO(signr, str, name, sicode, siaddr) \ +asmlinkage void name(struct pt_regs * regs, long interruption_code) \ +{ \ + siginfo_t info; \ + info.si_signo = signr; \ + info.si_errno = 0; \ + info.si_code = sicode; \ + info.si_addr = (void *)siaddr; \ + do_trap(interruption_code, signr, str, regs, &info); \ +} + +static void inline do_trap(long interruption_code, int signr, char *str, + struct pt_regs *regs, siginfo_t *info) +{ + if (regs->psw.mask & PSW_PROBLEM_STATE) { + struct task_struct *tsk = current; + tsk->thread.trap_no = interruption_code; + if (info) + force_sig_info(signr, info, tsk); + else + force_sig(signr, tsk); +#ifndef CONFIG_SYSCTL +#ifdef CONFIG_PROCESS_DEBUG + printk("User process fault: interruption code 0x%lX\n", + interruption_code); + show_regs(regs); +#endif +#else + if (sysctl_userprocess_debug) { + printk("User process fault: interruption code 0x%lX\n", + interruption_code); + show_regs(regs); + } +#endif + } else { + unsigned long fixup = search_exception_table(regs->psw.addr); + if (fixup) + regs->psw.addr = fixup; + else + die(str, regs, interruption_code); + } +} + +int do_debugger_trap(struct pt_regs *regs,int signal) +{ + if(regs->psw.mask&PSW_PROBLEM_STATE) + { + if(current->ptrace & PT_PTRACED) + force_sig(signal,current); + else + return 1; + } + else + { +#if CONFIG_REMOTE_DEBUG + if(gdb_stub_initialised) + { + gdb_stub_handle_exception((gdb_pt_regs *)regs,signal); + return 0; + } +#endif + return 1; + } + return 0; +} + +DO_ERROR(SIGSEGV, "Unknown program exception", default_trap_handler) +DO_ERROR(SIGILL, "privileged operation", privileged_op) +DO_ERROR(SIGILL, "execute exception", execute_exception) +DO_ERROR(SIGSEGV, "addressing exception", addressing_exception) +DO_ERROR(SIGFPE, "fixpoint divide exception", divide_exception) +DO_ERROR(SIGILL, "translation exception", translation_exception) +DO_ERROR(SIGILL, "special operand exception", special_op_exception) +DO_ERROR(SIGILL, "operand exception", operand_exception) +DO_ERROR(SIGILL, "specification exception", specification_exception); + +asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code) +{ + __u8 opcode[6]; + __u16 *location; + int do_sig = 0; + + lock_kernel(); + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + /* WARNING don't change this check back to */ + /* int problem_state=(regs->psw.mask & PSW_PROBLEM_STATE); */ + /* & then doing if(problem_state) an int is too small for this */ + /* check on 64 bit. */ + if(regs->psw.mask & PSW_PROBLEM_STATE) + get_user(*((__u16 *) opcode), location); + else + *((__u16 *)opcode)=*((__u16 *)location); + if(*((__u16 *)opcode)==S390_BREAKPOINT_U16) + { + if(do_debugger_trap(regs,SIGTRAP)) + do_sig=1; + } + else + do_sig = 1; + if (do_sig) + do_trap(interruption_code, SIGILL, "illegal operation", regs, NULL); + unlock_kernel(); +} + +asmlinkage void data_exception(struct pt_regs * regs, long interruption_code) +{ + __u16 *location; + int do_sig = 0; + + lock_kernel(); + location = (__u16 *)(regs->psw.addr-S390_lowcore.pgm_ilc); + __asm__ volatile ("stfpc %0\n\t" + : "=m" (current->thread.fp_regs.fpc)); + /* Same code should work when we implement fpu emulation */ + /* provided we call data exception from the fpu emulator */ + if(current->thread.fp_regs.fpc&FPC_DXC_MASK) + { + current->thread.ieee_instruction_pointer=(addr_t)location; + force_sig(SIGFPE, current); + } + else + do_sig = 1; + if (do_sig) + do_trap(interruption_code, SIGILL, "data exception", regs, NULL); + unlock_kernel(); +} + + + +/* init is done in lowcore.S and head.S */ + +void __init trap_init(void) +{ + int i; + + for (i = 0; i < 128; i++) + pgm_check_table[i] = &default_trap_handler; + pgm_check_table[1] = &illegal_op; + pgm_check_table[2] = &privileged_op; + pgm_check_table[3] = &execute_exception; + pgm_check_table[5] = &addressing_exception; + pgm_check_table[6] = &specification_exception; + pgm_check_table[7] = &data_exception; + pgm_check_table[9] = ÷_exception; + pgm_check_table[0x12] = &translation_exception; + pgm_check_table[0x13] = &special_op_exception; + pgm_check_table[0x15] = &operand_exception; + pgm_check_table[4] = &do_page_fault; + pgm_check_table[0x10] = &do_page_fault; + pgm_check_table[0x11] = &do_page_fault; + pgm_check_table[0x1C] = &privileged_op; + pgm_check_table[0x38] = &addressing_exception; + pgm_check_table[0x3B] = &do_page_fault; +} + + +void handle_per_exception(struct pt_regs *regs) +{ + if(regs->psw.mask&PSW_PROBLEM_STATE) + { + per_struct *per_info=¤t->thread.per_info; + per_info->lowcore.words.perc_atmid=S390_lowcore.per_perc_atmid; + per_info->lowcore.words.address=S390_lowcore.per_address; + per_info->lowcore.words.access_id=S390_lowcore.per_access_id; + } + if(do_debugger_trap(regs,SIGTRAP)) + { + /* I've seen this possibly a task structure being reused ? */ + printk("Spurious per exception detected\n"); + printk("switching off per tracing for this task.\n"); + show_regs(regs); + /* Hopefully switching off per tracing will help us survive */ + regs->psw.mask &= ~PSW_PER_MASK; + } +} + diff --git a/arch/s390x/kernel/wrapper32.S b/arch/s390x/kernel/wrapper32.S new file mode 100644 index 000000000..2cc5cb6cf --- /dev/null +++ b/arch/s390x/kernel/wrapper32.S @@ -0,0 +1,1071 @@ +/* +* arch/s390/kernel/sys_wrapper31.S +* wrapper for 31 bit compatible system calls. +* +* S390 version +* Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation +* Author(s): Gerhard Tonn (ton@de.ibm.com), +*/ + + .globl sys32_exit_wrapper +sys32_exit_wrapper: + lgfr %r2,%r2 # int + jg sys_exit # branch to sys_exit + + .globl sys32_read_wrapper +sys32_read_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # char * + llgfr %r4,%r4 # size_t + jg sys_read # branch to sys_read + + .globl sys32_write_wrapper +sys32_write_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # const char * + llgfr %r4,%r4 # size_t + jg sys_write # branch to system call + + .globl sys32_open_wrapper +sys32_open_wrapper: + llgtr %r2,%r2 # const char * + lgfr %r3,%r3 # int + lgfr %r4,%r4 # int + jg sys_open # branch to system call + + .globl sys32_close_wrapper +sys32_close_wrapper: + llgfr %r2,%r2 # unsigned int + jg sys_close # branch to system call + + .globl sys32_creat_wrapper +sys32_creat_wrapper: + llgtr %r2,%r2 # const char * + lgfr %r3,%r3 # int + jg sys_creat # branch to system call + + .globl sys32_link_wrapper +sys32_link_wrapper: + llgtr %r2,%r2 # const char * + llgtr %r3,%r3 # const char * + jg sys_link # branch to system call + + .globl sys32_unlink_wrapper +sys32_unlink_wrapper: + llgtr %r2,%r2 # const char * + jg sys_unlink # branch to system call + + .globl sys32_chdir_wrapper +sys32_chdir_wrapper: + llgtr %r2,%r2 # const char * + jg sys_chdir # branch to system call + + .globl sys32_time_wrapper +sys32_time_wrapper: + llgtr %r2,%r2 # int * + jg sys_time # branch to system call + + .globl sys32_mknod_wrapper +sys32_mknod_wrapper: + llgtr %r2,%r2 # const char * + lgfr %r3,%r3 # int + llgfr %r4,%r4 # dev + jg sys_mknod # branch to system call + + .globl sys32_chmod_wrapper +sys32_chmod_wrapper: + llgtr %r2,%r2 # const char * + llgfr %r3,%r3 # mode_t + jg sys_chmod # branch to system call + + .globl sys32_lchown16_wrapper +sys32_lchown16_wrapper: + llgtr %r2,%r2 # const char * + llgfr %r3,%r3 # __kernel_old_uid_emu31_t + llgfr %r4,%r4 # __kernel_old_uid_emu31_t + jg sys32_lchown16 # branch to system call + + .globl sys32_lseek_wrapper +sys32_lseek_wrapper: + llgfr %r2,%r2 # unsigned int + lgfr %r3,%r3 # off_t + llgfr %r4,%r4 # unsigned int + jg sys_lseek # branch to system call + +#sys32_getpid_wrapper # void + + .globl sys32_mount_wrapper +sys32_mount_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # char * + llgtr %r4,%r4 # char * + llgfr %r5,%r5 # unsigned long + llgtr %r6,%r6 # void * + jg sys32_mount # branch to system call + + .globl sys32_oldumount_wrapper +sys32_oldumount_wrapper: + llgtr %r2,%r2 # char * + jg sys_oldumount # branch to system call + + .globl sys32_setuid16_wrapper +sys32_setuid16_wrapper: + llgfr %r2,%r2 # __kernel_old_uid_emu31_t + jg sys32_setuid16 # branch to system call + +#sys32_getuid16_wrapper # void + + .globl sys32_ptrace_wrapper +sys32_ptrace_wrapper: + lgfr %r2,%r2 # long + lgfr %r3,%r3 # long + llgtr %r4,%r4 # long + lgfr %r5,%r5 # long + jg sys32_ptrace # branch to system call + + .globl sys32_alarm_wrapper +sys32_alarm_wrapper: + llgtr %r2,%r2 # unsigned int + jg sys_alarm # branch to system call + +#sys32_pause_wrapper # void + + .globl sys32_utime_wrapper +sys32_utime_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct utimbuf_emu31 * + jg sys32_utime # branch to system call + + .globl sys32_access_wrapper +sys32_access_wrapper: + llgtr %r2,%r2 # const char * + lgfr %r3,%r3 # int + jg sys_access # branch to system call + + .globl sys32_nice_wrapper +sys32_nice_wrapper: + lgfr %r2,%r2 # int + jg sys_nice # branch to system call + +#sys32_sync_wrapper # void + + .globl sys32_kill_wrapper +sys32_kill_wrapper: + lgfr %r2,%r2 # int + lgfr %r3,%r3 # int + jg sys_kill # branch to system call + + .globl sys32_rename_wrapper +sys32_rename_wrapper: + llgtr %r2,%r2 # const char * + llgtr %r3,%r3 # const char * + jg sys_rename # branch to system call + + .globl sys32_mkdir_wrapper +sys32_mkdir_wrapper: + llgtr %r2,%r2 # const char * + lgfr %r3,%r3 # int + jg sys_mkdir # branch to system call + + .globl sys32_rmdir_wrapper +sys32_rmdir_wrapper: + llgtr %r2,%r2 # const char * + jg sys_rmdir # branch to system call + + .globl sys32_dup_wrapper +sys32_dup_wrapper: + llgfr %r2,%r2 # unsigned int + jg sys_dup # branch to system call + + .globl sys32_pipe_wrapper +sys32_pipe_wrapper: + llgtr %r2,%r2 # u32 * + jg sys_pipe # branch to system call + + .globl sys32_times_wrapper +sys32_times_wrapper: + llgtr %r2,%r2 # struct tms_emu31 * + jg sys32_times # branch to system call + + .globl sys32_brk_wrapper +sys32_brk_wrapper: + llgtr %r2,%r2 # unsigned long + jg sys_brk # branch to system call + + .globl sys32_setgid16_wrapper +sys32_setgid16_wrapper: + llgfr %r2,%r2 # __kernel_old_gid_emu31_t + jg sys32_setgid16 # branch to system call + +#sys32_getgid16_wrapper # void + + .globl sys32_signal_wrapper +sys32_signal_wrapper: + lgfr %r2,%r2 # int + llgfr %r3,%r3 # __sighandler_t + jg sys_signal + +#sys32_geteuid16_wrapper # void + +#sys32_getegid16_wrapper # void + + .globl sys32_acct_wrapper +sys32_acct_wrapper: + llgtr %r2,%r2 # char * + jg sys_acct # branch to system call + + .globl sys32_umount_wrapper +sys32_umount_wrapper: + llgtr %r2,%r2 # char * + lgfr %r3,%r3 # int + jg sys_umount # branch to system call + + .globl sys32_ioctl_wrapper +sys32_ioctl_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned int + llgfr %r4,%r4 # unsigned int + jg sys32_ioctl # branch to system call + + .globl sys32_fcntl_wrapper +sys32_fcntl_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned int + llgfr %r4,%r4 # unsigned long + jg sys32_fcntl # branch to system call + + .globl sys32_setpgid_wrapper +sys32_setpgid_wrapper: + lgfr %r2,%r2 # pid_t + lgfr %r3,%r3 # pid_t + jg sys_setpgid # branch to system call + + .globl sys32_umask_wrapper +sys32_umask_wrapper: + lgfr %r3,%r3 # int + jg sys_umask # branch to system call + + .globl sys32_chroot_wrapper +sys32_chroot_wrapper: + llgtr %r2,%r2 # char * + jg sys_chroot # branch to system call + + .globl sys32_ustat_wrapper +sys32_ustat_wrapper: + llgfr %r2,%r2 # dev_t + llgtr %r3,%r3 # struct ustat * + jg sys_ustat + + .globl sys32_dup2_wrapper +sys32_dup2_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned int + jg sys_dup2 # branch to system call + +#sys32_getppid_wrapper # void + +#sys32_getpgrp_wrapper # void + +#sys32_setsid_wrapper # void + + .globl sys32_sigaction_wrapper +sys32_sigaction_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # const struct old_sigaction * + jg sys32_sigaction # branch to system call + + .globl sys32_setreuid16_wrapper +sys32_setreuid16_wrapper: + llgfr %r2,%r2 # __kernel_old_uid_emu31_t + llgfr %r3,%r3 # __kernel_old_uid_emu31_t + jg sys32_setreuid16 # branch to system call + + .globl sys32_setregid16_wrapper +sys32_setregid16_wrapper: + llgfr %r2,%r2 # __kernel_old_gid_emu31_t + llgfr %r3,%r3 # __kernel_old_gid_emu31_t + jg sys32_setregid16 # branch to system call + +#sys32_sigsuspend_wrapper # done in sigsuspend_glue + + .globl sys32_sigpending_wrapper +sys32_sigpending_wrapper: + llgtr %r2,%r2 # old_sigset_emu31_t * + jg sys32_sigpending # branch to system call + + .globl sys32_sethostname_wrapper +sys32_sethostname_wrapper: + llgtr %r2,%r2 # char * + lgfr %r3,%r3 # int + jg sys_sethostname # branch to system call + + .globl sys32_setrlimit_wrapper +sys32_setrlimit_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct rlimit_emu31 * + jg sys32_setrlimit # branch to system call + + .globl sys32_old_getrlimit_wrapper +sys32_old_getrlimit_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct rlimit_emu31 * + jg sys32_old_getrlimit # branch to system call + + .globl sys32_mmap2_wrapper +sys32_mmap2_wrapper: + llgtr %r2,%r2 # struct mmap_arg_struct_emu31 * + jg sys32_mmap2 # branch to system call + + .globl sys32_getrusage_wrapper +sys32_getrusage_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # struct rusage_emu31 * + jg sys32_getrusage # branch to system call + + .globl sys32_gettimeofday_wrapper +sys32_gettimeofday_wrapper: + llgtr %r2,%r2 # struct timeval_emu31 * + llgtr %r3,%r3 # struct timezone * + jg sys32_gettimeofday # branch to system call + + .globl sys32_settimeofday_wrapper +sys32_settimeofday_wrapper: + llgtr %r2,%r2 # struct timeval_emu31 * + llgtr %r3,%r3 # struct timezone * + jg sys32_settimeofday # branch to system call + + .globl sys32_getgroups16_wrapper +sys32_getgroups16_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # __kernel_old_gid_emu31_t * + jg sys32_getgroups16 # branch to system call + + .globl sys32_setgroups16_wrapper +sys32_setgroups16_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # __kernel_old_gid_emu31_t * + jg sys32_setgroups16 # branch to system call + + .globl sys32_symlink_wrapper +sys32_symlink_wrapper: + llgtr %r2,%r2 # const char * + llgtr %r3,%r3 # const char * + jg sys_symlink # branch to system call + + .globl sys32_readlink_wrapper +sys32_readlink_wrapper: + llgtr %r2,%r2 # const char * + llgtr %r3,%r3 # char * + lgfr %r4,%r4 # int + jg sys_readlink # branch to system call + + .globl sys32_uselib_wrapper +sys32_uselib_wrapper: + llgtr %r2,%r2 # const char * + jg sys_uselib # branch to system call + + .globl sys32_swapon_wrapper +sys32_swapon_wrapper: + llgtr %r2,%r2 # const char * + lgfr %r3,%r3 # int + jg sys_swapon # branch to system call + + .globl sys32_reboot_wrapper +sys32_reboot_wrapper: + lgfr %r2,%r2 # int + lgfr %r3,%r3 # int + llgfr %r4,%r4 # unsigned int + llgtr %r5,%r5 # void * + jg sys_reboot # branch to system call + + .globl old32_readdir_wrapper +old32_readdir_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # void * + llgfr %r4,%r4 # unsigned int + jg old32_readdir # branch to system call + + .globl old32_mmap_wrapper +old32_mmap_wrapper: + llgtr %r2,%r2 # struct mmap_arg_struct_emu31 * + jg old32_mmap # branch to system call + + .globl sys32_munmap_wrapper +sys32_munmap_wrapper: + llgfr %r2,%r2 # unsigned long + llgfr %r3,%r3 # size_t + jg sys_munmap # branch to system call + + .globl sys32_truncate_wrapper +sys32_truncate_wrapper: + llgtr %r2,%r2 # const char * + llgfr %r3,%r3 # unsigned long + jg sys_truncate # branch to system call + + .globl sys32_ftruncate_wrapper +sys32_ftruncate_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned long + jg sys_ftruncate # branch to system call + + .globl sys32_fchmod_wrapper +sys32_fchmod_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # mode_t + jg sys_fchmod # branch to system call + + .globl sys32_fchown16_wrapper +sys32_fchown16_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # __kernel_old_uid_emu31_t * + llgtr %r4,%r4 # __kernel_old_gid_emu31_t * + jg sys32_fchown16 # branch to system call + + .globl sys32_getpriority_wrapper +sys32_getpriority_wrapper: + lgfr %r2,%r2 # int + lgfr %r3,%r3 # int + jg sys_getpriority # branch to system call + + .globl sys32_setpriority_wrapper +sys32_setpriority_wrapper: + lgfr %r2,%r2 # int + lgfr %r3,%r3 # int + lgfr %r4,%r4 # int + jg sys_setpriority # branch to system call + + .globl sys32_statfs_wrapper +sys32_statfs_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct statfs_emu31 * + jg sys32_statfs # branch to system call + + .globl sys32_fstatfs_wrapper +sys32_fstatfs_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct statfs_emu31 * + jg sys32_fstatfs # branch to system call + + .globl sys32_socketcall_wrapper +sys32_socketcall_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # u32 * + jg sys32_socketcall # branch to system call + + .globl sys32_syslog_wrapper +sys32_syslog_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # char * + lgfr %r4,%r4 # int + jg sys_syslog # branch to system call + + .globl sys32_setitimer_wrapper +sys32_setitimer_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # struct itimerval_emu31 * + llgtr %r4,%r4 # struct itimerval_emu31 * + jg sys32_setitimer # branch to system call + + .globl sys32_getitimer_wrapper +sys32_getitimer_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # struct itimerval_emu31 * + jg sys32_getitimer # branch to system call + + .globl sys32_newstat_wrapper +sys32_newstat_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct stat_emu31 * + jg sys32_newstat # branch to system call + + .globl sys32_newlstat_wrapper +sys32_newlstat_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct stat_emu31 * + jg sys32_newlstat # branch to system call + + .globl sys32_newfstat_wrapper +sys32_newfstat_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct stat_emu31 * + jg sys32_newfstat # branch to system call + +#sys32_vhangup_wrapper # void + + .globl sys32_wait4_wrapper +sys32_wait4_wrapper: + lgfr %r2,%r2 # pid_t + llgtr %r3,%r3 # unsigned int * + lgfr %r4,%r4 # int + llgtr %r5,%r5 # struct rusage * + jg sys32_wait4 # branch to system call + + .globl sys32_swapoff_wrapper +sys32_swapoff_wrapper: + llgtr %r2,%r2 # const char * + jg sys_swapoff # branch to system call + + .globl sys32_sysinfo_wrapper +sys32_sysinfo_wrapper: + llgtr %r2,%r2 # struct sysinfo_emu31 * + jg sys32_sysinfo # branch to system call + + .globl sys32_ipc_wrapper +sys32_ipc_wrapper: + llgfr %r2,%r2 # uint + lgfr %r3,%r3 # int + lgfr %r4,%r4 # int + lgfr %r5,%r5 # int + llgtr %r6,%r6 # void * + jg sys32_ipc # branch to system call + + .globl sys32_fsync_wrapper +sys32_fsync_wrapper: + llgfr %r2,%r2 # unsigned int + jg sys_fsync # branch to system call + +#sys32_sigreturn_wrapper # done in sigreturn_glue + +#sys32_clone_wrapper # done in clone_glue + + .globl sys32_setdomainname_wrapper +sys32_setdomainname_wrapper: + llgtr %r2,%r2 # char * + lgfr %r3,%r3 # int + jg sys_setdomainname # branch to system call + + .globl sys32_newuname_wrapper +sys32_newuname_wrapper: + llgtr %r2,%r2 # struct new_utsname * + jg sys_newuname # branch to system call + + .globl sys32_adjtimex_wrapper +sys32_adjtimex_wrapper: + llgtr %r2,%r2 # struct timex_emu31 * + jg sys32_adjtimex # branch to system call + + .globl sys32_mprotect_wrapper +sys32_mprotect_wrapper: + llgtr %r2,%r2 # unsigned long (actually pointer + llgfr %r3,%r3 # size_t + llgfr %r4,%r4 # unsigned long + jg sys_mprotect # branch to system call + + .globl sys32_sigprocmask_wrapper +sys32_sigprocmask_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # old_sigset_emu31 * + llgtr %r4,%r4 # old_sigset_emu31 * + jg sys32_sigprocmask # branch to system call + + .globl sys32_create_module_wrapper +sys32_create_module_wrapper: + llgtr %r2,%r2 # const char * + llgfr %r3,%r3 # size_t + jg sys32_create_module # branch to system call + + .globl sys32_init_module_wrapper +sys32_init_module_wrapper: + llgtr %r2,%r2 # const char * + llgtr %r3,%r3 # struct module * + jg sys32_init_module # branch to system call + + .globl sys32_delete_module_wrapper +sys32_delete_module_wrapper: + llgtr %r2,%r2 # const char * + jg sys32_delete_module # branch to system call + + .globl sys32_get_kernel_syms_wrapper +sys32_get_kernel_syms_wrapper: + llgtr %r2,%r2 # struct kernel_sym_emu31 * + jg sys32_get_kernel_syms # branch to system call + + .globl sys32_quotactl_wrapper +sys32_quotactl_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # const char * + lgfr %r4,%r4 # int + llgtr %r5,%r5 # caddr_t + jg sys32_quotactl # branch to system call + + .globl sys32_getpgid_wrapper +sys32_getpgid_wrapper: + lgfr %r2,%r2 # pid_t + jg sys_getpgid # branch to system call + + .globl sys32_fchdir_wrapper +sys32_fchdir_wrapper: + llgfr %r2,%r2 # unsigned int + jg sys_fchdir # branch to system call + + .globl sys32_bdflush_wrapper +sys32_bdflush_wrapper: + lgfr %r2,%r2 # int + lgfr %r3,%r3 # long + jg sys_bdflush # branch to system call + + .globl sys32_sysfs_wrapper +sys32_sysfs_wrapper: + lgfr %r2,%r2 # int + llgfr %r3,%r3 # unsigned long + llgfr %r4,%r4 # unsigned long + jg sys_sysfs # branch to system call + + .globl sys32_personality_wrapper +sys32_personality_wrapper: + llgfr %r2,%r2 # unsigned long + jg sys_personality # branch to system call + + .globl sys32_setfsuid16_wrapper +sys32_setfsuid16_wrapper: + llgfr %r2,%r2 # __kernel_old_uid_emu31_t + jg sys32_setfsuid16 # branch to system call + + .globl sys32_setfsgid16_wrapper +sys32_setfsgid16_wrapper: + llgfr %r2,%r2 # __kernel_old_gid_emu31_t + jg sys32_setfsgid16 # branch to system call + + .globl sys32_llseek_wrapper +sys32_llseek_wrapper: + llgfr %r2,%r2 # unsigned int + lgfr %r3,%r3 # off_t + llgfr %r4,%r4 # unsigned int + jg sys_llseek # branch to system call + + .globl sys32_getdents_wrapper +sys32_getdents_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # void * + llgfr %r4,%r4 # unsigned int + jg sys32_getdents # branch to system call + + .globl sys32_select_wrapper +sys32_select_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # fd_set * + llgtr %r4,%r4 # fd_set * + llgtr %r5,%r5 # fd_set * + llgtr %r6,%r6 # struct timeval_emu31 * + jg sys32_select # branch to system call + + .globl sys32_flock_wrapper +sys32_flock_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned int + jg sys_flock # branch to system call + + .globl sys32_msync_wrapper +sys32_msync_wrapper: + llgfr %r2,%r2 # unsigned long + llgfr %r3,%r3 # size_t + lgfr %r4,%r4 # int + jg sys_msync # branch to system call + + .globl sys32_readv_wrapper +sys32_readv_wrapper: + llgfr %r2,%r2 # unsigned long + llgtr %r3,%r3 # const struct iovec_emu31 * + llgfr %r4,%r4 # unsigned long + jg sys32_readv # branch to system call + + .globl sys32_writev_wrapper +sys32_writev_wrapper: + llgfr %r2,%r2 # unsigned long + llgtr %r3,%r3 # const struct iovec_emu31 * + llgfr %r4,%r4 # unsigned long + jg sys32_writev # branch to system call + + .globl sys32_getsid_wrapper +sys32_getsid_wrapper: + lgfr %r2,%r2 # pid_t + jg sys_getsid # branch to system call + + .globl sys32_fdatasync_wrapper +sys32_fdatasync_wrapper: + llgfr %r2,%r2 # unsigned int + jg sys_fdatasync # branch to system call + +#sys32_sysctl_wrapper # tbd + + .globl sys32_mlock_wrapper +sys32_mlock_wrapper: + llgfr %r2,%r2 # unsigned long + llgfr %r3,%r3 # size_t + jg sys_mlock # branch to system call + + .globl sys32_munlock_wrapper +sys32_munlock_wrapper: + llgfr %r2,%r2 # unsigned long + llgfr %r3,%r3 # size_t + jg sys_munlock # branch to system call + + .globl sys32_mlockall_wrapper +sys32_mlockall_wrapper: + lgfr %r2,%r2 # int + jg sys_mlockall # branch to system call + +#sys32_munlockall_wrapper # void + + .globl sys32_sched_setparam_wrapper +sys32_sched_setparam_wrapper: + lgfr %r2,%r2 # pid_t + llgtr %r3,%r3 # struct sched_param * + jg sys_sched_setparam # branch to system call + + .globl sys32_sched_getparam_wrapper +sys32_sched_getparam_wrapper: + lgfr %r2,%r2 # pid_t + llgtr %r3,%r3 # struct sched_param * + jg sys_sched_getparam # branch to system call + + .globl sys32_sched_setscheduler_wrapper +sys32_sched_setscheduler_wrapper: + lgfr %r2,%r2 # pid_t + lgfr %r3,%r3 # int + llgtr %r4,%r4 # struct sched_param * + jg sys_sched_setscheduler # branch to system call + + .globl sys32_sched_getscheduler_wrapper +sys32_sched_getscheduler_wrapper: + lgfr %r2,%r2 # pid_t + jg sys_sched_getscheduler # branch to system call + +#sys32_sched_yield_wrapper # void + + .globl sys32_sched_get_priority_max_wrapper +sys32_sched_get_priority_max_wrapper: + lgfr %r2,%r2 # int + jg sys_sched_get_priority_max # branch to system call + + .globl sys32_sched_get_priority_min_wrapper +sys32_sched_get_priority_min_wrapper: + lgfr %r2,%r2 # int + jg sys_sched_get_priority_min # branch to system call + + .globl sys32_sched_rr_get_interval_wrapper +sys32_sched_rr_get_interval_wrapper: + lgfr %r2,%r2 # pid_t + llgtr %r3,%r3 # struct timespec_emu31 * + jg sys32_sched_rr_get_interval # branch to system call + + .globl sys32_nanosleep_wrapper +sys32_nanosleep_wrapper: + llgtr %r2,%r2 # struct timespec_emu31 * + llgtr %r3,%r3 # struct timespec_emu31 * + jg sys32_nanosleep # branch to system call + + .globl sys32_mremap_wrapper +sys32_mremap_wrapper: + llgfr %r2,%r2 # unsigned long + llgfr %r3,%r3 # unsigned long + llgfr %r4,%r4 # unsigned long + llgfr %r5,%r5 # unsigned long + llgfr %r6,%r6 # unsigned long + jg sys_mremap # branch to system call + + .globl sys32_setresuid16_wrapper +sys32_setresuid16_wrapper: + llgfr %r2,%r2 # __kernel_old_uid_emu31_t + llgfr %r3,%r3 # __kernel_old_uid_emu31_t + llgfr %r4,%r4 # __kernel_old_uid_emu31_t + jg sys32_setresuid16 # branch to system call + + .globl sys32_getresuid16_wrapper +sys32_getresuid16_wrapper: + llgtr %r2,%r2 # __kernel_old_uid_emu31_t * + llgtr %r3,%r3 # __kernel_old_uid_emu31_t * + llgtr %r4,%r4 # __kernel_old_uid_emu31_t * + jg sys32_getresuid16 # branch to system call + + .globl sys32_query_module_wrapper +sys32_query_module_wrapper: + llgtr %r2,%r2 # const char * + lgfr %r3,%r3 # int + llgtr %r4,%r4 # char * + llgfr %r5,%r5 # size_t + llgtr %r6,%r6 # size_t * + jg sys32_query_module # branch to system call + + .globl sys32_poll_wrapper +sys32_poll_wrapper: + llgtr %r2,%r2 # struct pollfd * + llgfr %r3,%r3 # unsigned int + lgfr %r4,%r4 # long + jg sys_poll # branch to system call + + .globl sys32_nfsservctl_wrapper +sys32_nfsservctl_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # struct nfsctl_arg_emu31 * + llgtr %r4,%r4 # union nfsctl_res_emu31 * + jg sys32_nfsservctl # branch to system call + + .globl sys32_setresgid16_wrapper +sys32_setresgid16_wrapper: + llgfr %r2,%r2 # __kernel_old_gid_emu31_t + llgfr %r3,%r3 # __kernel_old_gid_emu31_t + llgfr %r4,%r4 # __kernel_old_gid_emu31_t + jg sys32_setresgid16 # branch to system call + + .globl sys32_getresgid16_wrapper +sys32_getresgid16_wrapper: + llgtr %r2,%r2 # __kernel_old_gid_emu31_t * + llgtr %r3,%r3 # __kernel_old_gid_emu31_t * + llgtr %r4,%r4 # __kernel_old_gid_emu31_t * + jg sys32_getresgid16 # branch to system call + + .globl sys32_prctl_wrapper +sys32_prctl_wrapper: + lgfr %r2,%r2 # int + llgfr %r3,%r3 # unsigned long + llgfr %r4,%r4 # unsigned long + llgfr %r5,%r5 # unsigned long + llgfr %r6,%r6 # unsigned long + jg sys_prctl # branch to system call + +#sys32_rt_sigreturn_wrapper # done in rt_sigreturn_glue + + .globl sys32_rt_sigaction_wrapper +sys32_rt_sigaction_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # const struct sigaction_emu31 * + llgtr %r4,%r4 # const struct sigaction_emu31 * + llgfr %r5,%r5 # size_t + jg sys32_rt_sigaction # branch to system call + + .globl sys32_rt_sigprocmask_wrapper +sys32_rt_sigprocmask_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # old_sigset_emu31 * + llgtr %r4,%r4 # old_sigset_emu31 * + jg sys32_rt_sigprocmask # branch to system call + + .globl sys32_rt_sigpending_wrapper +sys32_rt_sigpending_wrapper: + llgtr %r2,%r2 # sigset_emu31 * + llgfr %r3,%r3 # size_t + jg sys32_rt_sigpending # branch to system call + + .globl sys32_rt_sigtimedwait_wrapper +sys32_rt_sigtimedwait_wrapper: + llgtr %r2,%r2 # const sigset_emu31_t * + llgtr %r3,%r3 # siginfo_emu31_t * + llgtr %r4,%r4 # const struct timespec_emu31 * + llgfr %r5,%r5 # size_t + jg sys32_rt_sigtimedwait # branch to system call + + .globl sys32_rt_sigqueueinfo_wrapper +sys32_rt_sigqueueinfo_wrapper: + lgfr %r2,%r2 # int + lgfr %r3,%r3 # int + llgtr %r4,%r4 # siginfo_emu31_t * + jg sys32_rt_sigqueueinfo # branch to system call + +#sys32_rt_sigsuspend_wrapper # done in rt_sigsuspend_glue + + .globl sys32_pread_wrapper +sys32_pread_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # char * + llgfr %r4,%r4 # size_t + llgfr %r5,%r5 # u32 + llgfr %r6,%r6 # u32 + jg sys32_pread # branch to system call + + .globl sys32_pwrite_wrapper +sys32_pwrite_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # const char * + llgfr %r4,%r4 # size_t + llgfr %r5,%r5 # u32 + llgfr %r6,%r6 # u32 + jg sys32_pwrite # branch to system call + + .globl sys32_chown16_wrapper +sys32_chown16_wrapper: + llgtr %r2,%r2 # const char * + llgfr %r3,%r3 # __kernel_old_uid_emu31_t + llgfr %r4,%r4 # __kernel_old_gid_emu31_t + jg sys32_chown16 # branch to system call + + .globl sys32_getcwd_wrapper +sys32_getcwd_wrapper: + llgtr %r2,%r2 # char * + llgfr %r3,%r3 # unsigned long + jg sys_getcwd # branch to system call + + .globl sys32_capget_wrapper +sys32_capget_wrapper: + llgtr %r2,%r2 # cap_user_header_t + llgtr %r3,%r3 # cap_user_data_t + jg sys_capget # branch to system call + + .globl sys32_capset_wrapper +sys32_capset_wrapper: + llgtr %r2,%r2 # cap_user_header_t + llgtr %r3,%r3 # const cap_user_data_t + jg sys_capset # branch to system call + + .globl sys32_sigaltstack_wrapper +sys32_sigaltstack_wrapper: + llgtr %r2,%r2 # const stack_emu31_t * + llgtr %r3,%r3 # stack_emu31_t * + jg sys32_sigaltstack + + .globl sys32_sendfile_wrapper +sys32_sendfile_wrapper: + lgfr %r2,%r2 # int + lgfr %r3,%r3 # int + llgtr %r4,%r4 # __kernel_off_emu31_t * + llgfr %r5,%r5 # size_t + jg sys32_sendfile # branch to system call + +#sys32_vfork_wrapper # done in vfork_glue + + .globl sys32_truncate64_wrapper +sys32_truncate64_wrapper: + llgtr %r2,%r2 # const char * + lgfr %r3,%r3 # s32 + llgfr %r4,%r4 # u32 + jg sys32_truncate64 # branch to system call + + .globl sys32_ftruncate64_wrapper +sys32_ftruncate64_wrapper: + llgfr %r2,%r2 # unsigned int + lgfr %r3,%r3 # s32 + llgfr %r4,%r4 # u32 + jg sys32_ftruncate64 # branch to system call + + .globl sys32_lchown_wrapper +sys32_lchown_wrapper: + llgtr %r2,%r2 # const char * + llgfr %r3,%r3 # uid_t + llgfr %r4,%r4 # gid_t + jg sys_lchown # branch to system call + +#sys32_getuid_wrapper # void +#sys32_getgid_wrapper # void +#sys32_geteuid_wrapper # void +#sys32_getegid_wrapper # void + + .globl sys32_setreuid_wrapper +sys32_setreuid_wrapper: + llgfr %r2,%r2 # uid_t + llgfr %r3,%r3 # uid_t + jg sys_setreuid # branch to system call + + .globl sys32_setregid_wrapper +sys32_setregid_wrapper: + llgfr %r2,%r2 # gid_t + llgfr %r3,%r3 # gid_t + jg sys_setregid # branch to system call + + .globl sys32_getgroups_wrapper +sys32_getgroups_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # gid_t * + jg sys_getgroups # branch to system call + + .globl sys32_setgroups_wrapper +sys32_setgroups_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # gid_t * + jg sys_setgroups # branch to system call + + .globl sys32_fchown_wrapper +sys32_fchown_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # uid_t + llgfr %r4,%r4 # gid_t + jg sys_fchown # branch to system call + + .globl sys32_setresuid_wrapper +sys32_setresuid_wrapper: + llgfr %r2,%r2 # uid_t + llgfr %r3,%r3 # uid_t + llgfr %r4,%r4 # uid_t + jg sys_setresuid # branch to system call + + .globl sys32_getresuid_wrapper +sys32_getresuid_wrapper: + llgtr %r2,%r2 # uid_t * + llgtr %r3,%r3 # uid_t * + llgtr %r4,%r4 # uid_t * + jg sys_getresuid # branch to system call + + .globl sys32_setresgid_wrapper +sys32_setresgid_wrapper: + llgfr %r2,%r2 # gid_t + llgfr %r3,%r3 # gid_t + llgfr %r4,%r4 # gid_t + jg sys_setresgid # branch to system call + + .globl sys32_getresgid_wrapper +sys32_getresgid_wrapper: + llgtr %r2,%r2 # gid_t * + llgtr %r3,%r3 # gid_t * + llgtr %r4,%r4 # gid_t * + jg sys_getresgid # branch to system call + + .globl sys32_chown_wrapper +sys32_chown_wrapper: + llgtr %r2,%r2 # const char * + llgfr %r3,%r3 # uid_t + llgfr %r4,%r4 # gid_t + jg sys_chown # branch to system call + + .globl sys32_setuid_wrapper +sys32_setuid_wrapper: + llgfr %r2,%r2 # uid_t + jg sys_setuid # branch to system call + + .globl sys32_setgid_wrapper +sys32_setgid_wrapper: + llgfr %r2,%r2 # gid_t + jg sys_setgid # branch to system call + + .globl sys32_setfsuid_wrapper +sys32_setfsuid_wrapper: + llgfr %r2,%r2 # uid_t + jg sys_setfsuid # branch to system call + + .globl sys32_setfsgid_wrapper +sys32_setfsgid_wrapper: + llgfr %r2,%r2 # gid_t + jg sys_setfsgid # branch to system call + + .globl sys32_pivot_root_wrapper +sys32_pivot_root_wrapper: + llgtr %r2,%r2 # const char * + llgtr %r3,%r3 # const char * + jg sys_pivot_root # branch to system call + + .globl sys32_mincore_wrapper +sys32_mincore_wrapper: + llgfr %r2,%r2 # unsigned long + llgfr %r3,%r3 # size_t + llgtr %r4,%r4 # unsigned char * + jg sys_mincore # branch to system call + + .globl sys32_madvise_wrapper +sys32_madvise_wrapper: + llgfr %r2,%r2 # unsigned long + llgfr %r3,%r3 # size_t + lgfr %r4,%r4 # int + jg sys_madvise # branch to system call + + .globl sys32_getdents64_wrapper +sys32_getdents64_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # void * + llgfr %r4,%r4 # unsigned int + jg sys_getdents64 # branch to system call + + .globl sys32_fcntl64_wrapper +sys32_fcntl64_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned int + llgfr %r4,%r4 # unsigned long + jg sys32_fcntl64 # branch to system call + diff --git a/arch/s390x/lib/Makefile b/arch/s390x/lib/Makefile new file mode 100644 index 000000000..30ee0f662 --- /dev/null +++ b/arch/s390x/lib/Makefile @@ -0,0 +1,18 @@ +# +# Makefile for s390-specific library files.. +# + +ifdef SMP +.S.o: + $(CC) -D__ASSEMBLY__ $(AFLAGS) -traditional -c $< -o $*.o +else +.S.o: + $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o +endif + +L_TARGET = lib.a + +obj-y = checksum.o delay.o memset.o strcmp.o strncpy.o uaccess.o + +include $(TOPDIR)/Rules.make + diff --git a/arch/s390x/lib/checksum.c b/arch/s390x/lib/checksum.c new file mode 100644 index 000000000..489299b3a --- /dev/null +++ b/arch/s390x/lib/checksum.c @@ -0,0 +1,40 @@ +/* + * arch/s390/lib/checksum.c + * S390 fast network checksum routines + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Ulrich Hild (first version), + * Martin Schwidefsky (schwidefsky@de.ibm.com), + * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com), + * + * This file contains network checksum routines + */ + +#include <linux/string.h> +#include <linux/types.h> +#include <asm/uaccess.h> +#include <asm/byteorder.h> +#include <asm/checksum.h> + +/* + * computes a partial checksum, e.g. for TCP/UDP fragments + */ +unsigned int +csum_partial (const unsigned char *buff, int len, unsigned int sum) +{ + /* + * Experiments with ethernet and slip connections show that buff + * is aligned on either a 2-byte or 4-byte boundary. + */ + __asm__ __volatile__ ( + " lgr 2,%1\n" /* address in gpr 2 */ + " lgfr 3,%2\n" /* length in gpr 3 */ + "0: cksm %0,2\n" /* do checksum on longs */ + " jo 0b\n" + : "+&d" (sum) + : "d" (buff), "d" (len) + : "cc", "2", "3" ); + return sum; +} + diff --git a/arch/s390x/lib/delay.c b/arch/s390x/lib/delay.c new file mode 100644 index 000000000..012a95308 --- /dev/null +++ b/arch/s390x/lib/delay.c @@ -0,0 +1,50 @@ +/* + * arch/s390/kernel/delay.c + * Precise Delay Loops for S390 + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * Derived from "arch/i386/lib/delay.c" + * Copyright (C) 1993 Linus Torvalds + * Copyright (C) 1997 Martin Mares <mj@atrey.karlin.mff.cuni.cz> + */ + +#include <linux/config.h> +#include <linux/sched.h> +#include <linux/delay.h> + +#ifdef CONFIG_SMP +#include <asm/smp.h> +#endif + +void __delay(unsigned long loops) +{ + /* + * To end the bloody studid and useless discussion about the + * BogoMips number I took the liberty to define the __delay + * function in a way that that resulting BogoMips number will + * yield the megahertz number of the cpu. The important function + * is udelay and that is done using the tod clock. -- martin. + */ + __asm__ __volatile__( + "0: brct %0,0b" + : /* no outputs */ : "r" (loops/2) ); +} + +/* + * Waits for 'usecs' microseconds using the tod clock + */ +void __udelay(unsigned long usecs) +{ + uint64_t start_cc, end_cc; + + if (usecs == 0) + return; + asm volatile ("STCK %0" : "=m" (start_cc)); + do { + asm volatile ("STCK %0" : "=m" (end_cc)); + } while (((end_cc - start_cc)/4096) < usecs); +} + diff --git a/arch/s390x/lib/memset.S b/arch/s390x/lib/memset.S new file mode 100644 index 000000000..1e4b035d2 --- /dev/null +++ b/arch/s390x/lib/memset.S @@ -0,0 +1,30 @@ +/* + * arch/s390/lib/memset.S + * S390 fast memset routine + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address to memory area + * R3 = byte to fill memory with + * R4 = number of bytes to fill + */ + .globl memset +memset: + LTGR 4,4 + JZ memset_end + LGR 0,2 # save pointer to memory area + LGR 1,3 # move pad byte to R1 + LGR 3,4 + SGR 4,4 # no source for MVCLE, only a pad byte + SGR 5,5 + MVCLE 2,4,0(1) # thats it, MVCLE is your friend + JO .-4 + LGR 2,0 # return pointer to mem. +memset_end: + BR 14 + + diff --git a/arch/s390x/lib/strcmp.S b/arch/s390x/lib/strcmp.S new file mode 100644 index 000000000..124f3df26 --- /dev/null +++ b/arch/s390x/lib/strcmp.S @@ -0,0 +1,27 @@ +/* + * arch/s390/lib/strcmp.S + * S390 strcmp routine + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address of compare string + * R3 = address of test string + */ + .globl strcmp +strcmp: + SGR 0,0 + SGR 1,1 + CLST 2,3 + JO .-4 + JE strcmp_equal + IC 0,0(3) + IC 1,0(2) + SGR 1,0 +strcmp_equal: + LGR 2,1 + BR 14 + diff --git a/arch/s390x/lib/strncpy.S b/arch/s390x/lib/strncpy.S new file mode 100644 index 000000000..0360a38c3 --- /dev/null +++ b/arch/s390x/lib/strncpy.S @@ -0,0 +1,30 @@ +/* + * arch/s390/kernel/strncpy.S + * S390 strncpy routine + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + */ + +/* + * R2 = address of destination + * R3 = address of source string + * R4 = max number of bytes to copy + */ + .globl strncpy +strncpy: + LGR 1,2 # don't touch address in R2 + LTR 4,4 + JZ strncpy_exit # 0 bytes -> nothing to do + SGR 0,0 +strncpy_loop: + ICM 0,1,0(3) # ICM sets the cc, IC does not + LA 3,1(3) + STC 0,0(1) + LA 1,1(1) + JZ strncpy_exit # ICM inserted a 0x00 + BRCTG 4,strncpy_loop # R4 -= 1, jump to strncpy_loop if > 0 +strncpy_exit: + BR 14 + diff --git a/arch/s390x/lib/uaccess.S b/arch/s390x/lib/uaccess.S new file mode 100644 index 000000000..2a5356c19 --- /dev/null +++ b/arch/s390x/lib/uaccess.S @@ -0,0 +1,45 @@ +/* + * arch/s390x/lib/uaccess.S + * fixup routines for copy_{from|to}_user functions. + * + * S390 + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * These functions have a non-standard call interface + */ + +#include <asm/lowcore.h> + + .text + .align 4 + .globl __copy_from_user_fixup +__copy_from_user_fixup: + lg 1,__LC_PGM_OLD_PSW+8 +0: lghi 3,-4096 + ng 3,__LC_TRANS_EXC_ADDR + sgr 3,4 + bm 4(1) +1: mvcle 2,4,0 + b 4(1) + .section __ex_table,"a" + .align 8 + .quad 1b,0b + .previous + + .align 4 + .text + .globl __copy_to_user_fixup +__copy_to_user_fixup: + lg 1,__LC_PGM_OLD_PSW+8 +0: lghi 5,-4096 + ng 5,__LC_TRANS_EXC_ADDR + sgr 5,4 + bm 4(1) +1: mvcle 4,2,0 + b 4(1) + .section __ex_table,"a" + .align 8 + .quad 1b,0b + .previous + diff --git a/arch/s390x/mm/Makefile b/arch/s390x/mm/Makefile new file mode 100644 index 000000000..73e25bd30 --- /dev/null +++ b/arch/s390x/mm/Makefile @@ -0,0 +1,14 @@ +# +# Makefile for the linux i386-specific parts of the memory manager. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definition is now in the main makefile... + +O_TARGET := mm.o + +obj-y := init.o fault.o ioremap.o extable.o + +include $(TOPDIR)/Rules.make diff --git a/arch/s390x/mm/extable.c b/arch/s390x/mm/extable.c new file mode 100644 index 000000000..774e86854 --- /dev/null +++ b/arch/s390x/mm/extable.c @@ -0,0 +1,61 @@ +/* + * arch/s390/mm/extable.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/extable.c" + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <asm/uaccess.h> + +extern const struct exception_table_entry __start___ex_table[]; +extern const struct exception_table_entry __stop___ex_table[]; + +static inline unsigned long +search_one_table(const struct exception_table_entry *first, + const struct exception_table_entry *last, + unsigned long value) +{ + while (first <= last) { + const struct exception_table_entry *mid; + long diff; + + mid = (last - first) / 2 + first; + diff = mid->insn - value; + if (diff == 0) + return mid->fixup; + else if (diff < 0) + first = mid+1; + else + last = mid-1; + } + return 0; +} + +unsigned long +search_exception_table(unsigned long addr) +{ + unsigned long ret; + +#ifndef CONFIG_MODULES + /* There is only the kernel to search. */ + ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr); + if (ret) return FIX_PSW(ret); +#else + /* The kernel is the last "module" -- no need to treat it special. */ + struct module *mp; + for (mp = module_list; mp != NULL; mp = mp->next) { + if (mp->ex_table_start == NULL) + continue; + ret = search_one_table(mp->ex_table_start, + mp->ex_table_end - 1, addr); + if (ret) return FIX_PSW(ret); + } +#endif + + return 0; +} diff --git a/arch/s390x/mm/fault.c b/arch/s390x/mm/fault.c new file mode 100644 index 000000000..4c324b690 --- /dev/null +++ b/arch/s390x/mm/fault.c @@ -0,0 +1,266 @@ +/* + * arch/s390/mm/fault.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/fault.c" + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/hardirq.h> + +#ifdef CONFIG_SYSCTL +extern int sysctl_userprocess_debug; +#endif + +extern void die(const char *,struct pt_regs *,long); + +/* + * This routine handles page faults. It determines the address, + * and the problem, and then passes it off to one of the appropriate + * routines. + * + * error_code: + * ****0004 Protection -> Write-Protection (suprression) + * ****0010 Segment translation -> Not present (nullification) + * ****0011 Page translation -> Not present (nullification) + * ****003B Region third exception -> Not present (nullification) + */ +asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) +{ + struct task_struct *tsk; + struct mm_struct *mm; + struct vm_area_struct * vma; + unsigned long address; + unsigned long fixup; + int write; + unsigned long psw_mask; + unsigned long psw_addr; + int si_code = SEGV_MAPERR; + int kernel_address = 0; + + /* + * get psw mask of Program old psw to find out, + * if user or kernel mode + */ + + psw_mask = S390_lowcore.program_old_psw.mask; + psw_addr = S390_lowcore.program_old_psw.addr; + + /* + * get the failing address + * more specific the segment and page table portion of + * the address + */ + + address = S390_lowcore.trans_exc_code&-4096L; + + tsk = current; + mm = tsk->mm; + + if (in_interrupt() || !mm) + goto no_context; + + /* + * Check which address space the address belongs to + */ + switch (S390_lowcore.trans_exc_code & 3) + { + case 0: /* Primary Segment Table Descriptor */ + kernel_address = 1; + goto no_context; + + case 1: /* STD determined via access register */ + if (S390_lowcore.exc_access_id == 0) + { + kernel_address = 1; + goto no_context; + } + if (regs && S390_lowcore.exc_access_id < NUM_ACRS) + { + if (regs->acrs[S390_lowcore.exc_access_id] == 0) + { + kernel_address = 1; + goto no_context; + } + if (regs->acrs[S390_lowcore.exc_access_id] == 1) + { + /* user space address */ + break; + } + } + die("page fault via unknown access register", regs, error_code); + break; + + case 2: /* Secondary Segment Table Descriptor */ + case 3: /* Home Segment Table Descriptor */ + /* user space address */ + break; + } + + + /* + * When we get here, the fault happened in the current + * task's user address space, so we search the VMAs + */ + + down(&mm->mmap_sem); + + vma = find_vma(mm, address); + if (!vma) { + printk("no vma for address %lX\n",address); + goto bad_area; + } + if (vma->vm_start <= address) + goto good_area; + if (!(vma->vm_flags & VM_GROWSDOWN)) + goto bad_area; + if (expand_stack(vma, address)) + goto bad_area; +/* + * Ok, we have a good vm_area for this memory access, so + * we can handle it.. + */ +good_area: + write = 0; + si_code = SEGV_ACCERR; + + switch (error_code & 0xFF) { + case 0x04: /* write, present*/ + write = 1; + break; + case 0x10: /* not present*/ + case 0x11: /* not present*/ + case 0x3B: /* not present*/ + if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) + goto bad_area; + break; + default: + printk("code should be 4, 10 or 11 (%lX) \n",error_code&0xFF); + goto bad_area; + } + + /* + * If for any reason at all we couldn't handle the fault, + * make sure we exit gracefully rather than endlessly redo + * the fault. + */ + switch (handle_mm_fault(mm, vma, address, write)) { + case 1: + tsk->min_flt++; + break; + case 2: + tsk->maj_flt++; + break; + case 0: + goto do_sigbus; + default: + goto out_of_memory; + } + + up(&mm->mmap_sem); + return; + +/* + * Something tried to access memory that isn't in our memory map.. + * Fix it, but check if it's kernel or user first.. + */ +bad_area: + up(&mm->mmap_sem); + + /* User mode accesses just cause a SIGSEGV */ + if (psw_mask & PSW_PROBLEM_STATE) { + struct siginfo si; + tsk->thread.prot_addr = address; + tsk->thread.trap_no = error_code; +#ifndef CONFIG_SYSCTL +#ifdef CONFIG_PROCESS_DEBUG + printk("User process fault: interruption code 0x%lX\n",error_code); + printk("failing address: %lX\n",address); + show_regs(regs); +#endif +#else + if (sysctl_userprocess_debug) { + printk("User process fault: interruption code 0x%lX\n", + error_code); + printk("failing address: %lX\n", address); + show_regs(regs); + } +#endif + si.si_signo = SIGSEGV; + si.si_code = si_code; + si.si_addr = (void*) address; + force_sig_info(SIGSEGV, &si, tsk); + return; + } + +no_context: + /* Are we prepared to handle this kernel fault? */ + if ((fixup = search_exception_table(regs->psw.addr)) != 0) { + regs->psw.addr = fixup; + return; + } + +/* + * Oops. The kernel tried to access some bad page. We'll have to + * terminate things with extreme prejudice. + */ + if (kernel_address) + printk(KERN_ALERT "Unable to handle kernel pointer dereference" + " at virtual kernel address %016lx\n", address); + else + printk(KERN_ALERT "Unable to handle kernel paging request" + " at virtual user address %016lx\n", address); + +/* + * need to define, which information is useful here + */ + + die("Oops", regs, error_code); + do_exit(SIGKILL); + + +/* + * We ran out of memory, or some other thing happened to us that made + * us unable to handle the page fault gracefully. +*/ +out_of_memory: + up(&mm->mmap_sem); + printk("VM: killing process %s\n", tsk->comm); + if (psw_mask & PSW_PROBLEM_STATE) + do_exit(SIGKILL); + goto no_context; + +do_sigbus: + up(&mm->mmap_sem); + + /* + * Send a sigbus, regardless of whether we were in kernel + * or user mode. + */ + tsk->thread.prot_addr = address; + tsk->thread.trap_no = error_code; + force_sig(SIGBUS, tsk); + + /* Kernel mode? Handle exceptions or die */ + if (!(psw_mask & PSW_PROBLEM_STATE)) + goto no_context; +} diff --git a/arch/s390x/mm/init.c b/arch/s390x/mm/init.c new file mode 100644 index 000000000..91d66ccfe --- /dev/null +++ b/arch/s390x/mm/init.c @@ -0,0 +1,405 @@ +/* + * arch/s390/mm/init.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hpenner@de.ibm.com) + * + * Derived from "arch/i386/mm/init.c" + * Copyright (C) 1995 Linus Torvalds + */ + +#include <linux/config.h> +#include <linux/signal.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/ptrace.h> +#include <linux/mman.h> +#include <linux/mm.h> +#include <linux/swap.h> +#include <linux/smp.h> +#include <linux/init.h> +#ifdef CONFIG_BLK_DEV_INITRD +#include <linux/blk.h> +#endif +#include <linux/pagemap.h> +#include <linux/bootmem.h> + +#include <asm/processor.h> +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/pgalloc.h> +#include <asm/dma.h> +#include <asm/lowcore.h> + +static unsigned long totalram_pages; + +/* + * empty_bad_page is the page that is used for page faults when linux + * is out-of-memory. Older versions of linux just did a + * do_exit(), but using this instead means there is less risk + * for a process dying in kernel mode, possibly leaving an inode + * unused etc.. + * + * empty_bad_pte_table is the accompanying page-table: it is initialized + * to point to BAD_PAGE entries. + * + * empty_bad_pmd_table is the accompanying segment table: it is initialized + * to point to empty_bad_pte_table page tables. + * + * ZERO_PAGE is a special page that is used for zero-initialized + * data and COW. + */ + +pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE))); +char empty_bad_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +char empty_zero_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); +pmd_t empty_bad_pmd_table[PTRS_PER_PMD] __attribute__((__aligned__(PAGE_SIZE))); +pte_t empty_bad_pte_table[PTRS_PER_PTE] __attribute__((__aligned__(PAGE_SIZE))); + +static int test_access(unsigned long loc) +{ + static const int ssm_mask = 0x07000000L; + int rc, i; + + rc = 0; + for (i=0; i<2; i++) { + __asm__ __volatile__( + " slgr %0,%0\n" + " ssm %1\n" + " tprot 0(%2),0\n" + "0: jne 1f\n" + " lghi %0,1\n" + "1: ssm %3\n" + ".section __ex_table,\"a\"\n" + " .align 8\n" + " .quad 0b,1b\n" + ".previous" + : "+&d" (rc) : "i" (0), "a" (loc), "m" (ssm_mask) + : "cc"); + if (rc == 0) + break; + loc += 0x100000; + } + return rc; +} + +static pmd_t *get_bad_pmd_table(void) +{ + pmd_t v; + int i; + + pmd_set(&v, empty_bad_pte_table); + + for (i = 0; i < PTRS_PER_PMD; i++) + empty_bad_pmd_table[i] = v; + + return empty_bad_pmd_table; +} + +static pte_t *get_bad_pte_table(void) +{ + pte_t v; + int i; + + v = pte_mkdirty(mk_pte_phys(__pa(empty_bad_page), PAGE_SHARED)); + + for (i = 0; i < PAGE_SIZE/sizeof(pte_t); i++) + empty_bad_pte_table[i] = v; + + return empty_bad_pte_table; +} + +pmd_t * +get_pmd_slow(pgd_t *pgd, unsigned long offset) +{ + pmd_t *pmd; + int i; + + pmd = (pmd_t *) __get_free_pages(GFP_KERNEL,2); + if (pgd_none(*pgd)) { + if (pmd) { + for (i = 0; i < PTRS_PER_PMD; i++) + pmd_clear(pmd+i); + pgd_set(pgd, pmd); + return pmd + offset; + } + pmd = (pmd_t *) get_bad_pmd_table(); + pgd_set(pgd, pmd); + return NULL; + } + free_pages((unsigned long)pmd,2); + if (pgd_bad(*pgd)) + BUG(); + return (pmd_t *) pgd_page(*pgd) + offset; +} + +pte_t *get_pte_slow(pmd_t *pmd, unsigned long offset) +{ + pte_t *pte; + int i; + + pte = (pte_t*) __get_free_page(GFP_KERNEL); + if (pmd_none(*pmd)) { + if (pte) { + for (i=0;i<PTRS_PER_PTE;i++) + pte_clear(pte+i); + pmd_set(pmd,pte); + return pte + offset; + } + pte = (pte_t*) get_bad_pte_table(); + pmd_set(pmd,pte); + return NULL; + } + free_page(__pa(pte)); + if (pmd_bad(*pmd)) + BUG(); + return (pte_t *) pmd_page(*pmd) + offset; +} + +int do_check_pgt_cache(int low, int high) +{ + int freed = 0; + if(pgtable_cache_size > high) { + do { + if(pgd_quicklist) + free_pgd_slow(get_pgd_fast()), freed += 4; + if(pmd_quicklist) + free_pmd_slow(get_pmd_fast()), freed += 4; + if(pte_quicklist) + free_pte_slow(get_pte_fast()), freed++; + } while(pgtable_cache_size > low); + } + return freed; +} + +void show_mem(void) +{ + int i, total = 0,reserved = 0; + int shared = 0, cached = 0; + + printk("Mem-info:\n"); + show_free_areas(); + printk("Free swap: %6dkB\n",nr_swap_pages<<(PAGE_SHIFT-10)); + i = max_mapnr; + while (i-- > 0) { + total++; + if (PageReserved(mem_map+i)) + reserved++; + else if (PageSwapCache(mem_map+i)) + cached++; + else if (page_count(mem_map+i)) + shared += atomic_read(&mem_map[i].count) - 1; + } + printk("%d pages of RAM\n",total); + printk("%d reserved pages\n",reserved); + printk("%d pages shared\n",shared); + printk("%d pages swap cached\n",cached); + printk("%ld pages in page table cache\n",pgtable_cache_size); + show_buffers(); +} + +/* References to section boundaries */ + +extern unsigned long _text; +extern unsigned long _etext; +extern unsigned long _edata; +extern unsigned long __bss_start; +extern unsigned long _end; + +extern unsigned long __init_begin; +extern unsigned long __init_end; + +/* + * paging_init() sets up the page tables + */ + +unsigned long last_valid_pfn; + +void __init paging_init(void) +{ + pgd_t * pg_dir; + pmd_t * pm_dir; + pte_t * pt_dir; + pte_t pte; + int i,j,k; + unsigned long address=0; + unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | + _REGION_TABLE; + unsigned long end_mem = (unsigned long) __va(max_low_pfn*PAGE_SIZE); + static const int ssm_mask = 0x04000000L; + + unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0}; + unsigned long dma_pfn, high_pfn; + + dma_pfn = MAX_DMA_ADDRESS >> PAGE_SHIFT; + high_pfn = max_low_pfn; + + if (dma_pfn > high_pfn) + zones_size[ZONE_DMA] = high_pfn; + else { + zones_size[ZONE_DMA] = dma_pfn; + zones_size[ZONE_NORMAL] = high_pfn - dma_pfn; + } + + /* Initialize mem_map[]. */ + free_area_init(zones_size); + + + /* + * map whole physical memory to virtual memory (identity mapping) + */ + + pg_dir = swapper_pg_dir; + + for (i = 0 ; i < PTRS_PER_PGD ; i++,pg_dir++) { + + if (address >= end_mem) { + pgd_clear(pg_dir); + continue; + } + + pm_dir = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE*4); + pgd_set(pg_dir,pm_dir); + + for (j = 0 ; j < PTRS_PER_PMD ; j++,pm_dir++) { + if (address >= end_mem) { + pmd_clear(pm_dir); + continue; + } + + pt_dir = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE); + pmd_set(pm_dir,pt_dir); + + for (k = 0 ; k < PTRS_PER_PTE ; k++,pt_dir++) { + pte = mk_pte_phys(address, PAGE_KERNEL); + if (address >= end_mem) { + pte_clear(&pte); + continue; + } + set_pte(pt_dir, pte); + address += PAGE_SIZE; + } + } + } + + /* enable virtual mapping in kernel mode */ + __asm__ __volatile__("lctlg 1,1,%0\n\t" + "lctlg 7,7,%0\n\t" + "lctlg 13,13,%0\n\t" + "ssm %1" + : :"m" (pgdir_k), "m" (ssm_mask)); + + local_flush_tlb(); + + return; +} + +void __init mem_init(void) +{ + unsigned long codesize, reservedpages, datasize, initsize; + unsigned long tmp; + + max_mapnr = num_physpages = max_low_pfn; + high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); + + /* clear the zero-page */ + memset(empty_zero_page, 0, PAGE_SIZE); + + /* this will put all low memory onto the freelists */ + totalram_pages += free_all_bootmem(); + + /* mark usable pages in the mem_map[] and count reserved pages */ + reservedpages = 0; + tmp = 0; + do { + if (tmp && (tmp & 0x1ff) == 0 && + test_access(tmp * PAGE_SIZE) == 0) { + printk("2M Segment 0x%016lX not available\n", + tmp * PAGE_SIZE); + do { + set_bit(PG_reserved, &mem_map[tmp].flags); + reservedpages++; + tmp++; + } while (tmp < max_low_pfn && (tmp & 0x1ff)); + } else { + if (PageReserved(mem_map+tmp)) + reservedpages++; + tmp++; + } + } while (tmp < max_low_pfn); + + codesize = (unsigned long) &_etext - (unsigned long) &_text; + datasize = (unsigned long) &_edata - (unsigned long) &_etext; + initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; + printk("Memory: %luk/%luk available (%ldk kernel code, %ldk reserved, %ldk data, %ldk init)\n", + (unsigned long) nr_free_pages() << (PAGE_SHIFT-10), + max_mapnr << (PAGE_SHIFT-10), + codesize >> 10, + reservedpages << (PAGE_SHIFT-10), + datasize >>10, + initsize >> 10); +} + +void free_initmem(void) +{ + unsigned long addr; + + addr = (unsigned long)(&__init_begin); + for (; addr < (unsigned long)(&__init_end); addr += PAGE_SIZE) { + ClearPageReserved(virt_to_page(addr)); + set_page_count(virt_to_page(addr), 1); + free_page(addr); + totalram_pages++; + } + printk ("Freeing unused kernel memory: %ldk freed\n", + (&__init_end - &__init_begin) >> 10); +} + +#ifdef CONFIG_BLK_DEV_INITRD +void free_initrd_mem(unsigned long start, unsigned long end) +{ + if (start < end) + printk ("Freeing initrd memory: %ldk freed\n", (end - start) >> 10); + for (; start < end; start += PAGE_SIZE) { + ClearPageReserved(virt_to_page(start)); + set_page_count(virt_to_page(start), 1); + free_page(start); + totalram_pages++; + } +} +#endif + +void si_meminfo(struct sysinfo *val) +{ + val->totalram = totalram_pages; + val->sharedram = 0; + val->freeram = nr_free_pages(); + val->bufferram = atomic_read(&buffermem_pages); + val->totalhigh = 0; + val->freehigh = 0; + val->mem_unit = PAGE_SIZE; +} + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * Emacs will notice this stuff at the end of the file and automatically + * adjust the settings for this buffer only. This must remain at the end + * of the file. + * --------------------------------------------------------------------------- + * Local variables: + * c-indent-level: 4 + * c-brace-imaginary-offset: 0 + * c-brace-offset: -4 + * c-argdecl-indent: 4 + * c-label-offset: -4 + * c-continued-statement-offset: 4 + * c-continued-brace-offset: 0 + * indent-tabs-mode: nil + * tab-width: 8 + * End: + */ diff --git a/arch/s390x/mm/ioremap.c b/arch/s390x/mm/ioremap.c new file mode 100644 index 000000000..38acc4a22 --- /dev/null +++ b/arch/s390x/mm/ioremap.c @@ -0,0 +1,129 @@ +/* + * arch/s390/mm/ioremap.c + * + * S390 version + * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Hartmut Penner (hp@de.ibm.com) + * + * Derived from "arch/i386/mm/extable.c" + * (C) Copyright 1995 1996 Linus Torvalds + * + * Re-map IO memory to kernel address space so that we can access it. + * This is needed for high PCI addresses that aren't mapped in the + * 640k-1MB IO memory area on PC's + */ + +#include <linux/vmalloc.h> +#include <asm/io.h> +#include <asm/pgalloc.h> + +static inline void remap_area_pte(pte_t * pte, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PMD_MASK; + end = address + size; + if (end > PMD_SIZE) + end = PMD_SIZE; + if (address >= end) + BUG(); + do { + if (!pte_none(*pte)) { + printk("remap_area_pte: page already exists\n"); + BUG(); + } + set_pte(pte, mk_pte_phys(phys_addr, + __pgprot(_PAGE_PRESENT | flags))); + address += PAGE_SIZE; + phys_addr += PAGE_SIZE; + pte++; + } while (address && (address < end)); +} + +static inline int remap_area_pmd(pmd_t * pmd, unsigned long address, unsigned long size, + unsigned long phys_addr, unsigned long flags) +{ + unsigned long end; + + address &= ~PGDIR_MASK; + end = address + size; + if (end > PGDIR_SIZE) + end = PGDIR_SIZE; + phys_addr -= address; + if (address >= end) + BUG(); + do { + pte_t * pte = pte_alloc_kernel(pmd, address); + if (!pte) + return -ENOMEM; + remap_area_pte(pte, address, end - address, address + phys_addr, flags); + address = (address + PMD_SIZE) & PMD_MASK; + pmd++; + } while (address && (address < end)); + return 0; +} + +static int remap_area_pages(unsigned long address, unsigned long phys_addr, + unsigned long size, unsigned long flags) +{ + pgd_t * dir; + unsigned long end = address + size; + + phys_addr -= address; + dir = pgd_offset(&init_mm, address); + flush_cache_all(); + if (address >= end) + BUG(); + do { + pmd_t *pmd = pmd_alloc_kernel(dir, address); + if (!pmd) + return -ENOMEM; + if (remap_area_pmd(pmd, address, end - address, + phys_addr + address, flags)) + return -ENOMEM; + set_pgdir(address, *dir); + address = (address + PGDIR_SIZE) & PGDIR_MASK; + dir++; + } while (address && (address < end)); + flush_tlb_all(); + return 0; +} + +/* + * Generic mapping function (not visible outside): + */ + +/* + * Remap an arbitrary physical address space into the kernel virtual + * address space. Needed when the kernel wants to access high addresses + * directly. + */ +void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) +{ + void * addr; + struct vm_struct * area; + + if (phys_addr < virt_to_phys(high_memory)) + return phys_to_virt(phys_addr); + if (phys_addr & ~PAGE_MASK) + return NULL; + size = PAGE_ALIGN(size); + if (!size || size > phys_addr + size) + return NULL; + area = get_vm_area(size, VM_IOREMAP); + if (!area) + return NULL; + addr = area->addr; + if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) { + vfree(addr); + return NULL; + } + return addr; +} + +void iounmap(void *addr) +{ + if (addr > high_memory) + return vfree(addr); +} diff --git a/arch/s390x/tools/dasdfmt/Makefile b/arch/s390x/tools/dasdfmt/Makefile new file mode 100644 index 000000000..b60641bbc --- /dev/null +++ b/arch/s390x/tools/dasdfmt/Makefile @@ -0,0 +1,9 @@ +all: dasdfmt + +dasdfmt: dasdfmt.c + $(CC) -o $@ $^ + $(STRIP) $@ + +clean: + rm -f dasdfmt + diff --git a/arch/s390x/tools/dasdfmt/dasdfmt.8 b/arch/s390x/tools/dasdfmt/dasdfmt.8 new file mode 100644 index 000000000..9e6a4e89e --- /dev/null +++ b/arch/s390x/tools/dasdfmt/dasdfmt.8 @@ -0,0 +1,68 @@ +.TH DASDFMT 8 "Tue Jan 25 2000" +.UC 4 +.SH NAME +dasdfmt \- formatting of DSAD (ECKD) disk drives. +.SH SYNOPSIS +\fBdasdfmt\fR [-tvyLV] [-b \fIblockSize\fR] [-l \fIdiskLabel\fR] \fIdiskSpec\fR +.SH DESCRIPTION +\fBdasdfmt\fR formats a DASD (ECKD) disk drive to prepare it +for usage with Linux for S/390. \fBWARNING\fR: Incautious usage of +\fBdasdfmt\fR can result in \fBLOSS OF DATA\fR. + +.SH OPTIONS +.TP +\fB-t\fR +Disables any modification of the disk drive. \fBdasdfmt\fR just prints +out, what it \fBwould\fR do. + +.TP +\fB-v\fR +Increases verbosity. + +.TP +\fB-y\fR +Start formatting without further user-confirmation. + +.TP +\fB-L\fR +Omit the writing of a disk label after formatting. + +.TP +\fB-V\fR +Print version number and exit. + +.TP +\fB-b\fR \fIblockSize\fR +Specify blocksize to be used. \fIblocksize\fR must be a positive integer +and always be a power of two. Due due some limitations in the driver, +it is \fBstrongly\fR recommended to use a \fIblockSize\fR of \fI4096\fR. + +.TP +\fB-l\fR \fIdiskLabel\fR +Specify the label to be written to disk after formatting. If no label is +specified, a sensible default is used. \fIdiskLabel\fR is interpreted as +ASCII string and is automatically converted to EBCDIC. + +.TP +\fIdiskSpec\fR +This parameter specified the device to be formatted. It also can be +given in two variants: +.sp + \fB-f\fR \fB/dev/dasd\fR\fIX\fR +.br +or +.br + \fB-n\fR \fIdevnum\fR +.sp +The first form uses the commonly used +.SM UNIX +device notation where \fIX\fR is a single lowercase letter. +The second form uses simply the device number. + +.SH BUGS +None so far ;-) + +.SH AUTHOR +.nf +This man-page was written by Fritz Elfert <felfert@to.com> +.fi diff --git a/arch/s390x/tools/dasdfmt/dasdfmt.c b/arch/s390x/tools/dasdfmt/dasdfmt.c new file mode 100644 index 000000000..2820fc91d --- /dev/null +++ b/arch/s390x/tools/dasdfmt/dasdfmt.c @@ -0,0 +1,830 @@ +/* + * + * dasdfmt.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Corporation + * Author(s): Utz Bacher, <utz.bacher@de.ibm.com> + * + * Device-in-use-checks by Fritz Elfert, <felfert@to.com> + * + * Still to do: + * detect non-switch parameters ("dasdfmt -n 170 XY") and complain about them + */ + +/* #define _LINUX_BLKDEV_H */ + +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <getopt.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#include <mntent.h> +#define __KERNEL__ /* we want to use kdev_t and not have to define it */ +#include <linux/kdev_t.h> +#undef __KERNEL__ + +#include <linux/fs.h> +#include <asm/dasd.h> +#include <linux/hdreg.h> + +#define EXIT_MISUSE 1 +#define EXIT_BUSY 2 +#define TEMPFILENAME "/tmp/ddfXXXXXX" +#define TEMPFILENAMECHARS 8 /* 8 characters are fixed in all temp filenames */ +#define SLASHDEV "/dev/" +#define PROC_DASD_DEVICES "/proc/dasd/devices" +/* _PATH_MOUNTED is /etc/mtab - /proc/mounts does not show root-fs correctly */ +#define PROC_MOUNTS _PATH_MOUNTED +#define PROC_SWAPS "/proc/swaps" +#define DASD_DRIVER_NAME "dasd" +#define LABEL_LENGTH 10 +#define PROC_LINE_LENGTH 80 +#define ERR_LENGTH 80 + +#define MAX_FILELEN NAME_MAX+PATH_MAX + +#define GIVEN_DEVNO 1 +#define GIVEN_MAJOR 2 +#define GIVEN_MINOR 4 + +#define CHECK_START 1 +#define CHECK_END 2 +#define CHECK_BLKSIZE 4 +#define CHECK_ALL ~0 + +#define ERRMSG(x...) {fflush(stdout);fprintf(stderr,x);} +#define ERRMSG_EXIT(ec,x...) {fflush(stdout);fprintf(stderr,x);exit(ec);} + +#define CHECK_SPEC_MAX_ONCE(i,str) \ + {if (i>1) \ + ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ + "can only be specified once\n",prog_name);} + +#define PARSE_PARAM_INTO(x,param,base,str) \ + {x=(int)strtol(param,&endptr,base); \ + if (*endptr) \ + ERRMSG_EXIT(EXIT_MISUSE,"%s: " str " " \ + "is in invalid format\n",prog_name);} + +char *prog_name;/*="dasdfmt";*/ +char tempfilename[]=TEMPFILENAME; + +__u8 _ascebc[256] = +{ + /*00 NUL SOH STX ETX EOT ENQ ACK BEL */ + 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, + /*08 BS HT LF VT FF CR SO SI */ + /* ->NL */ + 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + /*10 DLE DC1 DC2 DC3 DC4 NAK SYN ETB */ + 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, + /*18 CAN EM SUB ESC FS GS RS US */ + /* ->IGS ->IRS ->IUS */ + 0x18, 0x19, 0x3F, 0x27, 0x22, 0x1D, 0x1E, 0x1F, + /*20 SP ! " # $ % & ' */ + 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, + /*28 ( ) * + , - . / */ + 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, + /*30 0 1 2 3 4 5 6 7 */ + 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + /*38 8 9 : ; < = > ? */ + 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, + /*40 @ A B C D E F G */ + 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, + /*48 H I J K L M N O */ + 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, + /*50 P Q R S T U V W */ + 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, + /*58 X Y Z [ \ ] ^ _ */ + 0xE7, 0xE8, 0xE9, 0xBA, 0xE0, 0xBB, 0xB0, 0x6D, + /*60 ` a b c d e f g */ + 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + /*68 h i j k l m n o */ + 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, + /*70 p q r s t u v w */ + 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + /*78 x y z { | } ~ DL */ + 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, + /*80*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*88*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*90*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*98*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*A8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*B8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*C8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*D8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E0 sz */ + 0x3F, 0x59, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*E8*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F0*/ + 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, + /*F8*/ + 0x90, 0x3F, 0x3F, 0x3F, 0x3F, 0xEA, 0x3F, 0xFF +}; + +void convert_label(char *str) +{ + int i; + for (i=0;i<LABEL_LENGTH;i++) str[i]=_ascebc[str[i]]; +} + +void +exit_usage(int exitcode) +{ +#ifdef RANGE_FORMATTING + printf("Usage: %s [-htvyLV] [-l <label>] [-b <blocksize>] [<range>] " \ + "<diskspec>\n\n",prog_name); +#else /* RANGE_FORMATTING */ + printf("Usage: %s [-htvyLV] [-l <label>] [-b <blocksize>] " \ + "<diskspec>\n\n",prog_name); +#endif /* RANGE_FORMATTING */ + printf(" -t means testmode\n"); + printf(" -v means verbose mode\n"); + printf(" -V means print version\n"); + printf(" -L means don't write disk label\n"); + printf(" <label> is a label which is converted to EBCDIC and " \ + "written to disk\n"); + printf(" <blocksize> has to be power of 2 and at least 512\n"); +#ifdef RANGE_FORMATTING + printf(" <range> is either\n"); + printf(" -s <start_track> -e <end_track>\n"); + printf(" or\n"); + printf(" -r <start_track>-<end_track>\n"); +#endif /* RANGE_FORMATTING */ + printf(" and <diskspec> is either\n"); + printf(" -f /dev/dasdX\n"); + printf(" or\n"); + printf(" -n <s390-devnr>\n"); + exit(exitcode); +} + +void +get_xno_from_xno(int *devno,kdev_t *major_no,kdev_t *minor_no,int mode) +{ + FILE *file; + int d,rc; + kdev_t mi,ma; + int mi_i,ma_i; /* for scanf :-( */ + char line[PROC_LINE_LENGTH]; + + file=fopen(PROC_DASD_DEVICES,"r"); + if (file==NULL) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to open " \ + PROC_DASD_DEVICES ": %s (do you have the /proc " \ + "filesystem enabled?)\n",prog_name,strerror(errno)); + + /* fgets(line,sizeof(line),file); omit first line */ + while (fgets(line,sizeof(line),file)!=NULL) { + rc=sscanf(line,"%X %*[(A-Z)] at (%d:%d)",&d,&ma_i,&mi_i); + ma=ma_i; + mi=mi_i; + if ( (rc==3) && + !((d!=*devno)&&(mode&GIVEN_DEVNO)) && + !((ma!=*major_no)&&(mode&GIVEN_MAJOR)) && + !((mi!=*minor_no)&&(mode&GIVEN_MINOR)) ) { + *devno=d; + *major_no=ma; + *minor_no=mi; + /* yes, this is a quick exit, but the easiest way */ + fclose(file); + return; + } + } + fclose(file); + + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to find device in the /proc " \ + "filesystem (are you sure to have the right param line?)\n", + prog_name); +} + +char * +get_devname_from_devno(int devno,int verbosity) +{ + kdev_t major_no,minor_no; + kdev_t file_major,file_minor; + struct stat stat_buf; + int rc; + int found; + char *devname; + char tmpname[MAX_FILELEN]; + + DIR *dp; + struct dirent *direntp; + + /**** get minor number ****/ + get_xno_from_xno(&devno,&major_no,&minor_no,GIVEN_DEVNO); + + /**** get device file ****/ + if ((dp=opendir(SLASHDEV)) == NULL) + ERRMSG_EXIT(EXIT_FAILURE,"%s: unable to read " SLASHDEV \ + "\n",prog_name); + found=0; + while ((direntp=readdir(dp)) != NULL) { + strcpy(tmpname,SLASHDEV); + strcat(tmpname,direntp->d_name); + rc=stat(tmpname,&stat_buf); + if (!rc) { + file_major=MAJOR(stat_buf.st_rdev); + file_minor=MINOR(stat_buf.st_rdev); + if ((file_major==major_no) && (file_minor==minor_no)) { + found=1; + break; + } + } + } + if (found) { + devname=malloc(strlen(direntp->d_name)); + strcpy(devname,tmpname); + } + rc=closedir(dp); + if (rc<0) ERRMSG("%s: unable to close directory " SLASHDEV \ + "; continuing\n",prog_name); + if (found) + return devname; + + if (verbosity>=1) + printf("I didn't find device node in " SLASHDEV \ + "; trying to create a temporary node\n"); + + /**** get temp file and create device node *****/ + rc=mkstemp(tempfilename); + if (rc==-1) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to get temporary " \ + "filename: %s\n",prog_name,strerror(errno)); + close(rc); + rc=unlink(tempfilename); + + rc=mknod(tempfilename,S_IFBLK|0600,MKDEV(major_no,minor_no)); + if (rc) + ERRMSG_EXIT(EXIT_FAILURE,"%s: failed to create temporary " \ + "device node %s: %s\n",prog_name,tempfilename, + strerror(errno)); + return tempfilename; +} + +char * +check_param(int mode,format_data_t data) +{ + char *s; + + if (NULL==(s=malloc(ERR_LENGTH))) + ERRMSG_EXIT(EXIT_FAILURE,"%s: not enough memory.\n",prog_name); + + if ((mode&CHECK_START)&&(data.start_unit<0)) { + strcpy(s,"start track must be greater than zero"); + goto exit; + } + if ((mode&CHECK_END)&&(data.stop_unit<-1)) { + strcpy(s,"end track must be -1 or greater than zero"); + goto exit; + } + if ((mode&CHECK_END)&&(data.start_unit>data.stop_unit)&& + (data.stop_unit!=-1)) { + strcpy(s,"end track must be higher than start track"); + goto exit; + } + + if ((mode&CHECK_BLKSIZE)&&(data.blksize<1)) { + strcpy(s,"blocksize must be a positive integer"); + goto exit; + } + if (mode&CHECK_BLKSIZE) while (data.blksize>0) { + if ((data.blksize%2)&&(data.blksize!=1)) { + strcpy(s,"blocksize must be a power of 2"); + goto exit; + } + data.blksize/=2; + } + + free(s); + return NULL; +exit: + return s; +} + +#define ASK_PRINTOUT printf("Please enter %s",output) +#define ASK_GETBUFFER fgets(buffer,sizeof(buffer),stdin) +#define ASK_SCANFORNUMBER(var) rc=sscanf(buffer,"%d%c",&var,&c) +#define ASK_COMPLAIN_FORMAT if ((rc==2)&&(c=='\n')) rc=1; \ + if (rc==-1) rc=1; /* this happens, if enter is pressed */ \ + if (rc!=1) printf(" -- wrong input, try again.\n") +#define ASK_CHECK_PARAM(mode) str=check_param(mode,params); \ + if (str!=NULL) { printf(" -- %s\n",str); rc=0; free(str); } + +format_data_t +ask_user_for_data(format_data_t params) +{ + char buffer[20]; /* should be enough for inputing track numbers */ + char c; + int i,rc; + char *str; + char output[60],o2[12]; + +#ifdef RANGE_FORMATTING + i=params.start_unit; + do { + params.start_unit=i; + sprintf(output,"the start track of the range to format " \ + "[%d]: ",i); + ASK_PRINTOUT; + ASK_GETBUFFER; + ASK_SCANFORNUMBER(params.start_unit); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_START); + } while (rc!=1); + + i=params.stop_unit; + do { + params.stop_unit=i; + sprintf(output,"the end track of the range to format ["); + if (i==-1) sprintf(o2,"END]: "); else + sprintf(o2,"%d]: ",i); + strcat(output,o2); + ASK_PRINTOUT; + ASK_GETBUFFER; + if ( (!strcasecmp(buffer,"end")) || + (!strcasecmp(buffer,"end\n")) ) { + rc=1; + params.stop_unit=-1; + } else { + ASK_SCANFORNUMBER(params.stop_unit); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_END); + } + } while (rc!=1); +#endif /* RANGE_FORMATTING */ + + i=params.blksize; + do { + params.blksize=i; + sprintf(output,"the blocksize of the formatting [%d]: ",i); + ASK_PRINTOUT; + ASK_GETBUFFER; + ASK_SCANFORNUMBER(params.blksize); + ASK_COMPLAIN_FORMAT; + ASK_CHECK_PARAM(CHECK_BLKSIZE); + } while (rc!=1); + + return params; +} + +/* Check if the device we are going to format is mounted. + * If true, complain and exit. + */ +void +check_mounted(int major, int minor) +{ + FILE *f; + int ishift = 0; + struct mntent *ment; + struct stat stbuf; + char line[128]; + + /* If whole disk to be formatted ... */ + if ((minor % (1U << DASD_PARTN_BITS)) == 0) { + /* ... ignore partition-selector */ + minor >>= DASD_PARTN_BITS; + ishift = DASD_PARTN_BITS; + } + /* + * first, check filesystems + */ + if (!(f = fopen(PROC_MOUNTS, "r"))) + ERRMSG_EXIT(EXIT_FAILURE, "%s: %s\n", PROC_MOUNTS, + strerror(errno)); + while ((ment = getmntent(f))) { + if (stat(ment->mnt_fsname, &stbuf) == 0) + if ((major == MAJOR(stbuf.st_rdev)) && + (minor == (MINOR(stbuf.st_rdev)>>ishift))) { + ERRMSG("%s: device is mounted on %s!!\n", + prog_name,ment->mnt_dir); + ERRMSG_EXIT(EXIT_BUSY, "If you really want to " + "format it, please unmount it.\n"); + } + } + fclose(f); + /* + * second, check active swap spaces + */ + if (!(f = fopen(PROC_SWAPS, "r"))) + ERRMSG_EXIT(EXIT_FAILURE, PROC_SWAPS " %s", strerror(errno)); + /* + * skip header line + */ + fgets(line, sizeof(line), f); + while (fgets(line, sizeof(line), f)) { + char *p; + for (p = line; *p && (!isspace(*p)); p++) ; + *p = '\0'; + if (stat(line, &stbuf) == 0) + if ((major == MAJOR(stbuf.st_rdev)) && + (minor == (MINOR(stbuf.st_rdev)>>ishift))) { + ERRMSG("%s: the device is in use for " + "swapping!!\n",prog_name); + ERRMSG_EXIT(EXIT_BUSY, "If you really want to " + "format it, please use swapoff %s.\n", + line); + } + } + fclose(f); +} + +void +do_format_dasd(char *dev_name,format_data_t format_params,int testmode, + int verbosity,int writenolabel,int labelspec, + char *label,int withoutprompt,int devno) +{ + int fd,rc; + struct stat stat_buf; + kdev_t minor_no,major_no; + int new_blksize; + unsigned int label_position; + struct hd_geometry new_geometry; + char inp_buffer[5]; /* to contain yes */ + + fd=open(dev_name,O_RDWR); + if (fd==-1) + ERRMSG_EXIT(EXIT_FAILURE,"%s: error opening device %s: " \ + "%s\n",prog_name,dev_name,strerror(errno)); + + if (verbosity>=1) { + } + + rc=stat(dev_name,&stat_buf); + if (rc) { + ERRMSG_EXIT(EXIT_FAILURE,"%s: error occured during stat: " \ + "%s\n",prog_name,strerror(errno)); + } else { + if (!S_ISBLK(stat_buf.st_mode)) + ERRMSG_EXIT(EXIT_FAILURE,"%s: file is not a " \ + "blockdevice.\n",prog_name); + major_no=MAJOR(stat_buf.st_rdev); + minor_no=MINOR(stat_buf.st_rdev); + } + check_mounted(major_no, minor_no); + + if ((!writenolabel) && (!labelspec)) { + sprintf(label,"LNX1 x%04x",devno); + } + + if ( ((withoutprompt)&&(verbosity>=1)) || + (!withoutprompt) ) { + get_xno_from_xno(&devno,&major_no,&minor_no, + GIVEN_MAJOR|GIVEN_MINOR); + printf("\nI am going to format the device %s in the " \ + "following way:\n",dev_name); + printf(" Device number of device : 0x%x\n",devno); + printf(" Major number of device : %u\n",major_no); + printf(" Minor number of device : %u\n",minor_no); + printf(" Labelling device : %s\n",(writenolabel)? + "no":"yes"); + if (!writenolabel) + printf(" Disk label : %s\n",label); +#ifdef RANGE_FORMATTING + printf(" Start track : %d\n" \ + ,format_params.start_unit); + printf(" End track : "); + if (format_params.stop_unit==-1) + printf("last track of disk\n"); + else + printf("%d\n",format_params.stop_unit); +#endif /* RANGE_FORMATTING */ + printf(" Blocksize : %d\n" \ + ,format_params.blksize); + if (testmode) printf("Test mode active, omitting ioctl.\n"); + } + + while (!testmode) { + if (!withoutprompt) { + printf("\n--->> ATTENTION! <<---\n"); + printf("All data in the specified range of that " \ + "device will be lost.\nType \"yes\" to " \ + "continue, no will leave the disk untouched: "); + fgets(inp_buffer,sizeof(inp_buffer),stdin); + if (strcasecmp(inp_buffer,"yes") && + strcasecmp(inp_buffer,"yes\n")) { + printf("Omitting ioctl call (disk will " \ + "NOT be formatted).\n"); + break; + } + } + + if ( !( (withoutprompt)&&(verbosity<1) )) + printf("Formatting the device. This may take a " \ + "while (get yourself a coffee).\n"); + rc=ioctl(fd,BIODASDFORMAT,format_params); + if (rc) + ERRMSG_EXIT(EXIT_FAILURE,"%s: the dasd driver " \ + "returned with the following error " \ + "message:\n%s\n",prog_name,strerror(errno)); + printf("Finished formatting the device.\n"); + + if (!writenolabel) { + if (verbosity>0) + printf("Retrieving disk geometry... "); + + rc=ioctl(fd,HDIO_GETGEO,&new_geometry); + if (rc) { + ERRMSG("%s: the ioctl call to get geometry " \ + "returned with the following error " \ + "message:\n%s\n",prog_name, + strerror(errno)); + goto reread; + } + + + rc=ioctl(fd,BLKGETSIZE,&new_blksize); + if (rc) { + ERRMSG("%s: the ioctl call to get blocksize " \ + "returned with the following error " \ + "message:\n%s\n",prog_name, + strerror(errno)); + goto reread; + } + + if (verbosity>0) printf("done\n"); + + label_position=new_geometry.start*new_blksize; + + if (verbosity>0) printf("Writing label... "); + convert_label(label); + rc=lseek(fd,label_position,SEEK_SET); + if (rc!=label_position) { + ERRMSG("%s: lseek on the device to %i " \ + "failed with the following error " \ + "message:\n%s\n",prog_name, + label_position,strerror(errno)); + goto reread; + } + rc=write(fd,label,LABEL_LENGTH); + if (rc!=LABEL_LENGTH) { + ERRMSG("%s: writing the label only wrote %d " \ + "bytes.\n",prog_name,rc); + goto reread; + } + + sync(); + sync(); + + if (verbosity>0) printf("done\n"); + } + reread: + printf("Rereading the partition table... "); + rc=ioctl(fd,BLKRRPART,NULL); + if (rc) { + ERRMSG("%s: error during rereading the partition " \ + "table: %s.\n",prog_name,strerror(errno)); + } else printf("done.\n"); + + break; + } + + rc=close(fd); + if (rc) + ERRMSG("%s: error during close: " \ + "%s; continuing.\n",prog_name,strerror(errno)); +} + + + +int main(int argc,char *argv[]) { + int verbosity; + int testmode; + int withoutprompt; + int writenolabel,labelspec; + + char *dev_name; + int devno; + char *dev_filename,*devno_param_str,*range_param_str; + char *start_param_str,*end_param_str,*blksize_param_str; + char label[LABEL_LENGTH+1]; + + format_data_t format_params; + + int rc; + int oc; + char *endptr; + + char c1,c2,cbuffer[6]; /* should be able to contain -end plus 1 char */ + int i,i1,i2; + char *str; + + int start_specified,end_specified,blksize_specified; + int devfile_specified,devno_specified,range_specified; + + /******************* initialization ********************/ + prog_name=argv[0]; + + endptr=NULL; + + /* set default values */ + format_params.start_unit=DASD_FORMAT_DEFAULT_START_UNIT; + format_params.stop_unit=DASD_FORMAT_DEFAULT_STOP_UNIT; + format_params.blksize=DASD_FORMAT_DEFAULT_BLOCKSIZE; + format_params.intensity=DASD_FORMAT_DEFAULT_INTENSITY; + testmode=0; + verbosity=0; + withoutprompt=0; + writenolabel=0; + labelspec=0; + for (i=0;i<LABEL_LENGTH;i++) label[i]=' '; + label[LABEL_LENGTH]=0; + + start_specified=end_specified=blksize_specified=0; + devfile_specified=devno_specified=range_specified=0; + + /*************** parse parameters **********************/ + + /* avoid error message generated by getopt */ + opterr=0; + +#ifdef RANGE_FORMATTING + while ( (oc=getopt(argc,argv,"r:s:e:b:n:l:f:hLty?vV")) !=EOF) { +#endif /* RANGE_FORMATTING */ + while ( (oc=getopt(argc,argv,"b:n:l:f:hLty?vV")) !=EOF) { + switch (oc) { + case 'y': + withoutprompt=1; + break; + + case 't': + testmode=1; + break; + + case 'v': + verbosity++; + break; + + case '?': /* fall-through */ + case ':': + exit_usage(EXIT_MISUSE); + + case 'h': + exit_usage(0); + + case 'V': + printf("%s version 0.99\n",prog_name); + exit(0); + + case 'l': + strncpy(label,optarg,LABEL_LENGTH); + if (strlen(optarg)<LABEL_LENGTH) + label[strlen(optarg)]=' '; + labelspec++; + break; + + case 'L': + writenolabel++; + break; + +#ifdef RANGE_FORMATTING + case 's' : + start_param_str=optarg; + start_specified++; + break; + + case 'e' : + end_param_str=optarg; + end_specified++; + break; + + case 'r' : + range_param_str=optarg; + range_specified++; + break; +#endif /* RANGE_FORMATTING */ + + case 'b' : + blksize_param_str=optarg; + blksize_specified++; + break; + + case 'n' : + devno_param_str=optarg; + devno_specified++; + break; + + case 'f' : + dev_filename=optarg; + devfile_specified++; + break; + } + } + + /******************** checking of parameters **************/ + + /* convert range into -s and -e */ + CHECK_SPEC_MAX_ONCE(range_specified,"formatting range"); + + while (range_specified) { + start_specified++; + end_specified++; + + /* scan for 1 or 2 integers, separated by a dash */ + rc=sscanf(range_param_str,"%d%c%d%c",&i1,&c1,&i2,&c2); + if ((rc==3)&&(c1=='-')) { + format_params.start_unit=i1; + format_params.stop_unit=i2; + break; + } + if (rc==1) { + format_params.start_unit=i1; + break; + } + + /* scan for integer and -END */ + rc=sscanf(range_param_str,"%d%s",&i1,cbuffer); + if ((rc==2)&&(!strcasecmp(cbuffer,"-END"))) { + format_params.start_unit=i1; + format_params.stop_unit=-1; + break; + } + ERRMSG_EXIT(EXIT_MISUSE,"%s: specified range " \ + "is in invalid format\n",prog_name); + } + + if ((!devfile_specified)&&(!devno_specified)) + ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ + "not specified\n",prog_name); + + if ((devfile_specified+devno_specified)>1) + ERRMSG_EXIT(EXIT_MISUSE,"%s: device to format " \ + "can only be specified once\n",prog_name); + + if ((!start_specified)&&(!end_specified)&&(!range_specified)&& + (!blksize_specified)) { + format_params=ask_user_for_data(format_params); + } + + CHECK_SPEC_MAX_ONCE(start_specified,"start track"); + CHECK_SPEC_MAX_ONCE(end_specified,"end track"); + CHECK_SPEC_MAX_ONCE(blksize_specified,"blocksize"); + CHECK_SPEC_MAX_ONCE(labelspec,"label"); + CHECK_SPEC_MAX_ONCE(writenolabel,"omit-label-writing flag"); + + if (devno_specified) + PARSE_PARAM_INTO(devno,devno_param_str,16,"device number"); + if (start_specified&&!range_specified) + PARSE_PARAM_INTO(format_params.start_unit,start_param_str,10, + "start track"); + if (end_specified&&!range_specified) + PARSE_PARAM_INTO(format_params.stop_unit,end_param_str,10, + "end track"); + if (blksize_specified) + PARSE_PARAM_INTO(format_params.blksize,blksize_param_str,10, + "blocksize"); + + /***********get dev_name *********************/ + dev_name=(devno_specified)? + get_devname_from_devno(devno,verbosity): + dev_filename; + + /*** range checking *********/ + str=check_param(CHECK_ALL,format_params); + if (str!=NULL) ERRMSG_EXIT(EXIT_MISUSE,"%s: %s\n",prog_name,str); + + /******* issue the real command and reread part table *******/ + do_format_dasd(dev_name,format_params,testmode,verbosity, + writenolabel,labelspec,label,withoutprompt,devno); + + /*************** cleanup ********************************/ + if (strncmp(dev_name,TEMPFILENAME,TEMPFILENAMECHARS)==0) { + rc=unlink(dev_name); + if ((rc)&&(verbosity>=1)) + ERRMSG("%s: temporary device node %s could not be " \ + "removed: %s\n",prog_name,dev_name, + strerror(errno)); + } else { + if (devno_specified) { + /* so we have allocated space for the filename */ + free(dev_name); + } + } + + return 0; +} diff --git a/arch/s390x/tools/silo/Makefile b/arch/s390x/tools/silo/Makefile new file mode 100644 index 000000000..62b11d7da --- /dev/null +++ b/arch/s390x/tools/silo/Makefile @@ -0,0 +1,15 @@ +all: silo + +silo.o: silo.c + $(CC) -c -o silo.o -O2 silo.c + +cfg.o: cfg.c + $(CC) -c -o cfg.o -O2 cfg.c + +silo: silo.o cfg.o + $(CC) -o $@ $^ + $(STRIP) $@ + +clean: + rm -f *.o silo + diff --git a/arch/s390x/tools/silo/cfg.c b/arch/s390x/tools/silo/cfg.c new file mode 100644 index 000000000..2b11d93b8 --- /dev/null +++ b/arch/s390x/tools/silo/cfg.c @@ -0,0 +1,373 @@ +/* cfg.c - Configuration file parser */ + +/* Copyright 1992-1997 Werner Almesberger. See file COPYING for details. */ + + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> +#include <string.h> + +#include "cfg.h" + +#define MAX_TOKEN 200 +#define MAX_VAR_NAME MAX_TOKEN + +static FILE *file; +static char flag_set; +static char *last_token = NULL,*last_item = NULL,*last_value = NULL; +static int line_num; +static char *file_name = NULL; +static int back = 0; /* can go back by one char */ + + +void pdie(char *msg) +{ + fflush(stdout); + perror(msg); + exit(1); +} + + +void die(char *fmt,...) +{ + va_list ap; + + fflush(stdout); + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + va_end(ap); + fputc('\n',stderr); + exit(1); +} + +char *pstrdup(const char *str) +{ + char *this; + + if ((this = strdup(str)) == NULL) pdie("Out of memory"); + return this; +} + +int cfg_open(char *name) +{ + if (!strcmp(name,"-")) file = stdin; + else if (!(file = fopen(file_name = name,"r"))) pdie(name); + line_num = 1; + return fileno(file); +} + +void cfg_error(char *msg,...) +{ + va_list ap; + + fflush(stdout); + va_start(ap,msg); + vfprintf(stderr,msg,ap); + va_end(ap); + if (!file_name) fputc('\n',stderr); + else fprintf(stderr," near line %d in file %s\n",line_num,file_name); + exit(1); +} + + +static int next_raw(void) +{ + int ch; + + if (!back) return getc(file); + ch = back; + back = 0; + return ch; +} + + +static int next(void) +{ + static char *var; + char buffer[MAX_VAR_NAME+1]; + int ch,braced; + char *put; + + if (back) { + ch = back; + back = 0; + return ch; + } + if (var && *var) return *var++; + ch = getc(file); + if (ch == '\\') { + ch = getc(file); + if (ch == '$') return ch; + ungetc(ch,file); + return '\\'; + } + if (ch != '$') return ch; + ch = getc(file); + braced = ch == '{'; + put = buffer; + if (!braced) *put++ = ch; + while (1) { + ch = getc(file); +#if 0 + if (!braced && ch < ' ') { + ungetc(ch,file); + break; + } +#endif + if (ch == EOF) cfg_error("EOF in variable name"); + if (ch < ' ') cfg_error("control character in variable name"); + if (braced && ch == '}') break; + if (!braced && !isalpha(ch) && !isdigit(ch) && ch != '_') { + ungetc(ch,file); + break; + } + if (put-buffer == MAX_VAR_NAME) cfg_error("variable name too long"); + *put++ = ch; + } + *put = 0; + if (!(var = getenv(buffer))) cfg_error("unknown variable \"%s\"",buffer); + return next(); +} + + +static void again(int ch) +{ + if (back) die("internal error: again invoked twice"); + back = ch; +} + + +static char *cfg_get_token(void) +{ + char buf[MAX_TOKEN+1]; + char *here; + int ch,escaped; + + if (last_token) { + here = last_token; + last_token = NULL; + return here; + } + while (1) { + while ((ch = next()), ch == ' ' || ch == '\t' || ch == '\n') + if (ch == '\n') line_num++; + if (ch == EOF) return NULL; + if (ch != '#') break; + while ((ch = next_raw()), ch != '\n') + if (ch == EOF) return NULL; + line_num++; + } + if (ch == '=') return pstrdup("="); + if (ch == '"') { + here = buf; + while (here-buf < MAX_TOKEN) { + if ((ch = next()) == EOF) cfg_error("EOF in quoted string"); + if (ch == '"') { + *here = 0; + return pstrdup(buf); + } + if (ch == '\\') { + ch = next(); + if (ch != '"' && ch != '\\' && ch != '\n') + cfg_error("Bad use of \\ in quoted string"); + if (ch == '\n') { + while ((ch = next()), ch == ' ' || ch == '\t'); + if (!ch) continue; + again(ch); + ch = ' '; + } + } + if (ch == '\n' || ch == '\t') + cfg_error("\\n and \\t are not allowed in quoted strings"); + *here++ = ch; + } + cfg_error("Quoted string is too long"); + return 0; /* not reached */ + } + here = buf; + escaped = 0; + while (here-buf < MAX_TOKEN) { + if (escaped) { + if (ch == EOF) cfg_error("\\ precedes EOF"); + if (ch == '\n') line_num++; + else *here++ = ch == '\t' ? ' ' : ch; + escaped = 0; + } + else { + if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '#' || + ch == '=' || ch == EOF) { + again(ch); + *here = 0; + return pstrdup(buf); + } + if (!(escaped = (ch == '\\'))) *here++ = ch; + } + ch = next(); + } + cfg_error("Token is too long"); + return 0; /* not reached */ +} + + +static void cfg_return_token(char *token) +{ + last_token = token; +} + + +static int cfg_next(char **item,char **value) +{ + char *this; + + if (last_item) { + *item = last_item; + *value = last_value; + last_item = NULL; + return 1; + } + *value = NULL; + if (!(*item = cfg_get_token())) return 0; + if (!strcmp(*item,"=")) cfg_error("Syntax error"); + if (!(this = cfg_get_token())) return 1; + if (strcmp(this,"=")) { + cfg_return_token(this); + return 1; + } + if (!(*value = cfg_get_token())) cfg_error("Value expected at EOF"); + if (!strcmp(*value,"=")) cfg_error("Syntax error after %s",*item); + return 1; +} + + +static void cfg_return(char *item,char *value) +{ + last_item = item; + last_value = value; +} + + +void cfg_init(CONFIG *table) +{ + while (table->type != cft_end) { + switch (table->type) { + case cft_strg: + if (table->data) free(table->data); + case cft_flag: + table->data = NULL; + break; + case cft_link: + table = ((CONFIG *) table->action)-1; + break; + default: + die("Unknown syntax code %d",table->type); + } + table++; + } +} + + +static int cfg_do_set(CONFIG *table,char *item,char *value,int copy, + void *context) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp(walk->name,item)) { + if (value && walk->type != cft_strg) + cfg_error("'%s' doesn't have a value",walk->name); + if (!value && walk->type == cft_strg) + cfg_error("Value expected for '%s'",walk->name); + if (walk->data) { + if (walk->context == context) + cfg_error("Duplicate entry '%s'",walk->name); + else { + fprintf(stderr,"Ignoring entry '%s'\n",walk->name); + if (!copy) free(value); + return 1; + } + } + if (walk->type == cft_flag) walk->data = &flag_set; + else if (walk->type == cft_strg) { + if (copy) walk->data = pstrdup(value); + else walk->data = value; + } + walk->context = context; + if (walk->action) ((void (*)(void)) walk->action)(); + break; + } + if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1; + } + if (walk->type != cft_end) return 1; + cfg_return(item,value); + return 0; +} + + +void cfg_set(CONFIG *table,char *item,char *value,void *context) +{ + if (!cfg_do_set(table,item,value,1,context)) + cfg_error("cfg_set: Can't set %s",item); +} + + +void cfg_unset(CONFIG *table,char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) + if (walk->name && !strcasecmp(walk->name,item)) { + if (!walk->data) die("internal error (cfg_unset %s, unset)",item); + if (walk->type == cft_strg) free(walk->data); + walk->data = NULL; + return; + } + die("internal error (cfg_unset %s, unknown",item); +} + + +int cfg_parse(CONFIG *table) +{ + char *item,*value; + + while (1) { + if (!cfg_next(&item,&value)) return 0; + if (!cfg_do_set(table,item,value,0,table)) return 1; + free(item); + } +} + + +int cfg_get_flag(CONFIG *table,char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp(walk->name,item)) { + if (walk->type != cft_flag) + die("cfg_get_flag: operating on non-flag %s",item); + return !!walk->data; + } + if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1; + } + die("cfg_get_flag: unknown item %s",item); + return 0; /* not reached */ +} + + +char *cfg_get_strg(CONFIG *table,char *item) +{ + CONFIG *walk; + + for (walk = table; walk->type != cft_end; walk++) { + if (walk->name && !strcasecmp(walk->name,item)) { + if (walk->type != cft_strg) + die("cfg_get_strg: operating on non-string %s",item); + return walk->data; + } + if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1; + } + die("cfg_get_strg: unknown item %s",item); + return 0; /* not reached */ +} diff --git a/arch/s390x/tools/silo/cfg.h b/arch/s390x/tools/silo/cfg.h new file mode 100644 index 000000000..97d10bf73 --- /dev/null +++ b/arch/s390x/tools/silo/cfg.h @@ -0,0 +1,58 @@ +/* cfg.h - Configuration file parser */ + +/* Copyright 1992-1996 Werner Almesberger. See file COPYING for details. */ + + +#ifndef CFG_H +#define CFG_H + +typedef enum { cft_strg, cft_flag, cft_link, cft_end } CFG_TYPE; + +typedef struct { + CFG_TYPE type; + char *name; + void *action; + void *data; + void *context; +} CONFIG; + +extern int cfg_open(char *name); + +/* Opens the configuration file. Returns the file descriptor of the open + file. */ + +extern void cfg_error(char *msg,...); + +/* Signals an error while parsing the configuration file and terminates the + program. */ + +extern void cfg_init(CONFIG *table); + +/* Initializes the specified table. */ + +extern void cfg_set(CONFIG *table,char *item,char *value,void *context); + +/* Sets the specified variable in table. If the variable has already been set + since the last call to cfg_init, a warning message is issued if the context + keys don't match or a fatal error is reported if they do. */ + +extern void cfg_unset(CONFIG *table,char *item); + +/* Unsets the specified variable in table. It is a fatal error if the variable + was not set. */ + +extern int cfg_parse(CONFIG *table); + +/* Parses the configuration file for variables contained in table. A non-zero + value is returned if a variable not found in table has been met. Zero is + returned if EOF has been reached. */ + +extern int cfg_get_flag(CONFIG *table,char *item); + +/* Returns one if the specified variable is set, zero if it isn't. */ + +extern char *cfg_get_strg(CONFIG *table,char *item); + +/* Returns the value of the specified variable if it is set, NULL otherwise. */ + +#endif diff --git a/arch/s390x/tools/silo/silo.c b/arch/s390x/tools/silo/silo.c new file mode 100644 index 000000000..9ac04ac2b --- /dev/null +++ b/arch/s390x/tools/silo/silo.c @@ -0,0 +1,573 @@ +/* + * arch/s390/boot/silo.c + * + * S390 version + * Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * + * Report bugs to: <linux390@de.ibm.com> + * + * Author(s): Holger Smolinski <Holger.Smolinski@de.ibm.com> + * Fritz Elfert <felfert@to.com> contributed support for + * /etc/silo.conf based on Intel's lilo + */ + +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <dirent.h> +#include <linux/fs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <asm/ioctl.h> + +#include "cfg.h" + +CONFIG cf_options[] = { + { cft_strg, "append", NULL, NULL,NULL }, + { cft_strg, "image", NULL, NULL,NULL }, + { cft_strg, "ipldevice", NULL, NULL,NULL }, + { cft_strg, "bootsect", NULL, NULL,NULL }, + { cft_strg, "map", NULL, NULL,NULL }, + { cft_strg, "parmfile", NULL, NULL,NULL }, + { cft_strg, "ramdisk", NULL, NULL,NULL }, + { cft_strg, "root", NULL, NULL,NULL }, + { cft_flag, "readonly", NULL, NULL,NULL }, + { cft_strg, "verbose", NULL, NULL,NULL }, + { cft_strg, "testlevel", NULL, NULL,NULL }, + { cft_end, NULL, NULL, NULL,NULL } +}; + +/* from dasd.h */ +#define DASD_PARTN_BITS 2 +#define BIODASDRWTB _IOWR('D',0,int) +/* end */ + +#define SILO_CFG "/etc/silo.conf" +#define SILO_IMAGE "./image" +#define SILO_BOOTMAP "./boot.map" +#define SILO_PARMFILE "./parmfile" +#define SILO_BOOTSECT "/boot/ipleckd.boot" + +#define PRINT_LEVEL(x,y...) if ( silo_options.verbosity >= x ) printf(y) +#define ERROR_LEVEL(x,y...) if ( silo_options.verbosity >= x ) fprintf(stderr,y) +#define TOGGLE(x) ((x)=((x)?(0):(1))) +#define GETARG(x) {int len=strlen(optarg);x=malloc(len);strncpy(x,optarg,len);PRINT_LEVEL(1,"%s set to %s\n",#x,optarg);} + +#define ITRY(x) if ( (x) == -1 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } +#define NTRY(x) if ( (x) == 0 ) { ERROR_LEVEL(0,"%s (line:%d) '%s' returned %d='%s'\n", __FILE__,__LINE__,#x,errno,strerror(errno)); usage(); exit(1); } + +#define MAX_CLUSTERS 256 +#define PARTN_MASK ((1 << DASD_PARTN_BITS) - 1) + +#define SILO_VERSION "1.1" + +struct silo_options + { + short int verbosity; + short int testlevel; + char *image; + char *ipldevice; + char *parmfile; + char *ramdisk; + char *bootsect; + char *conffile; + char *bootmap; + } +silo_options = +{ + 1, /* verbosity */ + 2, /* testlevel */ + SILO_IMAGE, /* image */ + NULL, /* ipldevice */ + SILO_PARMFILE, /* parmfile */ + NULL, /* initrd */ + SILO_BOOTSECT, /* bootsector */ + SILO_CFG, /* silo.conf file */ + SILO_BOOTMAP, /* boot.map */ +}; + +struct blockdesc + { + unsigned long off; + unsigned short ct; + unsigned long addr; + }; + +struct blocklist + { + struct blockdesc blk[MAX_CLUSTERS]; + unsigned short ix; + }; + +void +usage (void) +{ + printf ("Usage:\n"); + printf ("silo -d ipldevice [additional options]\n"); + printf ("-d /dev/node : set ipldevice to /dev/node\n"); + printf ("-f image : set image to image\n"); + printf ("-F conffile : specify configuration file (/etc/silo.conf)\n"); + printf ("-p parmfile : set parameter file to parmfile\n"); + printf ("-b bootsect : set bootsector to bootsect\n"); + printf ("Additional options\n"); + printf ("-B bootmap:\n"); + printf ("-v: increase verbosity level\n"); + printf ("-v#: set verbosity level to #\n"); + printf ("-t: decrease testing level\n"); + printf ("-h: print this message\n"); + printf ("-?: print this message\n"); + printf ("-V: print version\n"); +} + +int +read_cfg(struct silo_options *o) +{ + char *tmp; + if (access(o->conffile, R_OK) && (errno == ENOENT)) + return 0; + /* If errno != ENOENT, let cfg_open report an error */ + cfg_open(o->conffile); + cfg_parse(cf_options); + tmp = cfg_get_strg(cf_options, "ipldevice"); + if ( ! o->ipldevice && tmp ) + o->ipldevice = tmp; + tmp = cfg_get_strg(cf_options, "image"); + if ( ! strncmp(o-> image,SILO_IMAGE,strlen(SILO_IMAGE)) && tmp ) + o->image = tmp; + tmp = cfg_get_strg(cf_options, "parmfile"); + if ( !strncmp(o->parmfile,SILO_PARMFILE,strlen(SILO_PARMFILE)) && tmp) + o->parmfile = tmp; + if ( ! o -> ramdisk ) + o->ramdisk = cfg_get_strg(cf_options, "ramdisk"); + tmp = cfg_get_strg(cf_options, "bootsect"); + if ( !strncmp(o -> bootsect,SILO_BOOTSECT,strlen(SILO_BOOTSECT))&&tmp) + o->bootsect = tmp; + tmp = cfg_get_strg(cf_options, "map") ; + if ( !strncmp(o -> bootmap,SILO_BOOTMAP,strlen(SILO_BOOTMAP)) && tmp) + o->bootmap = tmp; + tmp = cfg_get_strg(cf_options, "verbose"); + if ( tmp ) { + unsigned short v; + sscanf (tmp, "%hu", &v); + o->verbosity = v; + } + tmp = cfg_get_strg(cf_options, "testlevel"); + if ( tmp ) { + unsigned short t; + sscanf (tmp, "%hu", &t); + o->testlevel += t; + } + return 1; +} + +char * +gen_tmpparm( char *pfile ) +{ + char *append = cfg_get_strg(cf_options, "append"); + char *root = cfg_get_strg(cf_options, "root"); + int ro = cfg_get_flag(cf_options, "readonly"); + FILE *f,*of; + char *fn; + char c; + char *tmpdir=NULL,*save=NULL; + + if (!append && !root && !ro) + return pfile; + of = fopen(pfile, "r"); + if ( of ) { + NTRY( fn = tempnam(NULL,"parm.")); + } else { + fn = pfile; + } + NTRY( f = fopen(fn, "a+")); + if ( of ) { + while ( ! feof (of) ) { + c=fgetc(of); + fputc(c,f); + } + } + if (root) + fprintf(f, " root=%s", root); + if (ro) + fprintf(f, " ro"); + if (append) + fprintf(f, " %s", append); + fprintf(f, "\n"); + fclose(f); + fclose(of); + printf ("tempfile is %s\n",fn); + return strdup(fn); +} + +int +parse_options (struct silo_options *o, int argc, char *argv[]) +{ + int rc = 0; + int oc; + + while ((oc = getopt (argc, argv, "Vf:F:d:p:r:b:B:h?v::t::")) != -1) + { + switch (oc) + { + case 'V': + printf("silo version: %s\n",SILO_VERSION); + exit(0); + case 'v': + { + unsigned short v; + if (optarg && sscanf (optarg, "%hu", &v)) + o->verbosity = v; + else + o->verbosity++; + PRINT_LEVEL (1, "Verbosity value is now %hu\n", o->verbosity); + break; + } + case 't': + { + unsigned short t; + if (optarg && sscanf (optarg, "%hu", &t)) + o->testlevel -= t; + else + o->testlevel--; + PRINT_LEVEL (1, "Testonly flag is now %d\n", o->testlevel); + break; + } + case 'h': + case '?': + usage (); + exit(0); + case 'd': + GETARG (o->ipldevice); + break; + case 'f': + GETARG (o->image); + break; + case 'F': + GETARG (o->conffile); + break; + case 'p': + GETARG (o->parmfile); + break; + case 'r': + GETARG (o->ramdisk); + break; + case 'b': + GETARG (o->bootsect); + break; + case 'B': + GETARG (o->bootmap); + default: + rc = EINVAL; + break; + } + } + read_cfg(o); + return rc; +} + +int +verify_device (char *name) +{ + int rc = 0; + struct stat dst; + struct stat st; + ITRY (stat (name, &dst)); + if (S_ISBLK (dst.st_mode)) + { + if (!(MINOR (dst.st_rdev) & PARTN_MASK)) + { + rc = dst.st_rdev; + } + else + /* invalid MINOR & PARTN_MASK */ + { + ERROR_LEVEL (1, "Cannot boot from partition %d %d %d", + (int) PARTN_MASK, (int) MINOR (dst.st_rdev), (int) (PARTN_MASK & MINOR (dst.st_rdev))); + rc = -1; + errno = EINVAL; + } + } + else + /* error S_ISBLK */ + { + ERROR_LEVEL (1, "%s is no block device\n", name); + rc = -1; + errno = EINVAL; + } + return rc; +} + +int +verify_file (char *name, int dev) +{ + int rc = 0; + struct stat dst; + struct stat st; + int bs = 1024; + int l; + + ITRY(stat ( name, &dst )); + if (S_ISREG (dst.st_mode)) + { + if ((unsigned) MAJOR (dev) == (unsigned) MAJOR (dst.st_dev) && (unsigned) MINOR (dev) == (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)) + { + /* whatever to do if all is ok... */ + } + else + /* devicenumber doesn't match */ + { + ERROR_LEVEL (1, "%s is not on device (%d/%d) but on (%d/%d)\n", name, (unsigned) MAJOR (dev), (unsigned) MINOR (dev), (unsigned) MAJOR (dst.st_dev), (unsigned) (MINOR (dst.st_dev) & ~PARTN_MASK)); + rc = -1; + errno = EINVAL; + } + } + else + /* error S_ISREG */ + { + ERROR_LEVEL (1, "%s is neither regular file nor linkto one\n", name); + rc = -1; + errno = EINVAL; + } + return rc; +} + +int +verify_options (struct silo_options *o) +{ + int rc = 0; + int dev = 0; + int crc = 0; + if (!o->ipldevice || !o->image || !o->bootsect) + { + if (!o->ipldevice) + fprintf(stderr,"ipldevice\n"); + if (!o->image) + fprintf(stderr,"image\n"); + if (!o->bootsect) + fprintf(stderr,"bootsect\n"); + + usage (); + exit (1); + } + PRINT_LEVEL (1, "Testlevel is set to %d\n",o->testlevel); + + PRINT_LEVEL (1, "IPL device is: '%s'", o->ipldevice); + ITRY (dev = verify_device (o->ipldevice)); + PRINT_LEVEL (2, "...ok...(%d/%d)", (unsigned short) MAJOR (dev), (unsigned short) MINOR (dev)); + PRINT_LEVEL (1, "\n"); + + PRINT_LEVEL (0, "bootsector is: '%s'", o->bootsect); + ITRY (verify_file (o->bootsect, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + if ( o -> testlevel > 0 && + ! strncmp( o->bootmap, SILO_BOOTMAP,strlen(SILO_BOOTMAP) )) { + NTRY( o -> bootmap = tempnam(NULL,"boot.")); + } + PRINT_LEVEL (0, "bootmap is set to: '%s'", o->bootmap); + if ( access ( o->bootmap, O_RDWR ) == -1 ) { + if ( errno == ENOENT ) { + ITRY (creat ( o-> bootmap, O_RDWR )); + } else { + PRINT_LEVEL(1,"Cannot acces bootmap file '%s': %s\n",o->bootmap, + strerror(errno)); + } + } + ITRY (verify_file (o->bootmap, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + PRINT_LEVEL (0, "Kernel image is: '%s'", o->image); + ITRY (verify_file (o->image, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + PRINT_LEVEL (0, "original parameterfile is: '%s'", o->parmfile); + ITRY (verify_file (o->parmfile, dev)); + PRINT_LEVEL (1, "...ok..."); + o->parmfile = gen_tmpparm(o->parmfile); + PRINT_LEVEL (0, "final parameterfile is: '%s'", o->parmfile); + ITRY (verify_file (o->parmfile, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + + if (o->ramdisk) + { + PRINT_LEVEL (0, "initialramdisk is: '%s'", o->ramdisk); + ITRY (verify_file (o->ramdisk, dev)); + PRINT_LEVEL (1, "...ok..."); + PRINT_LEVEL (0, "\n"); + } + + return crc; +} + + +int +add_file_to_blocklist (char *name, struct blocklist *lst, long addr) +{ + int fd; + int devfd; + struct stat fst; + int i; + int blk; + int bs; + int blocks; + + int rc = 0; + + ITRY (fd = open (name, O_RDONLY)); + ITRY (fstat (fd, &fst)); + ITRY (mknod ("/tmp/silodev", S_IFBLK | S_IRUSR | S_IWUSR, fst.st_dev)); + ITRY (devfd = open ("/tmp/silodev", O_RDONLY)); + ITRY (ioctl (fd, FIGETBSZ, &bs)); + blocks = (fst.st_size + bs - 1) / bs; + for (i = 0; i < blocks; i++) + { + blk = i; + ITRY (ioctl (fd, FIBMAP, &blk)); + if (blk) + { + int oldblk = blk; + ITRY (ioctl (devfd, BIODASDRWTB, &blk)); + if (blk <= 0) + { + ERROR_LEVEL (0, "BIODASDRWTB on blk %d returned %d\n", oldblk, blk); + break; + } + } + else + { + PRINT_LEVEL (1, "Filled hole on blk %d\n", i); + } + if (lst->ix == 0 || i == 0 || + lst->blk[lst->ix - 1].ct >= 128 || + (lst->blk[lst->ix - 1].off + lst->blk[lst->ix - 1].ct != blk && + !(lst->blk[lst->ix - 1].off == 0 && blk == 0))) + { + if (lst->ix >= MAX_CLUSTERS) + { + rc = 1; + errno = ENOMEM; + break; + } + lst->blk[lst->ix].off = blk; + lst->blk[lst->ix].ct = 1; + lst->blk[lst->ix].addr = addr + i * bs; + lst->ix++; + } + else + { + lst->blk[lst->ix - 1].ct++; + } + } + ITRY(unlink("/tmp/silodev")); + return rc; +} + +int +write_bootsect (struct silo_options *o, struct blocklist *blklst) +{ + int i; + int s_fd, d_fd, b_fd, bd_fd; + struct stat s_st, d_st, b_st; + int rc=0; + int bs, boots; + char *tmpdev; + char buffer[4096]={0,}; + ITRY (d_fd = open (o->ipldevice, O_RDWR | O_SYNC)); + ITRY (fstat (d_fd, &d_st)); + ITRY (s_fd = open (o->bootmap, O_RDWR | O_TRUNC | O_CREAT | O_SYNC)); + ITRY (verify_file (o->bootsect, d_st.st_rdev)); + for (i = 0; i < blklst->ix; i++) + { + int offset = blklst->blk[i].off; + int addrct = blklst->blk[i].addr | (blklst->blk[i].ct & 0xff); + PRINT_LEVEL (1, "ix %i: offset: %06x count: %02x address: 0x%08x\n", i, offset, blklst->blk[i].ct & 0xff, blklst->blk[i].addr); + if ( o->testlevel <= 1 ) { + NTRY (write (s_fd, &offset, sizeof (int))); + NTRY (write (s_fd, &addrct, sizeof (int))); + } + } + ITRY (ioctl (s_fd,FIGETBSZ, &bs)); + ITRY (stat (o->bootmap, &s_st)); + if (s_st.st_size > bs ) + { + ERROR_LEVEL (0,"%s is larger than one block\n", o->bootmap); + rc = -1; + errno = EINVAL; + } + boots=0; + NTRY ( tmpdev = tmpnam(NULL) ); + ITRY (mknod (tmpdev, S_IFBLK | S_IRUSR | S_IWUSR, s_st.st_dev)); + ITRY (bd_fd = open (tmpdev, O_RDONLY)); + ITRY (ioctl(s_fd,FIBMAP,&boots)); + ITRY (ioctl (bd_fd, BIODASDRWTB, &boots)); + PRINT_LEVEL (1, "Bootmap is in block no: 0x%08x\n", boots); + close (bd_fd); + close(s_fd); + ITRY (unlink(tmpdev)); + /* Now patch the bootsector */ + ITRY (b_fd = open (o->bootsect, O_RDONLY)); + NTRY (read (b_fd, buffer, 4096)); + memset (buffer + 0xe0, 0, 8); + *(int *) (buffer + 0xe0) = boots; + if ( o -> testlevel <= 0 ) { + NTRY (write (d_fd, buffer, 4096)); + NTRY (write (d_fd, buffer, 4096)); + } + close (b_fd); + close (d_fd); + return rc; +} + +int +do_silo (struct silo_options *o) +{ + int rc = 0; + + int device_fd; + int image_fd; + struct blocklist blklist; + memset (&blklist, 0, sizeof (struct blocklist)); + ITRY (add_file_to_blocklist (o->image, &blklist, 0x00000000)); + if (o->parmfile) + { + ITRY (add_file_to_blocklist (o->parmfile, &blklist, 0x00008000)); + } + if (o->ramdisk) + { + ITRY (add_file_to_blocklist (o->ramdisk, &blklist, 0x00800000)); + } + ITRY (write_bootsect (o, &blklist)); + return rc; +} + +int +main (int argct, char *argv[]) +{ + int rc = 0; + char *save=NULL; + char *tmpdir=getenv("TMPDIR"); + if (tmpdir) { + NTRY( save=(char*)malloc(strlen(tmpdir))); + NTRY( strncpy(save,tmpdir,strlen(tmpdir))); + } + ITRY( setenv("TMPDIR",".",1)); + ITRY (parse_options (&silo_options, argct, argv)); + ITRY (verify_options (&silo_options)); + if ( silo_options.testlevel > 0 ) { + printf ("WARNING: silo does not modify your volume. Use -t2 to change IPL records\n"); + } + ITRY (do_silo (&silo_options)); + if ( save ) + ITRY( setenv("TMPDIR",save,1)); + return rc; +} diff --git a/arch/s390x/tools/silo/silo.conf b/arch/s390x/tools/silo/silo.conf new file mode 100644 index 000000000..47d8a4551 --- /dev/null +++ b/arch/s390x/tools/silo/silo.conf @@ -0,0 +1,7 @@ +ipldevice = /dev/dasda +image = /boot/image +bootsect = /boot/ipleckd.boot +map = /boot/boot.map +root = /dev/dasd01 +readonly +append = "dasd=200-20f noinitrd" diff --git a/arch/s390x/vmlinux.lds b/arch/s390x/vmlinux.lds new file mode 100644 index 000000000..8d8ea9fcf --- /dev/null +++ b/arch/s390x/vmlinux.lds @@ -0,0 +1,78 @@ +/* ld script to make s390 Linux kernel + * Written by Martin Schwidefsky (schwidefsky@de.ibm.com) + */ +OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") +OUTPUT_ARCH(s390) +ENTRY(_start) +SECTIONS +{ + . = 0x00000000; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + *(.fixup) + *(.gnu.warning) + } = 0x0700 + .text.lock : { *(.text.lock) } /* out-of-line lock text */ + .rodata : { *(.rodata) } + .kstrtab : { *(.kstrtab) } + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + __start___ksymtab = .; /* Kernel symbol table */ + __ksymtab : { *(__ksymtab) } + __stop___ksymtab = .; + + _etext = .; /* End of text section */ + + .data : { /* Data */ + *(.data) + CONSTRUCTORS + } + + _edata = .; /* End of data section */ + + . = ALIGN(16384); /* init_task */ + .data.init_task : { *(.data.init_task) } + + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .text.init : { *(.text.init) } + .data.init : { *(.data.init) } + . = ALIGN(4096); + __init_end = .; + + __setup_start = .; + .setup.init : { *(.setup.init) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { *(.initcall.init) } + __initcall_end = .; + . = ALIGN(4096); + __init_end = .; + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + + __bss_start = .; /* BSS */ + .bss : { + *(.bss) + } + _end = . ; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } +} diff --git a/arch/sh/kernel/irq.c b/arch/sh/kernel/irq.c index 200148320..615d46148 100644 --- a/arch/sh/kernel/irq.c +++ b/arch/sh/kernel/irq.c @@ -22,7 +22,7 @@ #include <linux/ioport.h> #include <linux/interrupt.h> #include <linux/timex.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/smp.h> #include <linux/smp_lock.h> diff --git a/arch/sh/kernel/process.c b/arch/sh/kernel/process.c index 1ce22f0fd..a7810c914 100644 --- a/arch/sh/kernel/process.c +++ b/arch/sh/kernel/process.c @@ -24,7 +24,7 @@ #include <linux/smp_lock.h> #include <linux/stddef.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/user.h> #include <linux/a.out.h> diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c index b6fb1e9a8..7b5d77e90 100644 --- a/arch/sh/kernel/setup.c +++ b/arch/sh/kernel/setup.c @@ -17,7 +17,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/tty.h> diff --git a/arch/sparc/config.in b/arch/sparc/config.in index cb27cf77b..434345d1b 100644 --- a/arch/sparc/config.in +++ b/arch/sparc/config.in @@ -1,4 +1,4 @@ -# $Id: config.in,v 1.107 2001/01/06 00:46:44 davem Exp $ +# $Id: config.in,v 1.108 2001/02/18 09:02:59 davem Exp $ # For a description of the syntax of this configuration file, # see Documentation/kbuild/config-language.txt. # @@ -183,8 +183,8 @@ if [ "$CONFIG_SCSI" != "n" ]; then mainmenu_option next_comment comment 'SCSI low-level drivers' - tristate 'Sparc ESP Scsi Driver' CONFIG_SCSI_SUNESP $CONFIG_SCSI - tristate 'PTI Qlogic,ISP Driver' CONFIG_SCSI_QLOGICPTI $CONFIG_SCSI + dep_tristate 'Sparc ESP Scsi Driver' CONFIG_SCSI_SUNESP $CONFIG_SCSI + dep_tristate 'PTI Qlogic,ISP Driver' CONFIG_SCSI_QLOGICPTI $CONFIG_SCSI endmenu fi endmenu diff --git a/arch/sparc/kernel/ebus.c b/arch/sparc/kernel/ebus.c index 00c465b10..d8800b0f3 100644 --- a/arch/sparc/kernel/ebus.c +++ b/arch/sparc/kernel/ebus.c @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.15 2000/11/08 05:06:21 davem Exp $ +/* $Id: ebus.c,v 1.16 2001/02/13 01:16:43 davem Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -11,7 +11,7 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/string.h> #include <asm/system.h> diff --git a/arch/sparc/kernel/ioport.c b/arch/sparc/kernel/ioport.c index 41ef9737e..36ce64d83 100644 --- a/arch/sparc/kernel/ioport.c +++ b/arch/sparc/kernel/ioport.c @@ -1,4 +1,4 @@ -/* $Id: ioport.c,v 1.42 2000/12/05 00:56:36 anton Exp $ +/* $Id: ioport.c,v 1.44 2001/02/13 04:07:38 davem Exp $ * ioport.c: Simple io mapping allocator. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -32,7 +32,7 @@ #include <linux/types.h> #include <linux/ioport.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/pci.h> /* struct pci_dev */ #include <linux/proc_fs.h> @@ -225,7 +225,7 @@ _sparc_ioremap(struct resource *res, u32 bus, u32 pa, int sz) * XXX Playing with implementation details here. * On sparc64 Ebus has resources with precise boundaries. * We share drivers with sparc64. Too clever drivers use - * start of a resource instead of a base adress. + * start of a resource instead of a base address. * * XXX-2 This may be not valid anymore, clean when * interface to sbus_ioremap() is resolved. diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c index eaa7fc4a3..56bea7a28 100644 --- a/arch/sparc/kernel/irq.c +++ b/arch/sparc/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.109 2000/08/31 10:00:39 anton Exp $ +/* $Id: irq.c,v 1.110 2001/02/13 01:16:43 davem Exp $ * arch/sparc/kernel/irq.c: Interrupt request handling routines. On the * Sparc the IRQ's are basically 'cast in stone' * and you are supposed to probe the prom's device @@ -19,7 +19,7 @@ #include <linux/signal.h> #include <linux/sched.h> #include <linux/interrupt.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/init.h> #include <linux/smp.h> diff --git a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c index 9bc82a518..6ccc27153 100644 --- a/arch/sparc/kernel/pcic.c +++ b/arch/sparc/kernel/pcic.c @@ -1,4 +1,4 @@ -/* $Id: pcic.c,v 1.21 2001/01/18 00:23:00 davem Exp $ +/* $Id: pcic.c,v 1.22 2001/02/13 01:16:43 davem Exp $ * pcic.c: Sparc/PCI controller support * * Copyright (C) 1998 V. Roganov and G. Raiko @@ -15,7 +15,7 @@ #include <linux/types.h> #include <linux/init.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/ebus.h> #include <asm/sbus.h> /* for sanity check... */ diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index 878147763..1820eb12a 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.154 2000/10/05 06:12:57 anton Exp $ +/* $Id: process.c,v 1.155 2001/02/13 01:16:43 davem Exp $ * linux/arch/sparc/kernel/process.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -19,7 +19,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/config.h> diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index baed3a125..cba111a71 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c @@ -5,7 +5,7 @@ * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, * and David Mosberger. * - * Added Linux support -miguel (wierd, eh?, the orignal code was meant + * Added Linux support -miguel (weird, eh?, the orignal code was meant * to emulate SunOS). */ diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index aef0869e0..6cc594963 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.122 2001/01/01 01:46:15 davem Exp $ +/* $Id: setup.c,v 1.123 2001/02/13 01:16:43 davem Exp $ * linux/arch/sparc/kernel/setup.c * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -12,7 +12,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/smp.h> #include <linux/user.h> #include <linux/a.out.h> diff --git a/arch/sparc/kernel/sun4c_irq.c b/arch/sparc/kernel/sun4c_irq.c index 3f33ca265..90fbfd819 100644 --- a/arch/sparc/kernel/sun4c_irq.c +++ b/arch/sparc/kernel/sun4c_irq.c @@ -17,7 +17,7 @@ #include <linux/signal.h> #include <linux/sched.h> #include <linux/interrupt.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/init.h> #include <asm/ptrace.h> diff --git a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c index 4ce99b06b..6981f4d10 100644 --- a/arch/sparc/kernel/sun4d_irq.c +++ b/arch/sparc/kernel/sun4d_irq.c @@ -1,4 +1,4 @@ -/* $Id: sun4d_irq.c,v 1.26 2000/07/26 01:04:03 davem Exp $ +/* $Id: sun4d_irq.c,v 1.27 2001/02/13 01:16:43 davem Exp $ * arch/sparc/kernel/sun4d_irq.c: * SS1000/SC2000 interrupt handling. * @@ -14,7 +14,7 @@ #include <linux/signal.h> #include <linux/sched.h> #include <linux/interrupt.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> #include <linux/init.h> #include <linux/smp.h> diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index d14dbc2ac..098b2e58d 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c @@ -287,11 +287,11 @@ void __init smp4d_boot_cpus(void) smp_highest_cpu = i; } } - SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, (bogosum + 2500)/500000, ((bogosum + 2500)/5000)%100)); + SMP_PRINTK(("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, bogosum/(500000/HZ), (bogosum/(5000/HZ))%100)); printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, - (bogosum + 2500)/500000, - ((bogosum + 2500)/5000)%100); + bogosum/(500000/HZ), + (bogosum/(5000/HZ))%100); smp_activated = 1; smp_num_cpus = cpucount + 1; } diff --git a/arch/sparc/kernel/sun4m_irq.c b/arch/sparc/kernel/sun4m_irq.c index 2c79caf33..0d0477403 100644 --- a/arch/sparc/kernel/sun4m_irq.c +++ b/arch/sparc/kernel/sun4m_irq.c @@ -18,7 +18,7 @@ #include <linux/sched.h> #include <linux/smp.h> #include <linux/interrupt.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/init.h> #include <linux/ioport.h> diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 230a95e6b..04e3d65fa 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c @@ -256,8 +256,8 @@ void __init smp4m_boot_cpus(void) } printk("Total of %d Processors activated (%lu.%02lu BogoMIPS).\n", cpucount + 1, - (bogosum + 2500)/500000, - ((bogosum + 2500)/5000)%100); + bogosum/(500000/HZ), + (bogosum/(5000/HZ))%100); smp_activated = 1; smp_num_cpus = cpucount + 1; } diff --git a/arch/sparc/kernel/sys_sunos.c b/arch/sparc/kernel/sys_sunos.c index 24ae3829d..adaae6062 100644 --- a/arch/sparc/kernel/sys_sunos.c +++ b/arch/sparc/kernel/sys_sunos.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos.c,v 1.131 2001/01/24 21:05:12 davem Exp $ +/* $Id: sys_sunos.c,v 1.132 2001/02/13 01:16:43 davem Exp $ * sys_sunos.c: SunOS specific syscall compatibility support. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -28,7 +28,7 @@ #include <linux/utsname.h> #include <linux/major.h> #include <linux/stat.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/pagemap.h> #include <linux/errno.h> #include <linux/smp.h> diff --git a/arch/sparc/mm/io-unit.c b/arch/sparc/mm/io-unit.c index 1679384d6..be4522e1f 100644 --- a/arch/sparc/mm/io-unit.c +++ b/arch/sparc/mm/io-unit.c @@ -1,4 +1,4 @@ -/* $Id: io-unit.c,v 1.22 2000/08/09 00:00:15 davem Exp $ +/* $Id: io-unit.c,v 1.23 2001/02/13 01:16:43 davem Exp $ * io-unit.c: IO-UNIT specific routines for memory management. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -7,7 +7,7 @@ #include <linux/config.h> #include <linux/kernel.h> #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/spinlock.h> #include <asm/scatterlist.h> #include <asm/pgalloc.h> diff --git a/arch/sparc/mm/iommu.c b/arch/sparc/mm/iommu.c index 058fdd0d1..3bd631f36 100644 --- a/arch/sparc/mm/iommu.c +++ b/arch/sparc/mm/iommu.c @@ -1,4 +1,4 @@ -/* $Id: iommu.c,v 1.20 2000/08/09 00:00:15 davem Exp $ +/* $Id: iommu.c,v 1.21 2001/02/13 01:16:43 davem Exp $ * iommu.c: IOMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -11,7 +11,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/scatterlist.h> #include <asm/pgalloc.h> #include <asm/pgtable.h> diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index 0dba7d0a3..a5b2b117b 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -1,4 +1,4 @@ -/* $Id: srmmu.c,v 1.225 2000/11/30 08:37:31 anton Exp $ +/* $Id: srmmu.c,v 1.226 2001/02/13 01:16:44 davem Exp $ * srmmu.c: SRMMU specific routines for memory management. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) @@ -11,7 +11,7 @@ #include <linux/config.h> #include <linux/kernel.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/pagemap.h> #include <linux/init.h> diff --git a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c index 1b32dd8ef..ef5157b05 100644 --- a/arch/sparc/mm/sun4c.c +++ b/arch/sparc/mm/sun4c.c @@ -353,7 +353,7 @@ void __init sun4c_probe_vac(void) break; default: - prom_printf("Cannot initialize VAC - wierd sun4 model idprom->id_machtype = %d", idprom->id_machtype); + prom_printf("Cannot initialize VAC - weird sun4 model idprom->id_machtype = %d", idprom->id_machtype); prom_halt(); }; } else { diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index 5c208915b..2d67b5e54 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -161,15 +161,15 @@ if [ "$CONFIG_SCSI" != "n" ]; then mainmenu_option next_comment comment 'SCSI low-level drivers' - tristate 'Sparc ESP Scsi Driver' CONFIG_SCSI_SUNESP $CONFIG_SCSI - tristate 'PTI Qlogic, ISP Driver' CONFIG_SCSI_QLOGICPTI $CONFIG_SCSI + dep_tristate 'Sparc ESP Scsi Driver' CONFIG_SCSI_SUNESP $CONFIG_SCSI + dep_tristate 'PTI Qlogic, ISP Driver' CONFIG_SCSI_QLOGICPTI $CONFIG_SCSI if [ "$CONFIG_PCI" != "n" ]; then dep_tristate 'Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX $CONFIG_SCSI if [ "$CONFIG_SCSI_AIC7XXX" != "n" ]; then bool ' Enable tagged command queueing (TCQ) by default' CONFIG_AIC7XXX_TAGGED_QUEUEING int ' Maximum number of TCQ commands per device' CONFIG_AIC7XXX_CMDS_PER_DEVICE 8 - bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS N + bool ' Collect statistics to report in /proc' CONFIG_AIC7XXX_PROC_STATS int ' Delay in seconds after SCSI bus reset' CONFIG_AIC7XXX_RESET_DELAY 5 fi dep_tristate 'NCR53C8XX SCSI support' CONFIG_SCSI_NCR53C8XX $CONFIG_SCSI @@ -197,6 +197,8 @@ if [ "$CONFIG_SCSI" != "n" ]; then fi endmenu +source drivers/message/fusion/Config.in + source drivers/fc4/Config.in if [ "$CONFIG_PCI" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then diff --git a/arch/sparc64/kernel/binfmt_aout32.c b/arch/sparc64/kernel/binfmt_aout32.c index b066b8b96..bda2fb6a5 100644 --- a/arch/sparc64/kernel/binfmt_aout32.c +++ b/arch/sparc64/kernel/binfmt_aout32.c @@ -23,7 +23,7 @@ #include <linux/fcntl.h> #include <linux/ptrace.h> #include <linux/user.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/binfmts.h> #include <linux/personality.h> #include <linux/init.h> diff --git a/arch/sparc64/kernel/ebus.c b/arch/sparc64/kernel/ebus.c index e175fac27..6dbe45a6c 100644 --- a/arch/sparc64/kernel/ebus.c +++ b/arch/sparc64/kernel/ebus.c @@ -1,4 +1,4 @@ -/* $Id: ebus.c,v 1.53 2000/11/08 05:08:23 davem Exp $ +/* $Id: ebus.c,v 1.54 2001/02/13 01:16:44 davem Exp $ * ebus.c: PCI to EBus bridge device. * * Copyright (C) 1997 Eddie C. Dost (ecd@skynet.be) @@ -9,7 +9,7 @@ #include <linux/kernel.h> #include <linux/types.h> #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/string.h> #include <asm/system.h> diff --git a/arch/sparc64/kernel/ioctl32.c b/arch/sparc64/kernel/ioctl32.c index 67c1ec6d0..e2cdc3613 100644 --- a/arch/sparc64/kernel/ioctl32.c +++ b/arch/sparc64/kernel/ioctl32.c @@ -1,4 +1,4 @@ -/* $Id: ioctl32.c,v 1.105 2001/01/18 04:47:44 davem Exp $ +/* $Id: ioctl32.c,v 1.107 2001/02/13 01:16:44 davem Exp $ * ioctl32.c: Conversion between 32bit and 64bit native ioctls. * * Copyright (C) 1997-2000 Jakub Jelinek (jakub@redhat.com) @@ -16,7 +16,7 @@ #include <linux/smp_lock.h> #include <linux/ioctl.h> #include <linux/if.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/hdreg.h> #include <linux/raid/md.h> #include <linux/kd.h> @@ -3770,7 +3770,7 @@ IOCTL_TABLE_END unsigned int ioctl32_hash_table[1024]; -extern inline unsigned long ioctl32_hash(unsigned long cmd) +static inline unsigned long ioctl32_hash(unsigned long cmd) { return ((cmd >> 6) ^ (cmd >> 4) ^ cmd) & 0x3ff; } diff --git a/arch/sparc64/kernel/irq.c b/arch/sparc64/kernel/irq.c index 92bd3ed88..1bd29505b 100644 --- a/arch/sparc64/kernel/irq.c +++ b/arch/sparc64/kernel/irq.c @@ -1,4 +1,4 @@ -/* $Id: irq.c,v 1.94 2000/09/21 06:27:10 anton Exp $ +/* $Id: irq.c,v 1.95 2001/02/13 01:16:44 davem Exp $ * irq.c: UltraSparc IRQ handling/init/registry. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) @@ -13,7 +13,7 @@ #include <linux/signal.h> #include <linux/mm.h> #include <linux/interrupt.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/random.h> /* XXX ADD add_foo_randomness() calls... -DaveM */ #include <linux/init.h> #include <linux/delay.h> diff --git a/arch/sparc64/kernel/pci_common.c b/arch/sparc64/kernel/pci_common.c index 14473724f..ff7060c0a 100644 --- a/arch/sparc64/kernel/pci_common.c +++ b/arch/sparc64/kernel/pci_common.c @@ -1,11 +1,11 @@ -/* $Id: pci_common.c,v 1.12 2000/05/01 06:32:49 davem Exp $ +/* $Id: pci_common.c,v 1.13 2001/02/13 01:16:44 davem Exp $ * pci_common.c: PCI controller common support. * * Copyright (C) 1999 David S. Miller (davem@redhat.com) */ #include <linux/string.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/init.h> #include <asm/pbm.h> diff --git a/arch/sparc64/kernel/pci_psycho.c b/arch/sparc64/kernel/pci_psycho.c index fbd7832cd..7a671158e 100644 --- a/arch/sparc64/kernel/pci_psycho.c +++ b/arch/sparc64/kernel/pci_psycho.c @@ -1,4 +1,4 @@ -/* $Id: pci_psycho.c,v 1.18 2001/01/11 16:26:45 davem Exp $ +/* $Id: pci_psycho.c,v 1.19 2001/02/13 01:16:44 davem Exp $ * pci_psycho.c: PSYCHO/U2P specific PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) @@ -10,7 +10,7 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/pbm.h> #include <asm/iommu.h> diff --git a/arch/sparc64/kernel/pci_sabre.c b/arch/sparc64/kernel/pci_sabre.c index ec74a3696..cce2e5467 100644 --- a/arch/sparc64/kernel/pci_sabre.c +++ b/arch/sparc64/kernel/pci_sabre.c @@ -1,4 +1,4 @@ -/* $Id: pci_sabre.c,v 1.22 2001/01/16 13:03:48 anton Exp $ +/* $Id: pci_sabre.c,v 1.23 2001/02/13 01:16:44 davem Exp $ * pci_sabre.c: Sabre specific PCI controller support. * * Copyright (C) 1997, 1998, 1999 David S. Miller (davem@caipfs.rutgers.edu) @@ -10,7 +10,7 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/apb.h> #include <asm/pbm.h> diff --git a/arch/sparc64/kernel/pci_schizo.c b/arch/sparc64/kernel/pci_schizo.c index 9299c2531..daac0d783 100644 --- a/arch/sparc64/kernel/pci_schizo.c +++ b/arch/sparc64/kernel/pci_schizo.c @@ -1,4 +1,4 @@ -/* $Id: pci_schizo.c,v 1.2 2001/01/12 02:43:30 davem Exp $ +/* $Id: pci_schizo.c,v 1.3 2001/02/13 01:16:44 davem Exp $ * pci_schizo.c: SCHIZO specific PCI controller support. * * Copyright (C) 2001 David S. Miller (davem@redhat.com) @@ -8,7 +8,7 @@ #include <linux/types.h> #include <linux/pci.h> #include <linux/init.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/pbm.h> #include <asm/iommu.h> diff --git a/arch/sparc64/kernel/process.c b/arch/sparc64/kernel/process.c index 4534ad59b..3fc337d71 100644 --- a/arch/sparc64/kernel/process.c +++ b/arch/sparc64/kernel/process.c @@ -1,4 +1,4 @@ -/* $Id: process.c,v 1.113 2000/11/08 08:14:58 davem Exp $ +/* $Id: process.c,v 1.114 2001/02/13 01:16:44 davem Exp $ * arch/sparc64/kernel/process.c * * Copyright (C) 1995, 1996 David S. Miller (davem@caip.rutgers.edu) @@ -22,7 +22,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/user.h> #include <linux/a.out.h> #include <linux/config.h> diff --git a/arch/sparc64/kernel/ptrace.c b/arch/sparc64/kernel/ptrace.c index 977d0f35a..7f791c4d0 100644 --- a/arch/sparc64/kernel/ptrace.c +++ b/arch/sparc64/kernel/ptrace.c @@ -6,7 +6,7 @@ * Based upon code written by Ross Biro, Linus Torvalds, Bob Manson, * and David Mosberger. * - * Added Linux support -miguel (wierd, eh?, the orignal code was meant + * Added Linux support -miguel (weird, eh?, the orignal code was meant * to emulate SunOS). */ diff --git a/arch/sparc64/kernel/sbus.c b/arch/sparc64/kernel/sbus.c index 7baebc89e..30cac5e00 100644 --- a/arch/sparc64/kernel/sbus.c +++ b/arch/sparc64/kernel/sbus.c @@ -1,4 +1,4 @@ -/* $Id: sbus.c,v 1.12 2000/09/21 06:25:14 anton Exp $ +/* $Id: sbus.c,v 1.13 2001/02/13 01:16:44 davem Exp $ * sbus.c: UltraSparc SBUS controller support. * * Copyright (C) 1999 David S. Miller (davem@redhat.com) @@ -8,7 +8,7 @@ #include <linux/types.h> #include <linux/mm.h> #include <linux/spinlock.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/init.h> #include <asm/page.h> diff --git a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c index 11d29f21f..7817f4566 100644 --- a/arch/sparc64/kernel/setup.c +++ b/arch/sparc64/kernel/setup.c @@ -1,4 +1,4 @@ -/* $Id: setup.c,v 1.58 2001/01/01 01:46:15 davem Exp $ +/* $Id: setup.c,v 1.59 2001/02/13 01:16:44 davem Exp $ * linux/arch/sparc64/kernel/setup.c * * Copyright (C) 1995,1996 David S. Miller (davem@caip.rutgers.edu) @@ -12,7 +12,7 @@ #include <linux/stddef.h> #include <linux/unistd.h> #include <linux/ptrace.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/smp.h> #include <linux/user.h> #include <linux/a.out.h> diff --git a/arch/sparc64/kernel/starfire.c b/arch/sparc64/kernel/starfire.c index 6899e7c4a..4d89be41e 100644 --- a/arch/sparc64/kernel/starfire.c +++ b/arch/sparc64/kernel/starfire.c @@ -1,4 +1,4 @@ -/* $Id: starfire.c,v 1.8 2000/10/27 18:36:47 anton Exp $ +/* $Id: starfire.c,v 1.9 2001/02/13 01:16:44 davem Exp $ * starfire.c: Starfire/E10000 support. * * Copyright (C) 1998 David S. Miller (davem@redhat.com) @@ -6,7 +6,7 @@ */ #include <linux/kernel.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <asm/page.h> #include <asm/oplib.h> diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c index 391979c87..e3cd81c97 100644 --- a/arch/sparc64/kernel/sys_sparc.c +++ b/arch/sparc64/kernel/sys_sparc.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc.c,v 1.47 2000/11/29 05:56:12 anton Exp $ +/* $Id: sys_sparc.c,v 1.48 2001/02/13 01:16:44 davem Exp $ * linux/arch/sparc64/kernel/sys_sparc.c * * This file contains various random system calls that @@ -21,7 +21,7 @@ #include <linux/utsname.h> #include <linux/smp.h> #include <linux/smp_lock.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/ipc.h> #include <linux/personality.h> diff --git a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c index 184b4169d..d68b75cab 100644 --- a/arch/sparc64/kernel/sys_sparc32.c +++ b/arch/sparc64/kernel/sys_sparc32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sparc32.c,v 1.172 2001/01/24 21:05:13 davem Exp $ +/* $Id: sys_sparc32.c,v 1.173 2001/02/13 01:16:44 davem Exp $ * sys_sparc32.c: Conversion between 32bit and 64bit native syscalls. * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -25,7 +25,7 @@ #include <linux/sem.h> #include <linux/msg.h> #include <linux/shm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/uio.h> #include <linux/nfs_fs.h> #include <linux/smb_fs.h> diff --git a/arch/sparc64/kernel/sys_sunos32.c b/arch/sparc64/kernel/sys_sunos32.c index 58e4704c4..bfbd8841c 100644 --- a/arch/sparc64/kernel/sys_sunos32.c +++ b/arch/sparc64/kernel/sys_sunos32.c @@ -1,4 +1,4 @@ -/* $Id: sys_sunos32.c,v 1.56 2001/01/04 05:35:48 davem Exp $ +/* $Id: sys_sunos32.c,v 1.57 2001/02/13 01:16:44 davem Exp $ * sys_sunos32.c: SunOS binary compatability layer on sparc64. * * Copyright (C) 1995, 1996, 1997 David S. Miller (davem@caip.rutgers.edu) @@ -27,7 +27,7 @@ #include <linux/utsname.h> #include <linux/major.h> #include <linux/stat.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/pagemap.h> #include <linux/errno.h> #include <linux/smp.h> diff --git a/arch/sparc64/kernel/traps.c b/arch/sparc64/kernel/traps.c index 2562df1f3..cc703d3bc 100644 --- a/arch/sparc64/kernel/traps.c +++ b/arch/sparc64/kernel/traps.c @@ -1,4 +1,4 @@ -/* $Id: traps.c,v 1.68 2000/11/22 06:50:37 davem Exp $ +/* $Id: traps.c,v 1.70 2001/02/09 05:46:44 davem Exp $ * arch/sparc64/kernel/traps.c * * Copyright (C) 1995,1997 David S. Miller (davem@caip.rutgers.edu) @@ -639,6 +639,30 @@ void user_instruction_dump (unsigned int *pc) printk("\n"); } +void show_trace_task(struct task_struct *tsk) +{ + unsigned long pc, fp; + unsigned long task_base = (unsigned long)tsk; + struct reg_window *rw; + int count = 0; + + if (!tsk) + return; + + fp = tsk->thread.ksp + STACK_BIAS; + do { + /* Bogus frame pointer? */ + if (fp < (task_base + sizeof(struct task_struct)) || + fp >= (task_base + (2 * PAGE_SIZE))) + break; + rw = (struct reg_window *)fp; + pc = rw->ins[7]; + printk("[%016lx] ", pc); + fp = rw->ins[6] + STACK_BIAS; + } while (++count < 16); + printk("\n"); +} + void die_if_kernel(char *str, struct pt_regs *regs) { extern void __show_regs(struct pt_regs * regs); diff --git a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c index 289092756..15407f4f4 100644 --- a/arch/sparc64/mm/init.c +++ b/arch/sparc64/mm/init.c @@ -1,4 +1,4 @@ -/* $Id: init.c,v 1.161 2000/12/09 20:16:58 davem Exp $ +/* $Id: init.c,v 1.162 2001/02/13 01:16:44 davem Exp $ * arch/sparc64/mm/init.c * * Copyright (C) 1996-1999 David S. Miller (davem@caip.rutgers.edu) @@ -12,7 +12,7 @@ #include <linux/init.h> #include <linux/bootmem.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/blk.h> #include <linux/swap.h> #include <linux/swapctl.h> diff --git a/arch/sparc64/mm/modutil.c b/arch/sparc64/mm/modutil.c index b8e2833f0..41bd1f077 100644 --- a/arch/sparc64/mm/modutil.c +++ b/arch/sparc64/mm/modutil.c @@ -1,11 +1,11 @@ -/* $Id: modutil.c,v 1.6 2000/06/26 23:20:24 davem Exp $ +/* $Id: modutil.c,v 1.7 2001/02/13 01:16:44 davem Exp $ * arch/sparc64/mm/modutil.c * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Based upon code written by Linus Torvalds and others. */ -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <asm/uaccess.h> diff --git a/arch/sparc64/solaris/fs.c b/arch/sparc64/solaris/fs.c index e80d6b13f..8ca96f5b2 100644 --- a/arch/sparc64/solaris/fs.c +++ b/arch/sparc64/solaris/fs.c @@ -1,4 +1,4 @@ -/* $Id: fs.c,v 1.23 2000/08/29 07:01:54 davem Exp $ +/* $Id: fs.c,v 1.24 2001/02/13 01:16:44 davem Exp $ * fs.c: fs related syscall emulation for Solaris * * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -9,7 +9,7 @@ #include <linux/types.h> #include <linux/sched.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/fs.h> #include <linux/mm.h> #include <linux/file.h> diff --git a/arch/sparc64/solaris/socket.c b/arch/sparc64/solaris/socket.c index e016e4b1a..e40b38092 100644 --- a/arch/sparc64/solaris/socket.c +++ b/arch/sparc64/solaris/socket.c @@ -1,4 +1,4 @@ -/* $Id: socket.c,v 1.4 2000/11/18 02:11:00 davem Exp $ +/* $Id: socket.c,v 1.5 2001/02/13 01:16:44 davem Exp $ * socket.c: Socket syscall emulation for Solaris 2.6+ * * Copyright (C) 1998 Jakub Jelinek (jj@ultra.linux.cz) @@ -10,7 +10,7 @@ #include <linux/types.h> #include <linux/smp_lock.h> #include <linux/mm.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/socket.h> #include <linux/file.h> diff --git a/arch/sparc64/solaris/socksys.c b/arch/sparc64/solaris/socksys.c index bf2182381..a8e695c04 100644 --- a/arch/sparc64/solaris/socksys.c +++ b/arch/sparc64/solaris/socksys.c @@ -1,4 +1,4 @@ -/* $Id: socksys.c,v 1.17 2000/10/19 00:49:53 davem Exp $ +/* $Id: socksys.c,v 1.18 2001/02/13 01:16:44 davem Exp $ * socksys.c: /dev/inet/ stuff for Solaris emulation. * * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) @@ -16,7 +16,7 @@ #include <linux/file.h> #include <linux/init.h> #include <linux/poll.h> -#include <linux/malloc.h> +#include <linux/slab.h> #include <linux/in.h> #include <linux/devfs_fs_kernel.h> |