diff options
Diffstat (limited to 'arch/s390/kernel')
38 files changed, 15538 insertions, 0 deletions
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile new file mode 100644 index 000000000000..b41e0e199a7c --- /dev/null +++ b/arch/s390/kernel/Makefile @@ -0,0 +1,31 @@ +# +# Makefile for the linux kernel. +# + +EXTRA_AFLAGS := -traditional + +obj-y := bitmap.o traps.o time.o process.o \ + setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \ + semaphore.o s390_ext.o debug.o profile.o irq.o + +extra-$(CONFIG_ARCH_S390_31) += head.o +extra-$(CONFIG_ARCH_S390X) += head64.o +extra-y += init_task.o vmlinux.lds + +obj-$(CONFIG_MODULES) += s390_ksyms.o module.o +obj-$(CONFIG_SMP) += smp.o + +obj-$(CONFIG_S390_SUPPORT) += compat_linux.o compat_signal.o \ + compat_ioctl.o compat_wrapper.o \ + compat_exec_domain.o +obj-$(CONFIG_BINFMT_ELF32) += binfmt_elf32.o + +obj-$(CONFIG_ARCH_S390_31) += entry.o reipl.o +obj-$(CONFIG_ARCH_S390X) += entry64.o reipl64.o + +obj-$(CONFIG_VIRT_TIMER) += vtime.o + +# +# This is just to get the dependencies... +# +binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c new file mode 100644 index 000000000000..3f7018e9dbe4 --- /dev/null +++ b/arch/s390/kernel/asm-offsets.c @@ -0,0 +1,49 @@ +/* + * Generate definitions needed by assembly language modules. + * This code generates raw asm output which is post-processed to extract + * and format the required data. + */ + +#include <linux/config.h> +#include <linux/sched.h> + +/* Use marker if you need to separate the values later */ + +#define DEFINE(sym, val, marker) \ + asm volatile("\n->" #sym " %0 " #val " " #marker : : "i" (val)) + +#define BLANK() asm volatile("\n->" : : ) + +int main(void) +{ + DEFINE(__THREAD_info, offsetof(struct task_struct, thread_info),); + DEFINE(__THREAD_ksp, offsetof(struct task_struct, thread.ksp),); + DEFINE(__THREAD_per, offsetof(struct task_struct, thread.per_info),); + DEFINE(__THREAD_mm_segment, + offsetof(struct task_struct, thread.mm_segment),); + BLANK(); + DEFINE(__TASK_pid, offsetof(struct task_struct, pid),); + BLANK(); + DEFINE(__PER_atmid, offsetof(per_struct, lowcore.words.perc_atmid),); + DEFINE(__PER_address, offsetof(per_struct, lowcore.words.address),); + DEFINE(__PER_access_id, offsetof(per_struct, lowcore.words.access_id),); + BLANK(); + DEFINE(__TI_task, offsetof(struct thread_info, task),); + DEFINE(__TI_domain, offsetof(struct thread_info, exec_domain),); + DEFINE(__TI_flags, offsetof(struct thread_info, flags),); + DEFINE(__TI_cpu, offsetof(struct thread_info, cpu),); + DEFINE(__TI_precount, offsetof(struct thread_info, preempt_count),); + BLANK(); + DEFINE(__PT_ARGS, offsetof(struct pt_regs, args),); + DEFINE(__PT_PSW, offsetof(struct pt_regs, psw),); + DEFINE(__PT_GPRS, offsetof(struct pt_regs, gprs),); + DEFINE(__PT_ORIG_GPR2, offsetof(struct pt_regs, orig_gpr2),); + DEFINE(__PT_ILC, offsetof(struct pt_regs, ilc),); + DEFINE(__PT_TRAP, offsetof(struct pt_regs, trap),); + DEFINE(__PT_SIZE, sizeof(struct pt_regs),); + BLANK(); + DEFINE(__SF_BACKCHAIN, offsetof(struct stack_frame, back_chain),); + DEFINE(__SF_GPRS, offsetof(struct stack_frame, gprs),); + DEFINE(__SF_EMPTY, offsetof(struct stack_frame, empty1),); + return 0; +} diff --git a/arch/s390/kernel/binfmt_elf32.c b/arch/s390/kernel/binfmt_elf32.c new file mode 100644 index 000000000000..03ba5893f17b --- /dev/null +++ b/arch/s390/kernel/binfmt_elf32.c @@ -0,0 +1,210 @@ +/* + * 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 + +#include <linux/time.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 == EM_S390 || (x)->e_machine == EM_S390_OLD) \ + && (x)->e_ident[EI_CLASS] == ELF_CLASS) + +/* ELF register definitions */ +#define NUM_GPRS 16 +#define NUM_FPRS 16 +#define NUM_ACRS 16 + +/* For SVR4/S390 the function pointer to be registered with `atexit` is + passed in R14. */ +#define ELF_PLAT_INIT(_r, load_addr) \ + do { \ + _r->gprs[14] = 0; \ + } 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 (TASK_SIZE / 3 * 2) + +/* 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) dump_regs32(regs, &pr_reg); + +#define ELF_CORE_COPY_TASK_REGS(tsk, regs) dump_task_regs32(tsk, regs) + +#define ELF_CORE_COPY_FPREGS(tsk, fpregs) dump_task_fpu(tsk, fpregs) + +/* 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) + +#define SET_PERSONALITY(ex, ibcs2) \ +do { \ + if (ibcs2) \ + set_personality(PER_SVR4); \ + else if (current->personality != PER_LINUX32) \ + set_personality(PER_LINUX); \ + set_thread_flag(TIF_31BIT); \ +} while (0) + +#include "compat_linux.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; + +static inline int dump_regs32(struct pt_regs *ptregs, elf_gregset_t *regs) +{ + int i; + + memcpy(®s->psw.mask, &ptregs->psw.mask, 4); + memcpy(®s->psw.addr, (char *)&ptregs->psw.addr + 4, 4); + for (i = 0; i < NUM_GPRS; i++) + regs->gprs[i] = ptregs->gprs[i]; + save_access_regs(regs->acrs); + regs->orig_gpr2 = ptregs->orig_gpr2; + return 1; +} + +static inline int dump_task_regs32(struct task_struct *tsk, elf_gregset_t *regs) +{ + struct pt_regs *ptregs = __KSTK_PTREGS(tsk); + int i; + + memcpy(®s->psw.mask, &ptregs->psw.mask, 4); + memcpy(®s->psw.addr, (char *)&ptregs->psw.addr + 4, 4); + for (i = 0; i < NUM_GPRS; i++) + regs->gprs[i] = ptregs->gprs[i]; + memcpy(regs->acrs, tsk->thread.acrs, sizeof(regs->acrs)); + regs->orig_gpr2 = ptregs->orig_gpr2; + return 1; +} + +static inline int dump_task_fpu(struct task_struct *tsk, elf_fpregset_t *fpregs) +{ + if (tsk == current) + save_fp_regs((s390_fp_regs *) fpregs); + else + memcpy(fpregs, &tsk->thread.fp_regs, sizeof(elf_fpregset_t)); + return 1; +} + +#include <asm/processor.h> +#include <linux/module.h> +#include <linux/config.h> +#include <linux/elfcore.h> +#include <linux/binfmts.h> +#include <linux/compat.h> + +#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 compat_timeval pr_utime; /* User time */ + struct compat_timeval pr_stime; /* System time */ + struct compat_timeval pr_cutime; /* Cumulative user time */ + struct compat_timeval 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 init_elf_binfmt init_elf32_binfmt +*/ + +#undef start_thread +#define start_thread start_thread31 + +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 + +#undef cputime_to_timeval +#define cputime_to_timeval cputime_to_compat_timeval +static __inline__ void +cputime_to_compat_timeval(const cputime_t cputime, struct compat_timeval *value) +{ + value->tv_usec = cputime % 1000000; + value->tv_sec = cputime / 1000000; +} + +#include "../../../fs/binfmt_elf.c" + diff --git a/arch/s390/kernel/bitmap.S b/arch/s390/kernel/bitmap.S new file mode 100644 index 000000000000..dfb41f946e23 --- /dev/null +++ b/arch/s390/kernel/bitmap.S @@ -0,0 +1,56 @@ +/* + * 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 + + .globl _sb_findmap +_sb_findmap: + .byte 8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + .byte 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0 + diff --git a/arch/s390/kernel/compat_exec_domain.c b/arch/s390/kernel/compat_exec_domain.c new file mode 100644 index 000000000000..71d27c493568 --- /dev/null +++ b/arch/s390/kernel/compat_exec_domain.c @@ -0,0 +1,30 @@ +/* + * Support for 32-bit Linux for S390 personality. + * + * Copyright (C) 2000 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Gerhard Tonn (ton@de.ibm.com) + * + * + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/personality.h> +#include <linux/sched.h> + +struct exec_domain s390_exec_domain; + +static int __init +s390_init (void) +{ + s390_exec_domain.name = "Linux/s390"; + s390_exec_domain.handler = NULL; + s390_exec_domain.pers_low = PER_LINUX32; + s390_exec_domain.pers_high = PER_LINUX32; + s390_exec_domain.signal_map = default_exec_domain.signal_map; + s390_exec_domain.signal_invmap = default_exec_domain.signal_invmap; + register_exec_domain(&s390_exec_domain); + return 0; +} + +__initcall(s390_init); diff --git a/arch/s390/kernel/compat_ioctl.c b/arch/s390/kernel/compat_ioctl.c new file mode 100644 index 000000000000..96571ff7115d --- /dev/null +++ b/arch/s390/kernel/compat_ioctl.c @@ -0,0 +1,73 @@ +/* + * ioctl32.c: Conversion between 32bit and 64bit native ioctls. + * + * S390 version + * Copyright (C) 2000-2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Gerhard Tonn (ton@de.ibm.com) + * Arnd Bergmann (arndb@de.ibm.com) + * + * Original implementation from 32-bit Sparc compat code which is + * Copyright (C) 2000 Silicon Graphics, Inc. + * Written by Ulf Carlsson (ulfc@engr.sgi.com) + */ + +#include "compat_linux.h" +#define INCLUDES +#define CODE +#include "../../../fs/compat_ioctl.c" +#include <asm/dasd.h> +#include <asm/tape390.h> + +static int do_ioctl32_pointer(unsigned int fd, unsigned int cmd, + unsigned long arg, struct file *f) +{ + return sys_ioctl(fd, cmd, (unsigned long)compat_ptr(arg)); +} + +static int do_ioctl32_ulong(unsigned int fd, unsigned int cmd, + unsigned long arg, struct file *f) +{ + return sys_ioctl(fd, cmd, arg); +} + +#define COMPATIBLE_IOCTL(cmd) HANDLE_IOCTL((cmd),(ioctl_trans_handler_t)do_ioctl32_pointer) +#define ULONG_IOCTL(cmd) HANDLE_IOCTL((cmd),(ioctl_trans_handler_t)do_ioctl32_ulong) +#define HANDLE_IOCTL(cmd,handler) { (cmd), (ioctl_trans_handler_t)(handler), NULL }, + +struct ioctl_trans ioctl_start[] = { +/* architecture independent ioctls */ +#include <linux/compat_ioctl.h> +#define DECLARES +#include "../../../fs/compat_ioctl.c" + +/* s390 only ioctls */ +#if defined(CONFIG_DASD) || defined(CONFIG_DASD_MODULE) +COMPATIBLE_IOCTL(DASDAPIVER) +COMPATIBLE_IOCTL(BIODASDDISABLE) +COMPATIBLE_IOCTL(BIODASDENABLE) +COMPATIBLE_IOCTL(BIODASDRSRV) +COMPATIBLE_IOCTL(BIODASDRLSE) +COMPATIBLE_IOCTL(BIODASDSLCK) +COMPATIBLE_IOCTL(BIODASDINFO) +COMPATIBLE_IOCTL(BIODASDINFO2) +COMPATIBLE_IOCTL(BIODASDFMT) +COMPATIBLE_IOCTL(BIODASDPRRST) +COMPATIBLE_IOCTL(BIODASDQUIESCE) +COMPATIBLE_IOCTL(BIODASDRESUME) +COMPATIBLE_IOCTL(BIODASDPRRD) +COMPATIBLE_IOCTL(BIODASDPSRD) +COMPATIBLE_IOCTL(BIODASDGATTR) +COMPATIBLE_IOCTL(BIODASDSATTR) + +#endif + +#if defined(CONFIG_S390_TAPE) || defined(CONFIG_S390_TAPE_MODULE) +COMPATIBLE_IOCTL(TAPE390_DISPLAY) +#endif + +/* s390 doesn't need handlers here */ +COMPATIBLE_IOCTL(TIOCGSERIAL) +COMPATIBLE_IOCTL(TIOCSSERIAL) +}; + +int ioctl_table_size = ARRAY_SIZE(ioctl_start); diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c new file mode 100644 index 000000000000..614056222875 --- /dev/null +++ b/arch/s390/kernel/compat_linux.c @@ -0,0 +1,1045 @@ +/* + * 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) + * Thomas Spatzier (tspat@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/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/slab.h> +#include <linux/uio.h> +#include <linux/nfs_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/syscalls.h> +#include <linux/sysctl.h> +#include <linux/binfmts.h> +#include <linux/compat.h> +#include <linux/vfs.h> +#include <linux/ptrace.h> + +#include <asm/types.h> +#include <asm/ipc.h> +#include <asm/uaccess.h> +#include <asm/semaphore.h> + +#include <net/scm.h> +#include <net/sock.h> + +#include "compat_linux.h" + + +/* 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); +} + +static int groups16_to_user(u16 *grouplist, struct group_info *group_info) +{ + int i; + u16 group; + + for (i = 0; i < group_info->ngroups; i++) { + group = (u16)GROUP_AT(group_info, i); + if (put_user(group, grouplist+i)) + return -EFAULT; + } + + return 0; +} + +static int groups16_from_user(struct group_info *group_info, u16 *grouplist) +{ + int i; + u16 group; + + for (i = 0; i < group_info->ngroups; i++) { + if (get_user(group, grouplist+i)) + return -EFAULT; + GROUP_AT(group_info, i) = (gid_t)group; + } + + return 0; +} + +asmlinkage long sys32_getgroups16(int gidsetsize, u16 *grouplist) +{ + int i; + + if (gidsetsize < 0) + return -EINVAL; + + get_group_info(current->group_info); + i = current->group_info->ngroups; + if (gidsetsize) { + if (i > gidsetsize) { + i = -EINVAL; + goto out; + } + if (groups16_to_user(grouplist, current->group_info)) { + i = -EFAULT; + goto out; + } + } +out: + put_group_info(current->group_info); + return i; +} + +asmlinkage long sys32_setgroups16(int gidsetsize, u16 *grouplist) +{ + struct group_info *group_info; + int retval; + + if (!capable(CAP_SETGID)) + return -EPERM; + if ((unsigned)gidsetsize > NGROUPS_MAX) + return -EINVAL; + + group_info = groups_alloc(gidsetsize); + if (!group_info) + return -ENOMEM; + retval = groups16_from_user(group_info, grouplist); + if (retval) { + put_group_info(group_info); + return retval; + } + + retval = set_current_groups(group_info); + put_group_info(group_info); + + return retval; +} + +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. */ + +static inline long get_tv32(struct timeval *o, struct compat_timeval *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 compat_timeval *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))); +} + +/* + * sys32_ipc() is the de-multiplexer for the SysV IPC calls in 32bit emulation. + * + * This is really horribly ugly. + */ +asmlinkage long sys32_ipc(u32 call, int first, int second, int third, u32 ptr) +{ + if (call >> 16) /* hack for backward compatibility */ + return -EINVAL; + + call &= 0xffff; + + switch (call) { + case SEMTIMEDOP: + return compat_sys_semtimedop(first, compat_ptr(ptr), + second, compat_ptr(third)); + case SEMOP: + /* struct sembuf is the same on 32 and 64bit :)) */ + return sys_semtimedop(first, compat_ptr(ptr), + second, NULL); + case SEMGET: + return sys_semget(first, second, third); + case SEMCTL: + return compat_sys_semctl(first, second, third, + compat_ptr(ptr)); + case MSGSND: + return compat_sys_msgsnd(first, second, third, + compat_ptr(ptr)); + case MSGRCV: + return compat_sys_msgrcv(first, second, 0, third, + 0, compat_ptr(ptr)); + case MSGGET: + return sys_msgget((key_t) first, second); + case MSGCTL: + return compat_sys_msgctl(first, second, compat_ptr(ptr)); + case SHMAT: + return compat_sys_shmat(first, second, third, + 0, compat_ptr(ptr)); + case SHMDT: + return sys_shmdt(compat_ptr(ptr)); + case SHMGET: + return sys_shmget(first, (unsigned)second, third); + case SHMCTL: + return compat_sys_shmctl(first, second, compat_ptr(ptr)); + } + + return -ENOSYS; +} + +asmlinkage long 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 long 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); +} + +int cp_compat_stat(struct kstat *stat, struct compat_stat *statbuf) +{ + int err; + + if (!old_valid_dev(stat->dev) || !old_valid_dev(stat->rdev)) + return -EOVERFLOW; + + err = put_user(old_encode_dev(stat->dev), &statbuf->st_dev); + err |= put_user(stat->ino, &statbuf->st_ino); + err |= put_user(stat->mode, &statbuf->st_mode); + err |= put_user(stat->nlink, &statbuf->st_nlink); + err |= put_user(high2lowuid(stat->uid), &statbuf->st_uid); + err |= put_user(high2lowgid(stat->gid), &statbuf->st_gid); + err |= put_user(old_encode_dev(stat->rdev), &statbuf->st_rdev); + err |= put_user(stat->size, &statbuf->st_size); + err |= put_user(stat->atime.tv_sec, &statbuf->st_atime); + err |= put_user(stat->atime.tv_nsec, &statbuf->st_atime_nsec); + err |= put_user(stat->mtime.tv_sec, &statbuf->st_mtime); + err |= put_user(stat->mtime.tv_nsec, &statbuf->st_mtime_nsec); + err |= put_user(stat->ctime.tv_sec, &statbuf->st_ctime); + err |= put_user(stat->ctime.tv_nsec, &statbuf->st_ctime_nsec); + err |= put_user(stat->blksize, &statbuf->st_blksize); + err |= put_user(stat->blocks, &statbuf->st_blocks); +/* fixme + err |= put_user(0, &statbuf->__unused4[0]); + err |= put_user(0, &statbuf->__unused4[1]); +*/ + return err; +} + +struct sysinfo32 { + s32 uptime; + u32 loads[3]; + u32 totalram; + u32 freeram; + u32 sharedram; + u32 bufferram; + u32 totalswap; + u32 freeswap; + unsigned short procs; + unsigned short pads; + u32 totalhigh; + u32 freehigh; + unsigned int mem_unit; + char _f[8]; +}; + +asmlinkage long sys32_sysinfo(struct sysinfo32 __user *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); + err |= __put_user (s.totalhigh, &info->totalhigh); + err |= __put_user (s.freehigh, &info->freehigh); + err |= __put_user (s.mem_unit, &info->mem_unit); + if (err) + return -EFAULT; + return ret; +} + +asmlinkage long sys32_sched_rr_get_interval(compat_pid_t pid, + struct compat_timespec __user *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_compat_timespec(&t, interval)) + return -EFAULT; + return ret; +} + +asmlinkage long sys32_rt_sigprocmask(int how, compat_sigset_t __user *set, + compat_sigset_t __user *oset, size_t sigsetsize) +{ + sigset_t s; + compat_sigset_t s32; + int ret; + mm_segment_t old_fs = get_fs(); + + if (set) { + if (copy_from_user (&s32, set, sizeof(compat_sigset_t))) + 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(compat_sigset_t))) + return -EFAULT; + } + return 0; +} + +asmlinkage long sys32_rt_sigpending(compat_sigset_t __user *set, + size_t sigsetsize) +{ + sigset_t s; + compat_sigset_t 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(compat_sigset_t))) + return -EFAULT; + } + return ret; +} + +asmlinkage long +sys32_rt_sigqueueinfo(int pid, int sig, compat_siginfo_t __user *uinfo) +{ + siginfo_t info; + int ret; + mm_segment_t old_fs = get_fs(); + + if (copy_siginfo_from_user32(&info, uinfo)) + return -EFAULT; + set_fs (KERNEL_DS); + ret = sys_rt_sigqueueinfo(pid, sig, &info); + set_fs (old_fs); + return ret; +} + +/* + * 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 long +sys32_execve(struct pt_regs regs) +{ + int error; + char * filename; + + filename = getname(compat_ptr(regs.orig_gpr2)); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = compat_do_execve(filename, compat_ptr(regs.gprs[3]), + compat_ptr(regs.gprs[4]), ®s); + if (error == 0) + { + task_lock(current); + current->ptrace &= ~PT_DTRACE; + task_unlock(current); + 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 + +asmlinkage long +sys32_init_module(void __user *umod, unsigned long len, + const char __user *uargs) +{ + return sys_init_module(umod, len, uargs); +} + +asmlinkage long +sys32_delete_module(const char __user *name_user, unsigned int flags) +{ + return sys_delete_module(name_user, flags); +} + +#else /* CONFIG_MODULES */ + +asmlinkage long +sys32_init_module(void __user *umod, unsigned long len, + const char __user *uargs) +{ + return -ENOSYS; +} + +asmlinkage long +sys32_delete_module(const char __user *name_user, unsigned int flags) +{ + return -ENOSYS; +} + +#endif /* CONFIG_MODULES */ + +/* Translations due to time_t size differences. Which affects all + sorts of things, like timeval and itimerval. */ + +extern struct timezone sys_tz; + +asmlinkage long sys32_gettimeofday(struct compat_timeval *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; +} + +static inline long get_ts32(struct timespec *o, struct compat_timeval *i) +{ + long usec; + + if (!access_ok(VERIFY_READ, i, sizeof(*i))) + return -EFAULT; + if (__get_user(o->tv_sec, &i->tv_sec)) + return -EFAULT; + if (__get_user(usec, &i->tv_usec)) + return -EFAULT; + o->tv_nsec = usec * 1000; + return 0; +} + +asmlinkage long sys32_settimeofday(struct compat_timeval *tv, struct timezone *tz) +{ + struct timespec kts; + struct timezone ktz; + + if (tv) { + if (get_ts32(&kts, tv)) + return -EFAULT; + } + if (tz) { + if (copy_from_user(&ktz, tz, sizeof(ktz))) + return -EFAULT; + } + + return do_sys_settimeofday(tv ? &kts : NULL, tz ? &ktz : NULL); +} + +/* These are here just in case some old sparc32 binary calls it. */ +asmlinkage long sys32_pause(void) +{ + current->state = TASK_INTERRUPTIBLE; + schedule(); + return -ERESTARTNOHAND; +} + +asmlinkage long sys32_pread64(unsigned int fd, char *ubuf, + size_t count, u32 poshi, u32 poslo) +{ + if ((compat_ssize_t) count < 0) + return -EINVAL; + return sys_pread64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + +asmlinkage long sys32_pwrite64(unsigned int fd, const char *ubuf, + size_t count, u32 poshi, u32 poslo) +{ + if ((compat_ssize_t) count < 0) + return -EINVAL; + return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); +} + +asmlinkage compat_ssize_t sys32_readahead(int fd, u32 offhi, u32 offlo, s32 count) +{ + return sys_readahead(fd, ((loff_t)AA(offhi) << 32) | AA(offlo), count); +} + +asmlinkage long sys32_sendfile(int out_fd, int in_fd, compat_off_t *offset, size_t 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; +} + +asmlinkage long sys32_sendfile64(int out_fd, int in_fd, + compat_loff_t *offset, s32 count) +{ + mm_segment_t old_fs = get_fs(); + int ret; + loff_t lof; + + if (offset && get_user(lof, offset)) + return -EFAULT; + + set_fs(KERNEL_DS); + ret = sys_sendfile64(out_fd, in_fd, offset ? &lof : NULL, count); + set_fs(old_fs); + + if (offset && put_user(lof, offset)) + return -EFAULT; + + return ret; +} + +/* Handle adjtimex compatibility. */ + +struct timex32 { + u32 modes; + s32 offset, freq, maxerror, esterror; + s32 status, constant, precision, tolerance; + struct compat_timeval 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 long 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; +} + +#ifdef CONFIG_SYSCTL +struct __sysctl_args32 { + u32 name; + int nlen; + u32 oldval; + u32 oldlenp; + u32 newval; + u32 newlen; + u32 __unused[4]; +}; + +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; +} +#endif + +struct stat64_emu31 { + unsigned long long st_dev; + unsigned int __pad1; +#define STAT64_HAS_BROKEN_ST_INO 1 + u32 __st_ino; + unsigned int st_mode; + unsigned int st_nlink; + u32 st_uid; + u32 st_gid; + unsigned long long st_rdev; + unsigned int __pad3; + long st_size; + u32 st_blksize; + unsigned char __pad4[4]; + u32 __pad5; /* future possible st_blocks high bits */ + u32 st_blocks; /* Number 512-byte blocks allocated. */ + u32 st_atime; + u32 __pad6; + u32 st_mtime; + u32 __pad7; + u32 st_ctime; + u32 __pad8; /* will be high 32 bits of ctime someday */ + unsigned long st_ino; +}; + +static int cp_stat64(struct stat64_emu31 *ubuf, struct kstat *stat) +{ + struct stat64_emu31 tmp; + + memset(&tmp, 0, sizeof(tmp)); + + tmp.st_dev = huge_encode_dev(stat->dev); + tmp.st_ino = stat->ino; + tmp.__st_ino = (u32)stat->ino; + tmp.st_mode = stat->mode; + tmp.st_nlink = (unsigned int)stat->nlink; + tmp.st_uid = stat->uid; + tmp.st_gid = stat->gid; + tmp.st_rdev = huge_encode_dev(stat->rdev); + tmp.st_size = stat->size; + tmp.st_blksize = (u32)stat->blksize; + tmp.st_blocks = (u32)stat->blocks; + tmp.st_atime = (u32)stat->atime.tv_sec; + tmp.st_mtime = (u32)stat->mtime.tv_sec; + tmp.st_ctime = (u32)stat->ctime.tv_sec; + + return copy_to_user(ubuf,&tmp,sizeof(tmp)) ? -EFAULT : 0; +} + +asmlinkage long sys32_stat64(char * filename, struct stat64_emu31 * statbuf) +{ + struct kstat stat; + int ret = vfs_stat(filename, &stat); + if (!ret) + ret = cp_stat64(statbuf, &stat); + return ret; +} + +asmlinkage long sys32_lstat64(char * filename, struct stat64_emu31 * statbuf) +{ + struct kstat stat; + int ret = vfs_lstat(filename, &stat); + if (!ret) + ret = cp_stat64(statbuf, &stat); + return ret; +} + +asmlinkage long sys32_fstat64(unsigned long fd, struct stat64_emu31 * statbuf) +{ + struct kstat stat; + int ret = vfs_fstat(fd, &stat); + if (!ret) + ret = cp_stat64(statbuf, &stat); + 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) +{ + struct file * file = NULL; + unsigned long error = -EBADF; + + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); + if (!(flags & MAP_ANONYMOUS)) { + file = fget(fd); + if (!file) + goto out; + } + + down_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + if (!IS_ERR((void *) error) && error + len >= 0x80000000ULL) { + /* Result is out of bounds. */ + do_munmap(current->mm, addr, len); + error = -ENOMEM; + } + up_write(¤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; +} + +asmlinkage long sys32_read(unsigned int fd, char * buf, size_t count) +{ + if ((compat_ssize_t) count < 0) + return -EINVAL; + + return sys_read(fd, buf, count); +} + +asmlinkage long sys32_write(unsigned int fd, char * buf, size_t count) +{ + if ((compat_ssize_t) count < 0) + return -EINVAL; + + return sys_write(fd, buf, count); +} + +asmlinkage long sys32_clone(struct pt_regs regs) +{ + unsigned long clone_flags; + unsigned long newsp; + int *parent_tidptr, *child_tidptr; + + clone_flags = regs.gprs[3] & 0xffffffffUL; + newsp = regs.orig_gpr2 & 0x7fffffffUL; + parent_tidptr = (int *) (regs.gprs[4] & 0x7fffffffUL); + child_tidptr = (int *) (regs.gprs[5] & 0x7fffffffUL); + if (!newsp) + newsp = regs.gprs[15]; + return do_fork(clone_flags, newsp, ®s, 0, + parent_tidptr, child_tidptr); +} + +/* + * Wrapper function for sys_timer_create. + */ +extern asmlinkage long +sys_timer_create(clockid_t, struct sigevent *, timer_t *); + +asmlinkage long +sys32_timer_create(clockid_t which_clock, struct compat_sigevent *se32, + timer_t *timer_id) +{ + struct sigevent se; + timer_t ktimer_id; + mm_segment_t old_fs; + long ret; + + if (se32 == NULL) + return sys_timer_create(which_clock, NULL, timer_id); + + if (get_compat_sigevent(&se, se32)) + return -EFAULT; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + ret = sys_timer_create(which_clock, &se, &ktimer_id); + set_fs(old_fs); + + if (!ret) + ret = put_user (ktimer_id, timer_id); + + return ret; +} diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h new file mode 100644 index 000000000000..bf33dcfec7db --- /dev/null +++ b/arch/s390/kernel/compat_linux.h @@ -0,0 +1,197 @@ +#ifndef _ASM_S390X_S390_H +#define _ASM_S390X_S390_H + +#include <linux/config.h> +#include <linux/compat.h> +#include <linux/socket.h> +#include <linux/syscalls.h> +#include <linux/nfs_fs.h> +#include <linux/sunrpc/svc.h> +#include <linux/nfsd/nfsd.h> +#include <linux/nfsd/export.h> + +/* 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 */ +struct ipc_kludge_32 { + __u32 msgp; /* pointer */ + __s32 msgtyp; +}; + +struct old_sigaction32 { + __u32 sa_handler; /* Really a pointer, but need to deal with 32 bits */ + compat_old_sigset_t sa_mask; /* A 32 bit mask */ + __u32 sa_flags; + __u32 sa_restorer; /* Another 32 bit pointer */ +}; + +typedef struct compat_siginfo { + 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 { + timer_t _tid; /* timer id */ + int _overrun; /* overrun count */ + compat_sigval_t _sigval; /* same as below */ + int _sys_private; /* not to be passed to user */ + } _timer; + + /* POSIX.1b signals */ + struct { + pid_t _pid; /* sender's pid */ + uid_t _uid; /* sender's uid */ + compat_sigval_t _sigval; + } _rt; + + /* SIGCHLD */ + struct { + pid_t _pid; /* which child */ + uid_t _uid; /* sender's uid */ + int _status;/* exit code */ + compat_clock_t _utime; + compat_clock_t _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; +} compat_siginfo_t; + +/* + * 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 +#define si_tid _sifields._timer._tid +#define si_overrun _sifields._timer._overrun + +/* 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))); + +#define PSW32_MASK_PER 0x40000000UL +#define PSW32_MASK_DAT 0x04000000UL +#define PSW32_MASK_IO 0x02000000UL +#define PSW32_MASK_EXT 0x01000000UL +#define PSW32_MASK_KEY 0x00F00000UL +#define PSW32_MASK_MCHECK 0x00040000UL +#define PSW32_MASK_WAIT 0x00020000UL +#define PSW32_MASK_PSTATE 0x00010000UL +#define PSW32_MASK_ASC 0x0000C000UL +#define PSW32_MASK_CC 0x00003000UL +#define PSW32_MASK_PM 0x00000f00UL + +#define PSW32_ADDR_AMODE31 0x80000000UL +#define PSW32_ADDR_INSN 0x7FFFFFFFUL + +#define PSW32_BASE_BITS 0x00080000UL + +#define PSW32_ASC_PRIMARY 0x00000000UL +#define PSW32_ASC_ACCREG 0x00004000UL +#define PSW32_ASC_SECONDARY 0x00008000UL +#define PSW32_ASC_HOME 0x0000C000UL + +#define PSW32_USER_BITS (PSW32_BASE_BITS | PSW32_MASK_DAT | PSW32_ASC_HOME | \ + PSW32_MASK_IO | PSW32_MASK_EXT | PSW32_MASK_MCHECK | \ + PSW32_MASK_PSTATE) + +#define PSW32_MASK_MERGE(CURRENT,NEW) \ + (((CURRENT) & ~(PSW32_MASK_CC|PSW32_MASK_PM)) | \ + ((NEW) & (PSW32_MASK_CC|PSW32_MASK_PM))) + + +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[_COMPAT_NSIG_WORDS]; + __u32 sregs; /* pointer */ +}; + +/* asm/signal.h */ +struct sigaction32 { + __u32 sa_handler; /* pointer */ + __u32 sa_flags; + __u32 sa_restorer; /* pointer */ + compat_sigset_t sa_mask; /* mask last for extensibility */ +}; + +typedef struct { + __u32 ss_sp; /* pointer */ + int ss_flags; + compat_size_t ss_size; +} stack_t32; + +/* asm/ucontext.h */ +struct ucontext32 { + __u32 uc_flags; + __u32 uc_link; /* pointer */ + stack_t32 uc_stack; + _sigregs32 uc_mcontext; + compat_sigset_t uc_sigmask; /* mask last for extensibility */ +}; + +#endif /* _ASM_S390X_S390_H */ diff --git a/arch/s390/kernel/compat_ptrace.h b/arch/s390/kernel/compat_ptrace.h new file mode 100644 index 000000000000..419aef913ee1 --- /dev/null +++ b/arch/s390/kernel/compat_ptrace.h @@ -0,0 +1,83 @@ +#ifndef _PTRACE32_H +#define _PTRACE32_H + +#include "compat_linux.h" /* needed for _psw_t32 */ + +typedef struct { + __u32 cr[3]; +} per_cr_words32; + +typedef struct { + __u16 perc_atmid; /* 0x096 */ + __u32 address; /* 0x098 */ + __u8 access_id; /* 0x0a1 */ +} per_lowcore_words32; + +typedef struct { + union { + per_cr_words32 words; + } control_regs; + /* + * Use these flags instead of setting em_instruction_fetch + * directly they are used so that single stepping can be + * switched on & off while not affecting other tracing + */ + unsigned single_step : 1; + unsigned instruction_fetch : 1; + unsigned : 30; + /* + * These addresses are copied into cr10 & cr11 if single + * stepping is switched off + */ + __u32 starting_addr; + __u32 ending_addr; + union { + per_lowcore_words32 words; + } lowcore; +} per_struct32; + +struct user_regs_struct32 +{ + _psw_t32 psw; + u32 gprs[NUM_GPRS]; + u32 acrs[NUM_ACRS]; + u32 orig_gpr2; + s390_fp_regs fp_regs; + /* + * These per registers are in here so that gdb can modify them + * itself as there is no "official" ptrace interface for hardware + * watchpoints. This is the way intel does it. + */ + per_struct32 per_info; + u32 ieee_instruction_pointer; + /* Used to give failing instruction back to user for ieee exceptions */ +}; + +struct user32 { + /* We start with the registers, to mimic the way that "memory" + is returned from the ptrace(3,...) function. */ + struct user_regs_struct32 regs; /* Where the registers are actually stored */ + /* The rest of this junk is to help gdb figure out what goes where */ + u32 u_tsize; /* Text segment size (pages). */ + u32 u_dsize; /* Data segment size (pages). */ + u32 u_ssize; /* Stack segment size (pages). */ + u32 start_code; /* Starting virtual address of text. */ + u32 start_stack; /* Starting virtual address of stack area. + This is actually the bottom of the stack, + the top of the stack is always found in the + esp register. */ + s32 signal; /* Signal that caused the core dump. */ + u32 u_ar0; /* Used by gdb to help find the values for */ + /* the registers. */ + u32 magic; /* To uniquely identify a core file */ + char u_comm[32]; /* User command that was responsible */ +}; + +typedef struct +{ + __u32 len; + __u32 kernel_addr; + __u32 process_addr; +} ptrace_area_emu31; + +#endif /* _PTRACE32_H */ diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c new file mode 100644 index 000000000000..d05d65ac9694 --- /dev/null +++ b/arch/s390/kernel/compat_signal.c @@ -0,0 +1,648 @@ +/* + * 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/config.h> +#include <linux/compat.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 <linux/tty.h> +#include <linux/personality.h> +#include <linux/binfmts.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> +#include <asm/lowcore.h> +#include "compat_linux.h" +#include "compat_ptrace.h" + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + +typedef struct +{ + __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; + struct sigcontext32 sc; + _sigregs32 sregs; + int signo; + __u8 retcode[S390_SYSCALL_SIZE]; +} sigframe32; + +typedef struct +{ + __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; + __u8 retcode[S390_SYSCALL_SIZE]; + compat_siginfo_t info; + struct ucontext32 uc; +} rt_sigframe32; + +asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset)); + +int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from) +{ + int err; + + if (!access_ok (VERIFY_WRITE, to, sizeof(compat_siginfo_t))) + 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_RT >> 16: /* This is not generated by the kernel as of now. */ + case __SI_MESGQ >> 16: + err |= __put_user(from->si_int, &to->si_int); + /* fallthrough */ + 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((unsigned long) from->si_addr, + &to->si_addr); + break; + case __SI_POLL >> 16: + err |= __put_user(from->si_band, &to->si_band); + err |= __put_user(from->si_fd, &to->si_fd); + break; + case __SI_TIMER >> 16: + err |= __put_user(from->si_tid, &to->si_tid); + err |= __put_user(from->si_overrun, &to->si_overrun); + err |= __put_user(from->si_int, &to->si_int); + break; + default: + break; + } + } + return err; +} + +int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) +{ + int err; + u32 tmp; + + if (!access_ok (VERIFY_READ, from, sizeof(compat_siginfo_t))) + return -EFAULT; + + err = __get_user(to->si_signo, &from->si_signo); + err |= __get_user(to->si_errno, &from->si_errno); + err |= __get_user(to->si_code, &from->si_code); + + if (to->si_code < 0) + err |= __copy_from_user(&to->_sifields._pad, &from->_sifields._pad, SI_PAD_SIZE); + else { + switch (to->si_code >> 16) { + case __SI_RT >> 16: /* This is not generated by the kernel as of now. */ + case __SI_MESGQ >> 16: + err |= __get_user(to->si_int, &from->si_int); + /* fallthrough */ + case __SI_KILL >> 16: + err |= __get_user(to->si_pid, &from->si_pid); + err |= __get_user(to->si_uid, &from->si_uid); + break; + case __SI_CHLD >> 16: + err |= __get_user(to->si_pid, &from->si_pid); + err |= __get_user(to->si_uid, &from->si_uid); + err |= __get_user(to->si_utime, &from->si_utime); + err |= __get_user(to->si_stime, &from->si_stime); + err |= __get_user(to->si_status, &from->si_status); + break; + case __SI_FAULT >> 16: + err |= __get_user(tmp, &from->si_addr); + to->si_addr = (void *)(u64) (tmp & PSW32_ADDR_INSN); + break; + case __SI_POLL >> 16: + err |= __get_user(to->si_band, &from->si_band); + err |= __get_user(to->si_fd, &from->si_fd); + break; + case __SI_TIMER >> 16: + err |= __get_user(to->si_tid, &from->si_tid); + err |= __get_user(to->si_overrun, &from->si_overrun); + err |= __get_user(to->si_int, &from->si_int); + break; + default: + break; + } + } + 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->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + regs->gprs[2] = -EINTR; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage int +sys32_rt_sigsuspend(struct pt_regs * regs, compat_sigset_t __user *unewset, + size_t sigsetsize) +{ + sigset_t saveset, newset; + compat_sigset_t 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->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + regs->gprs[2] = -EINTR; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage long +sys32_sigaction(int sig, const struct old_sigaction32 __user *act, + struct old_sigaction32 __user *oact) +{ + struct k_sigaction new_ka, old_ka; + unsigned long sa_handler, sa_restorer; + int ret; + + if (act) { + compat_old_sigset_t mask; + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || + __get_user(sa_handler, &act->sa_handler) || + __get_user(sa_restorer, &act->sa_restorer)) + return -EFAULT; + new_ka.sa.sa_handler = (__sighandler_t) sa_handler; + new_ka.sa.sa_restorer = (void (*)(void)) sa_restorer; + __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) { + sa_handler = (unsigned long) old_ka.sa.sa_handler; + sa_restorer = (unsigned long) old_ka.sa.sa_restorer; + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(sa_handler, &oact->sa_handler) || + __put_user(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 __user *act, + struct sigaction32 __user *oact, size_t sigsetsize) +{ + struct k_sigaction new_ka, old_ka; + unsigned long sa_handler; + int ret; + compat_sigset_t set32; + + /* XXX: Don't preclude handling different sized sigset_t's. */ + if (sigsetsize != sizeof(compat_sigset_t)) + return -EINVAL; + + if (act) { + ret = get_user(sa_handler, &act->sa_handler); + ret |= __copy_from_user(&set32, &act->sa_mask, + sizeof(compat_sigset_t)); + 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; + new_ka.sa.sa_handler = (__sighandler_t) sa_handler; + } + + 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(compat_sigset_t)); + ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + } + + return ret; +} + +asmlinkage long +sys32_sigaltstack(const stack_t32 __user *uss, stack_t32 __user *uoss, + struct pt_regs *regs) +{ + stack_t kss, koss; + unsigned long ss_sp; + 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(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; + kss.ss_sp = (void *) ss_sp; + } + + set_fs (KERNEL_DS); + ret = do_sigaltstack((stack_t __user *) (uss ? &kss : NULL), + (stack_t __user *) (uoss ? &koss : NULL), + regs->gprs[15]); + set_fs (old_fs); + + if (!ret && uoss) { + if (!access_ok(VERIFY_WRITE, uoss, sizeof(*uoss))) + return -EFAULT; + ss_sp = (unsigned long) koss.ss_sp; + err |= __put_user(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 __user *sregs) +{ + _s390_regs_common32 regs32; + int err, i; + + regs32.psw.mask = PSW32_MASK_MERGE(PSW32_USER_BITS, + (__u32)(regs->psw.mask >> 32)); + regs32.psw.addr = PSW32_ADDR_AMODE31 | (__u32) regs->psw.addr; + for (i = 0; i < NUM_GPRS; i++) + regs32.gprs[i] = (__u32) regs->gprs[i]; + save_access_regs(current->thread.acrs); + memcpy(regs32.acrs, current->thread.acrs, sizeof(regs32.acrs)); + err = __copy_to_user(&sregs->regs, ®s32, sizeof(regs32)); + if (err) + return err; + save_fp_regs(¤t->thread.fp_regs); + /* s390_fp_regs and _s390_fp_regs32 are the same ! */ + return __copy_to_user(&sregs->fpregs, ¤t->thread.fp_regs, + sizeof(_s390_fp_regs32)); +} + +static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) +{ + _s390_regs_common32 regs32; + int err, i; + + /* Alwys make any pending restarted system call return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + err = __copy_from_user(®s32, &sregs->regs, sizeof(regs32)); + if (err) + return err; + regs->psw.mask = PSW_MASK_MERGE(regs->psw.mask, + (__u64)regs32.psw.mask << 32); + regs->psw.addr = (__u64)(regs32.psw.addr & PSW32_ADDR_INSN); + for (i = 0; i < NUM_GPRS; i++) + regs->gprs[i] = (__u64) regs32.gprs[i]; + memcpy(current->thread.acrs, regs32.acrs, sizeof(current->thread.acrs)); + restore_access_regs(current->thread.acrs); + + err = __copy_from_user(¤t->thread.fp_regs, &sregs->fpregs, + sizeof(_s390_fp_regs32)); + current->thread.fp_regs.fpc &= FPC_VALID_MASK; + if (err) + return err; + + restore_fp_regs(¤t->thread.fp_regs); + regs->trap = -1; /* disable syscall checks */ + return 0; +} + +asmlinkage long sys32_sigreturn(struct pt_regs *regs) +{ + sigframe32 __user *frame = (sigframe32 __user *)regs->gprs[15]; + sigset_t set; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigregs32(regs, &frame->sregs)) + goto badframe; + + return regs->gprs[2]; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage long sys32_rt_sigreturn(struct pt_regs *regs) +{ + rt_sigframe32 __user *frame = (rt_sigframe32 __user *)regs->gprs[15]; + sigset_t set; + stack_t st; + __u32 ss_sp; + int err; + mm_segment_t old_fs = get_fs(); + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) + goto badframe; + + err = __get_user(ss_sp, &frame->uc.uc_stack.ss_sp); + st.ss_sp = (void *) A((unsigned long)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; + + /* It is more difficult to avoid calling this function than to + call it and ignore errors. */ + set_fs (KERNEL_DS); + do_sigaltstack((stack_t __user *)&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 __user * +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 __user *)((sp - frame_size) & -8ul); +} + +static inline int map_signal(int sig) +{ + if (current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32) + return current_thread_info()->exec_domain->signal_invmap[sig]; + else + return sig; +} + +static void setup_frame32(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(sigframe32)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe32))) + goto give_sigsegv; + + if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32)) + goto give_sigsegv; + + if (save_sigregs32(regs, &frame->sregs)) + goto give_sigsegv; + if (__put_user((unsigned long) &frame->sregs, &frame->sc.sregs)) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->gprs[14] = (__u64) ka->sa.sa_restorer; + } else { + regs->gprs[14] = (__u64) frame->retcode; + if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, + (u16 __user *)(frame->retcode))) + goto give_sigsegv; + } + + /* Set up backchain. */ + if (__put_user(regs->gprs[15], (unsigned int __user *) frame)) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->gprs[15] = (__u64) frame; + regs->psw.addr = (__u64) ka->sa.sa_handler; + + regs->gprs[2] = map_signal(sig); + regs->gprs[3] = (__u64) &frame->sc; + + /* We forgot to include these in the sigcontext. + To avoid breaking binary compatibility, they are passed as args. */ + regs->gprs[4] = current->thread.trap_no; + regs->gprs[5] = current->thread.prot_addr; + + /* Place signal number on stack to allow backtrace from handler. */ + if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) + goto give_sigsegv; + return; + +give_sigsegv: + force_sigsegv(sig, current); +} + +static void setup_rt_frame32(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + int err = 0; + rt_sigframe32 __user *frame = get_sigframe(ka, regs, sizeof(rt_sigframe32)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe32))) + goto give_sigsegv; + + if (copy_siginfo_to_user32(&frame->info, info)) + goto give_sigsegv; + + /* 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(regs->gprs[15]), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= save_sigregs32(regs, &frame->uc.uc_mcontext); + 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) { + regs->gprs[14] = (__u64) ka->sa.sa_restorer; + } else { + regs->gprs[14] = (__u64) frame->retcode; + err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, + (u16 __user *)(frame->retcode)); + } + + /* Set up backchain. */ + if (__put_user(regs->gprs[15], (unsigned int __user *) frame)) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->gprs[15] = (__u64) frame; + regs->psw.addr = (__u64) ka->sa.sa_handler; + + regs->gprs[2] = map_signal(sig); + regs->gprs[3] = (__u64) &frame->info; + regs->gprs[4] = (__u64) &frame->uc; + return; + +give_sigsegv: + force_sigsegv(sig, current); +} + +/* + * OK, we're invoking a handler + */ + +void +handle_signal32(unsigned long sig, struct k_sigaction *ka, + siginfo_t *info, sigset_t *oldset, struct pt_regs * regs) +{ + /* 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_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S new file mode 100644 index 000000000000..7a607b1d0380 --- /dev/null +++ b/arch/s390/kernel/compat_wrapper.S @@ -0,0 +1,1443 @@ +/* +* 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), +* Thomas Spatzier (tspat@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 sys32_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 sys32_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 compat_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 compat_sys_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 + llgfr %r5,%r5 # long + jg sys_ptrace # branch to system call + + .globl sys32_alarm_wrapper +sys32_alarm_wrapper: + llgfr %r2,%r2 # unsigned int + jg sys_alarm # branch to system call + +#sys32_pause_wrapper # void + + .globl compat_sys_utime_wrapper +compat_sys_utime_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct compat_utimbuf * + jg compat_sys_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 compat_sys_times_wrapper +compat_sys_times_wrapper: + llgtr %r2,%r2 # struct compat_tms * + jg compat_sys_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 + llgtr %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 compat_sys_ioctl_wrapper +compat_sys_ioctl_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned int + llgfr %r4,%r4 # unsigned int + jg compat_sys_ioctl # branch to system call + + .globl compat_sys_fcntl_wrapper +compat_sys_fcntl_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned int + llgfr %r4,%r4 # unsigned long + jg compat_sys_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 %r2,%r2 # 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 * + llgtr %r4,%r4 # struct old_sigaction32 * + 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 compat_sys_sigpending_wrapper +compat_sys_sigpending_wrapper: + llgtr %r2,%r2 # compat_old_sigset_t * + jg compat_sys_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 compat_sys_setrlimit_wrapper +compat_sys_setrlimit_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct rlimit_emu31 * + jg compat_sys_setrlimit # branch to system call + + .globl compat_sys_old_getrlimit_wrapper +compat_sys_old_getrlimit_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct rlimit_emu31 * + jg compat_sys_old_getrlimit # branch to system call + + .globl compat_sys_getrlimit_wrapper +compat_sys_getrlimit_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct rlimit_emu31 * + jg compat_sys_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 compat_sys_getrusage_wrapper +compat_sys_getrusage_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # struct rusage_emu31 * + jg compat_sys_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 compat_sys_old_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 + llgfr %r3,%r3 # compat_uid_t + llgfr %r4,%r4 # compat_uid_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 compat_sys_statfs_wrapper +compat_sys_statfs_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct compat_statfs * + jg compat_sys_statfs # branch to system call + + .globl compat_sys_fstatfs_wrapper +compat_sys_fstatfs_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct compat_statfs * + jg compat_sys_fstatfs # branch to system call + + .globl compat_sys_socketcall_wrapper +compat_sys_socketcall_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # u32 * + jg compat_sys_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 compat_sys_setitimer_wrapper +compat_sys_setitimer_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # struct itimerval_emu31 * + llgtr %r4,%r4 # struct itimerval_emu31 * + jg compat_sys_setitimer # branch to system call + + .globl compat_sys_getitimer_wrapper +compat_sys_getitimer_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # struct itimerval_emu31 * + jg compat_sys_getitimer # branch to system call + + .globl compat_sys_newstat_wrapper +compat_sys_newstat_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct stat_emu31 * + jg compat_sys_newstat # branch to system call + + .globl compat_sys_newlstat_wrapper +compat_sys_newlstat_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct stat_emu31 * + jg compat_sys_newlstat # branch to system call + + .globl compat_sys_newfstat_wrapper +compat_sys_newfstat_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # struct stat_emu31 * + jg compat_sys_newfstat # branch to system call + +#sys32_vhangup_wrapper # void + + .globl compat_sys_wait4_wrapper +compat_sys_wait4_wrapper: + lgfr %r2,%r2 # pid_t + llgtr %r3,%r3 # unsigned int * + lgfr %r4,%r4 # int + llgtr %r5,%r5 # struct rusage * + jg compat_sys_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 + llgfr %r6,%r6 # u32 + 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 s390x_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 compat_sys_sigprocmask_wrapper +compat_sys_sigprocmask_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # compat_old_sigset_t * + llgtr %r4,%r4 # compat_old_sigset_t * + jg compat_sys_sigprocmask # branch to system call + + .globl sys32_init_module_wrapper +sys32_init_module_wrapper: + llgtr %r2,%r2 # void * + llgfr %r3,%r3 # unsigned long + llgtr %r4,%r4 # char * + jg sys32_init_module # branch to system call + + .globl sys32_delete_module_wrapper +sys32_delete_module_wrapper: + llgtr %r2,%r2 # const char * + llgfr %r3,%r3 # unsigned int + jg sys32_delete_module # branch to system call + + .globl sys32_quotactl_wrapper +sys32_quotactl_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # const char * + llgfr %r4,%r4 # qid_t + llgtr %r5,%r5 # caddr_t + jg sys_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 s390x_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 + llgfr %r3,%r3 # unsigned long + llgfr %r4,%r4 # unsigned long + llgtr %r5,%r5 # loff_t * + llgfr %r6,%r6 # 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 compat_sys_getdents # branch to system call + + .globl compat_sys_select_wrapper +compat_sys_select_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # compat_fd_set * + llgtr %r4,%r4 # compat_fd_set * + llgtr %r5,%r5 # compat_fd_set * + llgtr %r6,%r6 # struct compat_timeval * + jg compat_sys_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 compat_sys_readv_wrapper +compat_sys_readv_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # const struct compat_iovec * + llgfr %r4,%r4 # unsigned long + jg compat_sys_readv # branch to system call + + .globl compat_sys_writev_wrapper +compat_sys_writev_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # const struct compat_iovec * + llgfr %r4,%r4 # unsigned long + jg compat_sys_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 compat_timespec * + jg sys32_sched_rr_get_interval # branch to system call + + .globl compat_sys_nanosleep_wrapper +compat_sys_nanosleep_wrapper: + llgtr %r2,%r2 # struct compat_timespec * + llgtr %r3,%r3 # struct compat_timespec * + jg compat_sys_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_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 compat_sys_nfsservctl_wrapper +compat_sys_nfsservctl_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # struct compat_nfsctl_arg* + llgtr %r4,%r4 # union compat_nfsctl_res* + jg compat_sys_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 * + llgfr %r5,%r5 # size_t + 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 compat_sys_rt_sigtimedwait_wrapper +compat_sys_rt_sigtimedwait_wrapper: + llgtr %r2,%r2 # const sigset_emu31_t * + llgtr %r3,%r3 # siginfo_emu31_t * + llgtr %r4,%r4 # const struct compat_timespec * + llgfr %r5,%r5 # size_t + jg compat_sys_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_pread64_wrapper +sys32_pread64_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_pread64 # branch to system call + + .globl sys32_pwrite64_wrapper +sys32_pwrite64_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_pwrite64 # 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 * + llgfr %r3,%r3 # unsigned long + llgfr %r4,%r4 # unsigned long + jg sys32_truncate64 # branch to system call + + .globl sys32_ftruncate64_wrapper +sys32_ftruncate64_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned long + llgfr %r4,%r4 # unsigned long + 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 compat_sys_fcntl64_wrapper +compat_sys_fcntl64_wrapper: + llgfr %r2,%r2 # unsigned int + llgfr %r3,%r3 # unsigned int + llgfr %r4,%r4 # unsigned long + jg compat_sys_fcntl64 # branch to system call + + .globl sys32_stat64_wrapper +sys32_stat64_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct stat64 * + jg sys32_stat64 # branch to system call + + .globl sys32_lstat64_wrapper +sys32_lstat64_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # struct stat64 * + jg sys32_lstat64 # branch to system call + + .globl sys32_stime_wrapper +sys32_stime_wrapper: + llgtr %r2,%r2 # long * + jg compat_sys_stime # branch to system call + + .globl sys32_sysctl_wrapper +sys32_sysctl_wrapper: + llgtr %r2,%r2 # struct __sysctl_args32 * + jg sys32_sysctl + + .globl sys32_fstat64_wrapper +sys32_fstat64_wrapper: + llgfr %r2,%r2 # unsigned long + llgtr %r3,%r3 # struct stat64 * + jg sys32_fstat64 # branch to system call + + .globl compat_sys_futex_wrapper +compat_sys_futex_wrapper: + llgtr %r2,%r2 # u32 * + lgfr %r3,%r3 # int + lgfr %r4,%r4 # int + llgtr %r5,%r5 # struct compat_timespec * + llgtr %r6,%r6 # u32 * + lgf %r0,164(%r15) # int + stg %r0,160(%r15) + jg compat_sys_futex # branch to system call + + .globl sys32_setxattr_wrapper +sys32_setxattr_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # char * + llgtr %r4,%r4 # void * + llgfr %r5,%r5 # size_t + lgfr %r6,%r6 # int + jg sys_setxattr + + .globl sys32_lsetxattr_wrapper +sys32_lsetxattr_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # char * + llgtr %r4,%r4 # void * + llgfr %r5,%r5 # size_t + lgfr %r6,%r6 # int + jg sys_lsetxattr + + .globl sys32_fsetxattr_wrapper +sys32_fsetxattr_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # char * + llgtr %r4,%r4 # void * + llgfr %r5,%r5 # size_t + lgfr %r6,%r6 # int + jg sys_fsetxattr + + .globl sys32_getxattr_wrapper +sys32_getxattr_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # char * + llgtr %r4,%r4 # void * + llgfr %r5,%r5 # size_t + jg sys_getxattr + + .globl sys32_lgetxattr_wrapper +sys32_lgetxattr_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # char * + llgtr %r4,%r4 # void * + llgfr %r5,%r5 # size_t + jg sys_lgetxattr + + .globl sys32_fgetxattr_wrapper +sys32_fgetxattr_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # char * + llgtr %r4,%r4 # void * + llgfr %r5,%r5 # size_t + jg sys_fgetxattr + + .globl sys32_listxattr_wrapper +sys32_listxattr_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # char * + llgfr %r4,%r4 # size_t + jg sys_listxattr + + .globl sys32_llistxattr_wrapper +sys32_llistxattr_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # char * + llgfr %r4,%r4 # size_t + jg sys_llistxattr + + .globl sys32_flistxattr_wrapper +sys32_flistxattr_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # char * + llgfr %r4,%r4 # size_t + jg sys_flistxattr + + .globl sys32_removexattr_wrapper +sys32_removexattr_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # char * + jg sys_removexattr + + .globl sys32_lremovexattr_wrapper +sys32_lremovexattr_wrapper: + llgtr %r2,%r2 # char * + llgtr %r3,%r3 # char * + jg sys_lremovexattr + + .globl sys32_fremovexattr_wrapper +sys32_fremovexattr_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # char * + jg sys_fremovexattr + + .globl sys32_sched_setaffinity_wrapper +sys32_sched_setaffinity_wrapper: + lgfr %r2,%r2 # int + llgfr %r3,%r3 # unsigned int + llgtr %r4,%r4 # unsigned long * + jg compat_sys_sched_setaffinity + + .globl sys32_sched_getaffinity_wrapper +sys32_sched_getaffinity_wrapper: + lgfr %r2,%r2 # int + llgfr %r3,%r3 # unsigned int + llgtr %r4,%r4 # unsigned long * + jg compat_sys_sched_getaffinity + + .globl sys32_exit_group_wrapper +sys32_exit_group_wrapper: + lgfr %r2,%r2 # int + jg sys_exit_group # branch to system call + + .globl sys32_set_tid_address_wrapper +sys32_set_tid_address_wrapper: + llgtr %r2,%r2 # int * + jg sys_set_tid_address # branch to system call + + .globl sys_epoll_create_wrapper +sys_epoll_create_wrapper: + lgfr %r2,%r2 # int + jg sys_epoll_create # branch to system call + + .globl sys_epoll_ctl_wrapper +sys_epoll_ctl_wrapper: + lgfr %r2,%r2 # int + lgfr %r3,%r3 # int + lgfr %r4,%r4 # int + llgtr %r5,%r5 # struct epoll_event * + jg sys_epoll_ctl # branch to system call + + .globl sys_epoll_wait_wrapper +sys_epoll_wait_wrapper: + lgfr %r2,%r2 # int + llgtr %r3,%r3 # struct epoll_event * + lgfr %r4,%r4 # int + lgfr %r5,%r5 # int + jg sys_epoll_wait # branch to system call + + .globl sys32_lookup_dcookie_wrapper +sys32_lookup_dcookie_wrapper: + sllg %r2,%r2,32 # get high word of 64bit dcookie + or %r2,%r3 # get low word of 64bit dcookie + llgtr %r3,%r4 # char * + llgfr %r4,%r5 # size_t + jg sys_lookup_dcookie + + .globl sys32_fadvise64_wrapper +sys32_fadvise64_wrapper: + lgfr %r2,%r2 # int + sllg %r3,%r3,32 # get high word of 64bit loff_t + or %r3,%r4 # get low word of 64bit loff_t + llgfr %r4,%r5 # size_t (unsigned long) + lgfr %r5,%r6 # int + jg sys_fadvise64 + + .globl sys32_fadvise64_64_wrapper +sys32_fadvise64_64_wrapper: + llgtr %r2,%r2 # struct fadvise64_64_args * + jg s390_fadvise64_64 + + .globl sys32_clock_settime_wrapper +sys32_clock_settime_wrapper: + lgfr %r2,%r2 # clockid_t (int) + llgtr %r3,%r3 # struct compat_timespec * + jg compat_sys_clock_settime + + .globl sys32_clock_gettime_wrapper +sys32_clock_gettime_wrapper: + lgfr %r2,%r2 # clockid_t (int) + llgtr %r3,%r3 # struct compat_timespec * + jg compat_sys_clock_gettime + + .globl sys32_clock_getres_wrapper +sys32_clock_getres_wrapper: + lgfr %r2,%r2 # clockid_t (int) + llgtr %r3,%r3 # struct compat_timespec * + jg compat_sys_clock_getres + + .globl sys32_clock_nanosleep_wrapper +sys32_clock_nanosleep_wrapper: + lgfr %r2,%r2 # clockid_t (int) + lgfr %r3,%r3 # int + llgtr %r4,%r4 # struct compat_timespec * + llgtr %r5,%r5 # struct compat_timespec * + jg compat_sys_clock_nanosleep + + .globl sys32_timer_create_wrapper +sys32_timer_create_wrapper: + lgfr %r2,%r2 # timer_t (int) + llgtr %r3,%r3 # struct compat_sigevent * + llgtr %r4,%r4 # timer_t * + jg sys32_timer_create + + .globl sys32_timer_settime_wrapper +sys32_timer_settime_wrapper: + lgfr %r2,%r2 # timer_t (int) + lgfr %r3,%r3 # int + llgtr %r4,%r4 # struct compat_itimerspec * + llgtr %r5,%r5 # struct compat_itimerspec * + jg compat_sys_timer_settime + + .globl sys32_timer_gettime_wrapper +sys32_timer_gettime_wrapper: + lgfr %r2,%r2 # timer_t (int) + llgtr %r3,%r3 # struct compat_itimerspec * + jg compat_sys_timer_gettime + + .globl sys32_timer_getoverrun_wrapper +sys32_timer_getoverrun_wrapper: + lgfr %r2,%r2 # timer_t (int) + jg sys_timer_getoverrun + + .globl sys32_timer_delete_wrapper +sys32_timer_delete_wrapper: + lgfr %r2,%r2 # timer_t (int) + jg sys_timer_delete + + .globl sys32_io_setup_wrapper +sys32_io_setup_wrapper: + llgfr %r2,%r2 # unsigned int + llgtr %r3,%r3 # u32 * + jg compat_sys_io_setup + + .globl sys32_io_destroy_wrapper +sys32_io_destroy_wrapper: + llgfr %r2,%r2 # (aio_context_t) u32 + jg sys_io_destroy + + .globl sys32_io_getevents_wrapper +sys32_io_getevents_wrapper: + llgfr %r2,%r2 # (aio_context_t) u32 + lgfr %r3,%r3 # long + lgfr %r4,%r4 # long + llgtr %r5,%r5 # struct io_event * + llgtr %r6,%r6 # struct compat_timespec * + jg compat_sys_io_getevents + + .globl sys32_io_submit_wrapper +sys32_io_submit_wrapper: + llgfr %r2,%r2 # (aio_context_t) u32 + lgfr %r3,%r3 # long + llgtr %r4,%r4 # struct iocb ** + jg compat_sys_io_submit + + .globl sys32_io_cancel_wrapper +sys32_io_cancel_wrapper: + llgfr %r2,%r2 # (aio_context_t) u32 + llgtr %r3,%r3 # struct iocb * + llgtr %r4,%r4 # struct io_event * + jg sys_io_cancel + + .globl compat_sys_statfs64_wrapper +compat_sys_statfs64_wrapper: + llgtr %r2,%r2 # const char * + llgfr %r3,%r3 # compat_size_t + llgtr %r4,%r4 # struct compat_statfs64 * + jg compat_sys_statfs64 + + .globl compat_sys_fstatfs64_wrapper +compat_sys_fstatfs64_wrapper: + llgfr %r2,%r2 # unsigned int fd + llgfr %r3,%r3 # compat_size_t + llgtr %r4,%r4 # struct compat_statfs64 * + jg compat_sys_fstatfs64 + + .globl compat_sys_mq_open_wrapper +compat_sys_mq_open_wrapper: + llgtr %r2,%r2 # const char * + lgfr %r3,%r3 # int + llgfr %r4,%r4 # mode_t + llgtr %r5,%r5 # struct compat_mq_attr * + jg compat_sys_mq_open + + .globl sys32_mq_unlink_wrapper +sys32_mq_unlink_wrapper: + llgtr %r2,%r2 # const char * + jg sys_mq_unlink + + .globl compat_sys_mq_timedsend_wrapper +compat_sys_mq_timedsend_wrapper: + lgfr %r2,%r2 # mqd_t + llgtr %r3,%r3 # const char * + llgfr %r4,%r4 # size_t + llgfr %r5,%r5 # unsigned int + llgtr %r6,%r6 # const struct compat_timespec * + jg compat_sys_mq_timedsend + + .globl compat_sys_mq_timedreceive_wrapper +compat_sys_mq_timedreceive_wrapper: + lgfr %r2,%r2 # mqd_t + llgtr %r3,%r3 # char * + llgfr %r4,%r4 # size_t + llgtr %r5,%r5 # unsigned int * + llgtr %r6,%r6 # const struct compat_timespec * + jg compat_sys_mq_timedreceive + + .globl compat_sys_mq_notify_wrapper +compat_sys_mq_notify_wrapper: + lgfr %r2,%r2 # mqd_t + llgtr %r3,%r3 # struct compat_sigevent * + jg compat_sys_mq_notify + + .globl compat_sys_mq_getsetattr_wrapper +compat_sys_mq_getsetattr_wrapper: + lgfr %r2,%r2 # mqd_t + llgtr %r3,%r3 # struct compat_mq_attr * + llgtr %r4,%r4 # struct compat_mq_attr * + jg compat_sys_mq_getsetattr + + .globl compat_sys_add_key_wrapper +compat_sys_add_key_wrapper: + llgtr %r2,%r2 # const char * + llgtr %r3,%r3 # const char * + llgtr %r4,%r4 # const void * + llgfr %r5,%r5 # size_t + llgfr %r6,%r6 # (key_serial_t) u32 + jg sys_add_key + + .globl compat_sys_request_key_wrapper +compat_sys_request_key_wrapper: + llgtr %r2,%r2 # const char * + llgtr %r3,%r3 # const char * + llgtr %r4,%r4 # const void * + llgfr %r5,%r5 # (key_serial_t) u32 + jg sys_request_key + + .globl sys32_remap_file_pages_wrapper +sys32_remap_file_pages_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_remap_file_pages + + .globl compat_sys_waitid_wrapper +compat_sys_waitid_wrapper: + lgfr %r2,%r2 # int + lgfr %r3,%r3 # pid_t + llgtr %r4,%r4 # siginfo_emu31_t * + lgfr %r5,%r5 # int + llgtr %r6,%r6 # struct rusage_emu31 * + jg compat_sys_waitid diff --git a/arch/s390/kernel/cpcmd.c b/arch/s390/kernel/cpcmd.c new file mode 100644 index 000000000000..44df8dc07c59 --- /dev/null +++ b/arch/s390/kernel/cpcmd.c @@ -0,0 +1,111 @@ +/* + * 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), + * Christian Borntraeger (cborntra@de.ibm.com), + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/stddef.h> +#include <linux/string.h> +#include <asm/ebcdic.h> +#include <asm/cpcmd.h> +#include <asm/system.h> + +static DEFINE_SPINLOCK(cpcmd_lock); +static char cpcmd_buf[240]; + +/* + * the caller of __cpcmd has to ensure that the response buffer is below 2 GB + */ +void __cpcmd(char *cmd, char *response, int rlen) +{ + const int mask = 0x40000000L; + unsigned long flags; + int cmdlen; + + spin_lock_irqsave(&cpcmd_lock, flags); + cmdlen = strlen(cmd); + BUG_ON(cmdlen > 240); + strcpy(cpcmd_buf, cmd); + ASCEBC(cpcmd_buf, cmdlen); + + if (response != NULL && rlen > 0) { + memset(response, 0, rlen); +#ifndef CONFIG_ARCH_S390X + asm volatile ("LRA 2,0(%0)\n\t" + "LR 4,%1\n\t" + "O 4,%4\n\t" + "LRA 3,0(%2)\n\t" + "LR 5,%3\n\t" + ".long 0x83240008 # Diagnose X'08'\n\t" + : /* no output */ + : "a" (cpcmd_buf), "d" (cmdlen), + "a" (response), "d" (rlen), "m" (mask) + : "cc", "2", "3", "4", "5" ); +#else /* CONFIG_ARCH_S390X */ + 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 X'08'\n" + " sam64" + : /* no output */ + : "a" (cpcmd_buf), "d" (cmdlen), + "a" (response), "d" (rlen), "m" (mask) + : "cc", "2", "3", "4", "5" ); +#endif /* CONFIG_ARCH_S390X */ + EBCASC(response, rlen); + } else { +#ifndef CONFIG_ARCH_S390X + asm volatile ("LRA 2,0(%0)\n\t" + "LR 3,%1\n\t" + ".long 0x83230008 # Diagnose X'08'\n\t" + : /* no output */ + : "a" (cpcmd_buf), "d" (cmdlen) + : "2", "3" ); +#else /* CONFIG_ARCH_S390X */ + asm volatile (" lrag 2,0(%0)\n" + " lgr 3,%1\n" + " sam31\n" + " .long 0x83230008 # Diagnose X'08'\n" + " sam64" + : /* no output */ + : "a" (cpcmd_buf), "d" (cmdlen) + : "2", "3" ); +#endif /* CONFIG_ARCH_S390X */ + } + spin_unlock_irqrestore(&cpcmd_lock, flags); +} + +EXPORT_SYMBOL(__cpcmd); + +#ifdef CONFIG_ARCH_S390X +void cpcmd(char *cmd, char *response, int rlen) +{ + char *lowbuf; + if ((rlen == 0) || (response == NULL) + || !((unsigned long)response >> 31)) + __cpcmd(cmd, response, rlen); + else { + lowbuf = kmalloc(rlen, GFP_KERNEL | GFP_DMA); + if (!lowbuf) { + printk(KERN_WARNING + "cpcmd: could not allocate response buffer\n"); + return; + } + __cpcmd(cmd, lowbuf, rlen); + memcpy(response, lowbuf, rlen); + kfree(lowbuf); + } +} + +EXPORT_SYMBOL(cpcmd); +#endif /* CONFIG_ARCH_S390X */ diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c new file mode 100644 index 000000000000..91f8ce5543d3 --- /dev/null +++ b/arch/s390/kernel/debug.c @@ -0,0 +1,1286 @@ +/* + * 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/slab.h> +#include <linux/ctype.h> +#include <linux/sysctl.h> +#include <asm/uaccess.h> +#include <asm/semaphore.h> + +#include <linux/module.h> +#include <linux/init.h> + +#include <asm/debug.h> + +#define DEBUG_PROLOG_ENTRY -1 + +/* typedefs */ + +typedef struct file_private_info { + loff_t offset; /* offset of last read in file */ + int act_area; /* number of last formated area */ + int act_entry; /* last formated entry (offset */ + /* relative to beginning of last */ + /* formated area) */ + size_t act_entry_offset; /* up to this offset we copied */ + /* in last read the last formated */ + /* entry to userland */ + char temp_buf[2048]; /* buffer for output */ + debug_info_t *debug_info_org; /* original debug information */ + debug_info_t *debug_info_snap; /* snapshot of debug information */ + struct debug_view *view; /* used view of debug info */ +} file_private_info_t; + +typedef struct +{ + char *string; + /* + * This assumes that all args are converted into longs + * on L/390 this is the case for all types of parameter + * except of floats, and long long (32 bit) + * + */ + long args[0]; +} debug_sprintf_entry_t; + + +extern void tod_to_timeval(uint64_t todval, struct timeval *xtime); + +/* internal function prototyes */ + +static int debug_init(void); +static ssize_t debug_output(struct file *file, char __user *user_buf, + size_t user_len, loff_t * offset); +static ssize_t debug_input(struct file *file, const char __user *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 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 *user_buf, + size_t user_buf_size, loff_t * offset); +static int debug_input_flush_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char __user *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); + +static int debug_sprintf_format_fn(debug_info_t * id, struct debug_view *view, + char *out_buf, debug_sprintf_entry_t *curr_event); + +/* globals */ + +struct debug_view debug_raw_view = { + "raw", + NULL, + &debug_raw_header_fn, + &debug_raw_format_fn, + NULL, + NULL +}; + +struct debug_view debug_hex_ascii_view = { + "hex_ascii", + NULL, + &debug_dflt_header_fn, + &debug_hex_ascii_format_fn, + NULL, + NULL +}; + +struct debug_view debug_level_view = { + "level", + &debug_prolog_level_fn, + NULL, + NULL, + &debug_input_level_fn, + NULL +}; + +struct debug_view debug_flush_view = { + "flush", + NULL, + NULL, + NULL, + &debug_input_flush_fn, + NULL +}; + +struct debug_view debug_sprintf_view = { + "sprintf", + NULL, + &debug_dflt_header_fn, + (debug_format_proc_t*)&debug_sprintf_format_fn, + NULL, + NULL +}; + + +unsigned int debug_feature_version = __DEBUG_FEATURE_VERSION; + +/* static globals */ + +static debug_info_t *debug_area_first = NULL; +static debug_info_t *debug_area_last = NULL; +DECLARE_MUTEX(debug_lock); + +static int initialized; + +static struct file_operations debug_file_ops = { + .owner = THIS_MODULE, + .read = debug_output, + .write = debug_input, + .open = debug_open, + .release = debug_close, +}; + +static struct proc_dir_entry *debug_proc_root_entry; + +/* functions */ + +/* + * debug_info_alloc + * - alloc new debug-info + */ + +static debug_info_t* debug_info_alloc(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; + strlcpy(rc->name, name, sizeof(rc->name)); + memset(rc->views, 0, DEBUG_MAX_VIEWS * sizeof(struct debug_view *)); +#ifdef CONFIG_PROC_FS + memset(rc->proc_entries, 0 ,DEBUG_MAX_VIEWS * + sizeof(struct proc_dir_entry*)); +#endif /* CONFIG_PROC_FS */ + atomic_set(&(rc->ref_count), 0); + + 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_free + * - free memory debug-info + */ + +static void debug_info_free(debug_info_t* db_info){ + int i; + 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); + kfree(db_info); +} + +/* + * 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; + + rc = debug_info_alloc(name, page_order, nr_areas, buf_size); + if(!rc) + goto out; + + + /* create proc rood directory */ + rc->proc_root_entry = proc_mkdir(rc->name, debug_proc_root_entry); + + /* 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); +out: + return rc; +} + +/* + * debug_info_copy + * - copy debug-info + */ + +static debug_info_t* debug_info_copy(debug_info_t* in) +{ + int i; + debug_info_t* rc; + rc = debug_info_alloc(in->name, in->page_order, + in->nr_areas, in->buf_size); + if(!rc) + goto out; + + for(i = 0; i < in->nr_areas; i++){ + memcpy(rc->areas[i],in->areas[i], PAGE_SIZE << in->page_order); + } +out: + return rc; +} + +/* + * 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)) { +#ifdef DEBUG + printk(KERN_INFO "debug: freeing debug area %p (%s)\n", + db_info, db_info->name); +#endif + for (i = 0; i < DEBUG_MAX_VIEWS; i++) { + if (db_info->views[i] == NULL) + continue; +#ifdef CONFIG_PROC_FS + remove_proc_entry(db_info->proc_entries[i]->name, + db_info->proc_root_entry); +#endif + } +#ifdef CONFIG_PROC_FS + remove_proc_entry(db_info->proc_root_entry->name, + debug_proc_root_entry); +#endif + 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; + debug_info_free(db_info); + } +} + +/* + * debug_format_entry: + * - format one debug entry and return size of formated data + */ + +static int debug_format_entry(file_private_info_t *p_info) +{ + debug_info_t *id_org = p_info->debug_info_org; + debug_info_t *id_snap = p_info->debug_info_snap; + struct debug_view *view = p_info->view; + debug_entry_t *act_entry; + size_t len = 0; + if(p_info->act_entry == DEBUG_PROLOG_ENTRY){ + /* print prolog */ + if (view->prolog_proc) + len += view->prolog_proc(id_org, view,p_info->temp_buf); + goto out; + } + + act_entry = (debug_entry_t *) ((char*)id_snap->areas[p_info->act_area] + + p_info->act_entry); + + if (act_entry->id.stck == 0LL) + goto out; /* empty entry */ + if (view->header_proc) + len += view->header_proc(id_org, view, p_info->act_area, + act_entry, p_info->temp_buf + len); + if (view->format_proc) + len += view->format_proc(id_org, view, p_info->temp_buf + len, + DEBUG_DATA(act_entry)); + out: + return len; +} + +/* + * debug_next_entry: + * - goto next entry in p_info + */ + +extern inline int debug_next_entry(file_private_info_t *p_info) +{ + debug_info_t *id = p_info->debug_info_snap; + if(p_info->act_entry == DEBUG_PROLOG_ENTRY){ + p_info->act_entry = 0; + goto out; + } + if ((p_info->act_entry += id->entry_size) + > ((PAGE_SIZE << (id->page_order)) + - id->entry_size)){ + + /* next area */ + p_info->act_entry = 0; + p_info->act_area++; + if(p_info->act_area >= id->nr_areas) + return 1; + } +out: + return 0; +} + +/* + * debug_output: + * - called for user read() + * - copies formated debug entries to the user buffer + */ + +static ssize_t debug_output(struct file *file, /* file descriptor */ + char __user *user_buf, /* user buffer */ + size_t len, /* length of buffer */ + loff_t *offset) /* offset in the file */ +{ + size_t count = 0; + size_t entry_offset, size = 0; + file_private_info_t *p_info; + + p_info = ((file_private_info_t *) file->private_data); + if (*offset != p_info->offset) + return -EPIPE; + if(p_info->act_area >= p_info->debug_info_snap->nr_areas) + return 0; + + entry_offset = p_info->act_entry_offset; + + while(count < len){ + size = debug_format_entry(p_info); + size = min((len - count), (size - entry_offset)); + + if(size){ + if (copy_to_user(user_buf + count, + p_info->temp_buf + entry_offset, size)) + return -EFAULT; + } + count += size; + entry_offset = 0; + if(count != len) + if(debug_next_entry(p_info)) + goto out; + } +out: + p_info->offset = *offset + count; + p_info->act_entry_offset = size; + *offset = p_info->offset; + return count; +} + +/* + * debug_input: + * - called for user write() + * - calls input function of view + */ + +static ssize_t debug_input(struct file *file, + const char __user *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_org, + p_info->view, file, user_buf, + length, offset); + else + rc = -EPERM; + up(&debug_lock); + return rc; /* number of input characters */ +} + +/* + * 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, rc = 0; + file_private_info_t *p_info; + debug_info_t *debug_info, *debug_info_snapshot; + +#ifdef DEBUG + printk("debug_open\n"); +#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] == + PDE(file->f_dentry->d_inode)) { + goto found; /* found view ! */ + } + } + debug_info = debug_info->next; + } + /* no entry found */ + rc = -EINVAL; + goto out; + + found: + + /* make snapshot of current debug areas to get it consistent */ + + debug_info_snapshot = debug_info_copy(debug_info); + + if(!debug_info_snapshot){ +#ifdef DEBUG + printk(KERN_ERR "debug_open: debug_info_copy failed (out of mem)\n"); +#endif + rc = -ENOMEM; + goto out; + } + + if ((file->private_data = + kmalloc(sizeof(file_private_info_t), GFP_ATOMIC)) == 0) { +#ifdef DEBUG + printk(KERN_ERR "debug_open: kmalloc failed\n"); +#endif + debug_info_free(debug_info_snapshot); + rc = -ENOMEM; + goto out; + } + p_info = (file_private_info_t *) file->private_data; + p_info->offset = 0; + p_info->debug_info_snap = debug_info_snapshot; + p_info->debug_info_org = debug_info; + p_info->view = debug_info->views[i]; + p_info->act_area = 0; + p_info->act_entry = DEBUG_PROLOG_ENTRY; + p_info->act_entry_offset = 0; + + debug_info_get(debug_info); + + out: + up(&debug_lock); + 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 + p_info = (file_private_info_t *) file->private_data; + debug_info_free(p_info->debug_info_snap); + debug_info_put(p_info->debug_info_org); + kfree(file->private_data); + return 0; /* success */ +} + +/* + * 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; + + if (!initialized) + BUG(); + 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); + debug_register_view(rc, &debug_flush_view); +#ifdef DEBUG + printk(KERN_INFO + "debug: reserved %d areas of %d pages for debugging %s\n", + nr_areas, 1 << page_order, rc->name); +#endif + out: + if (rc == NULL){ + printk(KERN_ERR "debug: debug_register failed for %s\n",name); + } + 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); +#ifdef DEBUG + printk(KERN_INFO "debug: unregistering %s\n", id->name); +#endif + debug_info_put(id); + up(&debug_lock); + + out: + return; +} + +/* + * debug_set_level: + * - set actual debug level + */ + +void debug_set_level(debug_info_t* id, int new_level) +{ + unsigned 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; +#ifdef DEBUG + printk(KERN_INFO + "debug: %s: new level %i\n",id->name,id->level); +#endif + } + spin_unlock_irqrestore(&id->lock,flags); +} + + +/* + * proceed_active_entry: + * - set active entry to next in the ring buffer + */ + +extern 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 + */ + +extern inline void proceed_active_area(debug_info_t * id) +{ + id->active_area++; + id->active_area = id->active_area % id->nr_areas; +} + +/* + * get_active_entry: + */ + +extern 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_finish_entry: + * - set timestamp, caller address, cpu number etc. + */ + +extern inline void debug_finish_entry(debug_info_t * id, debug_entry_t* active, + int level, int exception) +{ + STCK(active->id.stck); + active->id.fields.cpuid = smp_processor_id(); + active->caller = __builtin_return_address(0); + active->id.fields.exception = exception; + active->id.fields.level = level; + proceed_active_entry(id); + if(exception) + proceed_active_area(id); +} + +static int debug_stoppable=1; +static int debug_active=1; + +#define CTL_S390DBF 5677 +#define CTL_S390DBF_STOPPABLE 5678 +#define CTL_S390DBF_ACTIVE 5679 + +/* + * proc handler for the running debug_active sysctl + * always allow read, allow write only if debug_stoppable is set or + * if debug_active is already off + */ +static int s390dbf_procactive(ctl_table *table, int write, struct file *filp, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + if (!write || debug_stoppable || !debug_active) + return proc_dointvec(table, write, filp, buffer, lenp, ppos); + else + return 0; +} + + +static struct ctl_table s390dbf_table[] = { + { + .ctl_name = CTL_S390DBF_STOPPABLE, + .procname = "debug_stoppable", + .data = &debug_stoppable, + .maxlen = sizeof(int), + .mode = S_IRUGO | S_IWUSR, + .proc_handler = &proc_dointvec, + .strategy = &sysctl_intvec, + }, + { + .ctl_name = CTL_S390DBF_ACTIVE, + .procname = "debug_active", + .data = &debug_active, + .maxlen = sizeof(int), + .mode = S_IRUGO | S_IWUSR, + .proc_handler = &s390dbf_procactive, + .strategy = &sysctl_intvec, + }, + { .ctl_name = 0 } +}; + +static struct ctl_table s390dbf_dir_table[] = { + { + .ctl_name = CTL_S390DBF, + .procname = "s390dbf", + .maxlen = 0, + .mode = S_IRUGO | S_IXUGO, + .child = s390dbf_table, + }, + { .ctl_name = 0 } +}; + +struct ctl_table_header *s390dbf_sysctl_header; + +void debug_stop_all(void) +{ + if (debug_stoppable) + debug_active = 0; +} + + +/* + * debug_event_common: + * - write debug entry with given size + */ + +debug_entry_t *debug_event_common(debug_info_t * id, int level, const void *buf, + int len) +{ + unsigned long flags; + debug_entry_t *active; + + if (!debug_active) + return NULL; + spin_lock_irqsave(&id->lock, flags); + active = get_active_entry(id); + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size)); + debug_finish_entry(id, active, level, 0); + spin_unlock_irqrestore(&id->lock, flags); + + return active; +} + +/* + * debug_exception_common: + * - write debug entry with given size and switch to next debug area + */ + +debug_entry_t *debug_exception_common(debug_info_t * id, int level, + const void *buf, int len) +{ + unsigned long flags; + debug_entry_t *active; + + if (!debug_active) + return NULL; + spin_lock_irqsave(&id->lock, flags); + active = get_active_entry(id); + memset(DEBUG_DATA(active), 0, id->buf_size); + memcpy(DEBUG_DATA(active), buf, min(len, id->buf_size)); + debug_finish_entry(id, active, level, 1); + spin_unlock_irqrestore(&id->lock, flags); + + return active; +} + +/* + * counts arguments in format string for sprintf view + */ + +extern inline int debug_count_numargs(char *string) +{ + int numargs=0; + + while(*string) { + if(*string++=='%') + numargs++; + } + return(numargs); +} + +/* + * debug_sprintf_event: + */ + +debug_entry_t *debug_sprintf_event(debug_info_t* id, + int level,char *string,...) +{ + va_list ap; + int numargs,idx; + unsigned long flags; + debug_sprintf_entry_t *curr_event; + debug_entry_t *active; + + if((!id) || (level > id->level)) + return NULL; + if (!debug_active) + return NULL; + numargs=debug_count_numargs(string); + + spin_lock_irqsave(&id->lock, flags); + active = get_active_entry(id); + curr_event=(debug_sprintf_entry_t *) DEBUG_DATA(active); + va_start(ap,string); + curr_event->string=string; + for(idx=0;idx<min(numargs,(int)(id->buf_size / sizeof(long))-1);idx++) + curr_event->args[idx]=va_arg(ap,long); + va_end(ap); + debug_finish_entry(id, active, level, 0); + spin_unlock_irqrestore(&id->lock, flags); + + return active; +} + +/* + * debug_sprintf_exception: + */ + +debug_entry_t *debug_sprintf_exception(debug_info_t* id, + int level,char *string,...) +{ + va_list ap; + int numargs,idx; + unsigned long flags; + debug_sprintf_entry_t *curr_event; + debug_entry_t *active; + + if((!id) || (level > id->level)) + return NULL; + if (!debug_active) + return NULL; + + numargs=debug_count_numargs(string); + + spin_lock_irqsave(&id->lock, flags); + active = get_active_entry(id); + curr_event=(debug_sprintf_entry_t *)DEBUG_DATA(active); + va_start(ap,string); + curr_event->string=string; + for(idx=0;idx<min(numargs,(int)(id->buf_size / sizeof(long))-1);idx++) + curr_event->args[idx]=va_arg(ap,long); + va_end(ap); + debug_finish_entry(id, active, level, 1); + spin_unlock_irqrestore(&id->lock, flags); + + return active; +} + +/* + * debug_init: + * - is called exactly once to initialize the debug feature + */ + +static int __init debug_init(void) +{ + int rc = 0; + + s390dbf_sysctl_header = register_sysctl_table(s390dbf_dir_table, 1); + down(&debug_lock); +#ifdef CONFIG_PROC_FS + debug_proc_root_entry = proc_mkdir(DEBUG_DIR_ROOT, NULL); +#endif /* CONFIG_PROC_FS */ + 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; + unsigned long flags; + mode_t mode = S_IFREG; + struct proc_dir_entry *pde; + + if (!id) + goto out; + if (view->prolog_proc || view->format_proc || view->header_proc) + mode |= S_IRUSR; + if (view->input_proc) + mode |= S_IWUSR; + pde = create_proc_entry(view->name, mode, id->proc_root_entry); + if (!pde){ + printk(KERN_WARNING "debug: create_proc_entry() failed! Cannot register view %s/%s\n", id->name,view->name); + rc = -1; + 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); + remove_proc_entry(pde->name, id->proc_root_entry); + rc = -1; + } + else { + id->views[i] = view; + pde->proc_fops = &debug_file_ops; + id->proc_entries[i] = pde; + } + 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; + unsigned 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 { +#ifdef CONFIG_PROC_FS + remove_proc_entry(id->proc_entries[i]->name, + id->proc_root_entry); +#endif + 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(id->level == -1) rc = sprintf(out_buf,"-\n"); + else rc = sprintf(out_buf, "%i\n", id->level); + 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 *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 (copy_from_user(input_buf, user_buf, 1)){ + rc = -EFAULT; + 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 */ +} + + +/* + * flushes debug areas + */ + +void debug_flush(debug_info_t* id, int area) +{ + unsigned long flags; + int i; + + if(!id) + return; + spin_lock_irqsave(&id->lock,flags); + if(area == DEBUG_FLUSH_ALL){ + id->active_area = 0; + memset(id->active_entry, 0, id->nr_areas * sizeof(int)); + for (i = 0; i < id->nr_areas; i++) + memset(id->areas[i], 0, PAGE_SIZE << id->page_order); + printk(KERN_INFO "debug: %s: all areas flushed\n",id->name); + } else if(area >= 0 && area < id->nr_areas) { + id->active_entry[area] = 0; + memset(id->areas[area], 0, PAGE_SIZE << id->page_order); + printk(KERN_INFO + "debug: %s: area %i has been flushed\n", + id->name, area); + } else { + printk(KERN_INFO + "debug: %s: area %i cannot be flushed (range: %i - %i)\n", + id->name, area, 0, id->nr_areas-1); + } + spin_unlock_irqrestore(&id->lock,flags); +} + +/* + * view function: flushes debug areas + */ + +static int debug_input_flush_fn(debug_info_t * id, struct debug_view *view, + struct file *file, const char __user *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 (copy_from_user(input_buf, user_buf, 1)){ + rc = -EFAULT; + goto out; + } + if(input_buf[0] == '-') { + debug_flush(id, DEBUG_FLUSH_ALL); + goto out; + } + if (isdigit(input_buf[0])) { + int area = ((int) input_buf[0] - (int) '0'); + debug_flush(id, area); + goto out; + } + + printk(KERN_INFO "debug: area `%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); + memcpy(out_buf,entry,sizeof(debug_entry_t)); + 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; + memcpy(out_buf, in_buf, id->buf_size); + 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; + + 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"); + 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; + unsigned int level; + + level = entry->id.fields.level; + 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) & PSW_ADDR_INSN; + rc += sprintf(out_buf, "%02i %011lu:%06lu %1u %1s %02i %p ", + area, time_val.tv_sec, time_val.tv_usec, level, + except_str, entry->id.fields.cpuid, (void *) caller); + return rc; +} + +/* + * prints debug data sprintf-formated: + * debug_sprinf_event/exception calls must be used together with this view + */ + +#define DEBUG_SPRINTF_MAX_ARGS 10 + +int debug_sprintf_format_fn(debug_info_t * id, struct debug_view *view, + char *out_buf, debug_sprintf_entry_t *curr_event) +{ + int num_longs, num_used_args = 0,i, rc = 0; + int index[DEBUG_SPRINTF_MAX_ARGS]; + + /* count of longs fit into one entry */ + num_longs = id->buf_size / sizeof(long); + + if(num_longs < 1) + goto out; /* bufsize of entry too small */ + if(num_longs == 1) { + /* no args, we use only the string */ + strcpy(out_buf, curr_event->string); + rc = strlen(curr_event->string); + goto out; + } + + /* number of arguments used for sprintf (without the format string) */ + num_used_args = min(DEBUG_SPRINTF_MAX_ARGS, (num_longs - 1)); + + memset(index,0, DEBUG_SPRINTF_MAX_ARGS * sizeof(int)); + + for(i = 0; i < num_used_args; i++) + index[i] = i; + + rc = sprintf(out_buf, curr_event->string, curr_event->args[index[0]], + curr_event->args[index[1]], curr_event->args[index[2]], + curr_event->args[index[3]], curr_event->args[index[4]], + curr_event->args[index[5]], curr_event->args[index[6]], + curr_event->args[index[7]], curr_event->args[index[8]], + curr_event->args[index[9]]); + +out: + + return rc; +} + +/* + * clean up module + */ +void __exit debug_exit(void) +{ +#ifdef DEBUG + printk("debug_cleanup_module: \n"); +#endif +#ifdef CONFIG_PROC_FS + remove_proc_entry(debug_proc_root_entry->name, NULL); +#endif /* CONFIG_PROC_FS */ + unregister_sysctl_table(s390dbf_sysctl_header); + return; +} + +/* + * module definitions + */ +core_initcall(debug_init); +module_exit(debug_exit); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(debug_register); +EXPORT_SYMBOL(debug_unregister); +EXPORT_SYMBOL(debug_set_level); +EXPORT_SYMBOL(debug_stop_all); +EXPORT_SYMBOL(debug_register_view); +EXPORT_SYMBOL(debug_unregister_view); +EXPORT_SYMBOL(debug_event_common); +EXPORT_SYMBOL(debug_exception_common); +EXPORT_SYMBOL(debug_hex_ascii_view); +EXPORT_SYMBOL(debug_raw_view); +EXPORT_SYMBOL(debug_dflt_header_fn); +EXPORT_SYMBOL(debug_sprintf_view); +EXPORT_SYMBOL(debug_sprintf_exception); +EXPORT_SYMBOL(debug_sprintf_event); diff --git a/arch/s390/kernel/ebcdic.c b/arch/s390/kernel/ebcdic.c new file mode 100644 index 000000000000..bb0f973137f0 --- /dev/null +++ b/arch/s390/kernel/ebcdic.c @@ -0,0 +1,400 @@ +/* + * 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 <linux/module.h> +#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 +}; + +EXPORT_SYMBOL(_ascebc_500); +EXPORT_SYMBOL(_ebcasc_500); +EXPORT_SYMBOL(_ascebc); +EXPORT_SYMBOL(_ebcasc); +EXPORT_SYMBOL(_ebc_tolower); +EXPORT_SYMBOL(_ebc_toupper); + diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S new file mode 100644 index 000000000000..c0e09b33febe --- /dev/null +++ b/arch/s390/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/cache.h> +#include <asm/lowcore.h> +#include <asm/errno.h> +#include <asm/ptrace.h> +#include <asm/thread_info.h> +#include <asm/offsets.h> +#include <asm/unistd.h> +#include <asm/page.h> + +/* + * Stack layout for the system_call stack entry. + * The first few entries are identical to the user_regs_struct. + */ +SP_PTREGS = STACK_FRAME_OVERHEAD +SP_ARGS = STACK_FRAME_OVERHEAD + __PT_ARGS +SP_PSW = STACK_FRAME_OVERHEAD + __PT_PSW +SP_R0 = STACK_FRAME_OVERHEAD + __PT_GPRS +SP_R1 = STACK_FRAME_OVERHEAD + __PT_GPRS + 4 +SP_R2 = STACK_FRAME_OVERHEAD + __PT_GPRS + 8 +SP_R3 = STACK_FRAME_OVERHEAD + __PT_GPRS + 12 +SP_R4 = STACK_FRAME_OVERHEAD + __PT_GPRS + 16 +SP_R5 = STACK_FRAME_OVERHEAD + __PT_GPRS + 20 +SP_R6 = STACK_FRAME_OVERHEAD + __PT_GPRS + 24 +SP_R7 = STACK_FRAME_OVERHEAD + __PT_GPRS + 28 +SP_R8 = STACK_FRAME_OVERHEAD + __PT_GPRS + 32 +SP_R9 = STACK_FRAME_OVERHEAD + __PT_GPRS + 36 +SP_R10 = STACK_FRAME_OVERHEAD + __PT_GPRS + 40 +SP_R11 = STACK_FRAME_OVERHEAD + __PT_GPRS + 44 +SP_R12 = STACK_FRAME_OVERHEAD + __PT_GPRS + 48 +SP_R13 = STACK_FRAME_OVERHEAD + __PT_GPRS + 52 +SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 56 +SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 60 +SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2 +SP_ILC = STACK_FRAME_OVERHEAD + __PT_ILC +SP_TRAP = STACK_FRAME_OVERHEAD + __PT_TRAP +SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE + +_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \ + _TIF_RESTART_SVC | _TIF_SINGLE_STEP ) +_TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NEED_RESCHED) + +STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER +STACK_SIZE = 1 << STACK_SHIFT + +#define BASED(name) name-system_call(%r13) + +/* + * 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 + */ + + .macro STORE_TIMER lc_offset +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + stpt \lc_offset +#endif + .endm + +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + .macro UPDATE_VTIME lc_from,lc_to,lc_sum + lm %r10,%r11,\lc_from + sl %r10,\lc_to + sl %r11,\lc_to+4 + bc 3,BASED(0f) + sl %r10,BASED(.Lc_1) +0: al %r10,\lc_sum + al %r11,\lc_sum+4 + bc 12,BASED(1f) + al %r10,BASED(.Lc_1) +1: stm %r10,%r11,\lc_sum + .endm +#endif + + .macro SAVE_ALL_BASE savearea + stm %r12,%r15,\savearea + l %r13,__LC_SVC_NEW_PSW+4 # load &system_call to %r13 + .endm + + .macro SAVE_ALL psworg,savearea,sync + la %r12,\psworg + .if \sync + tm \psworg+1,0x01 # test problem state bit + bz BASED(2f) # skip stack setup save + l %r15,__LC_KERNEL_STACK # problem state -> load ksp + .else + tm \psworg+1,0x01 # test problem state bit + bnz BASED(1f) # from user -> load async stack + clc \psworg+4(4),BASED(.Lcritical_end) + bhe BASED(0f) + clc \psworg+4(4),BASED(.Lcritical_start) + bl BASED(0f) + l %r14,BASED(.Lcleanup_critical) + basr %r14,%r14 + tm 0(%r12),0x01 # retest problem state after cleanup + bnz BASED(1f) +0: l %r14,__LC_ASYNC_STACK # are we already on the async stack ? + slr %r14,%r15 + sra %r14,STACK_SHIFT + be BASED(2f) +1: l %r15,__LC_ASYNC_STACK + .endif +#ifdef CONFIG_CHECK_STACK + b BASED(3f) +2: tml %r15,STACK_SIZE - CONFIG_STACK_GUARD + bz BASED(stack_overflow) +3: +#endif +2: s %r15,BASED(.Lc_spsize) # make room for registers & psw + mvc SP_PSW(8,%r15),0(%r12) # move user PSW to stack + la %r12,\psworg + st %r2,SP_ORIG_R2(%r15) # store original content of gpr 2 + icm %r12,12,__LC_SVC_ILC + stm %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack + st %r12,SP_ILC(%r15) + mvc SP_R12(16,%r15),\savearea # move %r12-%r15 to stack + la %r12,0 + st %r12,__SF_BACKCHAIN(%r15) # clear back chain + .endm + + .macro RESTORE_ALL sync + mvc __LC_RETURN_PSW(8),SP_PSW(%r15) # move user PSW to lowcore + .if !\sync + ni __LC_RETURN_PSW+1,0xfd # clear wait state bit + .endif + lm %r0,%r15,SP_R0(%r15) # load gprs 0-15 of user + STORE_TIMER __LC_EXIT_TIMER + lpsw __LC_RETURN_PSW # back to caller + .endm + +/* + * Scheduler resume function, called by switch_to + * gpr2 = (task_struct *) prev + * gpr3 = (task_struct *) next + * Returns: + * gpr2 = prev + */ + .globl __switch_to +__switch_to: + basr %r1,0 +__switch_to_base: + tm __THREAD_per(%r3),0xe8 # new process is using per ? + bz __switch_to_noper-__switch_to_base(%r1) # if not we're fine + stctl %c9,%c11,__SF_EMPTY(%r15) # We are using per stuff + clc __THREAD_per(12,%r3),__SF_EMPTY(%r15) + be __switch_to_noper-__switch_to_base(%r1) # we got away w/o bashing TLB's + lctl %c9,%c11,__THREAD_per(%r3) # Nope we didn't +__switch_to_noper: + stm %r6,%r15,__SF_GPRS(%r15)# store __switch_to registers of prev task + st %r15,__THREAD_ksp(%r2) # store kernel stack to prev->tss.ksp + l %r15,__THREAD_ksp(%r3) # load kernel stack from next->tss.ksp + lm %r6,%r15,__SF_GPRS(%r15)# load __switch_to registers of next task + st %r3,__LC_CURRENT # __LC_CURRENT = current task struct + lctl %c4,%c4,__TASK_pid(%r3) # load pid to control reg. 4 + l %r3,__THREAD_info(%r3) # load thread_info from task struct + st %r3,__LC_THREAD_INFO + ahi %r3,STACK_SIZE + st %r3,__LC_KERNEL_STACK # __LC_KERNEL_STACK = new kernel stack + br %r14 + +__critical_start: +/* + * SVC interrupt handler routine. System calls are synchronous events and + * are executed with interrupts enabled. + */ + + .globl system_call +system_call: + STORE_TIMER __LC_SYNC_ENTER_TIMER +sysc_saveall: + SAVE_ALL_BASE __LC_SAVE_AREA + SAVE_ALL __LC_SVC_OLD_PSW,__LC_SAVE_AREA,1 + lh %r7,0x8a # get svc number from lowcore +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +sysc_vtime: + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + bz BASED(sysc_do_svc) + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER +sysc_stime: + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER +sysc_update: + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +#endif +sysc_do_svc: + l %r9,__LC_THREAD_INFO # load pointer to thread_info struct + sla %r7,2 # *4 and test for svc 0 + bnz BASED(sysc_nr_ok) # svc number > 0 + # svc 0: system call number in %r1 + cl %r1,BASED(.Lnr_syscalls) + bnl BASED(sysc_nr_ok) + lr %r7,%r1 # copy svc number to %r7 + sla %r7,2 # *4 +sysc_nr_ok: + mvc SP_ARGS(4,%r15),SP_R7(%r15) +sysc_do_restart: + tm __TI_flags+3(%r9),(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT) + l %r8,sys_call_table-system_call(%r7,%r13) # get system call addr. + 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 + # changing anything here !! + +sysc_return: + tm SP_PSW+1(%r15),0x01 # returning to user ? + bno BASED(sysc_leave) + tm __TI_flags+3(%r9),_TIF_WORK_SVC + bnz BASED(sysc_work) # there is work to do (signals etc.) +sysc_leave: + RESTORE_ALL 1 + +# +# recheck if there is more work to do +# +sysc_work_loop: + tm __TI_flags+3(%r9),_TIF_WORK_SVC + bz BASED(sysc_leave) # there is no work to do +# +# One of the work bits is on. Find out which one. +# +sysc_work: + tm __TI_flags+3(%r9),_TIF_NEED_RESCHED + bo BASED(sysc_reschedule) + tm __TI_flags+3(%r9),_TIF_SIGPENDING + bo BASED(sysc_sigpending) + tm __TI_flags+3(%r9),_TIF_RESTART_SVC + bo BASED(sysc_restart) + tm __TI_flags+3(%r9),_TIF_SINGLE_STEP + bo BASED(sysc_singlestep) + b BASED(sysc_leave) + +# +# _TIF_NEED_RESCHED is set, call schedule +# +sysc_reschedule: + l %r1,BASED(.Lschedule) + la %r14,BASED(sysc_work_loop) + br %r1 # call scheduler + +# +# _TIF_SIGPENDING is set, call do_signal +# +sysc_sigpending: + ni __TI_flags+3(%r9),255-_TIF_SINGLE_STEP # clear TIF_SINGLE_STEP + la %r2,SP_PTREGS(%r15) # load pt_regs + sr %r3,%r3 # clear *oldset + l %r1,BASED(.Ldo_signal) + basr %r14,%r1 # call do_signal + tm __TI_flags+3(%r9),_TIF_RESTART_SVC + bo BASED(sysc_restart) + tm __TI_flags+3(%r9),_TIF_SINGLE_STEP + bo BASED(sysc_singlestep) + b BASED(sysc_leave) # out of here, do NOT recheck + +# +# _TIF_RESTART_SVC is set, set up registers and restart svc +# +sysc_restart: + ni __TI_flags+3(%r9),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC + l %r7,SP_R2(%r15) # load new svc number + sla %r7,2 + mvc SP_R2(4,%r15),SP_ORIG_R2(%r15) # restore first argument + lm %r2,%r6,SP_R2(%r15) # load svc arguments + b BASED(sysc_do_restart) # restart svc + +# +# _TIF_SINGLE_STEP is set, call do_single_step +# +sysc_singlestep: + ni __TI_flags+3(%r9),255-_TIF_SINGLE_STEP # clear TIF_SINGLE_STEP + mvi SP_TRAP+1(%r15),0x28 # set trap indication to pgm check + la %r2,SP_PTREGS(%r15) # address of register-save area + l %r1,BASED(.Lhandle_per) # load adr. of per handler + la %r14,BASED(sysc_return) # load adr. of system return + br %r1 # branch to do_single_step + +__critical_end: + +# +# call trace before and after sys_call +# +sysc_tracesys: + l %r1,BASED(.Ltrace) + la %r2,SP_PTREGS(%r15) # load pt_regs + la %r3,0 + srl %r7,2 + st %r7,SP_R2(%r15) + basr %r14,%r1 + clc SP_R2(4,%r15),BASED(.Lnr_syscalls) + bnl BASED(sysc_tracenogo) + l %r7,SP_R2(%r15) # strace might have changed the + sll %r7,2 # system call + l %r8,sys_call_table-system_call(%r7,%r13) +sysc_tracego: + 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 +sysc_tracenogo: + tm __TI_flags+3(%r9),(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT) + bz BASED(sysc_return) + l %r1,BASED(.Ltrace) + la %r2,SP_PTREGS(%r15) # load pt_regs + la %r3,1 + la %r14,BASED(sysc_return) + br %r1 + +# +# a new process exits the kernel with ret_from_fork +# + .globl ret_from_fork +ret_from_fork: + l %r13,__LC_SVC_NEW_PSW+4 + l %r9,__LC_THREAD_INFO # load pointer to thread_info struct + tm SP_PSW+1(%r15),0x01 # forking a kernel thread ? + bo BASED(0f) + st %r15,SP_R15(%r15) # store stack pointer for new kthread +0: l %r1,BASED(.Lschedtail) + basr %r14,%r1 + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + b BASED(sysc_return) + +# +# 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 + l %r1,BASED(.Lclone) + br %r1 # branch to sys_clone + +sys_fork_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,BASED(.Lfork) + br %r1 # branch to sys_fork + +sys_vfork_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,BASED(.Lvfork) + br %r1 # branch to sys_vfork + +sys_execve_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + l %r1,BASED(.Lexecve) + lr %r12,%r14 # save return address + basr %r14,%r1 # call sys_execve + ltr %r2,%r2 # check if execve failed + bnz 0(%r12) # it did fail -> store result in gpr2 + b 4(%r12) # SKIP ST 2,SP_R2(15) after BASR 14,8 + # in system_call/sysc_tracesys + +sys_sigreturn_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs as parameter + 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,BASED(.Lrt_sigreturn) + br %r1 # branch to sys_sigreturn + +# +# 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: + lr %r5,%r4 # move mask back + 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,BASED(.Lsigsuspend) + la %r14,4(%r14) # skip store of return value + br %r1 # branch to sys_sigsuspend + +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,BASED(.Lrt_sigsuspend) + la %r14,4(%r14) # skip store of return value + br %r1 # branch to sys_rt_sigsuspend + +sys_sigaltstack_glue: + la %r4,SP_PTREGS(%r15) # load pt_regs as parameter + l %r1,BASED(.Lsigaltstack) + br %r1 # branch to sys_sigreturn + + +/* + * 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?). + */ + STORE_TIMER __LC_SYNC_ENTER_TIMER + SAVE_ALL_BASE __LC_SAVE_AREA + tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception + bnz BASED(pgm_per) # got per exception -> special case + SAVE_ALL __LC_PGM_OLD_PSW,__LC_SAVE_AREA,1 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + bz BASED(pgm_no_vtime) + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +pgm_no_vtime: +#endif + l %r9,__LC_THREAD_INFO # load pointer to thread_info struct + l %r3,__LC_PGM_ILC # load program interruption code + la %r8,0x7f + nr %r8,%r3 +pgm_do_call: + l %r7,BASED(.Ljump_table) + sll %r8,2 + l %r7,0(%r8,%r7) # load address of handler routine + la %r2,SP_PTREGS(%r15) # address of register-save area + la %r14,BASED(sysc_return) + br %r7 # branch to interrupt-handler + +# +# handle per exception +# +pgm_per: + tm __LC_PGM_OLD_PSW,0x40 # test if per event recording is on + bnz BASED(pgm_per_std) # ok, normal per event from user space +# 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 + be BASED(pgm_svcper) +# no interesting special case, ignore PER event + lm %r12,%r15,__LC_SAVE_AREA + lpsw 0x28 + +# +# Normal per exception +# +pgm_per_std: + SAVE_ALL __LC_PGM_OLD_PSW,__LC_SAVE_AREA,1 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + bz BASED(pgm_no_vtime2) + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +pgm_no_vtime2: +#endif + l %r9,__LC_THREAD_INFO # load pointer to thread_info struct + l %r1,__TI_task(%r9) + mvc __THREAD_per+__PER_atmid(2,%r1),__LC_PER_ATMID + mvc __THREAD_per+__PER_address(4,%r1),__LC_PER_ADDRESS + mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID + oi __TI_flags+3(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP + l %r3,__LC_PGM_ILC # load program interruption code + la %r8,0x7f + nr %r8,%r3 # clear per-event-bit and ilc + be BASED(sysc_return) # only per or per+check ? + b BASED(pgm_do_call) + +# +# it was a single stepped SVC that is causing all the trouble +# +pgm_svcper: + SAVE_ALL __LC_SVC_OLD_PSW,__LC_SAVE_AREA,1 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + bz BASED(pgm_no_vtime3) + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +pgm_no_vtime3: +#endif + lh %r7,0x8a # get svc number from lowcore + l %r9,__LC_THREAD_INFO # load pointer to thread_info struct + l %r1,__TI_task(%r9) + mvc __THREAD_per+__PER_atmid(2,%r1),__LC_PER_ATMID + mvc __THREAD_per+__PER_address(4,%r1),__LC_PER_ADDRESS + mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID + oi __TI_flags+3(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + b BASED(sysc_do_svc) + +/* + * IO interrupt handler routine + */ + + .globl io_int_handler +io_int_handler: + STORE_TIMER __LC_ASYNC_ENTER_TIMER + stck __LC_INT_CLOCK + SAVE_ALL_BASE __LC_SAVE_AREA+16 + SAVE_ALL __LC_IO_OLD_PSW,__LC_SAVE_AREA+16,0 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + bz BASED(io_no_vtime) + UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER +io_no_vtime: +#endif + l %r9,__LC_THREAD_INFO # load pointer to thread_info struct + l %r1,BASED(.Ldo_IRQ) # load address of do_IRQ + la %r2,SP_PTREGS(%r15) # address of register-save area + basr %r14,%r1 # branch to standard irq handler + +io_return: + tm SP_PSW+1(%r15),0x01 # returning to user ? +#ifdef CONFIG_PREEMPT + bno BASED(io_preempt) # no -> check for preemptive scheduling +#else + bno BASED(io_leave) # no-> skip resched & signal +#endif + tm __TI_flags+3(%r9),_TIF_WORK_INT + bnz BASED(io_work) # there is work to do (signals etc.) +io_leave: + RESTORE_ALL 0 + +#ifdef CONFIG_PREEMPT +io_preempt: + icm %r0,15,__TI_precount(%r9) + bnz BASED(io_leave) + l %r1,SP_R15(%r15) + s %r1,BASED(.Lc_spsize) + mvc SP_PTREGS(__PT_SIZE,%r1),SP_PTREGS(%r15) + xc __SF_BACKCHAIN(4,%r1),__SF_BACKCHAIN(%r1) # clear back chain + lr %r15,%r1 +io_resume_loop: + tm __TI_flags+3(%r9),_TIF_NEED_RESCHED + bno BASED(io_leave) + mvc __TI_precount(4,%r9),BASED(.Lc_pactive) + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + l %r1,BASED(.Lschedule) + basr %r14,%r1 # call schedule + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts + xc __TI_precount(4,%r9),__TI_precount(%r9) + b BASED(io_resume_loop) +#endif + +# +# switch to kernel stack, then check the TIF bits +# +io_work: + l %r1,__LC_KERNEL_STACK + s %r1,BASED(.Lc_spsize) + mvc SP_PTREGS(__PT_SIZE,%r1),SP_PTREGS(%r15) + xc __SF_BACKCHAIN(4,%r1),__SF_BACKCHAIN(%r1) # clear back chain + lr %r15,%r1 +# +# One of the work bits is on. Find out which one. +# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED +# +io_work_loop: + tm __TI_flags+3(%r9),_TIF_NEED_RESCHED + bo BASED(io_reschedule) + tm __TI_flags+3(%r9),_TIF_SIGPENDING + bo BASED(io_sigpending) + b BASED(io_leave) + +# +# _TIF_NEED_RESCHED is set, call schedule +# +io_reschedule: + l %r1,BASED(.Lschedule) + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + basr %r14,%r1 # call scheduler + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts + tm __TI_flags+3(%r9),_TIF_WORK_INT + bz BASED(io_leave) # there is no work to do + b BASED(io_work_loop) + +# +# _TIF_SIGPENDING is set, call do_signal +# +io_sigpending: + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + la %r2,SP_PTREGS(%r15) # load pt_regs + sr %r3,%r3 # clear *oldset + l %r1,BASED(.Ldo_signal) + basr %r14,%r1 # call do_signal + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts + b BASED(io_leave) # out of here, do NOT recheck + +/* + * External interrupt handler routine + */ + + .globl ext_int_handler +ext_int_handler: + STORE_TIMER __LC_ASYNC_ENTER_TIMER + stck __LC_INT_CLOCK + SAVE_ALL_BASE __LC_SAVE_AREA+16 + SAVE_ALL __LC_EXT_OLD_PSW,__LC_SAVE_AREA+16,0 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + bz BASED(ext_no_vtime) + UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER +ext_no_vtime: +#endif + l %r9,__LC_THREAD_INFO # load pointer to thread_info struct + la %r2,SP_PTREGS(%r15) # address of register-save area + lh %r3,__LC_EXT_INT_CODE # get interruption code + l %r1,BASED(.Ldo_extint) + basr %r14,%r1 + b BASED(io_return) + +/* + * Machine check handler routines + */ + + .globl mcck_int_handler +mcck_int_handler: + STORE_TIMER __LC_ASYNC_ENTER_TIMER + SAVE_ALL_BASE __LC_SAVE_AREA+32 + SAVE_ALL __LC_MCK_OLD_PSW,__LC_SAVE_AREA+32,0 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + bz BASED(mcck_no_vtime) + UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER +mcck_no_vtime: +#endif + l %r1,BASED(.Ls390_mcck) + basr %r14,%r1 # call machine check handler +mcck_return: + RESTORE_ALL 0 + +#ifdef CONFIG_SMP +/* + * Restart interruption handler, kick starter for additional CPUs + */ + .globl restart_int_handler +restart_int_handler: + l %r15,__LC_SAVE_AREA+60 # load ksp + lctl %c0,%c15,__LC_CREGS_SAVE_AREA # get new ctl regs + lam %a0,%a15,__LC_AREGS_SAVE_AREA + lm %r6,%r15,__SF_GPRS(%r15) # load registers from clone + stosm __SF_EMPTY(%r15),0x04 # now we can turn dat on + 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 ... + */ + .globl restart_int_handler +restart_int_handler: + basr %r1,0 +restart_base: + lpsw restart_crash-restart_base(%r1) + .align 8 +restart_crash: + .long 0x000a0000,0x00000000 +restart_go: +#endif + +#ifdef CONFIG_CHECK_STACK +/* + * The synchronous or the asynchronous stack overflowed. We are dead. + * No need to properly save the registers, we are going to panic anyway. + * Setup a pt_regs so that show_trace can provide a good call trace. + */ +stack_overflow: + l %r15,__LC_PANIC_STACK # change to panic stack + sl %r15,BASED(.Lc_spsize) + mvc SP_PSW(8,%r15),0(%r12) # move user PSW to stack + stm %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack + la %r1,__LC_SAVE_AREA + ch %r12,BASED(.L0x020) # old psw addr == __LC_SVC_OLD_PSW ? + be BASED(0f) + ch %r12,BASED(.L0x028) # old psw addr == __LC_PGM_OLD_PSW ? + be BASED(0f) + la %r1,__LC_SAVE_AREA+16 +0: mvc SP_R12(16,%r15),0(%r1) # move %r12-%r15 to stack + xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear back chain + l %r1,BASED(1f) # branch to kernel_stack_overflow + la %r2,SP_PTREGS(%r15) # load pt_regs + br %r1 +1: .long kernel_stack_overflow +#endif + +cleanup_table_system_call: + .long system_call + 0x80000000, sysc_do_svc + 0x80000000 +cleanup_table_sysc_return: + .long sysc_return + 0x80000000, sysc_leave + 0x80000000 +cleanup_table_sysc_leave: + .long sysc_leave + 0x80000000, sysc_work_loop + 0x80000000 +cleanup_table_sysc_work_loop: + .long sysc_work_loop + 0x80000000, sysc_reschedule + 0x80000000 + +cleanup_critical: + clc 4(4,%r12),BASED(cleanup_table_system_call) + bl BASED(0f) + clc 4(4,%r12),BASED(cleanup_table_system_call+4) + bl BASED(cleanup_system_call) +0: + clc 4(4,%r12),BASED(cleanup_table_sysc_return) + bl BASED(0f) + clc 4(4,%r12),BASED(cleanup_table_sysc_return+4) + bl BASED(cleanup_sysc_return) +0: + clc 4(4,%r12),BASED(cleanup_table_sysc_leave) + bl BASED(0f) + clc 4(4,%r12),BASED(cleanup_table_sysc_leave+4) + bl BASED(cleanup_sysc_leave) +0: + clc 4(4,%r12),BASED(cleanup_table_sysc_work_loop) + bl BASED(0f) + clc 4(4,%r12),BASED(cleanup_table_sysc_work_loop+4) + bl BASED(cleanup_sysc_leave) +0: + br %r14 + +cleanup_system_call: + mvc __LC_RETURN_PSW(8),0(%r12) +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+4) + bh BASED(0f) + mvc __LC_SYNC_ENTER_TIMER(8),__LC_ASYNC_ENTER_TIMER +0: clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+8) + bhe BASED(cleanup_vtime) +#endif + clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn) + bh BASED(0f) + mvc __LC_SAVE_AREA(16),__LC_SAVE_AREA+16 +0: st %r13,__LC_SAVE_AREA+20 + SAVE_ALL __LC_SVC_OLD_PSW,__LC_SAVE_AREA,1 + st %r15,__LC_SAVE_AREA+28 + lh %r7,0x8a +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +cleanup_vtime: + clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+12) + bhe BASED(cleanup_stime) + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + bz BASED(cleanup_novtime) + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER +cleanup_stime: + clc __LC_RETURN_PSW+4(4),BASED(cleanup_system_call_insn+16) + bh BASED(cleanup_update) + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER +cleanup_update: + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +cleanup_novtime: +#endif + mvc __LC_RETURN_PSW+4(4),BASED(cleanup_table_system_call+4) + la %r12,__LC_RETURN_PSW + br %r14 +cleanup_system_call_insn: + .long sysc_saveall + 0x80000000 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + .long system_call + 0x80000000 + .long sysc_vtime + 0x80000000 + .long sysc_stime + 0x80000000 + .long sysc_update + 0x80000000 +#endif + +cleanup_sysc_return: + mvc __LC_RETURN_PSW(4),0(%r12) + mvc __LC_RETURN_PSW+4(4),BASED(cleanup_table_sysc_return) + la %r12,__LC_RETURN_PSW + br %r14 + +cleanup_sysc_leave: + clc 4(4,%r12),BASED(cleanup_sysc_leave_insn) + be BASED(0f) +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + mvc __LC_EXIT_TIMER(8),__LC_ASYNC_ENTER_TIMER + clc 4(4,%r12),BASED(cleanup_sysc_leave_insn+4) + be BASED(0f) +#endif + mvc __LC_RETURN_PSW(8),SP_PSW(%r15) + mvc __LC_SAVE_AREA+16(16),SP_R12(%r15) + lm %r0,%r11,SP_R0(%r15) + l %r15,SP_R15(%r15) +0: la %r12,__LC_RETURN_PSW + br %r14 +cleanup_sysc_leave_insn: +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + .long sysc_leave + 14 + 0x80000000 +#endif + .long sysc_leave + 10 + 0x80000000 + +/* + * Integer constants + */ + .align 4 +.Lc_spsize: .long SP_SIZE +.Lc_overhead: .long STACK_FRAME_OVERHEAD +.Lc_pactive: .long PREEMPT_ACTIVE +.Lnr_syscalls: .long NR_syscalls +.L0x018: .short 0x018 +.L0x020: .short 0x020 +.L0x028: .short 0x028 +.L0x030: .short 0x030 +.L0x038: .short 0x038 +.Lc_1: .long 1 + +/* + * Symbol constants + */ +.Ls390_mcck: .long s390_do_machine_check +.Ldo_IRQ: .long do_IRQ +.Ldo_extint: .long do_extint +.Ldo_signal: .long do_signal +.Lhandle_per: .long do_single_step +.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 +.Lschedtail: .long schedule_tail + +.Lcritical_start: + .long __critical_start + 0x80000000 +.Lcritical_end: + .long __critical_end + 0x80000000 +.Lcleanup_critical: + .long cleanup_critical + +#define SYSCALL(esa,esame,emu) .long esa + .globl sys_call_table +sys_call_table: +#include "syscalls.S" +#undef SYSCALL + diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S new file mode 100644 index 000000000000..51527ab8c8f9 --- /dev/null +++ b/arch/s390/kernel/entry64.S @@ -0,0 +1,881 @@ +/* + * 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/cache.h> +#include <asm/lowcore.h> +#include <asm/errno.h> +#include <asm/ptrace.h> +#include <asm/thread_info.h> +#include <asm/offsets.h> +#include <asm/unistd.h> +#include <asm/page.h> + +/* + * Stack layout for the system_call stack entry. + * The first few entries are identical to the user_regs_struct. + */ +SP_PTREGS = STACK_FRAME_OVERHEAD +SP_ARGS = STACK_FRAME_OVERHEAD + __PT_ARGS +SP_PSW = STACK_FRAME_OVERHEAD + __PT_PSW +SP_R0 = STACK_FRAME_OVERHEAD + __PT_GPRS +SP_R1 = STACK_FRAME_OVERHEAD + __PT_GPRS + 8 +SP_R2 = STACK_FRAME_OVERHEAD + __PT_GPRS + 16 +SP_R3 = STACK_FRAME_OVERHEAD + __PT_GPRS + 24 +SP_R4 = STACK_FRAME_OVERHEAD + __PT_GPRS + 32 +SP_R5 = STACK_FRAME_OVERHEAD + __PT_GPRS + 40 +SP_R6 = STACK_FRAME_OVERHEAD + __PT_GPRS + 48 +SP_R7 = STACK_FRAME_OVERHEAD + __PT_GPRS + 56 +SP_R8 = STACK_FRAME_OVERHEAD + __PT_GPRS + 64 +SP_R9 = STACK_FRAME_OVERHEAD + __PT_GPRS + 72 +SP_R10 = STACK_FRAME_OVERHEAD + __PT_GPRS + 80 +SP_R11 = STACK_FRAME_OVERHEAD + __PT_GPRS + 88 +SP_R12 = STACK_FRAME_OVERHEAD + __PT_GPRS + 96 +SP_R13 = STACK_FRAME_OVERHEAD + __PT_GPRS + 104 +SP_R14 = STACK_FRAME_OVERHEAD + __PT_GPRS + 112 +SP_R15 = STACK_FRAME_OVERHEAD + __PT_GPRS + 120 +SP_ORIG_R2 = STACK_FRAME_OVERHEAD + __PT_ORIG_GPR2 +SP_ILC = STACK_FRAME_OVERHEAD + __PT_ILC +SP_TRAP = STACK_FRAME_OVERHEAD + __PT_TRAP +SP_SIZE = STACK_FRAME_OVERHEAD + __PT_SIZE + +STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER +STACK_SIZE = 1 << STACK_SHIFT + +_TIF_WORK_SVC = (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \ + _TIF_RESTART_SVC | _TIF_SINGLE_STEP ) +_TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NEED_RESCHED) + +#define BASED(name) name-system_call(%r13) + + .macro STORE_TIMER lc_offset +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + stpt \lc_offset +#endif + .endm + +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + .macro UPDATE_VTIME lc_from,lc_to,lc_sum + lg %r10,\lc_from + slg %r10,\lc_to + alg %r10,\lc_sum + stg %r10,\lc_sum + .endm +#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 + */ + + .macro SAVE_ALL_BASE savearea + stmg %r12,%r15,\savearea + larl %r13,system_call + .endm + + .macro SAVE_ALL psworg,savearea,sync + la %r12,\psworg + .if \sync + tm \psworg+1,0x01 # test problem state bit + jz 2f # skip stack setup save + lg %r15,__LC_KERNEL_STACK # problem state -> load ksp + .else + tm \psworg+1,0x01 # test problem state bit + jnz 1f # from user -> load kernel stack + clc \psworg+8(8),BASED(.Lcritical_end) + jhe 0f + clc \psworg+8(8),BASED(.Lcritical_start) + jl 0f + brasl %r14,cleanup_critical + tm 0(%r12),0x01 # retest problem state after cleanup + jnz 1f +0: lg %r14,__LC_ASYNC_STACK # are we already on the async. stack ? + slgr %r14,%r15 + srag %r14,%r14,STACK_SHIFT + jz 2f +1: lg %r15,__LC_ASYNC_STACK # load async stack + .endif +#ifdef CONFIG_CHECK_STACK + j 3f +2: tml %r15,STACK_SIZE - CONFIG_STACK_GUARD + jz stack_overflow +3: +#endif +2: aghi %r15,-SP_SIZE # make room for registers & psw + mvc SP_PSW(16,%r15),0(%r12) # move user PSW to stack + la %r12,\psworg + stg %r2,SP_ORIG_R2(%r15) # store original content of gpr 2 + icm %r12,12,__LC_SVC_ILC + stmg %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack + st %r12,SP_ILC(%r15) + mvc SP_R12(32,%r15),\savearea # move %r12-%r15 to stack + la %r12,0 + stg %r12,__SF_BACKCHAIN(%r15) + .endm + + .macro RESTORE_ALL sync + mvc __LC_RETURN_PSW(16),SP_PSW(%r15) # move user PSW to lowcore + .if !\sync + ni __LC_RETURN_PSW+1,0xfd # clear wait state bit + .endif + lmg %r0,%r15,SP_R0(%r15) # load gprs 0-15 of user + STORE_TIMER __LC_EXIT_TIMER + lpswe __LC_RETURN_PSW # back to caller + .endm + +/* + * Scheduler resume function, called by switch_to + * gpr2 = (task_struct *) prev + * gpr3 = (task_struct *) next + * Returns: + * gpr2 = prev + */ + .globl __switch_to +__switch_to: + tm __THREAD_per+4(%r3),0xe8 # is the new process using per ? + jz __switch_to_noper # if not we're fine + stctg %c9,%c11,__SF_EMPTY(%r15)# We are using per stuff + clc __THREAD_per(24,%r3),__SF_EMPTY(%r15) + je __switch_to_noper # we got away without bashing TLB's + lctlg %c9,%c11,__THREAD_per(%r3) # Nope we didn't +__switch_to_noper: + stmg %r6,%r15,__SF_GPRS(%r15)# store __switch_to registers of prev task + stg %r15,__THREAD_ksp(%r2) # store kernel stack to prev->tss.ksp + lg %r15,__THREAD_ksp(%r3) # load kernel stack from next->tss.ksp + lmg %r6,%r15,__SF_GPRS(%r15)# load __switch_to registers of next task + stg %r3,__LC_CURRENT # __LC_CURRENT = current task struct + lctl %c4,%c4,__TASK_pid(%r3) # load pid to control reg. 4 + lg %r3,__THREAD_info(%r3) # load thread_info from task struct + stg %r3,__LC_THREAD_INFO + aghi %r3,STACK_SIZE + stg %r3,__LC_KERNEL_STACK # __LC_KERNEL_STACK = new kernel stack + br %r14 + +__critical_start: +/* + * SVC interrupt handler routine. System calls are synchronous events and + * are executed with interrupts enabled. + */ + + .globl system_call +system_call: + STORE_TIMER __LC_SYNC_ENTER_TIMER +sysc_saveall: + SAVE_ALL_BASE __LC_SAVE_AREA + SAVE_ALL __LC_SVC_OLD_PSW,__LC_SAVE_AREA,1 + llgh %r7,__LC_SVC_INT_CODE # get svc number from lowcore +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +sysc_vtime: + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + jz sysc_do_svc + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER +sysc_stime: + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER +sysc_update: + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +#endif +sysc_do_svc: + lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct + slag %r7,%r7,2 # *4 and test for svc 0 + jnz sysc_nr_ok + # svc 0: system call number in %r1 + cl %r1,BASED(.Lnr_syscalls) + jnl sysc_nr_ok + lgfr %r7,%r1 # clear high word in r1 + slag %r7,%r7,2 # svc 0: system call number in %r1 +sysc_nr_ok: + mvc SP_ARGS(8,%r15),SP_R7(%r15) +sysc_do_restart: + larl %r10,sys_call_table +#ifdef CONFIG_S390_SUPPORT + tm SP_PSW+3(%r15),0x01 # are we running in 31 bit mode ? + jo sysc_noemu + larl %r10,sys_call_table_emu # use 31 bit emulation system calls +sysc_noemu: +#endif + tm __TI_flags+7(%r9),(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT) + lgf %r8,0(%r7,%r10) # load address of system call routine + 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: + tm SP_PSW+1(%r15),0x01 # returning to user ? + jno sysc_leave + tm __TI_flags+7(%r9),_TIF_WORK_SVC + jnz sysc_work # there is work to do (signals etc.) +sysc_leave: + RESTORE_ALL 1 + +# +# recheck if there is more work to do +# +sysc_work_loop: + tm __TI_flags+7(%r9),_TIF_WORK_SVC + jz sysc_leave # there is no work to do +# +# One of the work bits is on. Find out which one. +# +sysc_work: + tm __TI_flags+7(%r9),_TIF_NEED_RESCHED + jo sysc_reschedule + tm __TI_flags+7(%r9),_TIF_SIGPENDING + jo sysc_sigpending + tm __TI_flags+7(%r9),_TIF_RESTART_SVC + jo sysc_restart + tm __TI_flags+7(%r9),_TIF_SINGLE_STEP + jo sysc_singlestep + j sysc_leave + +# +# _TIF_NEED_RESCHED is set, call schedule +# +sysc_reschedule: + larl %r14,sysc_work_loop + jg schedule # return point is sysc_return + +# +# _TIF_SIGPENDING is set, call do_signal +# +sysc_sigpending: + ni __TI_flags+7(%r9),255-_TIF_SINGLE_STEP # clear TIF_SINGLE_STEP + la %r2,SP_PTREGS(%r15) # load pt_regs + sgr %r3,%r3 # clear *oldset + brasl %r14,do_signal # call do_signal + tm __TI_flags+7(%r9),_TIF_RESTART_SVC + jo sysc_restart + tm __TI_flags+7(%r9),_TIF_SINGLE_STEP + jo sysc_singlestep + j sysc_leave # out of here, do NOT recheck + +# +# _TIF_RESTART_SVC is set, set up registers and restart svc +# +sysc_restart: + ni __TI_flags+7(%r9),255-_TIF_RESTART_SVC # clear TIF_RESTART_SVC + lg %r7,SP_R2(%r15) # load new svc number + slag %r7,%r7,2 # *4 + mvc SP_R2(8,%r15),SP_ORIG_R2(%r15) # restore first argument + lmg %r2,%r6,SP_R2(%r15) # load svc arguments + j sysc_do_restart # restart svc + +# +# _TIF_SINGLE_STEP is set, call do_single_step +# +sysc_singlestep: + ni __TI_flags+7(%r9),255-_TIF_SINGLE_STEP # clear TIF_SINGLE_STEP + lhi %r0,__LC_PGM_OLD_PSW + sth %r0,SP_TRAP(%r15) # set trap indication to pgm check + la %r2,SP_PTREGS(%r15) # address of register-save area + larl %r14,sysc_return # load adr. of system return + jg do_single_step # branch to do_sigtrap + + +__critical_end: + +# +# call syscall_trace before and after system call +# special linkage: %r12 contains the return address for trace_svc +# +sysc_tracesys: + la %r2,SP_PTREGS(%r15) # load pt_regs + la %r3,0 + srl %r7,2 + stg %r7,SP_R2(%r15) + brasl %r14,syscall_trace + lghi %r0,NR_syscalls + clg %r0,SP_R2(%r15) + jnh sysc_tracenogo + lg %r7,SP_R2(%r15) # strace might have changed the + sll %r7,2 # system call + lgf %r8,0(%r7,%r10) +sysc_tracego: + 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 +sysc_tracenogo: + tm __TI_flags+7(%r9),(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT) + jz sysc_return + la %r2,SP_PTREGS(%r15) # load pt_regs + la %r3,1 + larl %r14,sysc_return # return point is sysc_return + jg syscall_trace + +# +# a new process exits the kernel with ret_from_fork +# + .globl ret_from_fork +ret_from_fork: + lg %r13,__LC_SVC_NEW_PSW+8 + lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct + tm SP_PSW+1(%r15),0x01 # forking a kernel thread ? + jo 0f + stg %r15,SP_R15(%r15) # store stack pointer for new kthread +0: brasl %r14,schedule_tail + stosm 24(%r15),0x03 # reenable interrupts + j sysc_return + +# +# 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 + +#ifdef CONFIG_S390_SUPPORT +sys32_clone_glue: + la %r2,SP_PTREGS(%r15) # load pt_regs + jg sys32_clone # branch to sys32_clone +#endif + +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 + +/* + * 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?). + */ + STORE_TIMER __LC_SYNC_ENTER_TIMER + SAVE_ALL_BASE __LC_SAVE_AREA + tm __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception + jnz pgm_per # got per exception -> special case + SAVE_ALL __LC_PGM_OLD_PSW,__LC_SAVE_AREA,1 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + jz pgm_no_vtime + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +pgm_no_vtime: +#endif + lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct + lgf %r3,__LC_PGM_ILC # load program interruption code + lghi %r8,0x7f + ngr %r8,%r3 +pgm_do_call: + sll %r8,3 + larl %r1,pgm_check_table + lg %r1,0(%r8,%r1) # load address of handler routine + la %r2,SP_PTREGS(%r15) # address of register-save area + larl %r14,sysc_return + br %r1 # branch to interrupt-handler + +# +# handle per exception +# +pgm_per: + tm __LC_PGM_OLD_PSW,0x40 # test if per event recording is on + jnz pgm_per_std # ok, normal per event from user space +# 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 + lmg %r12,%r15,__LC_SAVE_AREA + lpswe __LC_PGM_OLD_PSW + +# +# Normal per exception +# +pgm_per_std: + SAVE_ALL __LC_PGM_OLD_PSW,__LC_SAVE_AREA,1 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + jz pgm_no_vtime2 + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +pgm_no_vtime2: +#endif + lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct + lg %r1,__TI_task(%r9) + mvc __THREAD_per+__PER_atmid(2,%r1),__LC_PER_ATMID + mvc __THREAD_per+__PER_address(8,%r1),__LC_PER_ADDRESS + mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID + oi __TI_flags+7(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP + lgf %r3,__LC_PGM_ILC # load program interruption code + lghi %r8,0x7f + ngr %r8,%r3 # clear per-event-bit and ilc + je sysc_return + j pgm_do_call + +# +# it was a single stepped SVC that is causing all the trouble +# +pgm_svcper: + SAVE_ALL __LC_SVC_OLD_PSW,__LC_SAVE_AREA,1 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + jz pgm_no_vtime3 + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +pgm_no_vtime3: +#endif + llgh %r7,__LC_SVC_INT_CODE # get svc number from lowcore + lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct + lg %r1,__TI_task(%r9) + mvc __THREAD_per+__PER_atmid(2,%r1),__LC_PER_ATMID + mvc __THREAD_per+__PER_address(8,%r1),__LC_PER_ADDRESS + mvc __THREAD_per+__PER_access_id(1,%r1),__LC_PER_ACCESS_ID + oi __TI_flags+7(%r9),_TIF_SINGLE_STEP # set TIF_SINGLE_STEP + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + j sysc_do_svc + +/* + * IO interrupt handler routine + */ + .globl io_int_handler +io_int_handler: + STORE_TIMER __LC_ASYNC_ENTER_TIMER + stck __LC_INT_CLOCK + SAVE_ALL_BASE __LC_SAVE_AREA+32 + SAVE_ALL __LC_IO_OLD_PSW,__LC_SAVE_AREA+32,0 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + jz io_no_vtime + UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER +io_no_vtime: +#endif + lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct + la %r2,SP_PTREGS(%r15) # address of register-save area + brasl %r14,do_IRQ # call standard irq handler + +io_return: + tm SP_PSW+1(%r15),0x01 # returning to user ? +#ifdef CONFIG_PREEMPT + jno io_preempt # no -> check for preemptive scheduling +#else + jno io_leave # no-> skip resched & signal +#endif + tm __TI_flags+7(%r9),_TIF_WORK_INT + jnz io_work # there is work to do (signals etc.) +io_leave: + RESTORE_ALL 0 + +#ifdef CONFIG_PREEMPT +io_preempt: + icm %r0,15,__TI_precount(%r9) + jnz io_leave + # switch to kernel stack + lg %r1,SP_R15(%r15) + aghi %r1,-SP_SIZE + mvc SP_PTREGS(__PT_SIZE,%r1),SP_PTREGS(%r15) + xc __SF_BACKCHAIN(8,%r1),__SF_BACKCHAIN(%r1) # clear back chain + lgr %r15,%r1 +io_resume_loop: + tm __TI_flags+7(%r9),_TIF_NEED_RESCHED + jno io_leave + larl %r1,.Lc_pactive + mvc __TI_precount(4,%r9),0(%r1) + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + brasl %r14,schedule # call schedule + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts + xc __TI_precount(4,%r9),__TI_precount(%r9) + j io_resume_loop +#endif + +# +# switch to kernel stack, then check TIF bits +# +io_work: + lg %r1,__LC_KERNEL_STACK + aghi %r1,-SP_SIZE + mvc SP_PTREGS(__PT_SIZE,%r1),SP_PTREGS(%r15) + xc __SF_BACKCHAIN(8,%r1),__SF_BACKCHAIN(%r1) # clear back chain + lgr %r15,%r1 +# +# One of the work bits is on. Find out which one. +# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED +# +io_work_loop: + tm __TI_flags+7(%r9),_TIF_NEED_RESCHED + jo io_reschedule + tm __TI_flags+7(%r9),_TIF_SIGPENDING + jo io_sigpending + j io_leave + +# +# _TIF_NEED_RESCHED is set, call schedule +# +io_reschedule: + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + brasl %r14,schedule # call scheduler + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts + tm __TI_flags+7(%r9),_TIF_WORK_INT + jz io_leave # there is no work to do + j io_work_loop + +# +# _TIF_SIGPENDING is set, call do_signal +# +io_sigpending: + stosm __SF_EMPTY(%r15),0x03 # reenable interrupts + la %r2,SP_PTREGS(%r15) # load pt_regs + slgr %r3,%r3 # clear *oldset + brasl %r14,do_signal # call do_signal + stnsm __SF_EMPTY(%r15),0xfc # disable I/O and ext. interrupts + j sysc_leave # out of here, do NOT recheck + +/* + * External interrupt handler routine + */ + .globl ext_int_handler +ext_int_handler: + STORE_TIMER __LC_ASYNC_ENTER_TIMER + stck __LC_INT_CLOCK + SAVE_ALL_BASE __LC_SAVE_AREA+32 + SAVE_ALL __LC_EXT_OLD_PSW,__LC_SAVE_AREA+32,0 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + jz ext_no_vtime + UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER +ext_no_vtime: +#endif + lg %r9,__LC_THREAD_INFO # load pointer to thread_info struct + la %r2,SP_PTREGS(%r15) # address of register-save area + llgh %r3,__LC_EXT_INT_CODE # get interruption code + brasl %r14,do_extint + j io_return + +/* + * Machine check handler routines + */ + .globl mcck_int_handler +mcck_int_handler: + STORE_TIMER __LC_ASYNC_ENTER_TIMER + SAVE_ALL_BASE __LC_SAVE_AREA+64 + SAVE_ALL __LC_MCK_OLD_PSW,__LC_SAVE_AREA+64,0 +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + jz mcck_no_vtime + UPDATE_VTIME __LC_EXIT_TIMER,__LC_ASYNC_ENTER_TIMER,__LC_USER_TIMER + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER + mvc __LC_LAST_UPDATE_TIMER(8),__LC_ASYNC_ENTER_TIMER +mcck_no_vtime: +#endif + brasl %r14,s390_do_machine_check +mcck_return: + RESTORE_ALL 0 + +#ifdef CONFIG_SMP +/* + * Restart interruption handler, kick starter for additional CPUs + */ + .globl restart_int_handler +restart_int_handler: + lg %r15,__LC_SAVE_AREA+120 # load ksp + lghi %r10,__LC_CREGS_SAVE_AREA + lctlg %c0,%c15,0(%r10) # get new ctl regs + lghi %r10,__LC_AREGS_SAVE_AREA + lam %a0,%a15,0(%r10) + lmg %r6,%r15,__SF_GPRS(%r15) # load registers from clone + stosm __SF_EMPTY(%r15),0x04 # now we can turn dat on + 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 + +#ifdef CONFIG_CHECK_STACK +/* + * The synchronous or the asynchronous stack overflowed. We are dead. + * No need to properly save the registers, we are going to panic anyway. + * Setup a pt_regs so that show_trace can provide a good call trace. + */ +stack_overflow: + lg %r15,__LC_PANIC_STACK # change to panic stack + aghi %r1,-SP_SIZE + mvc SP_PSW(16,%r15),0(%r12) # move user PSW to stack + stmg %r0,%r11,SP_R0(%r15) # store gprs %r0-%r11 to kernel stack + la %r1,__LC_SAVE_AREA + chi %r12,__LC_SVC_OLD_PSW + je 0f + chi %r12,__LC_PGM_OLD_PSW + je 0f + la %r1,__LC_SAVE_AREA+16 +0: mvc SP_R12(32,%r15),0(%r1) # move %r12-%r15 to stack + xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15) # clear back chain + la %r2,SP_PTREGS(%r15) # load pt_regs + jg kernel_stack_overflow +#endif + +cleanup_table_system_call: + .quad system_call, sysc_do_svc +cleanup_table_sysc_return: + .quad sysc_return, sysc_leave +cleanup_table_sysc_leave: + .quad sysc_leave, sysc_work_loop +cleanup_table_sysc_work_loop: + .quad sysc_work_loop, sysc_reschedule + +cleanup_critical: + clc 8(8,%r12),BASED(cleanup_table_system_call) + jl 0f + clc 8(8,%r12),BASED(cleanup_table_system_call+8) + jl cleanup_system_call +0: + clc 8(8,%r12),BASED(cleanup_table_sysc_return) + jl 0f + clc 8(8,%r12),BASED(cleanup_table_sysc_return+8) + jl cleanup_sysc_return +0: + clc 8(8,%r12),BASED(cleanup_table_sysc_leave) + jl 0f + clc 8(8,%r12),BASED(cleanup_table_sysc_leave+8) + jl cleanup_sysc_leave +0: + clc 8(8,%r12),BASED(cleanup_table_sysc_work_loop) + jl 0f + clc 8(8,%r12),BASED(cleanup_table_sysc_work_loop+8) + jl cleanup_sysc_leave +0: + br %r14 + +cleanup_system_call: + mvc __LC_RETURN_PSW(16),0(%r12) +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+8) + jh 0f + mvc __LC_SYNC_ENTER_TIMER(8),__LC_ASYNC_ENTER_TIMER +0: clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+16) + jhe cleanup_vtime +#endif + clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn) + jh 0f + mvc __LC_SAVE_AREA(32),__LC_SAVE_AREA+32 +0: stg %r13,__LC_SAVE_AREA+40 + SAVE_ALL __LC_SVC_OLD_PSW,__LC_SAVE_AREA,1 + stg %r15,__LC_SAVE_AREA+56 + llgh %r7,__LC_SVC_INT_CODE +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +cleanup_vtime: + clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+24) + jhe cleanup_stime + tm SP_PSW+1(%r15),0x01 # interrupting from user ? + jz cleanup_novtime + UPDATE_VTIME __LC_EXIT_TIMER,__LC_SYNC_ENTER_TIMER,__LC_USER_TIMER +cleanup_stime: + clc __LC_RETURN_PSW+8(8),BASED(cleanup_system_call_insn+32) + jh cleanup_update + UPDATE_VTIME __LC_LAST_UPDATE_TIMER,__LC_EXIT_TIMER,__LC_SYSTEM_TIMER +cleanup_update: + mvc __LC_LAST_UPDATE_TIMER(8),__LC_SYNC_ENTER_TIMER +cleanup_novtime: +#endif + mvc __LC_RETURN_PSW+8(8),BASED(cleanup_table_system_call+8) + la %r12,__LC_RETURN_PSW + br %r14 +cleanup_system_call_insn: + .quad sysc_saveall +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + .quad system_call + .quad sysc_vtime + .quad sysc_stime + .quad sysc_update +#endif + +cleanup_sysc_return: + mvc __LC_RETURN_PSW(8),0(%r12) + mvc __LC_RETURN_PSW+8(8),BASED(cleanup_table_sysc_return) + la %r12,__LC_RETURN_PSW + br %r14 + +cleanup_sysc_leave: + clc 8(8,%r12),BASED(cleanup_sysc_leave_insn) + je 0f +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + mvc __LC_EXIT_TIMER(8),__LC_ASYNC_ENTER_TIMER + clc 8(8,%r12),BASED(cleanup_sysc_leave_insn+8) + je 0f +#endif + mvc __LC_RETURN_PSW(16),SP_PSW(%r15) + mvc __LC_SAVE_AREA+32(32),SP_R12(%r15) + lmg %r0,%r11,SP_R0(%r15) + lg %r15,SP_R15(%r15) +0: la %r12,__LC_RETURN_PSW + br %r14 +cleanup_sysc_leave_insn: +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + .quad sysc_leave + 16 +#endif + .quad sysc_leave + 12 + +/* + * Integer constants + */ + .align 4 +.Lconst: +.Lc_pactive: .long PREEMPT_ACTIVE +.Lnr_syscalls: .long NR_syscalls +.L0x0130: .short 0x130 +.L0x0140: .short 0x140 +.L0x0150: .short 0x150 +.L0x0160: .short 0x160 +.L0x0170: .short 0x170 +.Lcritical_start: + .quad __critical_start +.Lcritical_end: + .quad __critical_end + +#define SYSCALL(esa,esame,emu) .long esame + .globl sys_call_table +sys_call_table: +#include "syscalls.S" +#undef SYSCALL + +#ifdef CONFIG_S390_SUPPORT + +#define SYSCALL(esa,esame,emu) .long emu + .globl sys_call_table_emu +sys_call_table_emu: +#include "syscalls.S" +#undef SYSCALL +#endif diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S new file mode 100644 index 000000000000..b804c55bd919 --- /dev/null +++ b/arch/s390/kernel/head.S @@ -0,0 +1,772 @@ +/* + * 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> +#include <asm/offsets.h> +#include <asm/thread_info.h> +#include <asm/page.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 __LC_IO_NEW_PSW(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 + l %r12,.Lparm # pointer to parameter area + st %r1,IPL_DEVICE-PARMAREA(%r12) # store ipl device number + +# +# 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 + tm __LC_CPUID,0xff # running VM ? + bno .Lnoreset + la %r2,.Lreset + lhi %r3,26 + .long 0x83230008 +.Lnoreset: +#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: + l %r1,.memsize + st %r3,0(%r1) + 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 +.memsize: .long memory_size +.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: lctl %c0,%c15,.Lctl-.LPG1(%r13) # load control registers + la %r12,_pstart-.LPG1(%r13) # pointer to parameter area + # move IPL device to lowcore + mvc __LC_IPLDEV(4),IPL_DEVICE-PARMAREA(%r12) + +# +# clear bss memory +# + l %r2,.Lbss_bgn-.LPG1(%r13) # start of bss + l %r3,.Lbss_end-.LPG1(%r13) # end of bss + sr %r3,%r2 # length of bss + sr %r4,%r4 # + sr %r5,%r5 # set src,length and pad to zero + sr %r0,%r0 # + mvcle %r2,%r4,0 # clear mem + jo .-4 # branch back, if not finish + + l %r2,.Lrcp-.LPG1(%r13) # Read SCP forced command word +.Lservicecall: + stosm .Lpmask-.LPG1(%r13),0x01 # authorize ext interrupts + + stctl %r0, %r0,.Lcr-.LPG1(%r13) # get cr0 + la %r1,0x200 # set bit 22 + o %r1,.Lcr-.LPG1(%r13) # or old cr0 with r1 + st %r1,.Lcr-.LPG1(%r13) + lctl %r0, %r0,.Lcr-.LPG1(%r13) # load modified cr0 + + mvc __LC_EXT_NEW_PSW(8),.Lpcext-.LPG1(%r13) # set postcall psw + la %r1, .Lsclph-.LPG1(%r13) + a %r1,__LC_EXT_NEW_PSW+4 # set handler + st %r1,__LC_EXT_NEW_PSW+4 + + la %r4,_pstart-.LPG1(%r13) # %r4 is our index for sccb stuff + la %r1, .Lsccb-PARMAREA(%r4) # our sccb + .insn rre,0xb2200000,%r2,%r1 # service call + ipm %r1 + srl %r1,28 # get cc code + xr %r3, %r3 + chi %r1,3 + be .Lfchunk-.LPG1(%r13) # leave + chi %r1,2 + be .Lservicecall-.LPG1(%r13) + lpsw .Lwaitsclp-.LPG1(%r13) +.Lsclph: + lh %r1,.Lsccbr-PARMAREA(%r4) + chi %r1,0x10 # 0x0010 is the sucess code + je .Lprocsccb # let's process the sccb + chi %r1,0x1f0 + bne .Lfchunk-.LPG1(%r13) # unhandled error code + c %r2, .Lrcp-.LPG1(%r13) # Did we try Read SCP forced + bne .Lfchunk-.LPG1(%r13) # if no, give up + l %r2, .Lrcp2-.LPG1(%r13) # try with Read SCP + b .Lservicecall-.LPG1(%r13) +.Lprocsccb: + lh %r1,.Lscpincr1-PARMAREA(%r4) # use this one if != 0 + chi %r1,0x00 + jne .Lscnd + l %r1,.Lscpincr2-PARMAREA(%r4) # otherwise use this one +.Lscnd: + xr %r3,%r3 # same logic + ic %r3,.Lscpa1-PARMAREA(%r4) + chi %r3,0x00 + jne .Lcompmem + l %r3,.Lscpa2-PARMAREA(%r13) +.Lcompmem: + mr %r2,%r1 # mem in MB on 128-bit + l %r1,.Lonemb-.LPG1(%r13) + mr %r2,%r1 # mem size in bytes in %r3 + b .Lfchunk-.LPG1(%r13) + +.Lpmask: + .byte 0 +.align 8 +.Lpcext:.long 0x00080000,0x80000000 +.Lcr: + .long 0x00 # place holder for cr0 +.Lwaitsclp: + .long 0x020A0000 + .long .Lsclph +.Lrcp: + .int 0x00120001 # Read SCP forced code +.Lrcp2: + .int 0x00020001 # Read SCP code +.Lonemb: + .int 0x100000 +.Lfchunk: + +# +# find memory chunks. +# + lr %r9,%r3 # end of mem + mvc __LC_PGM_NEW_PSW(8),.Lpcmem-.LPG1(%r13) + la %r1,1 # test in increments of 128KB + sll %r1,17 + l %r3,.Lmchunk-.LPG1(%r13) # get pointer to memory_chunk array + slr %r4,%r4 # set start of chunk to zero + slr %r5,%r5 # set end of chunk to zero + slr %r6,%r6 # set access code to zero + la %r10, MEMORY_CHUNKS # number of chunks +.Lloop: + tprot 0(%r5),0 # test protection of first byte + ipm %r7 + srl %r7,28 + clr %r6,%r7 # compare cc with last access code + be .Lsame-.LPG1(%r13) + b .Lchkmem-.LPG1(%r13) +.Lsame: + ar %r5,%r1 # add 128KB to end of chunk + bno .Lloop-.LPG1(%r13) # r1 < 0x80000000 -> loop +.Lchkmem: # > 2GB or tprot got a program check + clr %r4,%r5 # chunk size > 0? + be .Lchkloop-.LPG1(%r13) + st %r4,0(%r3) # store start address of chunk + lr %r0,%r5 + slr %r0,%r4 + st %r0,4(%r3) # store size of chunk + st %r6,8(%r3) # store type of chunk + la %r3,12(%r3) + l %r4,.Lmemsize-.LPG1(%r13) # address of variable memory_size + st %r5,0(%r4) # store last end to memory size + ahi %r10,-1 # update chunk number +.Lchkloop: + lr %r6,%r7 # set access code to last cc + # we got an exception or we're starting a new + # chunk , we must check if we should + # still try to find valid memory (if we detected + # the amount of available storage), and if we + # have chunks left + xr %r0,%r0 + clr %r0,%r9 # did we detect memory? + je .Ldonemem # if not, leave + chi %r10,0 # do we have chunks left? + je .Ldonemem + alr %r5,%r1 # add 128KB to end of chunk + lr %r4,%r5 # potential new chunk + clr %r5,%r9 # should we go on? + jl .Lloop +.Ldonemem: + l %r12,.Lmflags-.LPG1(%r13) # get address of machine_flags +# +# 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 3(%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 3(%r12),4 # set P/390 flag +.Lnop390: + +# +# find out if we have an IEEE fpu +# + mvc __LC_PGM_NEW_PSW(8),.Lpcfpu-.LPG1(%r13) + efpc %r0,0 # test IEEE extract fpc instruction + oi 3(%r12),2 # set IEEE fpu flag +.Lchkfpu: + +# +# find out if we have the CSP instruction +# + mvc __LC_PGM_NEW_PSW(8),.Lpccsp-.LPG1(%r13) + la %r0,0 + lr %r1,%r0 + la %r2,4 + csp %r0,%r2 # Test CSP instruction + oi 3(%r12),8 # set CSP flag +.Lchkcsp: + +# +# find out if we have the MVPG instruction +# + mvc __LC_PGM_NEW_PSW(8),.Lpcmvpg-.LPG1(%r13) + sr %r0,%r0 + la %r1,0 + la %r2,0 + mvpg %r1,%r2 # Test CSP instruction + oi 3(%r12),16 # set MVPG flag +.Lchkmvpg: + +# +# find out if we have the IDTE instruction +# + mvc __LC_PGM_NEW_PSW(8),.Lpcidte-.LPG1(%r13) + .long 0xb2b10000 # store facility list + tm 0xc8,0x08 # check bit for clearing-by-ASCE + bno .Lchkidte-.LPG1(%r13) + lhi %r1,2094 + lhi %r2,0 + .long 0xb98e2001 + oi 3(%r12),0x80 # set IDTE flag +.Lchkidte: + + lpsw .Lentry-.LPG1(13) # jump to _stext in primary-space, + # virtual and never return ... + .align 8 +.Lentry:.long 0x00080000,0x80000000 + _stext +.Lctl: .long 0x04b50002 # cr0: various things + .long 0 # cr1: primary space segment table + .long .Lduct # cr2: dispatchable unit control table + .long 0 # cr3: instruction authorization + .long 0 # cr4: instruction authorization + .long 0xffffffff # cr5: primary-aste origin + .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 +.Lpcmvpg:.long 0x00080000,0x80000000 + .Lchkmvpg +.Lpcidte:.long 0x00080000,0x80000000 + .Lchkidte +.Lmemsize:.long memory_size +.Lmchunk:.long memory_chunk +.Lmflags:.long machine_flags +.Lbss_bgn: .long __bss_start +.Lbss_end: .long _end + + .org PARMAREA-64 +.Lduct: .long 0,0,0,0,0,0,0,0 + .long 0,0,0,0,0,0,0,0 + +# +# params at 10400 (setup.h) +# + .org PARMAREA + .global _pstart +_pstart: + .long 0,0 # IPL_DEVICE + .long 0,RAMDISK_ORIGIN # INITRD_START + .long 0,RAMDISK_SIZE # INITRD_SIZE + + .org COMMAND_LINE + .byte "root=/dev/ram0 ro" + .byte 0 + .org 0x11000 +.Lsccb: + .hword 0x1000 # length, one page + .byte 0x00,0x00,0x00 + .byte 0x80 # variable response bit set +.Lsccbr: + .hword 0x00 # response code +.Lscpincr1: + .hword 0x00 +.Lscpa1: + .byte 0x00 + .fill 89,1,0 +.Lscpa2: + .int 0x00 +.Lscpincr2: + .quad 0x00 + .fill 3984,1,0 + .org 0x12000 + .global _pend +_pend: + +#ifdef CONFIG_SHARED_KERNEL + .org 0x100000 +#endif + +# +# startup-code, running in virtual mode +# + .globl _stext +_stext: basr %r13,0 # get base +.LPG2: +# +# Setup stack +# + l %r15,.Linittu-.LPG2(%r13) + mvc __LC_CURRENT(4),__TI_task(%r15) + ahi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union + THREAD_SIZE + st %r15,__LC_KERNEL_STACK # set end of kernel stack + ahi %r15,-96 + xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain + +# check control registers + stctl %c0,%c15,0(%r15) + oi 2(%r15),0x20 # enable sigp external interrupts + oi 0(%r15),0x10 # switch on low address protection + lctl %c0,%c15,0(%r15) + +# + lam 0,15,.Laregs-.LPG2(%r13) # load access regs needed by uaccess + l %r14,.Lstart-.LPG2(%r13) + basr %r14,%r14 # call start_kernel +# +# We returned from start_kernel ?!? PANIK +# + basr %r13,0 + lpsw .Ldw-.(%r13) # load disabled wait psw +# + .align 8 +.Ldw: .long 0x000a0000,0x00000000 +.Linittu: .long init_thread_union +.Lstart: .long start_kernel +.Laregs: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S new file mode 100644 index 000000000000..8366793bc371 --- /dev/null +++ b/arch/s390/kernel/head64.S @@ -0,0 +1,769 @@ +/* + * 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> +#include <asm/offsets.h> +#include <asm/thread_info.h> +#include <asm/page.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 + larl %r12,_pstart # pointer to parameter area + st %r1,IPL_DEVICE+4-PARMAREA(%r12) # store ipl device number + +# +# 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(%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+4-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 + tm __LC_CPUID,0xff # running VM ? + bno .Lnoreset + la %r2,.Lreset + lhi %r3,26 + .long 0x83230008 +.Lnoreset: +#endif + +# +# everything loaded, go for it +# +.Lnoload: + l %r1,.Lstartup + br %r1 + +.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: + l %r1,.memsize + st %r3,4(%r1) + slr %r0,%r0 + st %r0,INITRD_SIZE+4-PARMAREA(%r11) + st %r0,INITRD_START+4-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 +.memsize: .long memory_size +.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: sll %r13,1 # remove high order bit + srl %r13,1 + 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 + larl %r12,_pstart # pointer to parameter area + # move IPL device to lowcore + mvc __LC_IPLDEV(4),IPL_DEVICE+4-PARMAREA(%r12) + +# +# clear bss memory +# + larl %r2,__bss_start # start of bss segment + larl %r3,_end # end of bss segment + 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 + + l %r2,.Lrcp-.LPG1(%r13) # Read SCP forced command word +.Lservicecall: + stosm .Lpmask-.LPG1(%r13),0x01 # authorize ext interrupts + + stctg %r0,%r0,.Lcr-.LPG1(%r13) # get cr0 + la %r1,0x200 # set bit 22 + og %r1,.Lcr-.LPG1(%r13) # or old cr0 with r1 + stg %r1,.Lcr-.LPG1(%r13) + lctlg %r0,%r0,.Lcr-.LPG1(%r13) # load modified cr0 + + mvc __LC_EXT_NEW_PSW(8),.Lpcmsk-.LPG1(%r13) # set postcall psw + larl %r1,.Lsclph + stg %r1,__LC_EXT_NEW_PSW+8 # set handler + + larl %r4,_pstart # %r4 is our index for sccb stuff + la %r1,.Lsccb-PARMAREA(%r4) # our sccb + .insn rre,0xb2200000,%r2,%r1 # service call + ipm %r1 + srl %r1,28 # get cc code + xr %r3,%r3 + chi %r1,3 + be .Lfchunk-.LPG1(%r13) # leave + chi %r1,2 + be .Lservicecall-.LPG1(%r13) + lpsw .Lwaitsclp-.LPG1(%r13) +.Lsclph: + lh %r1,.Lsccbr-PARMAREA(%r4) + chi %r1,0x10 # 0x0010 is the sucess code + je .Lprocsccb # let's process the sccb + chi %r1,0x1f0 + bne .Lfchunk-.LPG1(%r13) # unhandled error code + c %r2,.Lrcp-.LPG1(%r13) # Did we try Read SCP forced + bne .Lfchunk-.LPG1(%r13) # if no, give up + l %r2,.Lrcp2-.LPG1(%r13) # try with Read SCP + b .Lservicecall-.LPG1(%r13) +.Lprocsccb: + lh %r1,.Lscpincr1-PARMAREA(%r4) # use this one if != 0 + chi %r1,0x00 + jne .Lscnd + lg %r1,.Lscpincr2-PARMAREA(%r4) # otherwise use this one +.Lscnd: + xr %r3,%r3 # same logic + ic %r3,.Lscpa1-PARMAREA(%r4) + chi %r3,0x00 + jne .Lcompmem + l %r3,.Lscpa2-PARMAREA(%r13) +.Lcompmem: + mlgr %r2,%r1 # mem in MB on 128-bit + l %r1,.Lonemb-.LPG1(%r13) + mlgr %r2,%r1 # mem size in bytes in %r3 + b .Lfchunk-.LPG1(%r13) + +.Lpmask: + .byte 0 + .align 8 +.Lcr: + .quad 0x00 # place holder for cr0 +.Lwaitsclp: + .long 0x020A0000 + .quad .Lsclph +.Lrcp: + .int 0x00120001 # Read SCP forced code +.Lrcp2: + .int 0x00020001 # Read SCP code +.Lonemb: + .int 0x100000 + +.Lfchunk: + # set program check new psw mask + mvc __LC_PGM_NEW_PSW(8),.Lpcmsk-.LPG1(%r13) + +# +# find memory chunks. +# + lgr %r9,%r3 # end of mem + larl %r1,.Lchkmem # set program check address + stg %r1,__LC_PGM_NEW_PSW+8 + la %r1,1 # test in increments of 128KB + sllg %r1,%r1,17 + larl %r3,memory_chunk + slgr %r4,%r4 # set start of chunk to zero + slgr %r5,%r5 # set end of chunk to zero + slr %r6,%r6 # set access code to zero + la %r10,MEMORY_CHUNKS # number of chunks +.Lloop: + tprot 0(%r5),0 # test protection of first byte + ipm %r7 + srl %r7,28 + clr %r6,%r7 # compare cc with last access code + je .Lsame + j .Lchkmem +.Lsame: + algr %r5,%r1 # add 128KB to end of chunk + # no need to check here, + brc 12,.Lloop # this is the same chunk +.Lchkmem: # > 16EB or tprot got a program check + clgr %r4,%r5 # chunk size > 0? + je .Lchkloop + stg %r4,0(%r3) # store start address of chunk + lgr %r0,%r5 + slgr %r0,%r4 + stg %r0,8(%r3) # store size of chunk + st %r6,20(%r3) # store type of chunk + la %r3,24(%r3) + larl %r8,memory_size + stg %r5,0(%r8) # store memory size + ahi %r10,-1 # update chunk number +.Lchkloop: + lr %r6,%r7 # set access code to last cc + # we got an exception or we're starting a new + # chunk , we must check if we should + # still try to find valid memory (if we detected + # the amount of available storage), and if we + # have chunks left + lghi %r4,1 + sllg %r4,%r4,31 + clgr %r5,%r4 + je .Lhsaskip + xr %r0, %r0 + clgr %r0, %r9 # did we detect memory? + je .Ldonemem # if not, leave + chi %r10, 0 # do we have chunks left? + je .Ldonemem +.Lhsaskip: + algr %r5,%r1 # add 128KB to end of chunk + lgr %r4,%r5 # potential new chunk + clgr %r5,%r9 # should we go on? + jl .Lloop +.Ldonemem: + + larl %r12,machine_flags +# +# find out if we are running under VM +# + stidp __LC_CPUID # store cpuid + tm __LC_CPUID,0xff # running under VM ? + bno 0f-.LPG1(%r13) + oi 7(%r12),1 # set VM flag +0: lh %r0,__LC_CPUID+4 # get cpu version + chi %r0,0x7490 # running on a P/390 ? + bne 1f-.LPG1(%r13) + oi 7(%r12),4 # set P/390 flag +1: + +# +# find out if we have the MVPG instruction +# + la %r1,0f-.LPG1(%r13) # set program check address + stg %r1,__LC_PGM_NEW_PSW+8 + sgr %r0,%r0 + lghi %r1,0 + lghi %r2,0 + mvpg %r1,%r2 # test MVPG instruction + oi 7(%r12),16 # set MVPG flag +0: + +# +# find out if the diag 0x44 works in 64 bit mode +# + la %r1,0f-.LPG1(%r13) # set program check address + stg %r1,__LC_PGM_NEW_PSW+8 + mvc __LC_DIAG44_OPCODE(8),.Lnop-.LPG1(%r13) + diag 0,0,0x44 # test diag 0x44 + oi 7(%r12),32 # set diag44 flag + mvc __LC_DIAG44_OPCODE(8),.Ldiag44-.LPG1(%r13) +0: + +# +# find out if we have the IDTE instruction +# + la %r1,0f-.LPG1(%r13) # set program check address + stg %r1,__LC_PGM_NEW_PSW+8 + .long 0xb2b10000 # store facility list + tm 0xc8,0x08 # check bit for clearing-by-ASCE + bno 0f-.LPG1(%r13) + lhi %r1,2094 + lhi %r2,0 + .long 0xb98e2001 + oi 7(%r12),0x80 # set IDTE flag +0: + + 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 .Lduct # cr2: dispatchable unit control table + .quad 0 # cr3: instruction authorization + .quad 0 # cr4: instruction authorization + .quad 0xffffffffffffffff # cr5: primary-aste origin + .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 +.Lpcmsk:.quad 0x0000000180000000 +.L4malign:.quad 0xffffffffffc00000 +.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 +.Lnop: .long 0x07000700 +.Ldiag44:.long 0x83000044 + + .org PARMAREA-64 +.Lduct: .long 0,0,0,0,0,0,0,0 + .long 0,0,0,0,0,0,0,0 + +# +# params at 10400 (setup.h) +# + .org PARMAREA + .global _pstart +_pstart: + .quad 0 # IPL_DEVICE + .quad RAMDISK_ORIGIN # INITRD_START + .quad RAMDISK_SIZE # INITRD_SIZE + + .org COMMAND_LINE + .byte "root=/dev/ram0 ro" + .byte 0 + .org 0x11000 +.Lsccb: + .hword 0x1000 # length, one page + .byte 0x00,0x00,0x00 + .byte 0x80 # variable response bit set +.Lsccbr: + .hword 0x00 # response code +.Lscpincr1: + .hword 0x00 +.Lscpa1: + .byte 0x00 + .fill 89,1,0 +.Lscpa2: + .int 0x00 +.Lscpincr2: + .quad 0x00 + .fill 3984,1,0 + .org 0x12000 + .global _pend +_pend: + +#ifdef CONFIG_SHARED_KERNEL + .org 0x100000 +#endif + +# +# startup-code, running in virtual mode +# + .globl _stext +_stext: basr %r13,0 # get base +.LPG2: +# +# Setup stack +# + larl %r15,init_thread_union + lg %r14,__TI_task(%r15) # cache current in lowcore + stg %r14,__LC_CURRENT + aghi %r15,1<<(PAGE_SHIFT+THREAD_ORDER) # init_task_union + THREAD_SIZE + stg %r15,__LC_KERNEL_STACK # set end of kernel stack + aghi %r15,-160 + xc __SF_BACKCHAIN(4,%r15),__SF_BACKCHAIN(%r15) # clear backchain + +# check control registers + stctg %c0,%c15,0(%r15) + oi 6(%r15),0x20 # enable sigp external interrupts + oi 4(%r15),0x10 # switch on low address 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 +# + .align 8 +.Ldw: .quad 0x0002000180000000,0x0000000000000000 +.Laregs: .long 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + diff --git a/arch/s390/kernel/init_task.c b/arch/s390/kernel/init_task.c new file mode 100644 index 000000000000..d73a74013e73 --- /dev/null +++ b/arch/s390/kernel/init_task.c @@ -0,0 +1,44 @@ +/* + * arch/s390/kernel/init_task.c + * + * S390 version + * + * Derived from "arch/i386/kernel/init_task.c" + */ + +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/init_task.h> +#include <linux/mqueue.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> + +static struct fs_struct init_fs = INIT_FS; +static struct files_struct init_files = INIT_FILES; +static struct signal_struct init_signals = INIT_SIGNALS(init_signals); +static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); +struct mm_struct init_mm = INIT_MM(init_mm); + +EXPORT_SYMBOL(init_mm); + +/* + * Initial thread structure. + * + * We need to make sure that this is 8192-byte aligned due to the + * way process stacks are handled. This is done by having a special + * "init_task" linker map entry.. + */ +union thread_union init_thread_union + __attribute__((__section__(".data.init_task"))) = + { INIT_THREAD_INFO(init_task) }; + +/* + * Initial task structure. + * + * All other task structs will be allocated on slabs in fork.c + */ +struct task_struct init_task = INIT_TASK(init_task); + +EXPORT_SYMBOL(init_task); diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c new file mode 100644 index 000000000000..480b6a5fef3a --- /dev/null +++ b/arch/s390/kernel/irq.c @@ -0,0 +1,105 @@ +/* + * arch/s390/kernel/irq.c + * + * S390 version + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), + * + * This file contains interrupt related functions. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> +#include <linux/seq_file.h> +#include <linux/cpu.h> + +/* + * show_interrupts is needed by /proc/interrupts. + */ +int show_interrupts(struct seq_file *p, void *v) +{ + static const char *intrclass_names[] = { "EXT", "I/O", }; + int i = *(loff_t *) v, j; + + if (i == 0) { + seq_puts(p, " "); + for_each_online_cpu(j) + seq_printf(p, "CPU%d ",j); + seq_putc(p, '\n'); + } + + if (i < NR_IRQS) { + seq_printf(p, "%s: ", intrclass_names[i]); +#ifndef CONFIG_SMP + seq_printf(p, "%10u ", kstat_irqs(i)); +#else + for_each_online_cpu(j) + seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); +#endif + seq_putc(p, '\n'); + + } + + return 0; +} + +/* + * For compatibilty only. S/390 specific setup of interrupts et al. is done + * much later in init_channel_subsystem(). + */ +void __init +init_IRQ(void) +{ + /* nothing... */ +} + +/* + * Switch to the asynchronous interrupt stack for softirq execution. + */ +extern void __do_softirq(void); + +asmlinkage void do_softirq(void) +{ + unsigned long flags, old, new; + + if (in_interrupt()) + return; + + local_irq_save(flags); + + account_system_vtime(current); + + local_bh_disable(); + + if (local_softirq_pending()) { + /* Get current stack pointer. */ + asm volatile("la %0,0(15)" : "=a" (old)); + /* Check against async. stack address range. */ + new = S390_lowcore.async_stack; + if (((new - old) >> (PAGE_SHIFT + THREAD_ORDER)) != 0) { + /* Need to switch to the async. stack. */ + new -= STACK_FRAME_OVERHEAD; + ((struct stack_frame *) new)->back_chain = old; + + asm volatile(" la 15,0(%0)\n" + " basr 14,%2\n" + " la 15,0(%1)\n" + : : "a" (new), "a" (old), + "a" (__do_softirq) + : "0", "1", "2", "3", "4", "5", "14", + "cc", "memory" ); + } else + /* We are already on the async stack. */ + __do_softirq(); + } + + account_system_vtime(current); + + __local_bh_enable(); + + local_irq_restore(flags); +} + +EXPORT_SYMBOL(do_softirq); diff --git a/arch/s390/kernel/module.c b/arch/s390/kernel/module.c new file mode 100644 index 000000000000..607d506689c8 --- /dev/null +++ b/arch/s390/kernel/module.c @@ -0,0 +1,405 @@ +/* + * arch/s390/kernel/module.c - Kernel module help for s390. + * + * S390 version + * Copyright (C) 2002, 2003 IBM Deutschland Entwicklung GmbH, + * IBM Corporation + * Author(s): Arnd Bergmann (arndb@de.ibm.com) + * Martin Schwidefsky (schwidefsky@de.ibm.com) + * + * based on i386 version + * Copyright (C) 2001 Rusty Russell. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/module.h> +#include <linux/elf.h> +#include <linux/vmalloc.h> +#include <linux/fs.h> +#include <linux/string.h> +#include <linux/kernel.h> + +#if 0 +#define DEBUGP printk +#else +#define DEBUGP(fmt , ...) +#endif + +#ifndef CONFIG_ARCH_S390X +#define PLT_ENTRY_SIZE 12 +#else /* CONFIG_ARCH_S390X */ +#define PLT_ENTRY_SIZE 20 +#endif /* CONFIG_ARCH_S390X */ + +void *module_alloc(unsigned long size) +{ + if (size == 0) + return NULL; + return vmalloc(size); +} + +/* Free memory returned from module_alloc */ +void module_free(struct module *mod, void *module_region) +{ + vfree(module_region); + /* FIXME: If module_region == mod->init_region, trim exception + table entries. */ +} + +static inline void +check_rela(Elf_Rela *rela, struct module *me) +{ + struct mod_arch_syminfo *info; + + info = me->arch.syminfo + ELF_R_SYM (rela->r_info); + switch (ELF_R_TYPE (rela->r_info)) { + case R_390_GOT12: /* 12 bit GOT offset. */ + case R_390_GOT16: /* 16 bit GOT offset. */ + case R_390_GOT20: /* 20 bit GOT offset. */ + case R_390_GOT32: /* 32 bit GOT offset. */ + case R_390_GOT64: /* 64 bit GOT offset. */ + case R_390_GOTENT: /* 32 bit PC rel. to GOT entry shifted by 1. */ + case R_390_GOTPLT12: /* 12 bit offset to jump slot. */ + case R_390_GOTPLT16: /* 16 bit offset to jump slot. */ + case R_390_GOTPLT20: /* 20 bit offset to jump slot. */ + case R_390_GOTPLT32: /* 32 bit offset to jump slot. */ + case R_390_GOTPLT64: /* 64 bit offset to jump slot. */ + case R_390_GOTPLTENT: /* 32 bit rel. offset to jump slot >> 1. */ + if (info->got_offset == -1UL) { + info->got_offset = me->arch.got_size; + me->arch.got_size += sizeof(void*); + } + break; + case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */ + case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */ + case R_390_PLT32: /* 32 bit PC relative PLT address. */ + case R_390_PLT64: /* 64 bit PC relative PLT address. */ + case R_390_PLTOFF16: /* 16 bit offset from GOT to PLT. */ + case R_390_PLTOFF32: /* 32 bit offset from GOT to PLT. */ + case R_390_PLTOFF64: /* 16 bit offset from GOT to PLT. */ + if (info->plt_offset == -1UL) { + info->plt_offset = me->arch.plt_size; + me->arch.plt_size += PLT_ENTRY_SIZE; + } + break; + case R_390_COPY: + case R_390_GLOB_DAT: + case R_390_JMP_SLOT: + case R_390_RELATIVE: + /* Only needed if we want to support loading of + modules linked with -shared. */ + break; + } +} + +/* + * Account for GOT and PLT relocations. We can't add sections for + * got and plt but we can increase the core module size. + */ +int +module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs, + char *secstrings, struct module *me) +{ + Elf_Shdr *symtab; + Elf_Sym *symbols; + Elf_Rela *rela; + char *strings; + int nrela, i, j; + + /* Find symbol table and string table. */ + symtab = 0; + for (i = 0; i < hdr->e_shnum; i++) + switch (sechdrs[i].sh_type) { + case SHT_SYMTAB: + symtab = sechdrs + i; + break; + } + if (!symtab) { + printk(KERN_ERR "module %s: no symbol table\n", me->name); + return -ENOEXEC; + } + + /* Allocate one syminfo structure per symbol. */ + me->arch.nsyms = symtab->sh_size / sizeof(Elf_Sym); + me->arch.syminfo = vmalloc(me->arch.nsyms * + sizeof(struct mod_arch_syminfo)); + if (!me->arch.syminfo) + return -ENOMEM; + symbols = (void *) hdr + symtab->sh_offset; + strings = (void *) hdr + sechdrs[symtab->sh_link].sh_offset; + for (i = 0; i < me->arch.nsyms; i++) { + if (symbols[i].st_shndx == SHN_UNDEF && + strcmp(strings + symbols[i].st_name, + "_GLOBAL_OFFSET_TABLE_") == 0) + /* "Define" it as absolute. */ + symbols[i].st_shndx = SHN_ABS; + me->arch.syminfo[i].got_offset = -1UL; + me->arch.syminfo[i].plt_offset = -1UL; + me->arch.syminfo[i].got_initialized = 0; + me->arch.syminfo[i].plt_initialized = 0; + } + + /* Search for got/plt relocations. */ + me->arch.got_size = me->arch.plt_size = 0; + for (i = 0; i < hdr->e_shnum; i++) { + if (sechdrs[i].sh_type != SHT_RELA) + continue; + nrela = sechdrs[i].sh_size / sizeof(Elf_Rela); + rela = (void *) hdr + sechdrs[i].sh_offset; + for (j = 0; j < nrela; j++) + check_rela(rela + j, me); + } + + /* Increase core size by size of got & plt and set start + offsets for got and plt. */ + me->core_size = ALIGN(me->core_size, 4); + me->arch.got_offset = me->core_size; + me->core_size += me->arch.got_size; + me->arch.plt_offset = me->core_size; + me->core_size += me->arch.plt_size; + return 0; +} + +int +apply_relocate(Elf_Shdr *sechdrs, const char *strtab, unsigned int symindex, + unsigned int relsec, struct module *me) +{ + printk(KERN_ERR "module %s: RELOCATION unsupported\n", + me->name); + return -ENOEXEC; +} + +static inline int +apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab, + struct module *me) +{ + struct mod_arch_syminfo *info; + Elf_Addr loc, val; + int r_type, r_sym; + + /* This is where to make the change */ + loc = base + rela->r_offset; + /* This is the symbol it is referring to. Note that all + undefined symbols have been resolved. */ + r_sym = ELF_R_SYM(rela->r_info); + r_type = ELF_R_TYPE(rela->r_info); + info = me->arch.syminfo + r_sym; + val = symtab[r_sym].st_value; + + switch (r_type) { + case R_390_8: /* Direct 8 bit. */ + case R_390_12: /* Direct 12 bit. */ + case R_390_16: /* Direct 16 bit. */ + case R_390_20: /* Direct 20 bit. */ + case R_390_32: /* Direct 32 bit. */ + case R_390_64: /* Direct 64 bit. */ + val += rela->r_addend; + if (r_type == R_390_8) + *(unsigned char *) loc = val; + else if (r_type == R_390_12) + *(unsigned short *) loc = (val & 0xfff) | + (*(unsigned short *) loc & 0xf000); + else if (r_type == R_390_16) + *(unsigned short *) loc = val; + else if (r_type == R_390_20) + *(unsigned int *) loc = + (*(unsigned int *) loc & 0xf00000ff) | + (val & 0xfff) << 16 | (val & 0xff000) >> 4; + else if (r_type == R_390_32) + *(unsigned int *) loc = val; + else if (r_type == R_390_64) + *(unsigned long *) loc = val; + break; + case R_390_PC16: /* PC relative 16 bit. */ + case R_390_PC16DBL: /* PC relative 16 bit shifted by 1. */ + case R_390_PC32DBL: /* PC relative 32 bit shifted by 1. */ + case R_390_PC32: /* PC relative 32 bit. */ + case R_390_PC64: /* PC relative 64 bit. */ + val += rela->r_addend - loc; + if (r_type == R_390_PC16) + *(unsigned short *) loc = val; + else if (r_type == R_390_PC16DBL) + *(unsigned short *) loc = val >> 1; + else if (r_type == R_390_PC32DBL) + *(unsigned int *) loc = val >> 1; + else if (r_type == R_390_PC32) + *(unsigned int *) loc = val; + else if (r_type == R_390_PC64) + *(unsigned long *) loc = val; + break; + case R_390_GOT12: /* 12 bit GOT offset. */ + case R_390_GOT16: /* 16 bit GOT offset. */ + case R_390_GOT20: /* 20 bit GOT offset. */ + case R_390_GOT32: /* 32 bit GOT offset. */ + case R_390_GOT64: /* 64 bit GOT offset. */ + case R_390_GOTENT: /* 32 bit PC rel. to GOT entry shifted by 1. */ + case R_390_GOTPLT12: /* 12 bit offset to jump slot. */ + case R_390_GOTPLT20: /* 20 bit offset to jump slot. */ + case R_390_GOTPLT16: /* 16 bit offset to jump slot. */ + case R_390_GOTPLT32: /* 32 bit offset to jump slot. */ + case R_390_GOTPLT64: /* 64 bit offset to jump slot. */ + case R_390_GOTPLTENT: /* 32 bit rel. offset to jump slot >> 1. */ + if (info->got_initialized == 0) { + Elf_Addr *gotent; + + gotent = me->module_core + me->arch.got_offset + + info->got_offset; + *gotent = val; + info->got_initialized = 1; + } + val = info->got_offset + rela->r_addend; + if (r_type == R_390_GOT12 || + r_type == R_390_GOTPLT12) + *(unsigned short *) loc = (val & 0xfff) | + (*(unsigned short *) loc & 0xf000); + else if (r_type == R_390_GOT16 || + r_type == R_390_GOTPLT16) + *(unsigned short *) loc = val; + else if (r_type == R_390_GOT20 || + r_type == R_390_GOTPLT20) + *(unsigned int *) loc = + (*(unsigned int *) loc & 0xf00000ff) | + (val & 0xfff) << 16 | (val & 0xff000) >> 4; + else if (r_type == R_390_GOT32 || + r_type == R_390_GOTPLT32) + *(unsigned int *) loc = val; + else if (r_type == R_390_GOTENT || + r_type == R_390_GOTPLTENT) + *(unsigned int *) loc = + (val + (Elf_Addr) me->module_core - loc) >> 1; + else if (r_type == R_390_GOT64 || + r_type == R_390_GOTPLT64) + *(unsigned long *) loc = val; + break; + case R_390_PLT16DBL: /* 16 bit PC rel. PLT shifted by 1. */ + case R_390_PLT32DBL: /* 32 bit PC rel. PLT shifted by 1. */ + case R_390_PLT32: /* 32 bit PC relative PLT address. */ + case R_390_PLT64: /* 64 bit PC relative PLT address. */ + case R_390_PLTOFF16: /* 16 bit offset from GOT to PLT. */ + case R_390_PLTOFF32: /* 32 bit offset from GOT to PLT. */ + case R_390_PLTOFF64: /* 16 bit offset from GOT to PLT. */ + if (info->plt_initialized == 0) { + unsigned int *ip; + ip = me->module_core + me->arch.plt_offset + + info->plt_offset; +#ifndef CONFIG_ARCH_S390X + ip[0] = 0x0d105810; /* basr 1,0; l 1,6(1); br 1 */ + ip[1] = 0x100607f1; + ip[2] = val; +#else /* CONFIG_ARCH_S390X */ + ip[0] = 0x0d10e310; /* basr 1,0; lg 1,10(1); br 1 */ + ip[1] = 0x100a0004; + ip[2] = 0x07f10000; + ip[3] = (unsigned int) (val >> 32); + ip[4] = (unsigned int) val; +#endif /* CONFIG_ARCH_S390X */ + info->plt_initialized = 1; + } + if (r_type == R_390_PLTOFF16 || + r_type == R_390_PLTOFF32 + || r_type == R_390_PLTOFF64 + ) + val = me->arch.plt_offset - me->arch.got_offset + + info->plt_offset + rela->r_addend; + else + val = (Elf_Addr) me->module_core + + me->arch.plt_offset + info->plt_offset + + rela->r_addend - loc; + if (r_type == R_390_PLT16DBL) + *(unsigned short *) loc = val >> 1; + else if (r_type == R_390_PLTOFF16) + *(unsigned short *) loc = val; + else if (r_type == R_390_PLT32DBL) + *(unsigned int *) loc = val >> 1; + else if (r_type == R_390_PLT32 || + r_type == R_390_PLTOFF32) + *(unsigned int *) loc = val; + else if (r_type == R_390_PLT64 || + r_type == R_390_PLTOFF64) + *(unsigned long *) loc = val; + break; + case R_390_GOTOFF16: /* 16 bit offset to GOT. */ + case R_390_GOTOFF32: /* 32 bit offset to GOT. */ + case R_390_GOTOFF64: /* 64 bit offset to GOT. */ + val = val + rela->r_addend - + ((Elf_Addr) me->module_core + me->arch.got_offset); + if (r_type == R_390_GOTOFF16) + *(unsigned short *) loc = val; + else if (r_type == R_390_GOTOFF32) + *(unsigned int *) loc = val; + else if (r_type == R_390_GOTOFF64) + *(unsigned long *) loc = val; + break; + case R_390_GOTPC: /* 32 bit PC relative offset to GOT. */ + case R_390_GOTPCDBL: /* 32 bit PC rel. off. to GOT shifted by 1. */ + val = (Elf_Addr) me->module_core + me->arch.got_offset + + rela->r_addend - loc; + if (r_type == R_390_GOTPC) + *(unsigned int *) loc = val; + else if (r_type == R_390_GOTPCDBL) + *(unsigned int *) loc = val >> 1; + break; + case R_390_COPY: + case R_390_GLOB_DAT: /* Create GOT entry. */ + case R_390_JMP_SLOT: /* Create PLT entry. */ + case R_390_RELATIVE: /* Adjust by program base. */ + /* Only needed if we want to support loading of + modules linked with -shared. */ + break; + default: + printk(KERN_ERR "module %s: Unknown relocation: %u\n", + me->name, r_type); + return -ENOEXEC; + } + return 0; +} + +int +apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, + unsigned int symindex, unsigned int relsec, + struct module *me) +{ + Elf_Addr base; + Elf_Sym *symtab; + Elf_Rela *rela; + unsigned long i, n; + int rc; + + DEBUGP("Applying relocate section %u to %u\n", + relsec, sechdrs[relsec].sh_info); + base = sechdrs[sechdrs[relsec].sh_info].sh_addr; + symtab = (Elf_Sym *) sechdrs[symindex].sh_addr; + rela = (Elf_Rela *) sechdrs[relsec].sh_addr; + n = sechdrs[relsec].sh_size / sizeof(Elf_Rela); + + for (i = 0; i < n; i++, rela++) { + rc = apply_rela(rela, base, symtab, me); + if (rc) + return rc; + } + return 0; +} + +int module_finalize(const Elf_Ehdr *hdr, + const Elf_Shdr *sechdrs, + struct module *me) +{ + vfree(me->arch.syminfo); + return 0; +} + +void module_arch_cleanup(struct module *mod) +{ +} diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c new file mode 100644 index 000000000000..7aea25d6e300 --- /dev/null +++ b/arch/s390/kernel/process.c @@ -0,0 +1,416 @@ +/* + * 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.. + */ + +#include <linux/config.h> +#include <linux/compiler.h> +#include <linux/cpu.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/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 <linux/module.h> +#include <linux/notifier.h> + +#include <asm/uaccess.h> +#include <asm/pgtable.h> +#include <asm/system.h> +#include <asm/io.h> +#include <asm/processor.h> +#include <asm/irq.h> +#include <asm/timer.h> + +asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); + +/* + * Return saved PC of a blocked thread. used in kernel/sched. + * resume in entry.S does not create a new stack frame, it + * just stores the registers %r6-%r15 to the frame given by + * schedule. We want to return the address of the caller of + * schedule, so we have to walk the backchain one time to + * find the frame schedule() store its return address. + */ +unsigned long thread_saved_pc(struct task_struct *tsk) +{ + struct stack_frame *sf; + + sf = (struct stack_frame *) tsk->thread.ksp; + sf = (struct stack_frame *) sf->back_chain; + return sf->gprs[8]; +} + +/* + * Need to know about CPUs going idle? + */ +static struct notifier_block *idle_chain; + +int register_idle_notifier(struct notifier_block *nb) +{ + return notifier_chain_register(&idle_chain, nb); +} +EXPORT_SYMBOL(register_idle_notifier); + +int unregister_idle_notifier(struct notifier_block *nb) +{ + return notifier_chain_unregister(&idle_chain, nb); +} +EXPORT_SYMBOL(unregister_idle_notifier); + +void do_monitor_call(struct pt_regs *regs, long interruption_code) +{ + /* disable monitor call class 0 */ + __ctl_clear_bit(8, 15); + + notifier_call_chain(&idle_chain, CPU_NOT_IDLE, + (void *)(long) smp_processor_id()); +} + +/* + * The idle loop on a S390... + */ +void default_idle(void) +{ + psw_t wait_psw; + unsigned long reg; + int cpu, rc; + + local_irq_disable(); + if (need_resched()) { + local_irq_enable(); + schedule(); + return; + } + + /* CPU is going idle. */ + cpu = smp_processor_id(); + rc = notifier_call_chain(&idle_chain, CPU_IDLE, (void *)(long) cpu); + if (rc != NOTIFY_OK && rc != NOTIFY_DONE) + BUG(); + if (rc != NOTIFY_OK) { + local_irq_enable(); + return; + } + + /* enable monitor call class 0 */ + __ctl_set_bit(8, 15); + +#ifdef CONFIG_HOTPLUG_CPU + if (cpu_is_offline(smp_processor_id())) + cpu_die(); +#endif + + /* + * Wait for external, I/O or machine check interrupt and + * switch off machine check bit after the wait has ended. + */ + wait_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK | PSW_MASK_WAIT | + PSW_MASK_IO | PSW_MASK_EXT; +#ifndef CONFIG_ARCH_S390X + asm volatile ( + " basr %0,0\n" + "0: la %0,1f-0b(%0)\n" + " st %0,4(%1)\n" + " oi 4(%1),0x80\n" + " lpsw 0(%1)\n" + "1: la %0,2f-1b(%0)\n" + " st %0,4(%1)\n" + " oi 4(%1),0x80\n" + " ni 1(%1),0xf9\n" + " lpsw 0(%1)\n" + "2:" + : "=&a" (reg) : "a" (&wait_psw) : "memory", "cc" ); +#else /* CONFIG_ARCH_S390X */ + asm volatile ( + " larl %0,0f\n" + " stg %0,8(%1)\n" + " lpswe 0(%1)\n" + "0: larl %0,1f\n" + " stg %0,8(%1)\n" + " ni 1(%1),0xf9\n" + " lpswe 0(%1)\n" + "1:" + : "=&a" (reg) : "a" (&wait_psw) : "memory", "cc" ); +#endif /* CONFIG_ARCH_S390X */ +} + +void cpu_idle(void) +{ + for (;;) + default_idle(); +} + +void show_regs(struct pt_regs *regs) +{ + struct task_struct *tsk = current; + + printk("CPU: %d %s\n", tsk->thread_info->cpu, print_tainted()); + printk("Process %s (pid: %d, task: %p, ksp: %p)\n", + current->comm, current->pid, (void *) tsk, + (void *) tsk->thread.ksp); + + show_registers(regs); + /* Show stack backtrace if pt_regs is from kernel mode */ + if (!(regs->psw.mask & PSW_MASK_PSTATE)) + show_trace(0,(unsigned long *) regs->gprs[15]); +} + +extern void kernel_thread_starter(void); + +__asm__(".align 4\n" + "kernel_thread_starter:\n" + " la 2,0(10)\n" + " basr 14,9\n" + " la 2,0\n" + " br 11\n"); + +int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) +{ + struct pt_regs regs; + + memset(®s, 0, sizeof(regs)); + regs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_EXT; + regs.psw.addr = (unsigned long) kernel_thread_starter | PSW_ADDR_AMODE; + regs.gprs[9] = (unsigned long) fn; + regs.gprs[10] = (unsigned long) arg; + regs.gprs[11] = (unsigned long) do_exit; + regs.orig_gpr2 = -1; + + /* Ok, create the new process.. */ + return do_fork(flags | CLONE_VM | CLONE_UNTRACED, + 0, ®s, 0, NULL, NULL); +} + +/* + * Free current thread data structures etc.. + */ +void exit_thread(void) +{ +} + +void flush_thread(void) +{ + clear_used_math(); + clear_tsk_thread_flag(current, TIF_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 fake_frame + { + struct stack_frame sf; + struct pt_regs childregs; + } *frame; + + frame = ((struct fake_frame *) + (THREAD_SIZE + (unsigned long) p->thread_info)) - 1; + p->thread.ksp = (unsigned long) frame; + /* Store access registers to kernel stack of new process. */ + frame->childregs = *regs; + frame->childregs.gprs[2] = 0; /* child returns 0 on fork. */ + frame->childregs.gprs[15] = new_stackp; + frame->sf.back_chain = 0; + + /* new return point is ret_from_fork */ + frame->sf.gprs[8] = (unsigned long) ret_from_fork; + + /* fake return stack for resume(), don't go back to schedule */ + frame->sf.gprs[9] = (unsigned long) frame; + + /* Save access registers to new thread structure. */ + save_access_regs(&p->thread.acrs[0]); + +#ifndef CONFIG_ARCH_S390X + /* + * save fprs to current->thread.fp_regs to merge them with + * the emulated registers and then copy the result to the child. + */ + save_fp_regs(¤t->thread.fp_regs); + memcpy(&p->thread.fp_regs, ¤t->thread.fp_regs, + sizeof(s390_fp_regs)); + p->thread.user_seg = __pa((unsigned long) p->mm->pgd) | _SEGMENT_TABLE; + /* Set a new TLS ? */ + if (clone_flags & CLONE_SETTLS) + p->thread.acrs[0] = regs->gprs[6]; +#else /* CONFIG_ARCH_S390X */ + /* Save the fpu registers to new thread structure. */ + save_fp_regs(&p->thread.fp_regs); + p->thread.user_seg = __pa((unsigned long) p->mm->pgd) | _REGION_TABLE; + /* Set a new TLS ? */ + if (clone_flags & CLONE_SETTLS) { + if (test_thread_flag(TIF_31BIT)) { + p->thread.acrs[0] = (unsigned int) regs->gprs[6]; + } else { + p->thread.acrs[0] = (unsigned int)(regs->gprs[6] >> 32); + p->thread.acrs[1] = (unsigned int) regs->gprs[6]; + } + } +#endif /* CONFIG_ARCH_S390X */ + /* start new process with ar4 pointing to the correct address space */ + p->thread.mm_segment = get_fs(); + /* Don't copy debug registers */ + memset(&p->thread.per_info,0,sizeof(p->thread.per_info)); + + return 0; +} + +asmlinkage long sys_fork(struct pt_regs regs) +{ + return do_fork(SIGCHLD, regs.gprs[15], ®s, 0, NULL, NULL); +} + +asmlinkage long sys_clone(struct pt_regs regs) +{ + unsigned long clone_flags; + unsigned long newsp; + int __user *parent_tidptr, *child_tidptr; + + clone_flags = regs.gprs[3]; + newsp = regs.orig_gpr2; + parent_tidptr = (int __user *) regs.gprs[4]; + child_tidptr = (int __user *) regs.gprs[5]; + if (!newsp) + newsp = regs.gprs[15]; + return do_fork(clone_flags, newsp, ®s, 0, + parent_tidptr, child_tidptr); +} + +/* + * 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 long sys_vfork(struct pt_regs regs) +{ + return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, + regs.gprs[15], ®s, 0, NULL, NULL); +} + +/* + * sys_execve() executes a new program. + */ +asmlinkage long sys_execve(struct pt_regs regs) +{ + int error; + char * filename; + + filename = getname((char __user *) regs.orig_gpr2); + error = PTR_ERR(filename); + if (IS_ERR(filename)) + goto out; + error = do_execve(filename, (char __user * __user *) regs.gprs[3], + (char __user * __user *) regs.gprs[4], ®s); + if (error == 0) { + task_lock(current); + current->ptrace &= ~PT_DTRACE; + task_unlock(current); + current->thread.fp_regs.fpc = 0; + if (MACHINE_HAS_IEEE) + asm volatile("sfpc %0,%0" : : "d" (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) +{ +#ifndef CONFIG_ARCH_S390X + /* + * save fprs to current->thread.fp_regs to merge them with + * the emulated registers and then copy the result to the dump. + */ + save_fp_regs(¤t->thread.fp_regs); + memcpy(fpregs, ¤t->thread.fp_regs, sizeof(s390_fp_regs)); +#else /* CONFIG_ARCH_S390X */ + save_fp_regs(fpregs); +#endif /* CONFIG_ARCH_S390X */ + 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 = current->mm->end_code >> PAGE_SHIFT; + dump->u_dsize = (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 = (TASK_SIZE - dump->start_stack) >> PAGE_SHIFT; + memcpy(&dump->regs, regs, sizeof(s390_regs)); + dump_fpu (regs, &dump->regs.fp_regs); + dump->regs.per_info = current->thread.per_info; +} + +unsigned long get_wchan(struct task_struct *p) +{ + struct stack_frame *sf, *low, *high; + unsigned long return_address; + int count; + + if (!p || p == current || p->state == TASK_RUNNING || !p->thread_info) + return 0; + low = (struct stack_frame *) p->thread_info; + high = (struct stack_frame *) + ((unsigned long) p->thread_info + THREAD_SIZE) - 1; + sf = (struct stack_frame *) (p->thread.ksp & PSW_ADDR_INSN); + if (sf <= low || sf > high) + return 0; + for (count = 0; count < 16; count++) { + sf = (struct stack_frame *) (sf->back_chain & PSW_ADDR_INSN); + if (sf <= low || sf > high) + return 0; + return_address = sf->gprs[8] & PSW_ADDR_INSN; + if (!in_sched_functions(return_address)) + return return_address; + } + return 0; +} + diff --git a/arch/s390/kernel/profile.c b/arch/s390/kernel/profile.c new file mode 100644 index 000000000000..7ba777eec1a8 --- /dev/null +++ b/arch/s390/kernel/profile.c @@ -0,0 +1,20 @@ +/* + * arch/s390/kernel/profile.c + * + * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Thomas Spatzier (tspat@de.ibm.com) + * + */ +#include <linux/proc_fs.h> +#include <linux/profile.h> + +static struct proc_dir_entry * root_irq_dir; + +void init_irq_proc(void) +{ + /* create /proc/irq */ + root_irq_dir = proc_mkdir("irq", 0); + + /* create /proc/irq/prof_cpu_mask */ + create_prof_cpu_mask(root_irq_dir); +} diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c new file mode 100644 index 000000000000..647233c02fc8 --- /dev/null +++ b/arch/s390/kernel/ptrace.c @@ -0,0 +1,738 @@ +/* + * 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), + * Martin Schwidefsky (schwidefsky@de.ibm.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 <linux/kernel.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/smp_lock.h> +#include <linux/errno.h> +#include <linux/ptrace.h> +#include <linux/user.h> +#include <linux/security.h> +#include <linux/audit.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> + +#ifdef CONFIG_S390_SUPPORT +#include "compat_ptrace.h" +#endif + +static void +FixPerRegisters(struct task_struct *task) +{ + struct pt_regs *regs; + per_struct *per_info; + + regs = __KSTK_PTREGS(task); + 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 (test_thread_flag(TIF_31BIT)) + per_info->control_regs.bits.ending_addr = 0x7fffffffUL; + else +#endif + per_info->control_regs.bits.ending_addr = PSW_ADDR_INSN; + } 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_MASK_PER; + else + regs->psw.mask &= ~PSW_MASK_PER; + + if (per_info->control_regs.bits.em_storage_alteration) + per_info->control_regs.bits.storage_alt_space_ctl = 1; + else + per_info->control_regs.bits.storage_alt_space_ctl = 0; +} + +void +set_single_step(struct task_struct *task) +{ + task->thread.per_info.single_step = 1; + FixPerRegisters(task); +} + +void +clear_single_step(struct task_struct *task) +{ + task->thread.per_info.single_step = 0; + FixPerRegisters(task); +} + +/* + * Called by kernel/ptrace.c when detaching.. + * + * Make sure single step bits etc are not set. + */ +void +ptrace_disable(struct task_struct *child) +{ + /* make sure the single step bit is not set. */ + clear_single_step(child); +} + +#ifndef CONFIG_ARCH_S390X +# define __ADDR_MASK 3 +#else +# define __ADDR_MASK 7 +#endif + +/* + * Read the word at offset addr from the user area of a process. The + * trouble here is that the information is littered over different + * locations. The process registers are found on the kernel stack, + * the floating point stuff and the trace settings are stored in + * the task structure. In addition the different structures in + * struct user contain pad bytes that should be read as zeroes. + * Lovely... + */ +static int +peek_user(struct task_struct *child, addr_t addr, addr_t data) +{ + struct user *dummy = NULL; + addr_t offset, tmp; + + /* + * Stupid gdb peeks/pokes the access registers in 64 bit with + * an alignment of 4. Programmers from hell... + */ + if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK) + return -EIO; + + if (addr < (addr_t) &dummy->regs.acrs) { + /* + * psw and gprs are stored on the stack + */ + tmp = *(addr_t *)((addr_t) &__KSTK_PTREGS(child)->psw + addr); + if (addr == (addr_t) &dummy->regs.psw.mask) + /* Remove per bit from user psw. */ + tmp &= ~PSW_MASK_PER; + + } else if (addr < (addr_t) &dummy->regs.orig_gpr2) { + /* + * access registers are stored in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.acrs; + tmp = *(addr_t *)((addr_t) &child->thread.acrs + offset); + + } else if (addr == (addr_t) &dummy->regs.orig_gpr2) { + /* + * orig_gpr2 is stored on the kernel stack + */ + tmp = (addr_t) __KSTK_PTREGS(child)->orig_gpr2; + + } else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.fp_regs; + tmp = *(addr_t *)((addr_t) &child->thread.fp_regs + offset); + + } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) { + /* + * per_info is found in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.per_info; + tmp = *(addr_t *)((addr_t) &child->thread.per_info + offset); + + } else + tmp = 0; + + return put_user(tmp, (addr_t __user *) data); +} + +/* + * Write a word to the user area of a process at location addr. This + * operation does have an additional problem compared to peek_user. + * Stores to the program status word and on the floating point + * control register needs to get checked for validity. + */ +static int +poke_user(struct task_struct *child, addr_t addr, addr_t data) +{ + struct user *dummy = NULL; + addr_t offset; + + /* + * Stupid gdb peeks/pokes the access registers in 64 bit with + * an alignment of 4. Programmers from hell indeed... + */ + if ((addr & 3) || addr > sizeof(struct user) - __ADDR_MASK) + return -EIO; + + if (addr < (addr_t) &dummy->regs.acrs) { + /* + * psw and gprs are stored on the stack + */ + if (addr == (addr_t) &dummy->regs.psw.mask && +#ifdef CONFIG_S390_SUPPORT + data != PSW_MASK_MERGE(PSW_USER32_BITS, data) && +#endif + data != PSW_MASK_MERGE(PSW_USER_BITS, data)) + /* Invalid psw mask. */ + return -EINVAL; +#ifndef CONFIG_ARCH_S390X + if (addr == (addr_t) &dummy->regs.psw.addr) + /* I'd like to reject addresses without the + high order bit but older gdb's rely on it */ + data |= PSW_ADDR_AMODE; +#endif + *(addr_t *)((addr_t) &__KSTK_PTREGS(child)->psw + addr) = data; + + } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) { + /* + * access registers are stored in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.acrs; + *(addr_t *)((addr_t) &child->thread.acrs + offset) = data; + + } else if (addr == (addr_t) &dummy->regs.orig_gpr2) { + /* + * orig_gpr2 is stored on the kernel stack + */ + __KSTK_PTREGS(child)->orig_gpr2 = data; + + } else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + if (addr == (addr_t) &dummy->regs.fp_regs.fpc && + (data & ~FPC_VALID_MASK) != 0) + return -EINVAL; + offset = addr - (addr_t) &dummy->regs.fp_regs; + *(addr_t *)((addr_t) &child->thread.fp_regs + offset) = data; + + } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) { + /* + * per_info is found in the thread structure + */ + offset = addr - (addr_t) &dummy->regs.per_info; + *(addr_t *)((addr_t) &child->thread.per_info + offset) = data; + + } + + FixPerRegisters(child); + return 0; +} + +static int +do_ptrace_normal(struct task_struct *child, long request, long addr, long data) +{ + unsigned long tmp; + ptrace_area parea; + int copied, ret; + + switch (request) { + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + /* Remove high order bit from address (only for 31 bit). */ + addr &= PSW_ADDR_INSN; + /* read word at location addr. */ + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + if (copied != sizeof(tmp)) + return -EIO; + return put_user(tmp, (unsigned long __user *) data); + + case PTRACE_PEEKUSR: + /* read the word at location addr in the USER area. */ + return peek_user(child, addr, data); + + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + /* Remove high order bit from address (only for 31 bit). */ + addr &= PSW_ADDR_INSN; + /* write the word at location addr. */ + copied = access_process_vm(child, addr, &data, sizeof(data),1); + if (copied != sizeof(data)) + return -EIO; + return 0; + + case PTRACE_POKEUSR: + /* write the word at location addr in the USER area */ + return poke_user(child, addr, data); + + case PTRACE_PEEKUSR_AREA: + case PTRACE_POKEUSR_AREA: + if (copy_from_user(&parea, (void __user *) addr, + sizeof(parea))) + return -EFAULT; + addr = parea.kernel_addr; + data = parea.process_addr; + copied = 0; + while (copied < parea.len) { + if (request == PTRACE_PEEKUSR_AREA) + ret = peek_user(child, addr, data); + else { + addr_t tmp; + if (get_user (tmp, (addr_t __user *) data)) + return -EFAULT; + ret = poke_user(child, addr, tmp); + } + if (ret) + return ret; + addr += sizeof(unsigned long); + data += sizeof(unsigned long); + copied += sizeof(unsigned long); + } + return 0; + } + return ptrace_request(child, request, addr, data); +} + +#ifdef CONFIG_S390_SUPPORT +/* + * Now the fun part starts... a 31 bit program running in the + * 31 bit emulation tracing another program. PTRACE_PEEKTEXT, + * PTRACE_PEEKDATA, PTRACE_POKETEXT and PTRACE_POKEDATA are easy + * to handle, the difference to the 64 bit versions of the requests + * is that the access is done in multiples of 4 byte instead of + * 8 bytes (sizeof(unsigned long) on 31/64 bit). + * The ugly part are PTRACE_PEEKUSR, PTRACE_PEEKUSR_AREA, + * PTRACE_POKEUSR and PTRACE_POKEUSR_AREA. If the traced program + * is a 31 bit program too, the content of struct user can be + * emulated. A 31 bit program peeking into the struct user of + * a 64 bit program is a no-no. + */ + +/* + * Same as peek_user but for a 31 bit program. + */ +static int +peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data) +{ + struct user32 *dummy32 = NULL; + per_struct32 *dummy_per32 = NULL; + addr_t offset; + __u32 tmp; + + if (!test_thread_flag(TIF_31BIT) || + (addr & 3) || addr > sizeof(struct user) - 3) + return -EIO; + + if (addr < (addr_t) &dummy32->regs.acrs) { + /* + * psw and gprs are stored on the stack + */ + if (addr == (addr_t) &dummy32->regs.psw.mask) { + /* Fake a 31 bit psw mask. */ + tmp = (__u32)(__KSTK_PTREGS(child)->psw.mask >> 32); + tmp = PSW32_MASK_MERGE(PSW32_USER_BITS, tmp); + } else if (addr == (addr_t) &dummy32->regs.psw.addr) { + /* Fake a 31 bit psw address. */ + tmp = (__u32) __KSTK_PTREGS(child)->psw.addr | + PSW32_ADDR_AMODE31; + } else { + /* gpr 0-15 */ + tmp = *(__u32 *)((addr_t) &__KSTK_PTREGS(child)->psw + + addr*2 + 4); + } + } else if (addr < (addr_t) (&dummy32->regs.orig_gpr2)) { + /* + * access registers are stored in the thread structure + */ + offset = addr - (addr_t) &dummy32->regs.acrs; + tmp = *(__u32*)((addr_t) &child->thread.acrs + offset); + + } else if (addr == (addr_t) (&dummy32->regs.orig_gpr2)) { + /* + * orig_gpr2 is stored on the kernel stack + */ + tmp = *(__u32*)((addr_t) &__KSTK_PTREGS(child)->orig_gpr2 + 4); + + } else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + offset = addr - (addr_t) &dummy32->regs.fp_regs; + tmp = *(__u32 *)((addr_t) &child->thread.fp_regs + offset); + + } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) { + /* + * per_info is found in the thread structure + */ + offset = addr - (addr_t) &dummy32->regs.per_info; + /* This is magic. See per_struct and per_struct32. */ + if ((offset >= (addr_t) &dummy_per32->control_regs && + offset < (addr_t) (&dummy_per32->control_regs + 1)) || + (offset >= (addr_t) &dummy_per32->starting_addr && + offset <= (addr_t) &dummy_per32->ending_addr) || + offset == (addr_t) &dummy_per32->lowcore.words.address) + offset = offset*2 + 4; + else + offset = offset*2; + tmp = *(__u32 *)((addr_t) &child->thread.per_info + offset); + + } else + tmp = 0; + + return put_user(tmp, (__u32 __user *) data); +} + +/* + * Same as poke_user but for a 31 bit program. + */ +static int +poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data) +{ + struct user32 *dummy32 = NULL; + per_struct32 *dummy_per32 = NULL; + addr_t offset; + __u32 tmp; + + if (!test_thread_flag(TIF_31BIT) || + (addr & 3) || addr > sizeof(struct user32) - 3) + return -EIO; + + tmp = (__u32) data; + + if (addr < (addr_t) &dummy32->regs.acrs) { + /* + * psw, gprs, acrs and orig_gpr2 are stored on the stack + */ + if (addr == (addr_t) &dummy32->regs.psw.mask) { + /* Build a 64 bit psw mask from 31 bit mask. */ + if (tmp != PSW32_MASK_MERGE(PSW32_USER_BITS, tmp)) + /* Invalid psw mask. */ + return -EINVAL; + __KSTK_PTREGS(child)->psw.mask = + PSW_MASK_MERGE(PSW_USER32_BITS, (__u64) tmp << 32); + } else if (addr == (addr_t) &dummy32->regs.psw.addr) { + /* Build a 64 bit psw address from 31 bit address. */ + __KSTK_PTREGS(child)->psw.addr = + (__u64) tmp & PSW32_ADDR_INSN; + } else { + /* gpr 0-15 */ + *(__u32*)((addr_t) &__KSTK_PTREGS(child)->psw + + addr*2 + 4) = tmp; + } + } else if (addr < (addr_t) (&dummy32->regs.orig_gpr2)) { + /* + * access registers are stored in the thread structure + */ + offset = addr - (addr_t) &dummy32->regs.acrs; + *(__u32*)((addr_t) &child->thread.acrs + offset) = tmp; + + } else if (addr == (addr_t) (&dummy32->regs.orig_gpr2)) { + /* + * orig_gpr2 is stored on the kernel stack + */ + *(__u32*)((addr_t) &__KSTK_PTREGS(child)->orig_gpr2 + 4) = tmp; + + } else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) { + /* + * floating point regs. are stored in the thread structure + */ + if (addr == (addr_t) &dummy32->regs.fp_regs.fpc && + (tmp & ~FPC_VALID_MASK) != 0) + /* Invalid floating point control. */ + return -EINVAL; + offset = addr - (addr_t) &dummy32->regs.fp_regs; + *(__u32 *)((addr_t) &child->thread.fp_regs + offset) = tmp; + + } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) { + /* + * per_info is found in the thread structure. + */ + offset = addr - (addr_t) &dummy32->regs.per_info; + /* + * This is magic. See per_struct and per_struct32. + * By incident the offsets in per_struct are exactly + * twice the offsets in per_struct32 for all fields. + * The 8 byte fields need special handling though, + * because the second half (bytes 4-7) is needed and + * not the first half. + */ + if ((offset >= (addr_t) &dummy_per32->control_regs && + offset < (addr_t) (&dummy_per32->control_regs + 1)) || + (offset >= (addr_t) &dummy_per32->starting_addr && + offset <= (addr_t) &dummy_per32->ending_addr) || + offset == (addr_t) &dummy_per32->lowcore.words.address) + offset = offset*2 + 4; + else + offset = offset*2; + *(__u32 *)((addr_t) &child->thread.per_info + offset) = tmp; + + } + + FixPerRegisters(child); + return 0; +} + +static int +do_ptrace_emu31(struct task_struct *child, long request, long addr, long data) +{ + unsigned int tmp; /* 4 bytes !! */ + ptrace_area_emu31 parea; + int copied, ret; + + switch (request) { + case PTRACE_PEEKTEXT: + case PTRACE_PEEKDATA: + /* read word at location addr. */ + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0); + if (copied != sizeof(tmp)) + return -EIO; + return put_user(tmp, (unsigned int __user *) data); + + case PTRACE_PEEKUSR: + /* read the word at location addr in the USER area. */ + return peek_user_emu31(child, addr, data); + + case PTRACE_POKETEXT: + case PTRACE_POKEDATA: + /* write the word at location addr. */ + tmp = data; + copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 1); + if (copied != sizeof(tmp)) + return -EIO; + return 0; + + case PTRACE_POKEUSR: + /* write the word at location addr in the USER area */ + return poke_user_emu31(child, addr, data); + + case PTRACE_PEEKUSR_AREA: + case PTRACE_POKEUSR_AREA: + if (copy_from_user(&parea, (void __user *) addr, + sizeof(parea))) + return -EFAULT; + addr = parea.kernel_addr; + data = parea.process_addr; + copied = 0; + while (copied < parea.len) { + if (request == PTRACE_PEEKUSR_AREA) + ret = peek_user_emu31(child, addr, data); + else { + __u32 tmp; + if (get_user (tmp, (__u32 __user *) data)) + return -EFAULT; + ret = poke_user_emu31(child, addr, tmp); + } + if (ret) + return ret; + addr += sizeof(unsigned int); + data += sizeof(unsigned int); + copied += sizeof(unsigned int); + } + return 0; + case PTRACE_GETEVENTMSG: + return put_user((__u32) child->ptrace_message, + (unsigned int __user *) data); + case PTRACE_GETSIGINFO: + if (child->last_siginfo == NULL) + return -EINVAL; + return copy_siginfo_to_user32((compat_siginfo_t __user *) data, + child->last_siginfo); + case PTRACE_SETSIGINFO: + if (child->last_siginfo == NULL) + return -EINVAL; + return copy_siginfo_from_user32(child->last_siginfo, + (compat_siginfo_t __user *) data); + } + return ptrace_request(child, request, addr, data); +} +#endif + +#define PT32_IEEE_IP 0x13c + +static int +do_ptrace(struct task_struct *child, long request, long addr, long data) +{ + int ret; + + if (request == PTRACE_ATTACH) + return ptrace_attach(child); + + /* + * Special cases to get/store the ieee instructions pointer. + */ + if (child == current) { + if (request == PTRACE_PEEKUSR && addr == PT_IEEE_IP) + return peek_user(child, addr, data); + if (request == PTRACE_POKEUSR && addr == PT_IEEE_IP) + return poke_user(child, addr, data); +#ifdef CONFIG_S390_SUPPORT + if (request == PTRACE_PEEKUSR && + addr == PT32_IEEE_IP && test_thread_flag(TIF_31BIT)) + return peek_user_emu31(child, addr, data); + if (request == PTRACE_POKEUSR && + addr == PT32_IEEE_IP && test_thread_flag(TIF_31BIT)) + return poke_user_emu31(child, addr, data); +#endif + } + + ret = ptrace_check_attach(child, request == PTRACE_KILL); + if (ret < 0) + return ret; + + switch (request) { + case PTRACE_SYSCALL: + /* continue and stop at next (return from) syscall */ + case PTRACE_CONT: + /* restart after signal. */ + if ((unsigned long) data >= _NSIG) + return -EIO; + if (request == PTRACE_SYSCALL) + set_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + else + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + return 0; + + case PTRACE_KILL: + /* + * 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. + */ + if (child->exit_state == EXIT_ZOMBIE) /* already dead */ + return 0; + child->exit_code = SIGKILL; + /* make sure the single step bit is not set. */ + clear_single_step(child); + wake_up_process(child); + return 0; + + case PTRACE_SINGLESTEP: + /* set the trap flag. */ + if ((unsigned long) data >= _NSIG) + return -EIO; + clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE); + child->exit_code = data; + if (data) + set_tsk_thread_flag(child, TIF_SINGLE_STEP); + else + set_single_step(child); + /* give it a chance to run. */ + wake_up_process(child); + return 0; + + case PTRACE_DETACH: + /* detach a process that was attached. */ + return ptrace_detach(child, data); + + + /* Do requests that differ for 31/64 bit */ + default: +#ifdef CONFIG_S390_SUPPORT + if (test_thread_flag(TIF_31BIT)) + return do_ptrace_emu31(child, request, addr, data); +#endif + return do_ptrace_normal(child, request, addr, data); + } + /* Not reached. */ + return -EIO; +} + +asmlinkage long +sys_ptrace(long request, long pid, long addr, long data) +{ + struct task_struct *child; + int ret; + + lock_kernel(); + + if (request == PTRACE_TRACEME) { + /* are we already being traced? */ + ret = -EPERM; + if (current->ptrace & PT_PTRACED) + goto out; + ret = security_ptrace(current->parent, current); + if (ret) + goto out; + /* set the ptrace bit in the process flags. */ + current->ptrace |= PT_PTRACED; + goto out; + } + + ret = -EPERM; + if (pid == 1) /* you may not mess with init */ + 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 = do_ptrace(child, request, addr, data); + + put_task_struct(child); +out: + unlock_kernel(); + return ret; +} + +asmlinkage void +syscall_trace(struct pt_regs *regs, int entryexit) +{ + if (unlikely(current->audit_context)) { + if (!entryexit) + audit_syscall_entry(current, regs->gprs[2], + regs->orig_gpr2, regs->gprs[3], + regs->gprs[4], regs->gprs[5]); + else + audit_syscall_exit(current, regs->gprs[2]); + } + if (!test_thread_flag(TIF_SYSCALL_TRACE)) + return; + if (!(current->ptrace & PT_PTRACED)) + return; + ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) + ? 0x80 : 0)); + + /* + * this isn't the same as continuing with a signal, but it will do + * for normal use. strace only continues with a signal if the + * stopping signal is not SIGTRAP. -brl + */ + if (current->exit_code) { + send_sig(current->exit_code, current, 1); + current->exit_code = 0; + } +} diff --git a/arch/s390/kernel/reipl.S b/arch/s390/kernel/reipl.S new file mode 100644 index 000000000000..658e5ac484f9 --- /dev/null +++ b/arch/s390/kernel/reipl.S @@ -0,0 +1,78 @@ +/* + * 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) +.Lpg1: lctl %c6,%c6,.Lall-.Lpg0(%r13) + stctl %c0,%c0,.Lctlsave-.Lpg0(%r13) + ni .Lctlsave-.Lpg0(%r13),0xef + lctl %c0,%c0,.Lctlsave-.Lpg0(%r13) + lr %r1,%r2 + 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 + msch .Lschib-.Lpg0(%r13) + lhi %r0,5 +.Lssch: ssch .Liplorb-.Lpg0(%r13) + jz .L001 + brct %r0,.Lssch + bas %r14,.Ldisab-.Lpg0(%r13) +.L001: mvc __LC_IO_NEW_PSW(8),.Lionew-.Lpg0(%r13) +.Ltpi: lpsw .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 + lpsw 0 + sigp 0,0,0(6) +.Ldisab: st %r14,.Ldispsw+4-.Lpg0(%r13) + lpsw .Ldispsw-.Lpg0(%r13) + .align 8 +.Lall: .long 0xff000000 +.Lnull: .long 0x00000000 +.Lctlsave: .long 0x00000000 + .align 8 +.Lnewpsw: .long 0x00080000,0x80000000+.Lpg1 +.Lpcnew: .long 0x00080000,0x80000000+.Lecs +.Lionew: .long 0x00080000,0x80000000+.Lcont +.Lwaitpsw: .long 0x020a0000,0x00000000+.Ltpi +.Ldispsw: .long 0x000a0000,0x00000000 +.Liplccws: .long 0x02000000,0x60000018 + .long 0x08000008,0x20000001 +.Liplorb: .long 0x0049504c,0x0040ff80 + .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/s390/kernel/reipl64.S b/arch/s390/kernel/reipl64.S new file mode 100644 index 000000000000..4d090d60f3ef --- /dev/null +++ b/arch/s390/kernel/reipl64.S @@ -0,0 +1,96 @@ +/* + * 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) + lghi %r0,5 +.Lssch: ssch .Liplorb-.Lpg0(%r13) + jz .L001 + brct %r0,.Lssch + 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,0x0040ff80 + .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/s390/kernel/s390_ext.c b/arch/s390/kernel/s390_ext.c new file mode 100644 index 000000000000..3bdd38ec71da --- /dev/null +++ b/arch/s390/kernel/s390_ext.c @@ -0,0 +1,135 @@ +/* + * 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/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/errno.h> +#include <linux/kernel_stat.h> +#include <linux/interrupt.h> + +#include <asm/lowcore.h> +#include <asm/s390_ext.h> +#include <asm/irq.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, 0x4000 + * iucv and 0x2603 pfault) this is always the first element. + */ +ext_int_info_t *ext_int_hash[256] = { 0, }; + +int register_external_interrupt(__u16 code, ext_int_handler_t handler) +{ + ext_int_info_t *p; + int index; + + p = (ext_int_info_t *) kmalloc(sizeof(ext_int_info_t), GFP_ATOMIC); + if (p == NULL) + return -ENOMEM; + p->code = code; + p->handler = handler; + index = code & 0xff; + p->next = ext_int_hash[index]; + ext_int_hash[index] = p; + return 0; +} + +int register_early_external_interrupt(__u16 code, ext_int_handler_t handler, + ext_int_info_t *p) +{ + int index; + + if (p == NULL) + return -EINVAL; + p->code = code; + p->handler = handler; + index = code & 0xff; + 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; + kfree(p); + return 0; +} + +int unregister_early_external_interrupt(__u16 code, ext_int_handler_t handler, + ext_int_info_t *p) +{ + ext_int_info_t *q; + int index; + + if (p == NULL || p->code != code || p->handler != handler) + return -EINVAL; + index = code & 0xff; + q = ext_int_hash[index]; + if (p != q) { + while (q != NULL) { + if (q->next == p) + break; + q = q->next; + } + if (q == NULL) + return -ENOENT; + q->next = p->next; + } else + ext_int_hash[index] = p->next; + return 0; +} + +void do_extint(struct pt_regs *regs, unsigned short code) +{ + ext_int_info_t *p; + int index; + + irq_enter(); + asm volatile ("mc 0,0"); + if (S390_lowcore.int_clock >= S390_lowcore.jiffy_timer) + /** + * Make sure that the i/o interrupt did not "overtake" + * the last HZ timer interrupt. + */ + account_ticks(regs); + kstat_cpu(smp_processor_id()).irqs[EXTERNAL_INTERRUPT]++; + index = code & 0xff; + for (p = ext_int_hash[index]; p; p = p->next) { + if (likely(p->code == code)) { + if (likely(p->handler)) + p->handler(regs, code); + } + } + irq_exit(); +} + +EXPORT_SYMBOL(register_external_interrupt); +EXPORT_SYMBOL(unregister_external_interrupt); + diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c new file mode 100644 index 000000000000..11fd6d556d8f --- /dev/null +++ b/arch/s390/kernel/s390_ksyms.c @@ -0,0 +1,65 @@ +/* + * arch/s390/kernel/s390_ksyms.c + * + * S390 version + */ +#include <linux/config.h> +#include <linux/highuid.h> +#include <linux/module.h> +#include <linux/mm.h> +#include <linux/smp.h> +#include <linux/syscalls.h> +#include <linux/interrupt.h> +#include <linux/ioctl32.h> +#include <asm/checksum.h> +#include <asm/cpcmd.h> +#include <asm/delay.h> +#include <asm/pgalloc.h> +#include <asm/setup.h> +#ifdef CONFIG_IP_MULTICAST +#include <net/arp.h> +#endif + +/* + * memory management + */ +EXPORT_SYMBOL(_oi_bitmap); +EXPORT_SYMBOL(_ni_bitmap); +EXPORT_SYMBOL(_zb_findmap); +EXPORT_SYMBOL(_sb_findmap); +EXPORT_SYMBOL(__copy_from_user_asm); +EXPORT_SYMBOL(__copy_to_user_asm); +EXPORT_SYMBOL(__copy_in_user_asm); +EXPORT_SYMBOL(__clear_user_asm); +EXPORT_SYMBOL(__strncpy_from_user_asm); +EXPORT_SYMBOL(__strnlen_user_asm); +EXPORT_SYMBOL(diag10); +EXPORT_SYMBOL(default_storage_key); + +/* + * semaphore ops + */ +EXPORT_SYMBOL(__up); +EXPORT_SYMBOL(__down); +EXPORT_SYMBOL(__down_interruptible); + +/* + * binfmt_elf loader + */ +extern int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs); +EXPORT_SYMBOL(dump_fpu); +EXPORT_SYMBOL(overflowuid); +EXPORT_SYMBOL(overflowgid); +EXPORT_SYMBOL(empty_zero_page); + +/* + * misc. + */ +EXPORT_SYMBOL(machine_flags); +EXPORT_SYMBOL(__udelay); +EXPORT_SYMBOL(kernel_thread); +EXPORT_SYMBOL(csum_fold); +EXPORT_SYMBOL(console_mode); +EXPORT_SYMBOL(console_devno); +EXPORT_SYMBOL(console_irq); +EXPORT_SYMBOL(sys_wait4); diff --git a/arch/s390/kernel/semaphore.c b/arch/s390/kernel/semaphore.c new file mode 100644 index 000000000000..8dfb690c159f --- /dev/null +++ b/arch/s390/kernel/semaphore.c @@ -0,0 +1,108 @@ +/* + * 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 <linux/errno.h> +#include <linux/init.h> + +#include <asm/semaphore.h> + +/* + * Atomically update sem->count. Equivalent to: + * old_val = sem->count.counter; + * new_val = ((old_val >= 0) ? old_val : 0) + incr; + * sem->count.counter = new_val; + * return old_val; + */ +static inline int __sem_update_count(struct semaphore *sem, int incr) +{ + int old_val, new_val; + + __asm__ __volatile__(" l %0,0(%3)\n" + "0: ltr %1,%0\n" + " jhe 1f\n" + " lhi %1,0\n" + "1: ar %1,%4\n" + " cs %0,%1,0(%3)\n" + " jl 0b\n" + : "=&d" (old_val), "=&d" (new_val), + "=m" (sem->count) + : "a" (&sem->count), "d" (incr), "m" (sem->count) + : "cc" ); + return old_val; +} + +/* + * The inline function up() incremented count but the result + * was <= 0. This indicates that some process is waiting on + * the semaphore. The semaphore is free and we'll wake the + * first sleeping process, so we set count to 1 unless some + * other cpu has called up in the meantime in which case + * we just increment count by 1. + */ +void __up(struct semaphore *sem) +{ + __sem_update_count(sem, 1); + wake_up(&sem->wait); +} + +/* + * The inline function down() decremented count and the result + * was < 0. The wait loop will atomically test and update the + * semaphore counter following the rules: + * count > 0: decrement count, wake up queue and exit. + * count <= 0: set count to -1, go to sleep. + */ +void __sched __down(struct semaphore * sem) +{ + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + __set_task_state(tsk, TASK_UNINTERRUPTIBLE); + add_wait_queue_exclusive(&sem->wait, &wait); + while (__sem_update_count(sem, -1) <= 0) { + schedule(); + set_task_state(tsk, TASK_UNINTERRUPTIBLE); + } + remove_wait_queue(&sem->wait, &wait); + __set_task_state(tsk, TASK_RUNNING); + wake_up(&sem->wait); +} + +/* + * Same as __down() with an additional test for signals. + * If a signal is pending the count is updated as follows: + * count > 0: wake up queue and exit. + * count <= 0: set count to 0, wake up queue and exit. + */ +int __sched __down_interruptible(struct semaphore * sem) +{ + int retval = 0; + struct task_struct *tsk = current; + DECLARE_WAITQUEUE(wait, tsk); + + __set_task_state(tsk, TASK_INTERRUPTIBLE); + add_wait_queue_exclusive(&sem->wait, &wait); + while (__sem_update_count(sem, -1) <= 0) { + if (signal_pending(current)) { + __sem_update_count(sem, 0); + retval = -EINTR; + break; + } + schedule(); + set_task_state(tsk, TASK_INTERRUPTIBLE); + } + remove_wait_queue(&sem->wait, &wait); + __set_task_state(tsk, TASK_RUNNING); + wake_up(&sem->wait); + return retval; +} + diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c new file mode 100644 index 000000000000..c879c40aa7a5 --- /dev/null +++ b/arch/s390/kernel/setup.c @@ -0,0 +1,632 @@ +/* + * 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/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/stddef.h> +#include <linux/unistd.h> +#include <linux/ptrace.h> +#include <linux/slab.h> +#include <linux/user.h> +#include <linux/a.out.h> +#include <linux/tty.h> +#include <linux/ioport.h> +#include <linux/delay.h> +#include <linux/config.h> +#include <linux/init.h> +#include <linux/initrd.h> +#include <linux/bootmem.h> +#include <linux/root_dev.h> +#include <linux/console.h> +#include <linux/seq_file.h> +#include <linux/kernel_stat.h> + +#include <asm/uaccess.h> +#include <asm/system.h> +#include <asm/smp.h> +#include <asm/mmu_context.h> +#include <asm/cpcmd.h> +#include <asm/lowcore.h> +#include <asm/irq.h> + +/* + * Machine setup.. + */ +unsigned int console_mode = 0; +unsigned int console_devno = -1; +unsigned int console_irq = -1; +unsigned long memory_size = 0; +unsigned long machine_flags = 0; +unsigned int default_storage_key = 0; +struct { + unsigned long addr, size, type; +} memory_chunk[MEMORY_CHUNKS] = { { 0 } }; +#define CHUNK_READ_WRITE 0 +#define CHUNK_READ_ONLY 1 +volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ + +/* + * Setup options + */ +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, }; + +static struct resource code_resource = { + .name = "Kernel code", + .flags = IORESOURCE_BUSY | IORESOURCE_MEM, +}; + +static struct resource data_resource = { + .name = "Kernel data", + .flags = IORESOURCE_BUSY | IORESOURCE_MEM, +}; + +/* + * cpu_init() initializes state that is per-CPU. + */ +void __devinit cpu_init (void) +{ + int addr = hard_smp_processor_id(); + + /* + * 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; + + /* + * Force FPU initialization: + */ + clear_thread_flag(TIF_USEDFPU); + clear_used_math(); + + atomic_inc(&init_mm.mm_count); + current->active_mm = &init_mm; + if (current->mm) + BUG(); + enter_lazy_tlb(&init_mm, current); +} + +/* + * 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); + +/* + * condev= and conmode= setup parameter. + */ + +static int __init condev_setup(char *str) +{ + int vdev; + + vdev = simple_strtoul(str, &str, 0); + if (vdev >= 0 && vdev < 65536) { + console_devno = vdev; + console_irq = -1; + } + return 1; +} + +__setup("condev=", condev_setup); + +static int __init conmode_setup(char *str) +{ +#if defined(CONFIG_SCLP_CONSOLE) + if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0) + SET_CONSOLE_SCLP; +#endif +#if defined(CONFIG_TN3215_CONSOLE) + if (strncmp(str, "3215", 5) == 0) + SET_CONSOLE_3215; +#endif +#if defined(CONFIG_TN3270_CONSOLE) + if (strncmp(str, "3270", 5) == 0) + SET_CONSOLE_3270; +#endif + return 1; +} + +__setup("conmode=", conmode_setup); + +static void __init conmode_default(void) +{ + char query_buffer[1024]; + char *ptr; + + if (MACHINE_IS_VM) { + __cpcmd("QUERY CONSOLE", query_buffer, 1024); + console_devno = simple_strtoul(query_buffer + 5, NULL, 16); + ptr = strstr(query_buffer, "SUBCHANNEL ="); + console_irq = simple_strtoul(ptr + 13, NULL, 16); + __cpcmd("QUERY TERM", query_buffer, 1024); + ptr = strstr(query_buffer, "CONMODE"); + /* + * Set the conmode to 3215 so that the device recognition + * will set the cu_type of the console to 3215. If the + * conmode is 3270 and we don't set it back then both + * 3215 and the 3270 driver will try to access the console + * device (3215 as console and 3270 as normal tty). + */ + __cpcmd("TERM CONMODE 3215", NULL, 0); + if (ptr == NULL) { +#if defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; +#endif + return; + } + if (strncmp(ptr + 8, "3270", 4) == 0) { +#if defined(CONFIG_TN3270_CONSOLE) + SET_CONSOLE_3270; +#elif defined(CONFIG_TN3215_CONSOLE) + SET_CONSOLE_3215; +#elif defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; +#endif + } else if (strncmp(ptr + 8, "3215", 4) == 0) { +#if defined(CONFIG_TN3215_CONSOLE) + SET_CONSOLE_3215; +#elif defined(CONFIG_TN3270_CONSOLE) + SET_CONSOLE_3270; +#elif defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; +#endif + } + } else if (MACHINE_IS_P390) { +#if defined(CONFIG_TN3215_CONSOLE) + SET_CONSOLE_3215; +#elif defined(CONFIG_TN3270_CONSOLE) + SET_CONSOLE_3270; +#endif + } else { +#if defined(CONFIG_SCLP_CONSOLE) + SET_CONSOLE_SCLP; +#endif + } +} + +#ifdef CONFIG_SMP +extern void machine_restart_smp(char *); +extern void machine_halt_smp(void); +extern void machine_power_off_smp(void); + +void (*_machine_restart)(char *command) = machine_restart_smp; +void (*_machine_halt)(void) = machine_halt_smp; +void (*_machine_power_off)(void) = machine_power_off_smp; +#else +/* + * Reboot, halt and power_off routines for non SMP. + */ +extern void reipl(unsigned long devno); +static void do_machine_restart_nonsmp(char * __unused) +{ + if (MACHINE_IS_VM) + cpcmd ("IPL", NULL, 0); + else + reipl (0x10000 | S390_lowcore.ipl_device); +} + +static void do_machine_halt_nonsmp(void) +{ + if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) + cpcmd(vmhalt_cmd, NULL, 0); + signal_processor(smp_processor_id(), sigp_stop_and_store_status); +} + +static void do_machine_power_off_nonsmp(void) +{ + 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_restart)(char *command) = do_machine_restart_nonsmp; +void (*_machine_halt)(void) = do_machine_halt_nonsmp; +void (*_machine_power_off)(void) = do_machine_power_off_nonsmp; +#endif + + /* + * Reboot, halt and power_off stubs. They just call _machine_restart, + * _machine_halt or _machine_power_off. + */ + +void machine_restart(char *command) +{ + console_unblank(); + _machine_restart(command); +} + +EXPORT_SYMBOL(machine_restart); + +void machine_halt(void) +{ + console_unblank(); + _machine_halt(); +} + +EXPORT_SYMBOL(machine_halt); + +void machine_power_off(void) +{ + console_unblank(); + _machine_power_off(); +} + +EXPORT_SYMBOL(machine_power_off); + +/* + * Setup function called from init/main.c just after the banner + * was printed. + */ +extern char _pstart, _pend, _stext; + +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; + unsigned long start_pfn, end_pfn; + static unsigned int smptrap=0; + unsigned long delay = 0; + struct _lowcore *lc; + int i; + + if (smptrap) + return; + smptrap=1; + + /* + * print what head.S has found out about the machine + */ +#ifndef CONFIG_ARCH_S390X + printk((MACHINE_IS_VM) ? + "We are running under VM (31 bit mode)\n" : + "We are running native (31 bit mode)\n"); + printk((MACHINE_HAS_IEEE) ? + "This machine has an IEEE fpu\n" : + "This machine has no IEEE fpu\n"); +#else /* CONFIG_ARCH_S390X */ + printk((MACHINE_IS_VM) ? + "We are running under VM (64 bit mode)\n" : + "We are running native (64 bit mode)\n"); +#endif /* CONFIG_ARCH_S390X */ + + ROOT_DEV = Root_RAM0; + memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ +#ifndef CONFIG_ARCH_S390X + memory_end = memory_size & ~0x400000UL; /* align memory end to 4MB */ + /* + * 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; +#else /* CONFIG_ARCH_S390X */ + memory_end = memory_size & ~0x200000UL; /* detected in head.s */ +#endif /* CONFIG_ARCH_S390X */ + 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) { + 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) { + 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 requested amount of time */ + udelay(delay); + } + cn = *(from++); + if (!cn) + break; + if (cn == '\n') + cn = ' '; /* replace newlines with space */ + if (cn == 0x0d) + cn = ' '; /* replace 0x0d 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; + + /* + * partially used pages are not usable - thus + * we are rounding upwards: + */ + start_pfn = (__pa(&_end) + PAGE_SIZE - 1) >> PAGE_SHIFT; + end_pfn = max_pfn = memory_end >> PAGE_SHIFT; + + /* + * Initialize the boot-time allocator (with low memory only): + */ + bootmap_size = init_bootmem(start_pfn, end_pfn); + + /* + * Register RAM areas with the bootmem allocator. + */ + for (i = 0; i < 16 && memory_chunk[i].size > 0; i++) { + unsigned long start_chunk, end_chunk; + + if (memory_chunk[i].type != CHUNK_READ_WRITE) + continue; + start_chunk = (memory_chunk[i].addr + PAGE_SIZE - 1); + start_chunk >>= PAGE_SHIFT; + end_chunk = (memory_chunk[i].addr + memory_chunk[i].size); + end_chunk >>= PAGE_SHIFT; + if (start_chunk < start_pfn) + start_chunk = start_pfn; + if (end_chunk > end_pfn) + end_chunk = end_pfn; + if (start_chunk < end_chunk) + free_bootmem(start_chunk << PAGE_SHIFT, + (end_chunk - start_chunk) << 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 + + for (i = 0; i < 16 && memory_chunk[i].size > 0; i++) { + struct resource *res; + + res = alloc_bootmem_low(sizeof(struct resource)); + res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; + + switch (memory_chunk[i].type) { + case CHUNK_READ_WRITE: + res->name = "System RAM"; + break; + case CHUNK_READ_ONLY: + res->name = "System ROM"; + res->flags |= IORESOURCE_READONLY; + break; + default: + res->name = "reserved"; + } + res->start = memory_chunk[i].addr; + res->end = memory_chunk[i].addr + memory_chunk[i].size - 1; + request_resource(&iomem_resource, res); + request_resource(res, &code_resource); + request_resource(res, &data_resource); + } + + /* + * Setup lowcore for boot cpu + */ +#ifndef CONFIG_ARCH_S390X + lc = (struct _lowcore *) __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0); + memset(lc, 0, PAGE_SIZE); +#else /* CONFIG_ARCH_S390X */ + lc = (struct _lowcore *) __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0); + memset(lc, 0, 2*PAGE_SIZE); +#endif /* CONFIG_ARCH_S390X */ + lc->restart_psw.mask = PSW_BASE_BITS; + lc->restart_psw.addr = + PSW_ADDR_AMODE | (unsigned long) restart_int_handler; + lc->external_new_psw.mask = PSW_KERNEL_BITS; + lc->external_new_psw.addr = + PSW_ADDR_AMODE | (unsigned long) ext_int_handler; + lc->svc_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_EXT; + lc->svc_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) system_call; + lc->program_new_psw.mask = PSW_KERNEL_BITS; + lc->program_new_psw.addr = + PSW_ADDR_AMODE | (unsigned long)pgm_check_handler; + lc->mcck_new_psw.mask = PSW_KERNEL_BITS; + lc->mcck_new_psw.addr = + PSW_ADDR_AMODE | (unsigned long) mcck_int_handler; + lc->io_new_psw.mask = PSW_KERNEL_BITS; + lc->io_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) io_int_handler; + lc->ipl_device = S390_lowcore.ipl_device; + lc->jiffy_timer = -1LL; + lc->kernel_stack = ((unsigned long) &init_thread_union) + THREAD_SIZE; + lc->async_stack = (unsigned long) + __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0) + ASYNC_SIZE; +#ifdef CONFIG_CHECK_STACK + lc->panic_stack = (unsigned long) + __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0) + PAGE_SIZE; +#endif + lc->current_task = (unsigned long) init_thread_union.thread_info.task; + lc->thread_info = (unsigned long) &init_thread_union; +#ifdef CONFIG_ARCH_S390X + if (MACHINE_HAS_DIAG44) + lc->diag44_opcode = 0x83000044; + else + lc->diag44_opcode = 0x07000700; +#endif /* CONFIG_ARCH_S390X */ + set_prefix((u32)(unsigned long) lc); + cpu_init(); + __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr; + + /* + * Create kernel page tables and switch to virtual addressing. + */ + paging_init(); + + /* Setup default console */ + conmode_default(); +} + +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); +} + +/* + * show_cpuinfo - Get information on one CPU for use by procfs. + */ + +static int show_cpuinfo(struct seq_file *m, void *v) +{ + struct cpuinfo_S390 *cpuinfo; + unsigned long n = (unsigned long) v - 1; + + if (!n) { + seq_printf(m, "vendor_id : IBM/S390\n" + "# processors : %i\n" + "bogomips per cpu: %lu.%02lu\n", + num_online_cpus(), loops_per_jiffy/(500000/HZ), + (loops_per_jiffy/(5000/HZ))%100); + } + if (cpu_online(n)) { +#ifdef CONFIG_SMP + if (smp_processor_id() == n) + cpuinfo = &S390_lowcore.cpu_data; + else + cpuinfo = &lowcore_ptr[n]->cpu_data; +#else + cpuinfo = &S390_lowcore.cpu_data; +#endif + seq_printf(m, "processor %li: " + "version = %02X, " + "identification = %06X, " + "machine = %04X\n", + n, cpuinfo->cpu_id.version, + cpuinfo->cpu_id.ident, + cpuinfo->cpu_id.machine); + } + return 0; +} + +static void *c_start(struct seq_file *m, loff_t *pos) +{ + return *pos < NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL; +} +static void *c_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return c_start(m, pos); +} +static void c_stop(struct seq_file *m, void *v) +{ +} +struct seq_operations cpuinfo_op = { + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, +}; + diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c new file mode 100644 index 000000000000..610c1d03e975 --- /dev/null +++ b/arch/s390/kernel/signal.c @@ -0,0 +1,527 @@ +/* + * 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 <linux/tty.h> +#include <linux/personality.h> +#include <linux/binfmts.h> +#include <asm/ucontext.h> +#include <asm/uaccess.h> +#include <asm/lowcore.h> + +#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) + + +typedef struct +{ + __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; + struct sigcontext sc; + _sigregs sregs; + int signo; + __u8 retcode[S390_SYSCALL_SIZE]; +} sigframe; + +typedef struct +{ + __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; + __u8 retcode[S390_SYSCALL_SIZE]; + struct siginfo info; + struct ucontext uc; +} rt_sigframe; + +int do_signal(struct pt_regs *regs, sigset_t *oldset); + +/* + * 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->sighand->siglock); + saveset = current->blocked; + siginitset(¤t->blocked, mask); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + regs->gprs[2] = -EINTR; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage long +sys_rt_sigsuspend(struct pt_regs *regs, sigset_t __user *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->sighand->siglock); + saveset = current->blocked; + current->blocked = newset; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + regs->gprs[2] = -EINTR; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + schedule(); + if (do_signal(regs, &saveset)) + return -EINTR; + } +} + +asmlinkage long +sys_sigaction(int sig, const struct old_sigaction __user *act, + struct old_sigaction __user *oact) +{ + struct k_sigaction new_ka, old_ka; + int ret; + + if (act) { + old_sigset_t mask; + if (!access_ok(VERIFY_READ, act, sizeof(*act)) || + __get_user(new_ka.sa.sa_handler, &act->sa_handler) || + __get_user(new_ka.sa.sa_restorer, &act->sa_restorer)) + return -EFAULT; + __get_user(new_ka.sa.sa_flags, &act->sa_flags); + __get_user(mask, &act->sa_mask); + siginitset(&new_ka.sa.sa_mask, mask); + } + + ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); + + if (!ret && oact) { + if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || + __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || + __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer)) + return -EFAULT; + __put_user(old_ka.sa.sa_flags, &oact->sa_flags); + __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask); + } + + return ret; +} + +asmlinkage long +sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss, + struct pt_regs *regs) +{ + return do_sigaltstack(uss, uoss, regs->gprs[15]); +} + + + +/* Returns non-zero on fault. */ +static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) +{ + unsigned long old_mask = regs->psw.mask; + int err; + + save_access_regs(current->thread.acrs); + + /* Copy a 'clean' PSW mask to the user to avoid leaking + information about whether PER is currently on. */ + regs->psw.mask = PSW_MASK_MERGE(PSW_USER_BITS, regs->psw.mask); + err = __copy_to_user(&sregs->regs.psw, ®s->psw, + sizeof(sregs->regs.psw)+sizeof(sregs->regs.gprs)); + regs->psw.mask = old_mask; + if (err != 0) + return err; + err = __copy_to_user(&sregs->regs.acrs, current->thread.acrs, + sizeof(sregs->regs.acrs)); + if (err != 0) + return err; + /* + * We have to store the fp registers to current->thread.fp_regs + * to merge them with the emulated registers. + */ + save_fp_regs(¤t->thread.fp_regs); + return __copy_to_user(&sregs->fpregs, ¤t->thread.fp_regs, + sizeof(s390_fp_regs)); +} + +/* Returns positive number on error */ +static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) +{ + unsigned long old_mask = regs->psw.mask; + int err; + + /* Alwys make any pending restarted system call return -EINTR */ + current_thread_info()->restart_block.fn = do_no_restart_syscall; + + err = __copy_from_user(®s->psw, &sregs->regs.psw, + sizeof(sregs->regs.psw)+sizeof(sregs->regs.gprs)); + regs->psw.mask = PSW_MASK_MERGE(old_mask, regs->psw.mask); + regs->psw.addr |= PSW_ADDR_AMODE; + if (err) + return err; + err = __copy_from_user(¤t->thread.acrs, &sregs->regs.acrs, + sizeof(sregs->regs.acrs)); + if (err) + return err; + restore_access_regs(current->thread.acrs); + + err = __copy_from_user(¤t->thread.fp_regs, &sregs->fpregs, + sizeof(s390_fp_regs)); + current->thread.fp_regs.fpc &= FPC_VALID_MASK; + if (err) + return err; + + restore_fp_regs(¤t->thread.fp_regs); + regs->trap = -1; /* disable syscall checks */ + return 0; +} + +asmlinkage long sys_sigreturn(struct pt_regs *regs) +{ + sigframe __user *frame = (sigframe __user *)regs->gprs[15]; + sigset_t set; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigregs(regs, &frame->sregs)) + goto badframe; + + return regs->gprs[2]; + +badframe: + force_sig(SIGSEGV, current); + return 0; +} + +asmlinkage long sys_rt_sigreturn(struct pt_regs *regs) +{ + rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15]; + sigset_t set; + + if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) + goto badframe; + if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) + goto badframe; + + sigdelsetmask(&set, ~_BLOCKABLE); + spin_lock_irq(¤t->sighand->siglock); + current->blocked = set; + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (restore_sigregs(regs, &frame->uc.uc_mcontext)) + 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 __user * +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 (! sas_ss_flags(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 __user *)((sp - frame_size) & -8ul); +} + +static inline int map_signal(int sig) +{ + if (current_thread_info()->exec_domain + && current_thread_info()->exec_domain->signal_invmap + && sig < 32) + return current_thread_info()->exec_domain->signal_invmap[sig]; + else + return sig; +} + +static void setup_frame(int sig, struct k_sigaction *ka, + sigset_t *set, struct pt_regs * regs) +{ + sigframe __user *frame; + + frame = get_sigframe(ka, regs, sizeof(sigframe)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(sigframe))) + goto give_sigsegv; + + if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE)) + goto give_sigsegv; + + if (save_sigregs(regs, &frame->sregs)) + goto give_sigsegv; + if (__put_user(&frame->sregs, &frame->sc.sregs)) + goto give_sigsegv; + + /* Set up to return from userspace. If provided, use a stub + already in userspace. */ + if (ka->sa.sa_flags & SA_RESTORER) { + regs->gprs[14] = (unsigned long) + ka->sa.sa_restorer | PSW_ADDR_AMODE; + } else { + regs->gprs[14] = (unsigned long) + frame->retcode | PSW_ADDR_AMODE; + if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, + (u16 __user *)(frame->retcode))) + goto give_sigsegv; + } + + /* Set up backchain. */ + if (__put_user(regs->gprs[15], (addr_t __user *) frame)) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->gprs[15] = (unsigned long) frame; + regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE; + + regs->gprs[2] = map_signal(sig); + regs->gprs[3] = (unsigned long) &frame->sc; + + /* We forgot to include these in the sigcontext. + To avoid breaking binary compatibility, they are passed as args. */ + regs->gprs[4] = current->thread.trap_no; + regs->gprs[5] = current->thread.prot_addr; + + /* Place signal number on stack to allow backtrace from handler. */ + if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) + goto give_sigsegv; + return; + +give_sigsegv: + force_sigsegv(sig, current); +} + +static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, + sigset_t *set, struct pt_regs * regs) +{ + int err = 0; + rt_sigframe __user *frame; + + frame = get_sigframe(ka, regs, sizeof(rt_sigframe)); + if (!access_ok(VERIFY_WRITE, frame, sizeof(rt_sigframe))) + goto give_sigsegv; + + if (copy_siginfo_to_user(&frame->info, info)) + goto give_sigsegv; + + /* Create the ucontext. */ + err |= __put_user(0, &frame->uc.uc_flags); + err |= __put_user(0, &frame->uc.uc_link); + err |= __put_user((void *)current->sas_ss_sp, &frame->uc.uc_stack.ss_sp); + err |= __put_user(sas_ss_flags(regs->gprs[15]), + &frame->uc.uc_stack.ss_flags); + err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); + err |= save_sigregs(regs, &frame->uc.uc_mcontext); + 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) { + regs->gprs[14] = (unsigned long) + ka->sa.sa_restorer | PSW_ADDR_AMODE; + } else { + regs->gprs[14] = (unsigned long) + frame->retcode | PSW_ADDR_AMODE; + err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, + (u16 __user *)(frame->retcode)); + } + + /* Set up backchain. */ + if (__put_user(regs->gprs[15], (addr_t __user *) frame)) + goto give_sigsegv; + + /* Set up registers for signal handler */ + regs->gprs[15] = (unsigned long) frame; + regs->psw.addr = (unsigned long) ka->sa.sa_handler | PSW_ADDR_AMODE; + + regs->gprs[2] = map_signal(sig); + regs->gprs[3] = (unsigned long) &frame->info; + regs->gprs[4] = (unsigned long) &frame->uc; + return; + +give_sigsegv: + force_sigsegv(sig, 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) +{ + /* 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_NODEFER)) { + spin_lock_irq(¤t->sighand->siglock); + sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); + sigaddset(¤t->blocked,sig); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + } +} + +/* + * Note that 'init' is a special process: it doesn't get signals it doesn't + * want to handle. Thus you cannot kill init even with a SIGKILL even by + * mistake. + * + * 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) +{ + unsigned long retval = 0, continue_addr = 0, restart_addr = 0; + siginfo_t info; + int signr; + 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; + + /* Are we from a system call? */ + if (regs->trap == __LC_SVC_OLD_PSW) { + continue_addr = regs->psw.addr; + restart_addr = continue_addr - regs->ilc; + retval = regs->gprs[2]; + + /* Prepare for system call restart. We do this here so that a + debugger will see the already changed PSW. */ + if (retval == -ERESTARTNOHAND || + retval == -ERESTARTSYS || + retval == -ERESTARTNOINTR) { + regs->gprs[2] = regs->orig_gpr2; + regs->psw.addr = restart_addr; + } else if (retval == -ERESTART_RESTARTBLOCK) { + regs->gprs[2] = -EINTR; + } + } + + /* Get signal to deliver. When running under ptrace, at this point + the debugger may change all our registers ... */ + signr = get_signal_to_deliver(&info, &ka, regs, NULL); + + /* Depending on the signal settings we may need to revert the + decision to restart the system call. */ + if (signr > 0 && regs->psw.addr == restart_addr) { + if (retval == -ERESTARTNOHAND + || (retval == -ERESTARTSYS + && !(current->sighand->action[signr-1].sa.sa_flags + & SA_RESTART))) { + regs->gprs[2] = -EINTR; + regs->psw.addr = continue_addr; + } + } + + if (signr > 0) { + /* Whee! Actually deliver the signal. */ +#ifdef CONFIG_S390_SUPPORT + if (test_thread_flag(TIF_31BIT)) { + extern void handle_signal32(unsigned long sig, + struct k_sigaction *ka, + siginfo_t *info, + sigset_t *oldset, + struct pt_regs *regs); + handle_signal32(signr, &ka, &info, oldset, regs); + return 1; + } +#endif + handle_signal(signr, &ka, &info, oldset, regs); + return 1; + } + + /* Restart a different system call. */ + if (retval == -ERESTART_RESTARTBLOCK + && regs->psw.addr == continue_addr) { + regs->gprs[2] = __NR_restart_syscall; + set_thread_flag(TIF_RESTART_SVC); + } + return 0; +} diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c new file mode 100644 index 000000000000..fdfcf0488b49 --- /dev/null +++ b/arch/s390/kernel/smp.c @@ -0,0 +1,840 @@ +/* + * 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) + * Heiko Carstens (heiko.carstens@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/module.h> +#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 <linux/cache.h> +#include <linux/interrupt.h> +#include <linux/cpu.h> + +#include <asm/sigp.h> +#include <asm/pgalloc.h> +#include <asm/irq.h> +#include <asm/s390_ext.h> +#include <asm/cpcmd.h> +#include <asm/tlbflush.h> + +/* prototypes */ + +extern volatile int __cpu_logical_map[]; + +/* + * An array with a pointer the lowcore of every CPU. + */ + +struct _lowcore *lowcore_ptr[NR_CPUS]; + +cpumask_t cpu_online_map; +cpumask_t cpu_possible_map; + +static struct task_struct *current_set[NR_CPUS]; + +EXPORT_SYMBOL(cpu_online_map); + +/* + * Reboot, halt and power_off routines for SMP. + */ +extern char vmhalt_cmd[]; +extern char vmpoff_cmd[]; + +extern void reipl(unsigned long devno); + +static void smp_ext_bitcall(int, ec_bit_sig); +static void smp_ext_bitcall_others(ec_bit_sig); + +/* + * Structure and data for smp_call_function(). This is designed to minimise + * static memory requirements. It also looks cleaner. + */ +static DEFINE_SPINLOCK(call_lock); + +struct call_data_struct { + void (*func) (void *info); + void *info; + atomic_t started; + atomic_t finished; + int wait; +}; + +static struct call_data_struct * call_data; + +/* + * 'Call function' interrupt callback + */ +static void do_call_function(void) +{ + void (*func) (void *info) = call_data->func; + void *info = call_data->info; + int wait = call_data->wait; + + atomic_inc(&call_data->started); + (*func)(info); + if (wait) + atomic_inc(&call_data->finished); +} + +/* + * this function sends a 'generic call function' IPI to all other CPUs + * in the system. + */ + +int smp_call_function (void (*func) (void *info), void *info, int nonatomic, + 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. + * <nonatomic> 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 or from a bottom half handler. + */ +{ + struct call_data_struct data; + int cpus = num_online_cpus()-1; + + if (cpus <= 0) + return 0; + + /* Can deadlock when called with interrupts disabled */ + WARN_ON(irqs_disabled()); + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock(&call_lock); + call_data = &data; + /* Send a message to all other CPUs and wait for them to respond */ + smp_ext_bitcall_others(ec_call_function); + + /* Wait for response */ + while (atomic_read(&data.started) != cpus) + cpu_relax(); + + if (wait) + while (atomic_read(&data.finished) != cpus) + cpu_relax(); + spin_unlock(&call_lock); + + return 0; +} + +/* + * Call a function on one CPU + * cpu : the CPU the function should be executed on + * + * You must not call this function with disabled interrupts or from a + * hardware interrupt handler. You may call it from a bottom half. + * + * It is guaranteed that the called function runs on the specified CPU, + * preemption is disabled. + */ +int smp_call_function_on(void (*func) (void *info), void *info, + int nonatomic, int wait, int cpu) +{ + struct call_data_struct data; + int curr_cpu; + + if (!cpu_online(cpu)) + return -EINVAL; + + /* disable preemption for local function call */ + curr_cpu = get_cpu(); + + if (curr_cpu == cpu) { + /* direct call to function */ + func(info); + put_cpu(); + return 0; + } + + data.func = func; + data.info = info; + atomic_set(&data.started, 0); + data.wait = wait; + if (wait) + atomic_set(&data.finished, 0); + + spin_lock_bh(&call_lock); + call_data = &data; + smp_ext_bitcall(cpu, ec_call_function); + + /* Wait for response */ + while (atomic_read(&data.started) != 1) + cpu_relax(); + + if (wait) + while (atomic_read(&data.finished) != 1) + cpu_relax(); + + spin_unlock_bh(&call_lock); + put_cpu(); + return 0; +} +EXPORT_SYMBOL(smp_call_function_on); + +static inline void do_send_stop(void) +{ + int cpu, rc; + + /* stop all processors */ + for_each_online_cpu(cpu) { + if (cpu == smp_processor_id()) + continue; + do { + rc = signal_processor(cpu, sigp_stop); + } while (rc == sigp_busy); + } +} + +static inline void do_store_status(void) +{ + int cpu, rc; + + /* store status of all processors in their lowcores (real 0) */ + for_each_online_cpu(cpu) { + if (cpu == smp_processor_id()) + continue; + do { + rc = signal_processor_p( + (__u32)(unsigned long) lowcore_ptr[cpu], cpu, + sigp_store_status_at_address); + } while(rc == sigp_busy); + } +} + +/* + * this function sends a 'stop' sigp to all other CPUs in the system. + * it goes straight through. + */ +void smp_send_stop(void) +{ + /* write magic number to zero page (absolute 0) */ + lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC; + + /* stop other processors. */ + do_send_stop(); + + /* store status of other processors. */ + do_store_status(); +} + +/* + * Reboot, halt and power_off routines for SMP. + */ + +static void do_machine_restart(void * __unused) +{ + int cpu; + static atomic_t cpuid = ATOMIC_INIT(-1); + + if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid)) + signal_processor(smp_processor_id(), sigp_stop); + + /* Wait for all other cpus to enter stopped state */ + for_each_online_cpu(cpu) { + if (cpu == smp_processor_id()) + continue; + while(!smp_cpu_not_running(cpu)) + cpu_relax(); + } + + /* Store status of other cpus. */ + do_store_status(); + + /* + * Finally call reipl. Because we waited for all other + * cpus to enter this function we know that they do + * not hold any s390irq-locks (the cpus have been + * interrupted by an external interrupt and s390irq + * locks are always held disabled). + */ + if (MACHINE_IS_VM) + cpcmd ("IPL", NULL, 0); + else + reipl (0x10000 | S390_lowcore.ipl_device); +} + +void machine_restart_smp(char * __unused) +{ + on_each_cpu(do_machine_restart, NULL, 0, 0); +} + +static void do_wait_for_stop(void) +{ + unsigned long cr[16]; + + __ctl_store(cr, 0, 15); + cr[0] &= ~0xffff; + cr[6] = 0; + __ctl_load(cr, 0, 15); + for (;;) + enabled_wait(); +} + +static void do_machine_halt(void * __unused) +{ + static atomic_t cpuid = ATOMIC_INIT(-1); + + if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) { + 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); + } + do_wait_for_stop(); +} + +void machine_halt_smp(void) +{ + on_each_cpu(do_machine_halt, NULL, 0, 0); +} + +static void do_machine_power_off(void * __unused) +{ + static atomic_t cpuid = ATOMIC_INIT(-1); + + if (atomic_compare_and_swap(-1, smp_processor_id(), &cpuid) == 0) { + 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); + } + do_wait_for_stop(); +} + +void machine_power_off_smp(void) +{ + on_each_cpu(do_machine_power_off, NULL, 0, 0); +} + +/* + * This is the main routine where commands issued by other + * cpus are handled. + */ + +void do_ext_call_interrupt(struct pt_regs *regs, __u16 code) +{ + 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. + */ + bits = xchg(&S390_lowcore.ext_call_fast, 0); + + if (test_bit(ec_call_function, &bits)) + do_call_function(); +} + +/* + * Send an external call sigp to another cpu and return without waiting + * for its completion. + */ +static void smp_ext_bitcall(int cpu, ec_bit_sig sig) +{ + /* + * Set signaling bit in lowcore of target cpu and kick it + */ + set_bit(sig, (unsigned long *) &lowcore_ptr[cpu]->ext_call_fast); + while(signal_processor(cpu, sigp_external_call) == sigp_busy) + udelay(10); +} + +/* + * Send an external call sigp to every other cpu in the system and + * return without waiting for its completion. + */ +static void smp_ext_bitcall_others(ec_bit_sig sig) +{ + int cpu; + + for_each_online_cpu(cpu) { + if (cpu == smp_processor_id()) + continue; + /* + * Set signaling bit in lowcore of target cpu and kick it + */ + set_bit(sig, (unsigned long *) &lowcore_ptr[cpu]->ext_call_fast); + while (signal_processor(cpu, sigp_external_call) == sigp_busy) + udelay(10); + } +} + +#ifndef CONFIG_ARCH_S390X +/* + * this function sends a 'purge tlb' signal to another CPU. + */ +void smp_ptlb_callback(void *info) +{ + local_flush_tlb(); +} + +void smp_ptlb_all(void) +{ + on_each_cpu(smp_ptlb_callback, NULL, 0, 1); +} +EXPORT_SYMBOL(smp_ptlb_all); +#endif /* ! CONFIG_ARCH_S390X */ + +/* + * 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; + unsigned long orvals[16]; + unsigned long andvals[16]; +} ec_creg_mask_parms; + +/* + * callback for setting/clearing control bits + */ +void smp_ctl_bit_callback(void *info) { + ec_creg_mask_parms *pp; + unsigned long cregs[16]; + int i; + + pp = (ec_creg_mask_parms *) info; + __ctl_store(cregs[pp->start_ctl], pp->start_ctl, pp->end_ctl); + for (i = pp->start_ctl; i <= pp->end_ctl; i++) + cregs[i] = (cregs[i] & pp->andvals[i]) | pp->orvals[i]; + __ctl_load(cregs[pp->start_ctl], pp->start_ctl, pp->end_ctl); +} + +/* + * Set a bit in a control register of all cpus + */ +void smp_ctl_set_bit(int cr, int bit) { + ec_creg_mask_parms parms; + + parms.start_ctl = cr; + parms.end_ctl = cr; + parms.orvals[cr] = 1 << bit; + parms.andvals[cr] = -1L; + preempt_disable(); + smp_call_function(smp_ctl_bit_callback, &parms, 0, 1); + __ctl_set_bit(cr, bit); + preempt_enable(); +} + +/* + * Clear a bit in a control register of all cpus + */ +void smp_ctl_clear_bit(int cr, int bit) { + ec_creg_mask_parms parms; + + parms.start_ctl = cr; + parms.end_ctl = cr; + parms.orvals[cr] = 0; + parms.andvals[cr] = ~(1L << bit); + preempt_disable(); + smp_call_function(smp_ctl_bit_callback, &parms, 0, 1); + __ctl_clear_bit(cr, bit); + preempt_enable(); +} + +/* + * Lets check how many CPUs we have. + */ + +void +__init smp_check_cpus(unsigned int max_cpus) +{ + int cpu, num_cpus; + __u16 boot_cpu_addr; + + /* + * cpu 0 is the boot cpu. See smp_prepare_boot_cpu. + */ + + boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; + current_thread_info()->cpu = 0; + num_cpus = 1; + for (cpu = 0; cpu <= 65535 && num_cpus < max_cpus; cpu++) { + if ((__u16) cpu == boot_cpu_addr) + continue; + __cpu_logical_map[num_cpus] = (__u16) cpu; + if (signal_processor(num_cpus, sigp_sense) == + sigp_not_operational) + continue; + cpu_set(num_cpus, cpu_present_map); + num_cpus++; + } + + for (cpu = 1; cpu < max_cpus; cpu++) + cpu_set(cpu, cpu_possible_map); + + printk("Detected %d CPU's\n",(int) num_cpus); + printk("Boot cpu address %2X\n", boot_cpu_addr); +} + +/* + * Activate a secondary processor. + */ +extern void init_cpu_timer(void); +extern void init_cpu_vtimer(void); +extern int pfault_init(void); +extern void pfault_fini(void); + +int __devinit start_secondary(void *cpuvoid) +{ + /* Setup the cpu */ + cpu_init(); + /* init per CPU timer */ + init_cpu_timer(); +#ifdef CONFIG_VIRT_TIMER + init_cpu_vtimer(); +#endif +#ifdef CONFIG_PFAULT + /* Enable pfault pseudo page faults on this cpu. */ + pfault_init(); +#endif + /* Mark this cpu as online */ + cpu_set(smp_processor_id(), cpu_online_map); + /* Switch on interrupts */ + local_irq_enable(); + /* Print info about this processor */ + print_cpu_info(&S390_lowcore.cpu_data); + /* cpu_idle will call schedule for us */ + cpu_idle(); + return 0; +} + +static void __init smp_create_idle(unsigned int cpu) +{ + struct task_struct *p; + + /* + * don't care about the psw and regs settings since we'll never + * reschedule the forked task. + */ + p = fork_idle(cpu); + if (IS_ERR(p)) + panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p)); + current_set[cpu] = p; +} + +/* Reserving and releasing of CPUs */ + +static DEFINE_SPINLOCK(smp_reserve_lock); +static int smp_cpu_reserved[NR_CPUS]; + +int +smp_get_cpu(cpumask_t cpu_mask) +{ + unsigned long flags; + int cpu; + + spin_lock_irqsave(&smp_reserve_lock, flags); + /* Try to find an already reserved cpu. */ + for_each_cpu_mask(cpu, cpu_mask) { + if (smp_cpu_reserved[cpu] != 0) { + smp_cpu_reserved[cpu]++; + /* Found one. */ + goto out; + } + } + /* Reserve a new cpu from cpu_mask. */ + for_each_cpu_mask(cpu, cpu_mask) { + if (cpu_online(cpu)) { + smp_cpu_reserved[cpu]++; + goto out; + } + } + cpu = -ENODEV; +out: + spin_unlock_irqrestore(&smp_reserve_lock, flags); + return cpu; +} + +void +smp_put_cpu(int cpu) +{ + unsigned long flags; + + spin_lock_irqsave(&smp_reserve_lock, flags); + smp_cpu_reserved[cpu]--; + spin_unlock_irqrestore(&smp_reserve_lock, flags); +} + +static inline int +cpu_stopped(int cpu) +{ + __u32 status; + + /* Check for stopped state */ + if (signal_processor_ps(&status, 0, cpu, sigp_sense) == sigp_status_stored) { + if (status & 0x40) + return 1; + } + return 0; +} + +/* Upping and downing of CPUs */ + +int +__cpu_up(unsigned int cpu) +{ + struct task_struct *idle; + struct _lowcore *cpu_lowcore; + struct stack_frame *sf; + sigp_ccode ccode; + int curr_cpu; + + for (curr_cpu = 0; curr_cpu <= 65535; curr_cpu++) { + __cpu_logical_map[cpu] = (__u16) curr_cpu; + if (cpu_stopped(cpu)) + break; + } + + if (!cpu_stopped(cpu)) + return -ENODEV; + + ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]), + cpu, sigp_set_prefix); + if (ccode){ + printk("sigp_set_prefix failed for cpu %d " + "with condition code %d\n", + (int) cpu, (int) ccode); + return -EIO; + } + + idle = current_set[cpu]; + cpu_lowcore = lowcore_ptr[cpu]; + cpu_lowcore->kernel_stack = (unsigned long) + idle->thread_info + (THREAD_SIZE); + sf = (struct stack_frame *) (cpu_lowcore->kernel_stack + - sizeof(struct pt_regs) + - sizeof(struct stack_frame)); + memset(sf, 0, sizeof(struct stack_frame)); + sf->gprs[9] = (unsigned long) sf; + cpu_lowcore->save_area[15] = (unsigned long) sf; + __ctl_store(cpu_lowcore->cregs_save_area[0], 0, 15); + __asm__ __volatile__("stam 0,15,0(%0)" + : : "a" (&cpu_lowcore->access_regs_save_area) + : "memory"); + cpu_lowcore->percpu_offset = __per_cpu_offset[cpu]; + cpu_lowcore->current_task = (unsigned long) idle; + cpu_lowcore->cpu_data.cpu_nr = cpu; + eieio(); + signal_processor(cpu,sigp_restart); + + while (!cpu_online(cpu)) + cpu_relax(); + return 0; +} + +int +__cpu_disable(void) +{ + unsigned long flags; + ec_creg_mask_parms cr_parms; + + spin_lock_irqsave(&smp_reserve_lock, flags); + if (smp_cpu_reserved[smp_processor_id()] != 0) { + spin_unlock_irqrestore(&smp_reserve_lock, flags); + return -EBUSY; + } + +#ifdef CONFIG_PFAULT + /* Disable pfault pseudo page faults on this cpu. */ + pfault_fini(); +#endif + + /* disable all external interrupts */ + + cr_parms.start_ctl = 0; + cr_parms.end_ctl = 0; + cr_parms.orvals[0] = 0; + cr_parms.andvals[0] = ~(1<<15 | 1<<14 | 1<<13 | 1<<12 | + 1<<11 | 1<<10 | 1<< 6 | 1<< 4); + smp_ctl_bit_callback(&cr_parms); + + /* disable all I/O interrupts */ + + cr_parms.start_ctl = 6; + cr_parms.end_ctl = 6; + cr_parms.orvals[6] = 0; + cr_parms.andvals[6] = ~(1<<31 | 1<<30 | 1<<29 | 1<<28 | + 1<<27 | 1<<26 | 1<<25 | 1<<24); + smp_ctl_bit_callback(&cr_parms); + + /* disable most machine checks */ + + cr_parms.start_ctl = 14; + cr_parms.end_ctl = 14; + cr_parms.orvals[14] = 0; + cr_parms.andvals[14] = ~(1<<28 | 1<<27 | 1<<26 | 1<<25 | 1<<24); + smp_ctl_bit_callback(&cr_parms); + + spin_unlock_irqrestore(&smp_reserve_lock, flags); + return 0; +} + +void +__cpu_die(unsigned int cpu) +{ + /* Wait until target cpu is down */ + while (!smp_cpu_not_running(cpu)) + cpu_relax(); + printk("Processor %d spun down\n", cpu); +} + +void +cpu_die(void) +{ + idle_task_exit(); + signal_processor(smp_processor_id(), sigp_stop); + BUG(); + for(;;); +} + +/* + * Cycle through the processors and setup structures. + */ + +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + unsigned long stack; + unsigned int cpu; + 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_check_cpus(max_cpus); + memset(lowcore_ptr,0,sizeof(lowcore_ptr)); + /* + * Initialize prefix pages and stacks for all possible cpus + */ + print_cpu_info(&S390_lowcore.cpu_data); + + for(i = 0; i < NR_CPUS; i++) { + if (!cpu_possible(i)) + continue; + lowcore_ptr[i] = (struct _lowcore *) + __get_free_pages(GFP_KERNEL|GFP_DMA, + sizeof(void*) == 8 ? 1 : 0); + stack = __get_free_pages(GFP_KERNEL,ASYNC_ORDER); + if (lowcore_ptr[i] == NULL || stack == 0ULL) + panic("smp_boot_cpus failed to allocate memory\n"); + + *(lowcore_ptr[i]) = S390_lowcore; + lowcore_ptr[i]->async_stack = stack + (ASYNC_SIZE); +#ifdef CONFIG_CHECK_STACK + stack = __get_free_pages(GFP_KERNEL,0); + if (stack == 0ULL) + panic("smp_boot_cpus failed to allocate memory\n"); + lowcore_ptr[i]->panic_stack = stack + (PAGE_SIZE); +#endif + } + set_prefix((u32)(unsigned long) lowcore_ptr[smp_processor_id()]); + + for_each_cpu(cpu) + if (cpu != smp_processor_id()) + smp_create_idle(cpu); +} + +void __devinit smp_prepare_boot_cpu(void) +{ + BUG_ON(smp_processor_id() != 0); + + cpu_set(0, cpu_online_map); + cpu_set(0, cpu_present_map); + cpu_set(0, cpu_possible_map); + S390_lowcore.percpu_offset = __per_cpu_offset[0]; + current_set[0] = current; +} + +void smp_cpus_done(unsigned int max_cpus) +{ + cpu_present_map = cpu_possible_map; +} + +/* + * 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; +} + +static DEFINE_PER_CPU(struct cpu, cpu_devices); + +static int __init topology_init(void) +{ + int cpu; + int ret; + + for_each_cpu(cpu) { + ret = register_cpu(&per_cpu(cpu_devices, cpu), cpu, NULL); + if (ret) + printk(KERN_WARNING "topology_init: register_cpu %d " + "failed (%d)\n", cpu, ret); + } + return 0; +} + +subsys_initcall(topology_init); + +EXPORT_SYMBOL(cpu_possible_map); +EXPORT_SYMBOL(lowcore_ptr); +EXPORT_SYMBOL(smp_ctl_set_bit); +EXPORT_SYMBOL(smp_ctl_clear_bit); +EXPORT_SYMBOL(smp_call_function); +EXPORT_SYMBOL(smp_get_cpu); +EXPORT_SYMBOL(smp_put_cpu); + diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c new file mode 100644 index 000000000000..efe6b83b53f7 --- /dev/null +++ b/arch/s390/kernel/sys_s390.c @@ -0,0 +1,270 @@ +/* + * 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), + * Thomas Spatzier (tspat@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/syscalls.h> +#include <linux/mman.h> +#include <linux/file.h> +#include <linux/utsname.h> +#ifdef CONFIG_ARCH_S390X +#include <linux/personality.h> +#endif /* CONFIG_ARCH_S390X */ + +#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 __user *fildes) +{ + int fd[2]; + int error; + + error = do_pipe(fd); + if (!error) { + if (copy_to_user(fildes, fd, 2*sizeof(int))) + error = -EFAULT; + } + return error; +} + +/* common code for old and new mmaps */ +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_write(¤t->mm->mmap_sem); + error = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); + up_write(¤t->mm->mmap_sem); + + if (file) + fput(file); +out: + return error; +} + +/* + * Perform the select(nd, in, out, ex, tv) and mmap() system + * 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.. + */ + +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 __user *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 __user *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; +} + +#ifndef CONFIG_ARCH_S390X +struct sel_arg_struct { + unsigned long n; + fd_set *inp, *outp, *exp; + struct timeval *tvp; +}; + +asmlinkage long old_select(struct sel_arg_struct __user *arg) +{ + struct sel_arg_struct a; + + if (copy_from_user(&a, arg, sizeof(a))) + return -EFAULT; + /* sys_select() does the appropriate kernel locking */ + return sys_select(a.n, a.inp, a.outp, a.exp, a.tvp); + +} +#endif /* CONFIG_ARCH_S390X */ + +/* + * sys_ipc() is the de-multiplexer for the SysV IPC calls.. + * + * This is really horribly ugly. + */ +asmlinkage long sys_ipc(uint call, int first, unsigned long second, + unsigned long third, void __user *ptr) +{ + struct ipc_kludge tmp; + int ret; + + switch (call) { + case SEMOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + (unsigned)second, NULL); + case SEMTIMEDOP: + return sys_semtimedop(first, (struct sembuf __user *)ptr, + (unsigned)second, + (const struct timespec __user *) third); + case SEMGET: + return sys_semget(first, (int)second, third); + case SEMCTL: { + union semun fourth; + if (!ptr) + return -EINVAL; + if (get_user(fourth.__pad, (void __user * __user *) ptr)) + return -EFAULT; + return sys_semctl(first, (int)second, third, fourth); + } + case MSGSND: + return sys_msgsnd (first, (struct msgbuf __user *) ptr, + (size_t)second, third); + break; + case MSGRCV: + if (!ptr) + return -EINVAL; + if (copy_from_user (&tmp, (struct ipc_kludge __user *) ptr, + sizeof (struct ipc_kludge))) + return -EFAULT; + return sys_msgrcv (first, tmp.msgp, + (size_t)second, tmp.msgtyp, third); + case MSGGET: + return sys_msgget((key_t)first, (int)second); + case MSGCTL: + return sys_msgctl(first, (int)second, + (struct msqid_ds __user *)ptr); + + case SHMAT: { + ulong raddr; + ret = do_shmat(first, (char __user *)ptr, + (int)second, &raddr); + if (ret) + return ret; + return put_user (raddr, (ulong __user *) third); + break; + } + case SHMDT: + return sys_shmdt ((char __user *)ptr); + case SHMGET: + return sys_shmget(first, (size_t)second, third); + case SHMCTL: + return sys_shmctl(first, (int)second, + (struct shmid_ds __user *) ptr); + default: + return -ENOSYS; + + } + + return -EINVAL; +} + +#ifdef CONFIG_ARCH_S390X +asmlinkage long s390x_newuname(struct new_utsname __user *name) +{ + int ret = sys_newuname(name); + + if (current->personality == PER_LINUX32 && !ret) { + ret = copy_to_user(name->machine, "s390\0\0\0\0", 8); + if (ret) ret = -EFAULT; + } + return ret; +} + +asmlinkage long s390x_personality(unsigned long personality) +{ + int ret; + + if (current->personality == PER_LINUX32 && personality == PER_LINUX) + personality = PER_LINUX32; + ret = sys_personality(personality); + if (ret == PER_LINUX32) + ret = PER_LINUX; + + return ret; +} +#endif /* CONFIG_ARCH_S390X */ + +/* + * Wrapper function for sys_fadvise64/fadvise64_64 + */ +#ifndef CONFIG_ARCH_S390X + +asmlinkage long +s390_fadvise64(int fd, u32 offset_high, u32 offset_low, size_t len, int advice) +{ + return sys_fadvise64(fd, (u64) offset_high << 32 | offset_low, + len, advice); +} + +#endif + +struct fadvise64_64_args { + int fd; + long long offset; + long long len; + int advice; +}; + +asmlinkage long +s390_fadvise64_64(struct fadvise64_64_args __user *args) +{ + struct fadvise64_64_args a; + + if ( copy_from_user(&a, args, sizeof(a)) ) + return -EFAULT; + return sys_fadvise64_64(a.fd, a.offset, a.len, a.advice); +} + diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S new file mode 100644 index 000000000000..515938628f82 --- /dev/null +++ b/arch/s390/kernel/syscalls.S @@ -0,0 +1,292 @@ +/* + * definitions for sys_call_table, each line represents an + * entry in the table in the form + * SYSCALL(31 bit syscall, 64 bit syscall, 31 bit emulated syscall) + * + * this file is meant to be included from entry.S and entry64.S + */ + +#define NI_SYSCALL SYSCALL(sys_ni_syscall,sys_ni_syscall,sys_ni_syscall) + +NI_SYSCALL /* 0 */ +SYSCALL(sys_exit,sys_exit,sys32_exit_wrapper) +SYSCALL(sys_fork_glue,sys_fork_glue,sys_fork_glue) +SYSCALL(sys_read,sys_read,sys32_read_wrapper) +SYSCALL(sys_write,sys_write,sys32_write_wrapper) +SYSCALL(sys_open,sys_open,sys32_open_wrapper) /* 5 */ +SYSCALL(sys_close,sys_close,sys32_close_wrapper) +SYSCALL(sys_restart_syscall,sys_restart_syscall,sys_restart_syscall) +SYSCALL(sys_creat,sys_creat,sys32_creat_wrapper) +SYSCALL(sys_link,sys_link,sys32_link_wrapper) +SYSCALL(sys_unlink,sys_unlink,sys32_unlink_wrapper) /* 10 */ +SYSCALL(sys_execve_glue,sys_execve_glue,sys32_execve_glue) +SYSCALL(sys_chdir,sys_chdir,sys32_chdir_wrapper) +SYSCALL(sys_time,sys_ni_syscall,sys32_time_wrapper) /* old time syscall */ +SYSCALL(sys_mknod,sys_mknod,sys32_mknod_wrapper) +SYSCALL(sys_chmod,sys_chmod,sys32_chmod_wrapper) /* 15 */ +SYSCALL(sys_lchown16,sys_ni_syscall,sys32_lchown16_wrapper) /* old lchown16 syscall*/ +NI_SYSCALL /* old break syscall holder */ +NI_SYSCALL /* old stat syscall holder */ +SYSCALL(sys_lseek,sys_lseek,sys32_lseek_wrapper) +SYSCALL(sys_getpid,sys_getpid,sys_getpid) /* 20 */ +SYSCALL(sys_mount,sys_mount,sys32_mount_wrapper) +SYSCALL(sys_oldumount,sys_oldumount,sys32_oldumount_wrapper) +SYSCALL(sys_setuid16,sys_ni_syscall,sys32_setuid16_wrapper) /* old setuid16 syscall*/ +SYSCALL(sys_getuid16,sys_ni_syscall,sys32_getuid16) /* old getuid16 syscall*/ +SYSCALL(sys_stime,sys_ni_syscall,sys32_stime_wrapper) /* 25 old stime syscall */ +SYSCALL(sys_ptrace,sys_ptrace,sys32_ptrace_wrapper) +SYSCALL(sys_alarm,sys_alarm,sys32_alarm_wrapper) +NI_SYSCALL /* old fstat syscall */ +SYSCALL(sys_pause,sys_pause,sys32_pause) +SYSCALL(sys_utime,sys_utime,compat_sys_utime_wrapper) /* 30 */ +NI_SYSCALL /* old stty syscall */ +NI_SYSCALL /* old gtty syscall */ +SYSCALL(sys_access,sys_access,sys32_access_wrapper) +SYSCALL(sys_nice,sys_nice,sys32_nice_wrapper) +NI_SYSCALL /* 35 old ftime syscall */ +SYSCALL(sys_sync,sys_sync,sys_sync) +SYSCALL(sys_kill,sys_kill,sys32_kill_wrapper) +SYSCALL(sys_rename,sys_rename,sys32_rename_wrapper) +SYSCALL(sys_mkdir,sys_mkdir,sys32_mkdir_wrapper) +SYSCALL(sys_rmdir,sys_rmdir,sys32_rmdir_wrapper) /* 40 */ +SYSCALL(sys_dup,sys_dup,sys32_dup_wrapper) +SYSCALL(sys_pipe,sys_pipe,sys32_pipe_wrapper) +SYSCALL(sys_times,sys_times,compat_sys_times_wrapper) +NI_SYSCALL /* old prof syscall */ +SYSCALL(sys_brk,sys_brk,sys32_brk_wrapper) /* 45 */ +SYSCALL(sys_setgid16,sys_ni_syscall,sys32_setgid16_wrapper) /* old setgid16 syscall*/ +SYSCALL(sys_getgid16,sys_ni_syscall,sys32_getgid16) /* old getgid16 syscall*/ +SYSCALL(sys_signal,sys_signal,sys32_signal_wrapper) +SYSCALL(sys_geteuid16,sys_ni_syscall,sys32_geteuid16) /* old geteuid16 syscall */ +SYSCALL(sys_getegid16,sys_ni_syscall,sys32_getegid16) /* 50 old getegid16 syscall */ +SYSCALL(sys_acct,sys_acct,sys32_acct_wrapper) +SYSCALL(sys_umount,sys_umount,sys32_umount_wrapper) +NI_SYSCALL /* old lock syscall */ +SYSCALL(sys_ioctl,sys_ioctl,compat_sys_ioctl_wrapper) +SYSCALL(sys_fcntl,sys_fcntl,compat_sys_fcntl_wrapper) /* 55 */ +NI_SYSCALL /* intel mpx syscall */ +SYSCALL(sys_setpgid,sys_setpgid,sys32_setpgid_wrapper) +NI_SYSCALL /* old ulimit syscall */ +NI_SYSCALL /* old uname syscall */ +SYSCALL(sys_umask,sys_umask,sys32_umask_wrapper) /* 60 */ +SYSCALL(sys_chroot,sys_chroot,sys32_chroot_wrapper) +SYSCALL(sys_ustat,sys_ustat,sys32_ustat_wrapper) +SYSCALL(sys_dup2,sys_dup2,sys32_dup2_wrapper) +SYSCALL(sys_getppid,sys_getppid,sys_getppid) +SYSCALL(sys_getpgrp,sys_getpgrp,sys_getpgrp) /* 65 */ +SYSCALL(sys_setsid,sys_setsid,sys_setsid) +SYSCALL(sys_sigaction,sys_sigaction,sys32_sigaction_wrapper) +NI_SYSCALL /* old sgetmask syscall*/ +NI_SYSCALL /* old ssetmask syscall*/ +SYSCALL(sys_setreuid16,sys_ni_syscall,sys32_setreuid16_wrapper) /* old setreuid16 syscall */ +SYSCALL(sys_setregid16,sys_ni_syscall,sys32_setregid16_wrapper) /* old setregid16 syscall */ +SYSCALL(sys_sigsuspend_glue,sys_sigsuspend_glue,sys32_sigsuspend_glue) +SYSCALL(sys_sigpending,sys_sigpending,compat_sys_sigpending_wrapper) +SYSCALL(sys_sethostname,sys_sethostname,sys32_sethostname_wrapper) +SYSCALL(sys_setrlimit,sys_setrlimit,compat_sys_setrlimit_wrapper) /* 75 */ +SYSCALL(sys_old_getrlimit,sys_getrlimit,compat_sys_old_getrlimit_wrapper) +SYSCALL(sys_getrusage,sys_getrusage,compat_sys_getrusage_wrapper) +SYSCALL(sys_gettimeofday,sys_gettimeofday,sys32_gettimeofday_wrapper) +SYSCALL(sys_settimeofday,sys_settimeofday,sys32_settimeofday_wrapper) +SYSCALL(sys_getgroups16,sys_ni_syscall,sys32_getgroups16_wrapper) /* 80 old getgroups16 syscall */ +SYSCALL(sys_setgroups16,sys_ni_syscall,sys32_setgroups16_wrapper) /* old setgroups16 syscall */ +NI_SYSCALL /* old select syscall */ +SYSCALL(sys_symlink,sys_symlink,sys32_symlink_wrapper) +NI_SYSCALL /* old lstat syscall */ +SYSCALL(sys_readlink,sys_readlink,sys32_readlink_wrapper) /* 85 */ +SYSCALL(sys_uselib,sys_uselib,sys32_uselib_wrapper) +SYSCALL(sys_swapon,sys_swapon,sys32_swapon_wrapper) +SYSCALL(sys_reboot,sys_reboot,sys32_reboot_wrapper) +SYSCALL(sys_ni_syscall,sys_ni_syscall,old32_readdir_wrapper) /* old readdir syscall */ +SYSCALL(old_mmap,old_mmap,old32_mmap_wrapper) /* 90 */ +SYSCALL(sys_munmap,sys_munmap,sys32_munmap_wrapper) +SYSCALL(sys_truncate,sys_truncate,sys32_truncate_wrapper) +SYSCALL(sys_ftruncate,sys_ftruncate,sys32_ftruncate_wrapper) +SYSCALL(sys_fchmod,sys_fchmod,sys32_fchmod_wrapper) +SYSCALL(sys_fchown16,sys_ni_syscall,sys32_fchown16_wrapper) /* 95 old fchown16 syscall*/ +SYSCALL(sys_getpriority,sys_getpriority,sys32_getpriority_wrapper) +SYSCALL(sys_setpriority,sys_setpriority,sys32_setpriority_wrapper) +NI_SYSCALL /* old profil syscall */ +SYSCALL(sys_statfs,sys_statfs,compat_sys_statfs_wrapper) +SYSCALL(sys_fstatfs,sys_fstatfs,compat_sys_fstatfs_wrapper) /* 100 */ +NI_SYSCALL /* ioperm for i386 */ +SYSCALL(sys_socketcall,sys_socketcall,compat_sys_socketcall_wrapper) +SYSCALL(sys_syslog,sys_syslog,sys32_syslog_wrapper) +SYSCALL(sys_setitimer,sys_setitimer,compat_sys_setitimer_wrapper) +SYSCALL(sys_getitimer,sys_getitimer,compat_sys_getitimer_wrapper) /* 105 */ +SYSCALL(sys_newstat,sys_newstat,compat_sys_newstat_wrapper) +SYSCALL(sys_newlstat,sys_newlstat,compat_sys_newlstat_wrapper) +SYSCALL(sys_newfstat,sys_newfstat,compat_sys_newfstat_wrapper) +NI_SYSCALL /* old uname syscall */ +SYSCALL(sys_lookup_dcookie,sys_lookup_dcookie,sys32_lookup_dcookie_wrapper) /* 110 */ +SYSCALL(sys_vhangup,sys_vhangup,sys_vhangup) +NI_SYSCALL /* old "idle" system call */ +NI_SYSCALL /* vm86old for i386 */ +SYSCALL(sys_wait4,sys_wait4,compat_sys_wait4_wrapper) +SYSCALL(sys_swapoff,sys_swapoff,sys32_swapoff_wrapper) /* 115 */ +SYSCALL(sys_sysinfo,sys_sysinfo,sys32_sysinfo_wrapper) +SYSCALL(sys_ipc,sys_ipc,sys32_ipc_wrapper) +SYSCALL(sys_fsync,sys_fsync,sys32_fsync_wrapper) +SYSCALL(sys_sigreturn_glue,sys_sigreturn_glue,sys32_sigreturn_glue) +SYSCALL(sys_clone_glue,sys_clone_glue,sys32_clone_glue) /* 120 */ +SYSCALL(sys_setdomainname,sys_setdomainname,sys32_setdomainname_wrapper) +SYSCALL(sys_newuname,s390x_newuname,sys32_newuname_wrapper) +NI_SYSCALL /* modify_ldt for i386 */ +SYSCALL(sys_adjtimex,sys_adjtimex,sys32_adjtimex_wrapper) +SYSCALL(sys_mprotect,sys_mprotect,sys32_mprotect_wrapper) /* 125 */ +SYSCALL(sys_sigprocmask,sys_sigprocmask,compat_sys_sigprocmask_wrapper) +NI_SYSCALL /* old "create module" */ +SYSCALL(sys_init_module,sys_init_module,sys32_init_module_wrapper) +SYSCALL(sys_delete_module,sys_delete_module,sys32_delete_module_wrapper) +NI_SYSCALL /* 130: old get_kernel_syms */ +SYSCALL(sys_quotactl,sys_quotactl,sys32_quotactl_wrapper) +SYSCALL(sys_getpgid,sys_getpgid,sys32_getpgid_wrapper) +SYSCALL(sys_fchdir,sys_fchdir,sys32_fchdir_wrapper) +SYSCALL(sys_bdflush,sys_bdflush,sys32_bdflush_wrapper) +SYSCALL(sys_sysfs,sys_sysfs,sys32_sysfs_wrapper) /* 135 */ +SYSCALL(sys_personality,s390x_personality,sys32_personality_wrapper) +NI_SYSCALL /* for afs_syscall */ +SYSCALL(sys_setfsuid16,sys_ni_syscall,sys32_setfsuid16_wrapper) /* old setfsuid16 syscall */ +SYSCALL(sys_setfsgid16,sys_ni_syscall,sys32_setfsgid16_wrapper) /* old setfsgid16 syscall */ +SYSCALL(sys_llseek,sys_llseek,sys32_llseek_wrapper) /* 140 */ +SYSCALL(sys_getdents,sys_getdents,sys32_getdents_wrapper) +SYSCALL(sys_select,sys_select,compat_sys_select_wrapper) +SYSCALL(sys_flock,sys_flock,sys32_flock_wrapper) +SYSCALL(sys_msync,sys_msync,sys32_msync_wrapper) +SYSCALL(sys_readv,sys_readv,compat_sys_readv_wrapper) /* 145 */ +SYSCALL(sys_writev,sys_writev,compat_sys_writev_wrapper) +SYSCALL(sys_getsid,sys_getsid,sys32_getsid_wrapper) +SYSCALL(sys_fdatasync,sys_fdatasync,sys32_fdatasync_wrapper) +SYSCALL(sys_sysctl,sys_sysctl,sys32_sysctl_wrapper) +SYSCALL(sys_mlock,sys_mlock,sys32_mlock_wrapper) /* 150 */ +SYSCALL(sys_munlock,sys_munlock,sys32_munlock_wrapper) +SYSCALL(sys_mlockall,sys_mlockall,sys32_mlockall_wrapper) +SYSCALL(sys_munlockall,sys_munlockall,sys_munlockall) +SYSCALL(sys_sched_setparam,sys_sched_setparam,sys32_sched_setparam_wrapper) +SYSCALL(sys_sched_getparam,sys_sched_getparam,sys32_sched_getparam_wrapper) /* 155 */ +SYSCALL(sys_sched_setscheduler,sys_sched_setscheduler,sys32_sched_setscheduler_wrapper) +SYSCALL(sys_sched_getscheduler,sys_sched_getscheduler,sys32_sched_getscheduler_wrapper) +SYSCALL(sys_sched_yield,sys_sched_yield,sys_sched_yield) +SYSCALL(sys_sched_get_priority_max,sys_sched_get_priority_max,sys32_sched_get_priority_max_wrapper) +SYSCALL(sys_sched_get_priority_min,sys_sched_get_priority_min,sys32_sched_get_priority_min_wrapper) /* 160 */ +SYSCALL(sys_sched_rr_get_interval,sys_sched_rr_get_interval,sys32_sched_rr_get_interval_wrapper) +SYSCALL(sys_nanosleep,sys_nanosleep,compat_sys_nanosleep_wrapper) +SYSCALL(sys_mremap,sys_mremap,sys32_mremap_wrapper) +SYSCALL(sys_setresuid16,sys_ni_syscall,sys32_setresuid16_wrapper) /* old setresuid16 syscall */ +SYSCALL(sys_getresuid16,sys_ni_syscall,sys32_getresuid16_wrapper) /* 165 old getresuid16 syscall */ +NI_SYSCALL /* for vm86 */ +NI_SYSCALL /* old sys_query_module */ +SYSCALL(sys_poll,sys_poll,sys32_poll_wrapper) +SYSCALL(sys_nfsservctl,sys_nfsservctl,compat_sys_nfsservctl_wrapper) +SYSCALL(sys_setresgid16,sys_ni_syscall,sys32_setresgid16_wrapper) /* 170 old setresgid16 syscall */ +SYSCALL(sys_getresgid16,sys_ni_syscall,sys32_getresgid16_wrapper) /* old getresgid16 syscall */ +SYSCALL(sys_prctl,sys_prctl,sys32_prctl_wrapper) +SYSCALL(sys_rt_sigreturn_glue,sys_rt_sigreturn_glue,sys32_rt_sigreturn_glue) +SYSCALL(sys_rt_sigaction,sys_rt_sigaction,sys32_rt_sigaction_wrapper) +SYSCALL(sys_rt_sigprocmask,sys_rt_sigprocmask,sys32_rt_sigprocmask_wrapper) /* 175 */ +SYSCALL(sys_rt_sigpending,sys_rt_sigpending,sys32_rt_sigpending_wrapper) +SYSCALL(sys_rt_sigtimedwait,sys_rt_sigtimedwait,compat_sys_rt_sigtimedwait_wrapper) +SYSCALL(sys_rt_sigqueueinfo,sys_rt_sigqueueinfo,sys32_rt_sigqueueinfo_wrapper) +SYSCALL(sys_rt_sigsuspend_glue,sys_rt_sigsuspend_glue,sys32_rt_sigsuspend_glue) +SYSCALL(sys_pread64,sys_pread64,sys32_pread64_wrapper) /* 180 */ +SYSCALL(sys_pwrite64,sys_pwrite64,sys32_pwrite64_wrapper) +SYSCALL(sys_chown16,sys_ni_syscall,sys32_chown16_wrapper) /* old chown16 syscall */ +SYSCALL(sys_getcwd,sys_getcwd,sys32_getcwd_wrapper) +SYSCALL(sys_capget,sys_capget,sys32_capget_wrapper) +SYSCALL(sys_capset,sys_capset,sys32_capset_wrapper) /* 185 */ +SYSCALL(sys_sigaltstack_glue,sys_sigaltstack_glue,sys32_sigaltstack_glue) +SYSCALL(sys_sendfile,sys_sendfile64,sys32_sendfile_wrapper) +NI_SYSCALL /* streams1 */ +NI_SYSCALL /* streams2 */ +SYSCALL(sys_vfork_glue,sys_vfork_glue,sys_vfork_glue) /* 190 */ +SYSCALL(sys_getrlimit,sys_getrlimit,compat_sys_getrlimit_wrapper) +SYSCALL(sys_mmap2,sys_mmap2,sys32_mmap2_wrapper) +SYSCALL(sys_truncate64,sys_ni_syscall,sys32_truncate64_wrapper) +SYSCALL(sys_ftruncate64,sys_ni_syscall,sys32_ftruncate64_wrapper) +SYSCALL(sys_stat64,sys_ni_syscall,sys32_stat64_wrapper) /* 195 */ +SYSCALL(sys_lstat64,sys_ni_syscall,sys32_lstat64_wrapper) +SYSCALL(sys_fstat64,sys_ni_syscall,sys32_fstat64_wrapper) +SYSCALL(sys_lchown,sys_lchown,sys32_lchown_wrapper) +SYSCALL(sys_getuid,sys_getuid,sys_getuid) +SYSCALL(sys_getgid,sys_getgid,sys_getgid) /* 200 */ +SYSCALL(sys_geteuid,sys_geteuid,sys_geteuid) +SYSCALL(sys_getegid,sys_getegid,sys_getegid) +SYSCALL(sys_setreuid,sys_setreuid,sys32_setreuid_wrapper) +SYSCALL(sys_setregid,sys_setregid,sys32_setregid_wrapper) +SYSCALL(sys_getgroups,sys_getgroups,sys32_getgroups_wrapper) /* 205 */ +SYSCALL(sys_setgroups,sys_setgroups,sys32_setgroups_wrapper) +SYSCALL(sys_fchown,sys_fchown,sys32_fchown_wrapper) +SYSCALL(sys_setresuid,sys_setresuid,sys32_setresuid_wrapper) +SYSCALL(sys_getresuid,sys_getresuid,sys32_getresuid_wrapper) +SYSCALL(sys_setresgid,sys_setresgid,sys32_setresgid_wrapper) /* 210 */ +SYSCALL(sys_getresgid,sys_getresgid,sys32_getresgid_wrapper) +SYSCALL(sys_chown,sys_chown,sys32_chown_wrapper) +SYSCALL(sys_setuid,sys_setuid,sys32_setuid_wrapper) +SYSCALL(sys_setgid,sys_setgid,sys32_setgid_wrapper) +SYSCALL(sys_setfsuid,sys_setfsuid,sys32_setfsuid_wrapper) /* 215 */ +SYSCALL(sys_setfsgid,sys_setfsgid,sys32_setfsgid_wrapper) +SYSCALL(sys_pivot_root,sys_pivot_root,sys32_pivot_root_wrapper) +SYSCALL(sys_mincore,sys_mincore,sys32_mincore_wrapper) +SYSCALL(sys_madvise,sys_madvise,sys32_madvise_wrapper) +SYSCALL(sys_getdents64,sys_getdents64,sys32_getdents64_wrapper) /* 220 */ +SYSCALL(sys_fcntl64,sys_ni_syscall,compat_sys_fcntl64_wrapper) +SYSCALL(sys_readahead,sys_readahead,sys32_readahead) +SYSCALL(sys_sendfile64,sys_ni_syscall,sys32_sendfile64) +SYSCALL(sys_setxattr,sys_setxattr,sys32_setxattr_wrapper) +SYSCALL(sys_lsetxattr,sys_lsetxattr,sys32_lsetxattr_wrapper) /* 225 */ +SYSCALL(sys_fsetxattr,sys_fsetxattr,sys32_fsetxattr_wrapper) +SYSCALL(sys_getxattr,sys_getxattr,sys32_getxattr_wrapper) +SYSCALL(sys_lgetxattr,sys_lgetxattr,sys32_lgetxattr_wrapper) +SYSCALL(sys_fgetxattr,sys_fgetxattr,sys32_fgetxattr_wrapper) +SYSCALL(sys_listxattr,sys_listxattr,sys32_listxattr_wrapper) /* 230 */ +SYSCALL(sys_llistxattr,sys_llistxattr,sys32_llistxattr_wrapper) +SYSCALL(sys_flistxattr,sys_flistxattr,sys32_flistxattr_wrapper) +SYSCALL(sys_removexattr,sys_removexattr,sys32_removexattr_wrapper) +SYSCALL(sys_lremovexattr,sys_lremovexattr,sys32_lremovexattr_wrapper) +SYSCALL(sys_fremovexattr,sys_fremovexattr,sys32_fremovexattr_wrapper) /* 235 */ +SYSCALL(sys_gettid,sys_gettid,sys_gettid) +SYSCALL(sys_tkill,sys_tkill,sys_tkill) +SYSCALL(sys_futex,sys_futex,compat_sys_futex_wrapper) +SYSCALL(sys_sched_setaffinity,sys_sched_setaffinity,sys32_sched_setaffinity_wrapper) +SYSCALL(sys_sched_getaffinity,sys_sched_getaffinity,sys32_sched_getaffinity_wrapper) /* 240 */ +SYSCALL(sys_tgkill,sys_tgkill,sys_tgkill) +NI_SYSCALL /* reserved for TUX */ +SYSCALL(sys_io_setup,sys_io_setup,sys32_io_setup_wrapper) +SYSCALL(sys_io_destroy,sys_io_destroy,sys32_io_destroy_wrapper) +SYSCALL(sys_io_getevents,sys_io_getevents,sys32_io_getevents_wrapper) /* 245 */ +SYSCALL(sys_io_submit,sys_io_submit,sys32_io_submit_wrapper) +SYSCALL(sys_io_cancel,sys_io_cancel,sys32_io_cancel_wrapper) +SYSCALL(sys_exit_group,sys_exit_group,sys32_exit_group_wrapper) +SYSCALL(sys_epoll_create,sys_epoll_create,sys_epoll_create_wrapper) +SYSCALL(sys_epoll_ctl,sys_epoll_ctl,sys_epoll_ctl_wrapper) /* 250 */ +SYSCALL(sys_epoll_wait,sys_epoll_wait,sys_epoll_wait_wrapper) +SYSCALL(sys_set_tid_address,sys_set_tid_address,sys32_set_tid_address_wrapper) +SYSCALL(s390_fadvise64,sys_fadvise64_64,sys32_fadvise64_wrapper) +SYSCALL(sys_timer_create,sys_timer_create,sys32_timer_create_wrapper) +SYSCALL(sys_timer_settime,sys_timer_settime,sys32_timer_settime_wrapper) /* 255 */ +SYSCALL(sys_timer_gettime,sys_timer_gettime,sys32_timer_gettime_wrapper) +SYSCALL(sys_timer_getoverrun,sys_timer_getoverrun,sys32_timer_getoverrun_wrapper) +SYSCALL(sys_timer_delete,sys_timer_delete,sys32_timer_delete_wrapper) +SYSCALL(sys_clock_settime,sys_clock_settime,sys32_clock_settime_wrapper) +SYSCALL(sys_clock_gettime,sys_clock_gettime,sys32_clock_gettime_wrapper) /* 260 */ +SYSCALL(sys_clock_getres,sys_clock_getres,sys32_clock_getres_wrapper) +SYSCALL(sys_clock_nanosleep,sys_clock_nanosleep,sys32_clock_nanosleep_wrapper) +NI_SYSCALL /* reserved for vserver */ +SYSCALL(s390_fadvise64_64,sys_ni_syscall,sys32_fadvise64_64_wrapper) +SYSCALL(sys_statfs64,sys_statfs64,compat_sys_statfs64_wrapper) +SYSCALL(sys_fstatfs64,sys_fstatfs64,compat_sys_fstatfs64_wrapper) +SYSCALL(sys_remap_file_pages,sys_remap_file_pages,sys32_remap_file_pages_wrapper) +NI_SYSCALL /* 268 sys_mbind */ +NI_SYSCALL /* 269 sys_get_mempolicy */ +NI_SYSCALL /* 270 sys_set_mempolicy */ +SYSCALL(sys_mq_open,sys_mq_open,compat_sys_mq_open_wrapper) +SYSCALL(sys_mq_unlink,sys_mq_unlink,sys32_mq_unlink_wrapper) +SYSCALL(sys_mq_timedsend,sys_mq_timedsend,compat_sys_mq_timedsend_wrapper) +SYSCALL(sys_mq_timedreceive,sys_mq_timedreceive,compat_sys_mq_timedreceive_wrapper) +SYSCALL(sys_mq_notify,sys_mq_notify,compat_sys_mq_notify_wrapper) /* 275 */ +SYSCALL(sys_mq_getsetattr,sys_mq_getsetattr,compat_sys_mq_getsetattr_wrapper) +NI_SYSCALL /* reserved for kexec */ +SYSCALL(sys_add_key,sys_add_key,compat_sys_add_key_wrapper) +SYSCALL(sys_request_key,sys_request_key,compat_sys_request_key_wrapper) +SYSCALL(sys_keyctl,sys_keyctl,compat_sys_keyctl) /* 280 */ +SYSCALL(sys_waitid,sys_waitid,compat_sys_waitid_wrapper) diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c new file mode 100644 index 000000000000..061e81138dc2 --- /dev/null +++ b/arch/s390/kernel/time.c @@ -0,0 +1,382 @@ +/* + * arch/s390/kernel/time.c + * Time of day based timer functions. + * + * 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/config.h> +#include <linux/errno.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/param.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/interrupt.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/types.h> +#include <linux/profile.h> +#include <linux/timex.h> +#include <linux/notifier.h> + +#include <asm/uaccess.h> +#include <asm/delay.h> +#include <asm/s390_ext.h> +#include <asm/div64.h> +#include <asm/irq.h> +#include <asm/timer.h> + +/* change this if you have some constant time drift */ +#define USECS_PER_JIFFY ((unsigned long) 1000000/HZ) +#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12) + +/* + * Create a small time difference between the timer interrupts + * on the different cpus to avoid lock contention. + */ +#define CPU_DEVIATION (smp_processor_id() << 12) + +#define TICK_SIZE tick + +u64 jiffies_64 = INITIAL_JIFFIES; + +EXPORT_SYMBOL(jiffies_64); + +static ext_int_info_t ext_int_info_cc; +static u64 init_timer_cc; +static u64 jiffies_timer_cc; +static u64 xtime_cc; + +extern unsigned long wall_jiffies; + +/* + * Scheduler clock - returns current time in nanosec units. + */ +unsigned long long sched_clock(void) +{ + return ((get_clock() - jiffies_timer_cc) * 1000) >> 12; +} + +void tod_to_timeval(__u64 todval, struct timespec *xtime) +{ + unsigned long long sec; + + sec = todval >> 12; + do_div(sec, 1000000); + xtime->tv_sec = sec; + todval -= (sec * 1000000) << 12; + xtime->tv_nsec = ((todval * 1000) >> 12); +} + +static inline unsigned long do_gettimeoffset(void) +{ + __u64 now; + + now = (get_clock() - jiffies_timer_cc) >> 12; + /* We require the offset from the latest update of xtime */ + now -= (__u64) wall_jiffies*USECS_PER_JIFFY; + return (unsigned long) now; +} + +/* + * This version of gettimeofday has microsecond resolution. + */ +void do_gettimeofday(struct timeval *tv) +{ + unsigned long flags; + unsigned long seq; + unsigned long usec, sec; + + do { + seq = read_seqbegin_irqsave(&xtime_lock, flags); + + sec = xtime.tv_sec; + usec = xtime.tv_nsec / 1000 + do_gettimeoffset(); + } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); + + while (usec >= 1000000) { + usec -= 1000000; + sec++; + } + + tv->tv_sec = sec; + tv->tv_usec = usec; +} + +EXPORT_SYMBOL(do_gettimeofday); + +int do_settimeofday(struct timespec *tv) +{ + time_t wtm_sec, sec = tv->tv_sec; + long wtm_nsec, nsec = tv->tv_nsec; + + if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) + return -EINVAL; + + write_seqlock_irq(&xtime_lock); + /* This is revolting. We need to set the xtime.tv_nsec + * 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! + */ + nsec -= do_gettimeoffset() * 1000; + + wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); + wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); + + set_normalized_timespec(&xtime, sec, nsec); + set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); + + time_adjust = 0; /* stop active adjtime() */ + time_status |= STA_UNSYNC; + time_maxerror = NTP_PHASE_LIMIT; + time_esterror = NTP_PHASE_LIMIT; + write_sequnlock_irq(&xtime_lock); + clock_was_set(); + return 0; +} + +EXPORT_SYMBOL(do_settimeofday); + + +#ifdef CONFIG_PROFILING +#define s390_do_profile(regs) profile_tick(CPU_PROFILING, regs) +#else +#define s390_do_profile(regs) do { ; } while(0) +#endif /* CONFIG_PROFILING */ + + +/* + * timer_interrupt() needs to keep up the real-time clock, + * as well as call the "do_timer()" routine every clocktick + */ +void account_ticks(struct pt_regs *regs) +{ + __u64 tmp; + __u32 ticks, xticks; + + /* Calculate how many ticks have passed. */ + if (S390_lowcore.int_clock < S390_lowcore.jiffy_timer) { + /* + * We have to program the clock comparator even if + * no tick has passed. That happens if e.g. an i/o + * interrupt wakes up an idle processor that has + * switched off its hz timer. + */ + tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION; + asm volatile ("SCKC %0" : : "m" (tmp)); + return; + } + tmp = S390_lowcore.int_clock - S390_lowcore.jiffy_timer; + if (tmp >= 2*CLK_TICKS_PER_JIFFY) { /* more than two ticks ? */ + ticks = __div(tmp, CLK_TICKS_PER_JIFFY) + 1; + S390_lowcore.jiffy_timer += + CLK_TICKS_PER_JIFFY * (__u64) ticks; + } else if (tmp >= CLK_TICKS_PER_JIFFY) { + ticks = 2; + S390_lowcore.jiffy_timer += 2*CLK_TICKS_PER_JIFFY; + } else { + ticks = 1; + S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY; + } + + /* set clock comparator for next tick */ + tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION; + asm volatile ("SCKC %0" : : "m" (tmp)); + +#ifdef CONFIG_SMP + /* + * Do not rely on the boot cpu to do the calls to do_timer. + * Spread it over all cpus instead. + */ + write_seqlock(&xtime_lock); + if (S390_lowcore.jiffy_timer > xtime_cc) { + tmp = S390_lowcore.jiffy_timer - xtime_cc; + if (tmp >= 2*CLK_TICKS_PER_JIFFY) { + xticks = __div(tmp, CLK_TICKS_PER_JIFFY); + xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY; + } else { + xticks = 1; + xtime_cc += CLK_TICKS_PER_JIFFY; + } + while (xticks--) + do_timer(regs); + } + write_sequnlock(&xtime_lock); +#else + for (xticks = ticks; xticks > 0; xticks--) + do_timer(regs); +#endif + +#ifdef CONFIG_VIRT_CPU_ACCOUNTING + account_user_vtime(current); +#else + while (ticks--) + update_process_times(user_mode(regs)); +#endif + + s390_do_profile(regs); +} + +#ifdef CONFIG_NO_IDLE_HZ + +#ifdef CONFIG_NO_IDLE_HZ_INIT +int sysctl_hz_timer = 0; +#else +int sysctl_hz_timer = 1; +#endif + +/* + * Stop the HZ tick on the current CPU. + * Only cpu_idle may call this function. + */ +static inline void stop_hz_timer(void) +{ + __u64 timer; + + if (sysctl_hz_timer != 0) + return; + + cpu_set(smp_processor_id(), nohz_cpu_mask); + + /* + * Leave the clock comparator set up for the next timer + * tick if either rcu or a softirq is pending. + */ + if (rcu_pending(smp_processor_id()) || local_softirq_pending()) { + cpu_clear(smp_processor_id(), nohz_cpu_mask); + return; + } + + /* + * This cpu is going really idle. Set up the clock comparator + * for the next event. + */ + timer = (__u64) (next_timer_interrupt() - jiffies) + jiffies_64; + timer = jiffies_timer_cc + timer * CLK_TICKS_PER_JIFFY; + asm volatile ("SCKC %0" : : "m" (timer)); +} + +/* + * Start the HZ tick on the current CPU. + * Only cpu_idle may call this function. + */ +static inline void start_hz_timer(void) +{ + if (!cpu_isset(smp_processor_id(), nohz_cpu_mask)) + return; + account_ticks(__KSTK_PTREGS(current)); + cpu_clear(smp_processor_id(), nohz_cpu_mask); +} + +static int nohz_idle_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + switch (action) { + case CPU_IDLE: + stop_hz_timer(); + break; + case CPU_NOT_IDLE: + start_hz_timer(); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block nohz_idle_nb = { + .notifier_call = nohz_idle_notify, +}; + +void __init nohz_init(void) +{ + if (register_idle_notifier(&nohz_idle_nb)) + panic("Couldn't register idle notifier"); +} + +#endif + +/* + * Start the clock comparator on the current CPU. + */ +void init_cpu_timer(void) +{ + unsigned long cr0; + __u64 timer; + + timer = jiffies_timer_cc + jiffies_64 * CLK_TICKS_PER_JIFFY; + S390_lowcore.jiffy_timer = timer + CLK_TICKS_PER_JIFFY; + timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION; + asm volatile ("SCKC %0" : : "m" (timer)); + /* allow clock comparator timer interrupt */ + __ctl_store(cr0, 0, 0); + cr0 |= 0x800; + __ctl_load(cr0, 0, 0); +} + +extern void vtime_init(void); + +/* + * Initialize the TOD clock and the CPU timer of + * the boot cpu. + */ +void __init time_init(void) +{ + __u64 set_time_cc; + int cc; + + /* kick the TOD clock */ + asm volatile ("STCK 0(%1)\n\t" + "IPM %0\n\t" + "SRL %0,28" : "=r" (cc) : "a" (&init_timer_cc) + : "memory", "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; + } + jiffies_timer_cc = init_timer_cc - jiffies_64 * CLK_TICKS_PER_JIFFY; + + /* set xtime */ + xtime_cc = init_timer_cc + CLK_TICKS_PER_JIFFY; + set_time_cc = init_timer_cc - 0x8126d60e46000000LL + + (0x3c26700LL*1000000*4096); + tod_to_timeval(set_time_cc, &xtime); + set_normalized_timespec(&wall_to_monotonic, + -xtime.tv_sec, -xtime.tv_nsec); + + /* request the clock comparator external interrupt */ + if (register_early_external_interrupt(0x1004, 0, + &ext_int_info_cc) != 0) + panic("Couldn't request external interrupt 0x1004"); + + init_cpu_timer(); + +#ifdef CONFIG_NO_IDLE_HZ + nohz_init(); +#endif + +#ifdef CONFIG_VIRT_TIMER + vtime_init(); +#endif +} + diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c new file mode 100644 index 000000000000..8b90e9528b91 --- /dev/null +++ b/arch/s390/kernel/traps.c @@ -0,0 +1,738 @@ +/* + * 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/interrupt.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/kallsyms.h> + +#include <asm/system.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/atomic.h> +#include <asm/mathemu.h> +#include <asm/cpcmd.h> +#include <asm/s390_ext.h> +#include <asm/lowcore.h> +#include <asm/debug.h> + +/* 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_protection_exception; +extern pgm_check_handler_t do_dat_exception; +extern pgm_check_handler_t do_pseudo_page_fault; +#ifdef CONFIG_PFAULT +extern int pfault_init(void); +extern void pfault_fini(void); +extern void pfault_interrupt(struct pt_regs *regs, __u16 error_code); +static ext_int_info_t ext_int_pfault; +#endif +extern pgm_check_handler_t do_monitor_call; + +#define stack_pointer ({ void **sp; asm("la %0,0(15)" : "=&d" (sp)); sp; }) + +#ifndef CONFIG_ARCH_S390X +#define FOURLONG "%08lx %08lx %08lx %08lx\n" +static int kstack_depth_to_print = 12; +#else /* CONFIG_ARCH_S390X */ +#define FOURLONG "%016lx %016lx %016lx %016lx\n" +static int kstack_depth_to_print = 20; +#endif /* CONFIG_ARCH_S390X */ + +/* + * For show_trace we have tree different stack to consider: + * - the panic stack which is used if the kernel stack has overflown + * - the asynchronous interrupt stack (cpu related) + * - the synchronous kernel stack (process related) + * The stack trace can start at any of the three stack and can potentially + * touch all of them. The order is: panic stack, async stack, sync stack. + */ +static unsigned long +__show_trace(unsigned long sp, unsigned long low, unsigned long high) +{ + struct stack_frame *sf; + struct pt_regs *regs; + + while (1) { + sp = sp & PSW_ADDR_INSN; + if (sp < low || sp > high - sizeof(*sf)) + return sp; + sf = (struct stack_frame *) sp; + printk("([<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN); + print_symbol("%s)\n", sf->gprs[8] & PSW_ADDR_INSN); + /* Follow the backchain. */ + while (1) { + low = sp; + sp = sf->back_chain & PSW_ADDR_INSN; + if (!sp) + break; + if (sp <= low || sp > high - sizeof(*sf)) + return sp; + sf = (struct stack_frame *) sp; + printk(" [<%016lx>] ", sf->gprs[8] & PSW_ADDR_INSN); + print_symbol("%s\n", sf->gprs[8] & PSW_ADDR_INSN); + } + /* Zero backchain detected, check for interrupt frame. */ + sp = (unsigned long) (sf + 1); + if (sp <= low || sp > high - sizeof(*regs)) + return sp; + regs = (struct pt_regs *) sp; + printk(" [<%016lx>] ", regs->psw.addr & PSW_ADDR_INSN); + print_symbol("%s\n", regs->psw.addr & PSW_ADDR_INSN); + low = sp; + sp = regs->gprs[15]; + } +} + +void show_trace(struct task_struct *task, unsigned long * stack) +{ + register unsigned long __r15 asm ("15"); + unsigned long sp; + + sp = (unsigned long) stack; + if (!sp) + sp = task ? task->thread.ksp : __r15; + printk("Call Trace:\n"); +#ifdef CONFIG_CHECK_STACK + sp = __show_trace(sp, S390_lowcore.panic_stack - 4096, + S390_lowcore.panic_stack); +#endif + sp = __show_trace(sp, S390_lowcore.async_stack - ASYNC_SIZE, + S390_lowcore.async_stack); + if (task) + __show_trace(sp, (unsigned long) task->thread_info, + (unsigned long) task->thread_info + THREAD_SIZE); + else + __show_trace(sp, S390_lowcore.thread_info, + S390_lowcore.thread_info + THREAD_SIZE); + printk("\n"); +} + +void show_stack(struct task_struct *task, unsigned long *sp) +{ + register unsigned long * __r15 asm ("15"); + unsigned long *stack; + int i; + + // debugging aid: "show_stack(NULL);" prints the + // back trace for this cpu. + + if (!sp) + sp = task ? (unsigned long *) task->thread.ksp : __r15; + + stack = sp; + for (i = 0; i < kstack_depth_to_print; i++) { + if (((addr_t) stack & (THREAD_SIZE-1)) == 0) + break; + if (i && ((i * sizeof (long) % 32) == 0)) + printk("\n "); + printk("%p ", (void *)*stack++); + } + printk("\n"); + show_trace(task, sp); +} + +/* + * The architecture-independent dump_stack generator + */ +void dump_stack(void) +{ + show_stack(0, 0); +} + +EXPORT_SYMBOL(dump_stack); + +void show_registers(struct pt_regs *regs) +{ + mm_segment_t old_fs; + char *mode; + int i; + + mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl"; + printk("%s PSW : %p %p", + mode, (void *) regs->psw.mask, + (void *) regs->psw.addr); + print_symbol(" (%s)\n", regs->psw.addr & PSW_ADDR_INSN); + printk("%s GPRS: " FOURLONG, mode, + regs->gprs[0], regs->gprs[1], regs->gprs[2], regs->gprs[3]); + printk(" " FOURLONG, + regs->gprs[4], regs->gprs[5], regs->gprs[6], regs->gprs[7]); + printk(" " FOURLONG, + regs->gprs[8], regs->gprs[9], regs->gprs[10], regs->gprs[11]); + printk(" " FOURLONG, + regs->gprs[12], regs->gprs[13], regs->gprs[14], regs->gprs[15]); + +#if 0 + /* FIXME: this isn't needed any more but it changes the ksymoops + * input. To remove or not to remove ... */ + save_access_regs(regs->acrs); + printk("%s ACRS: %08x %08x %08x %08x\n", mode, + regs->acrs[0], regs->acrs[1], regs->acrs[2], regs->acrs[3]); + printk(" %08x %08x %08x %08x\n", + regs->acrs[4], regs->acrs[5], regs->acrs[6], regs->acrs[7]); + printk(" %08x %08x %08x %08x\n", + regs->acrs[8], regs->acrs[9], regs->acrs[10], regs->acrs[11]); + printk(" %08x %08x %08x %08x\n", + regs->acrs[12], regs->acrs[13], regs->acrs[14], regs->acrs[15]); +#endif + + /* + * Print the first 20 byte of the instruction stream at the + * time of the fault. + */ + old_fs = get_fs(); + if (regs->psw.mask & PSW_MASK_PSTATE) + set_fs(USER_DS); + else + set_fs(KERNEL_DS); + printk("%s Code: ", mode); + for (i = 0; i < 20; i++) { + unsigned char c; + if (__get_user(c, (char __user *)(regs->psw.addr + i))) { + printk(" Bad PSW."); + break; + } + printk("%02x ", c); + } + set_fs(old_fs); + + printk("\n"); +} + +/* This is called from fs/proc/array.c */ +char *task_show_regs(struct task_struct *task, char *buffer) +{ + struct pt_regs *regs; + + regs = __KSTK_PTREGS(task); + buffer += sprintf(buffer, "task: %p, ksp: %p\n", + task, (void *)task->thread.ksp); + buffer += sprintf(buffer, "User PSW : %p %p\n", + (void *) regs->psw.mask, (void *)regs->psw.addr); + + buffer += sprintf(buffer, "User GPRS: " FOURLONG, + regs->gprs[0], regs->gprs[1], + regs->gprs[2], regs->gprs[3]); + buffer += sprintf(buffer, " " FOURLONG, + regs->gprs[4], regs->gprs[5], + regs->gprs[6], regs->gprs[7]); + buffer += sprintf(buffer, " " FOURLONG, + regs->gprs[8], regs->gprs[9], + regs->gprs[10], regs->gprs[11]); + buffer += sprintf(buffer, " " FOURLONG, + regs->gprs[12], regs->gprs[13], + regs->gprs[14], regs->gprs[15]); + buffer += sprintf(buffer, "User ACRS: %08x %08x %08x %08x\n", + task->thread.acrs[0], task->thread.acrs[1], + task->thread.acrs[2], task->thread.acrs[3]); + buffer += sprintf(buffer, " %08x %08x %08x %08x\n", + task->thread.acrs[4], task->thread.acrs[5], + task->thread.acrs[6], task->thread.acrs[7]); + buffer += sprintf(buffer, " %08x %08x %08x %08x\n", + task->thread.acrs[8], task->thread.acrs[9], + task->thread.acrs[10], task->thread.acrs[11]); + buffer += sprintf(buffer, " %08x %08x %08x %08x\n", + task->thread.acrs[12], task->thread.acrs[13], + task->thread.acrs[14], task->thread.acrs[15]); + return buffer; +} + +DEFINE_SPINLOCK(die_lock); + +void die(const char * str, struct pt_regs * regs, long err) +{ + static int die_counter; + + debug_stop_all(); + console_verbose(); + spin_lock_irq(&die_lock); + bust_spinlocks(1); + printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); + show_regs(regs); + bust_spinlocks(0); + spin_unlock_irq(&die_lock); + if (in_interrupt()) + panic("Fatal exception in interrupt"); + if (panic_on_oops) + panic("Fatal exception: panic_on_oops"); + do_exit(SIGSEGV); +} + +static void inline +report_user_fault(long interruption_code, struct pt_regs *regs) +{ +#if defined(CONFIG_SYSCTL) + if (!sysctl_userprocess_debug) + return; +#endif +#if defined(CONFIG_SYSCTL) || defined(CONFIG_PROCESS_DEBUG) + printk("User process fault: interruption code 0x%lX\n", + interruption_code); + show_regs(regs); +#endif +} + +static void inline do_trap(long interruption_code, int signr, char *str, + struct pt_regs *regs, siginfo_t *info) +{ + /* + * We got all needed information from the lowcore and can + * now safely switch on interrupts. + */ + if (regs->psw.mask & PSW_MASK_PSTATE) + local_irq_enable(); + + if (regs->psw.mask & PSW_MASK_PSTATE) { + struct task_struct *tsk = current; + + tsk->thread.trap_no = interruption_code & 0xffff; + force_sig_info(signr, info, tsk); + report_user_fault(interruption_code, regs); + } else { + const struct exception_table_entry *fixup; + fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN); + if (fixup) + regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE; + else + die(str, regs, interruption_code); + } +} + +static inline void *get_check_address(struct pt_regs *regs) +{ + return (void *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN); +} + +void do_single_step(struct pt_regs *regs) +{ + if ((current->ptrace & PT_PTRACED) != 0) + force_sig(SIGTRAP, current); +} + +asmlinkage void +default_trap_handler(struct pt_regs * regs, long interruption_code) +{ + if (regs->psw.mask & PSW_MASK_PSTATE) { + local_irq_enable(); + do_exit(SIGSEGV); + report_user_fault(interruption_code, regs); + } else + die("Unknown program exception", regs, interruption_code); +} + +#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); \ +} + +DO_ERROR_INFO(SIGILL, "addressing exception", addressing_exception, + ILL_ILLADR, get_check_address(regs)) +DO_ERROR_INFO(SIGILL, "execute exception", execute_exception, + ILL_ILLOPN, get_check_address(regs)) +DO_ERROR_INFO(SIGFPE, "fixpoint divide exception", divide_exception, + FPE_INTDIV, get_check_address(regs)) +DO_ERROR_INFO(SIGFPE, "fixpoint overflow exception", overflow_exception, + FPE_INTOVF, get_check_address(regs)) +DO_ERROR_INFO(SIGFPE, "HFP overflow exception", hfp_overflow_exception, + FPE_FLTOVF, get_check_address(regs)) +DO_ERROR_INFO(SIGFPE, "HFP underflow exception", hfp_underflow_exception, + FPE_FLTUND, get_check_address(regs)) +DO_ERROR_INFO(SIGFPE, "HFP significance exception", hfp_significance_exception, + FPE_FLTRES, get_check_address(regs)) +DO_ERROR_INFO(SIGFPE, "HFP divide exception", hfp_divide_exception, + FPE_FLTDIV, get_check_address(regs)) +DO_ERROR_INFO(SIGFPE, "HFP square root exception", hfp_sqrt_exception, + FPE_FLTINV, get_check_address(regs)) +DO_ERROR_INFO(SIGILL, "operand exception", operand_exception, + ILL_ILLOPN, get_check_address(regs)) +DO_ERROR_INFO(SIGILL, "privileged operation", privileged_op, + ILL_PRVOPC, get_check_address(regs)) +DO_ERROR_INFO(SIGILL, "special operation exception", special_op_exception, + ILL_ILLOPN, get_check_address(regs)) +DO_ERROR_INFO(SIGILL, "translation exception", translation_exception, + ILL_ILLOPN, get_check_address(regs)) + +static inline void +do_fp_trap(struct pt_regs *regs, void *location, + int fpc, long interruption_code) +{ + siginfo_t si; + + si.si_signo = SIGFPE; + si.si_errno = 0; + si.si_addr = location; + si.si_code = 0; + /* FPC[2] is Data Exception Code */ + if ((fpc & 0x00000300) == 0) { + /* bits 6 and 7 of DXC are 0 iff IEEE exception */ + if (fpc & 0x8000) /* invalid fp operation */ + si.si_code = FPE_FLTINV; + else if (fpc & 0x4000) /* div by 0 */ + si.si_code = FPE_FLTDIV; + else if (fpc & 0x2000) /* overflow */ + si.si_code = FPE_FLTOVF; + else if (fpc & 0x1000) /* underflow */ + si.si_code = FPE_FLTUND; + else if (fpc & 0x0800) /* inexact */ + si.si_code = FPE_FLTRES; + } + current->thread.ieee_instruction_pointer = (addr_t) location; + do_trap(interruption_code, SIGFPE, + "floating point exception", regs, &si); +} + +asmlinkage void illegal_op(struct pt_regs * regs, long interruption_code) +{ + siginfo_t info; + __u8 opcode[6]; + __u16 *location; + int signal = 0; + + location = (__u16 *) get_check_address(regs); + + /* + * We got all needed information from the lowcore and can + * now safely switch on interrupts. + */ + if (regs->psw.mask & PSW_MASK_PSTATE) + local_irq_enable(); + + if (regs->psw.mask & PSW_MASK_PSTATE) { + get_user(*((__u16 *) opcode), (__u16 __user *) location); + if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) { + if (current->ptrace & PT_PTRACED) + force_sig(SIGTRAP, current); + else + signal = SIGILL; +#ifdef CONFIG_MATHEMU + } else if (opcode[0] == 0xb3) { + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_b3(opcode, regs); + } else if (opcode[0] == 0xed) { + get_user(*((__u32 *) (opcode+2)), + (__u32 *)(location+1)); + signal = math_emu_ed(opcode, regs); + } else if (*((__u16 *) opcode) == 0xb299) { + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_srnm(opcode, regs); + } else if (*((__u16 *) opcode) == 0xb29c) { + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_stfpc(opcode, regs); + } else if (*((__u16 *) opcode) == 0xb29d) { + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_lfpc(opcode, regs); +#endif + } else + signal = SIGILL; + } else + signal = SIGILL; + +#ifdef CONFIG_MATHEMU + if (signal == SIGFPE) + do_fp_trap(regs, location, + current->thread.fp_regs.fpc, interruption_code); + else if (signal == SIGSEGV) { + info.si_signo = signal; + info.si_errno = 0; + info.si_code = SEGV_MAPERR; + info.si_addr = (void *) location; + do_trap(interruption_code, signal, + "user address fault", regs, &info); + } else +#endif + if (signal) { + info.si_signo = signal; + info.si_errno = 0; + info.si_code = ILL_ILLOPC; + info.si_addr = (void *) location; + do_trap(interruption_code, signal, + "illegal operation", regs, &info); + } +} + + +#ifdef CONFIG_MATHEMU +asmlinkage void +specification_exception(struct pt_regs * regs, long interruption_code) +{ + __u8 opcode[6]; + __u16 *location = NULL; + int signal = 0; + + location = (__u16 *) get_check_address(regs); + + /* + * We got all needed information from the lowcore and can + * now safely switch on interrupts. + */ + if (regs->psw.mask & PSW_MASK_PSTATE) + local_irq_enable(); + + if (regs->psw.mask & PSW_MASK_PSTATE) { + get_user(*((__u16 *) opcode), location); + switch (opcode[0]) { + case 0x28: /* LDR Rx,Ry */ + signal = math_emu_ldr(opcode); + break; + case 0x38: /* LER Rx,Ry */ + signal = math_emu_ler(opcode); + break; + case 0x60: /* STD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_std(opcode, regs); + break; + case 0x68: /* LD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_ld(opcode, regs); + break; + case 0x70: /* STE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_ste(opcode, regs); + break; + case 0x78: /* LE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_le(opcode, regs); + break; + default: + signal = SIGILL; + break; + } + } else + signal = SIGILL; + + if (signal == SIGFPE) + do_fp_trap(regs, location, + current->thread.fp_regs.fpc, interruption_code); + else if (signal) { + siginfo_t info; + info.si_signo = signal; + info.si_errno = 0; + info.si_code = ILL_ILLOPN; + info.si_addr = location; + do_trap(interruption_code, signal, + "specification exception", regs, &info); + } +} +#else +DO_ERROR_INFO(SIGILL, "specification exception", specification_exception, + ILL_ILLOPN, get_check_address(regs)); +#endif + +asmlinkage void data_exception(struct pt_regs * regs, long interruption_code) +{ + __u16 *location; + int signal = 0; + + location = (__u16 *) get_check_address(regs); + + /* + * We got all needed information from the lowcore and can + * now safely switch on interrupts. + */ + if (regs->psw.mask & PSW_MASK_PSTATE) + local_irq_enable(); + + if (MACHINE_HAS_IEEE) + __asm__ volatile ("stfpc %0\n\t" + : "=m" (current->thread.fp_regs.fpc)); + +#ifdef CONFIG_MATHEMU + else if (regs->psw.mask & PSW_MASK_PSTATE) { + __u8 opcode[6]; + get_user(*((__u16 *) opcode), location); + switch (opcode[0]) { + case 0x28: /* LDR Rx,Ry */ + signal = math_emu_ldr(opcode); + break; + case 0x38: /* LER Rx,Ry */ + signal = math_emu_ler(opcode); + break; + case 0x60: /* STD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_std(opcode, regs); + break; + case 0x68: /* LD R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_ld(opcode, regs); + break; + case 0x70: /* STE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_ste(opcode, regs); + break; + case 0x78: /* LE R,D(X,B) */ + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_le(opcode, regs); + break; + case 0xb3: + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_b3(opcode, regs); + break; + case 0xed: + get_user(*((__u32 *) (opcode+2)), + (__u32 *)(location+1)); + signal = math_emu_ed(opcode, regs); + break; + case 0xb2: + if (opcode[1] == 0x99) { + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_srnm(opcode, regs); + } else if (opcode[1] == 0x9c) { + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_stfpc(opcode, regs); + } else if (opcode[1] == 0x9d) { + get_user(*((__u16 *) (opcode+2)), location+1); + signal = math_emu_lfpc(opcode, regs); + } else + signal = SIGILL; + break; + default: + signal = SIGILL; + break; + } + } +#endif + if (current->thread.fp_regs.fpc & FPC_DXC_MASK) + signal = SIGFPE; + else + signal = SIGILL; + if (signal == SIGFPE) + do_fp_trap(regs, location, + current->thread.fp_regs.fpc, interruption_code); + else if (signal) { + siginfo_t info; + info.si_signo = signal; + info.si_errno = 0; + info.si_code = ILL_ILLOPN; + info.si_addr = location; + do_trap(interruption_code, signal, + "data exception", regs, &info); + } +} + +asmlinkage void space_switch_exception(struct pt_regs * regs, long int_code) +{ + siginfo_t info; + + /* Set user psw back to home space mode. */ + if (regs->psw.mask & PSW_MASK_PSTATE) + regs->psw.mask |= PSW_ASC_HOME; + /* Send SIGILL. */ + info.si_signo = SIGILL; + info.si_errno = 0; + info.si_code = ILL_PRVOPC; + info.si_addr = get_check_address(regs); + do_trap(int_code, SIGILL, "space switch event", regs, &info); +} + +asmlinkage void kernel_stack_overflow(struct pt_regs * regs) +{ + die("Kernel stack overflow", regs, 0); + panic("Corrupt kernel stack, can't continue."); +} + + +/* 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[4] = &do_protection_exception; + pgm_check_table[5] = &addressing_exception; + pgm_check_table[6] = &specification_exception; + pgm_check_table[7] = &data_exception; + pgm_check_table[8] = &overflow_exception; + pgm_check_table[9] = ÷_exception; + pgm_check_table[0x0A] = &overflow_exception; + pgm_check_table[0x0B] = ÷_exception; + pgm_check_table[0x0C] = &hfp_overflow_exception; + pgm_check_table[0x0D] = &hfp_underflow_exception; + pgm_check_table[0x0E] = &hfp_significance_exception; + pgm_check_table[0x0F] = &hfp_divide_exception; + pgm_check_table[0x10] = &do_dat_exception; + pgm_check_table[0x11] = &do_dat_exception; + pgm_check_table[0x12] = &translation_exception; + pgm_check_table[0x13] = &special_op_exception; +#ifndef CONFIG_ARCH_S390X + pgm_check_table[0x14] = &do_pseudo_page_fault; +#else /* CONFIG_ARCH_S390X */ + pgm_check_table[0x38] = &do_dat_exception; + pgm_check_table[0x39] = &do_dat_exception; + pgm_check_table[0x3A] = &do_dat_exception; + pgm_check_table[0x3B] = &do_dat_exception; +#endif /* CONFIG_ARCH_S390X */ + pgm_check_table[0x15] = &operand_exception; + pgm_check_table[0x1C] = &space_switch_exception; + pgm_check_table[0x1D] = &hfp_sqrt_exception; + pgm_check_table[0x40] = &do_monitor_call; + + if (MACHINE_IS_VM) { + /* + * First try to get pfault pseudo page faults going. + * If this isn't available turn on pagex page faults. + */ +#ifdef CONFIG_PFAULT + /* request the 0x2603 external interrupt */ + if (register_early_external_interrupt(0x2603, pfault_interrupt, + &ext_int_pfault) != 0) + panic("Couldn't request external interrupt 0x2603"); + + if (pfault_init() == 0) + return; + + /* Tough luck, no pfault. */ + unregister_early_external_interrupt(0x2603, pfault_interrupt, + &ext_int_pfault); +#endif +#ifndef CONFIG_ARCH_S390X + cpcmd("SET PAGEX ON", NULL, 0); +#endif + } +} diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S new file mode 100644 index 000000000000..89fdb3808bc0 --- /dev/null +++ b/arch/s390/kernel/vmlinux.lds.S @@ -0,0 +1,130 @@ +/* ld script to make s390 Linux kernel + * Written by Martin Schwidefsky (schwidefsky@de.ibm.com) + */ + +#include <asm-generic/vmlinux.lds.h> +#include <linux/config.h> + +#ifndef CONFIG_ARCH_S390X +OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390") +OUTPUT_ARCH(s390) +ENTRY(_start) +jiffies = jiffies_64 + 4; +#else +OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390") +OUTPUT_ARCH(s390:64-bit) +ENTRY(_start) +jiffies = jiffies_64; +#endif + +SECTIONS +{ + . = 0x00000000; + _text = .; /* Text and read-only data */ + .text : { + *(.text) + SCHED_TEXT + LOCK_TEXT + *(.fixup) + *(.gnu.warning) + } = 0x0700 + + _etext = .; /* End of text section */ + + . = ALIGN(16); /* Exception table */ + __start___ex_table = .; + __ex_table : { *(__ex_table) } + __stop___ex_table = .; + + RODATA + +#ifdef CONFIG_SHARED_KERNEL + . = ALIGN(1048576); /* VM shared segments are 1MB aligned */ + + _eshared = .; /* End of shareable data */ +#endif + + .data : { /* Data */ + *(.data) + CONSTRUCTORS + } + + . = ALIGN(4096); + __nosave_begin = .; + .data_nosave : { *(.data.nosave) } + . = ALIGN(4096); + __nosave_end = .; + + . = ALIGN(4096); + .data.page_aligned : { *(.data.idt) } + + . = ALIGN(32); + .data.cacheline_aligned : { *(.data.cacheline_aligned) } + + _edata = .; /* End of data section */ + + . = ALIGN(8192); /* init_task */ + .data.init_task : { *(.data.init_task) } + + /* will be freed after init */ + . = ALIGN(4096); /* Init code and data */ + __init_begin = .; + .init.text : { + _sinittext = .; + *(.init.text) + _einittext = .; + } + .init.data : { *(.init.data) } + . = ALIGN(256); + __setup_start = .; + .init.setup : { *(.init.setup) } + __setup_end = .; + __initcall_start = .; + .initcall.init : { + *(.initcall1.init) + *(.initcall2.init) + *(.initcall3.init) + *(.initcall4.init) + *(.initcall5.init) + *(.initcall6.init) + *(.initcall7.init) + } + __initcall_end = .; + __con_initcall_start = .; + .con_initcall.init : { *(.con_initcall.init) } + __con_initcall_end = .; + SECURITY_INIT + . = ALIGN(256); + __initramfs_start = .; + .init.ramfs : { *(.init.initramfs) } + . = ALIGN(2); + __initramfs_end = .; + . = ALIGN(256); + __per_cpu_start = .; + .data.percpu : { *(.data.percpu) } + __per_cpu_end = .; + . = ALIGN(4096); + __init_end = .; + /* freed after init ends here */ + + __bss_start = .; /* BSS */ + .bss : { *(.bss) } + . = ALIGN(2); + __bss_stop = .; + + _end = . ; + + /* Sections to be discarded */ + /DISCARD/ : { + *(.exitcall.exit) + } + + /* 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/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c new file mode 100644 index 000000000000..bb6cf02418a2 --- /dev/null +++ b/arch/s390/kernel/vtime.c @@ -0,0 +1,565 @@ +/* + * arch/s390/kernel/vtime.c + * Virtual cpu timer based timer functions. + * + * S390 version + * Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation + * Author(s): Jan Glauber <jan.glauber@de.ibm.com> + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/time.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/smp.h> +#include <linux/types.h> +#include <linux/timex.h> +#include <linux/notifier.h> +#include <linux/kernel_stat.h> +#include <linux/rcupdate.h> +#include <linux/posix-timers.h> + +#include <asm/s390_ext.h> +#include <asm/timer.h> + +#define VTIMER_MAGIC (TIMER_MAGIC + 1) +static ext_int_info_t ext_int_info_timer; +DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer); + +#ifdef CONFIG_VIRT_CPU_ACCOUNTING +/* + * Update process times based on virtual cpu times stored by entry.S + * to the lowcore fields user_timer, system_timer & steal_clock. + */ +void account_user_vtime(struct task_struct *tsk) +{ + cputime_t cputime; + __u64 timer, clock; + int rcu_user_flag; + + timer = S390_lowcore.last_update_timer; + clock = S390_lowcore.last_update_clock; + asm volatile (" STPT %0\n" /* Store current cpu timer value */ + " STCK %1" /* Store current tod clock value */ + : "=m" (S390_lowcore.last_update_timer), + "=m" (S390_lowcore.last_update_clock) ); + S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer; + S390_lowcore.steal_clock += S390_lowcore.last_update_clock - clock; + + cputime = S390_lowcore.user_timer >> 12; + rcu_user_flag = cputime != 0; + S390_lowcore.user_timer -= cputime << 12; + S390_lowcore.steal_clock -= cputime << 12; + account_user_time(tsk, cputime); + + cputime = S390_lowcore.system_timer >> 12; + S390_lowcore.system_timer -= cputime << 12; + S390_lowcore.steal_clock -= cputime << 12; + account_system_time(tsk, HARDIRQ_OFFSET, cputime); + + cputime = S390_lowcore.steal_clock; + if ((__s64) cputime > 0) { + cputime >>= 12; + S390_lowcore.steal_clock -= cputime << 12; + account_steal_time(tsk, cputime); + } + + run_local_timers(); + if (rcu_pending(smp_processor_id())) + rcu_check_callbacks(smp_processor_id(), rcu_user_flag); + scheduler_tick(); + run_posix_cpu_timers(tsk); +} + +/* + * Update process times based on virtual cpu times stored by entry.S + * to the lowcore fields user_timer, system_timer & steal_clock. + */ +void account_system_vtime(struct task_struct *tsk) +{ + cputime_t cputime; + __u64 timer; + + timer = S390_lowcore.last_update_timer; + asm volatile (" STPT %0" /* Store current cpu timer value */ + : "=m" (S390_lowcore.last_update_timer) ); + S390_lowcore.system_timer += timer - S390_lowcore.last_update_timer; + + cputime = S390_lowcore.system_timer >> 12; + S390_lowcore.system_timer -= cputime << 12; + S390_lowcore.steal_clock -= cputime << 12; + account_system_time(tsk, 0, cputime); +} + +static inline void set_vtimer(__u64 expires) +{ + __u64 timer; + + asm volatile (" STPT %0\n" /* Store current cpu timer value */ + " SPT %1" /* Set new value immediatly afterwards */ + : "=m" (timer) : "m" (expires) ); + S390_lowcore.system_timer += S390_lowcore.last_update_timer - timer; + S390_lowcore.last_update_timer = expires; + + /* store expire time for this CPU timer */ + per_cpu(virt_cpu_timer, smp_processor_id()).to_expire = expires; +} +#else +static inline void set_vtimer(__u64 expires) +{ + S390_lowcore.last_update_timer = expires; + asm volatile ("SPT %0" : : "m" (S390_lowcore.last_update_timer)); + + /* store expire time for this CPU timer */ + per_cpu(virt_cpu_timer, smp_processor_id()).to_expire = expires; +} +#endif + +static void start_cpu_timer(void) +{ + struct vtimer_queue *vt_list; + + vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + set_vtimer(vt_list->idle); +} + +static void stop_cpu_timer(void) +{ + __u64 done; + struct vtimer_queue *vt_list; + + vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + + /* nothing to do */ + if (list_empty(&vt_list->list)) { + vt_list->idle = VTIMER_MAX_SLICE; + goto fire; + } + + /* store progress */ + asm volatile ("STPT %0" : "=m" (done)); + + /* + * If done is negative we do not stop the CPU timer + * because we will get instantly an interrupt that + * will start the CPU timer again. + */ + if (done & 1LL<<63) + return; + else + vt_list->offset += vt_list->to_expire - done; + + /* save the actual expire value */ + vt_list->idle = done; + + /* + * We cannot halt the CPU timer, we just write a value that + * nearly never expires (only after 71 years) and re-write + * the stored expire value if we continue the timer + */ + fire: + set_vtimer(VTIMER_MAX_SLICE); +} + +/* + * Sorted add to a list. List is linear searched until first bigger + * element is found. + */ +static void list_add_sorted(struct vtimer_list *timer, struct list_head *head) +{ + struct vtimer_list *event; + + list_for_each_entry(event, head, entry) { + if (event->expires > timer->expires) { + list_add_tail(&timer->entry, &event->entry); + return; + } + } + list_add_tail(&timer->entry, head); +} + +/* + * Do the callback functions of expired vtimer events. + * Called from within the interrupt handler. + */ +static void do_callbacks(struct list_head *cb_list, struct pt_regs *regs) +{ + struct vtimer_queue *vt_list; + struct vtimer_list *event, *tmp; + void (*fn)(unsigned long, struct pt_regs*); + unsigned long data; + + if (list_empty(cb_list)) + return; + + vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + + list_for_each_entry_safe(event, tmp, cb_list, entry) { + fn = event->function; + data = event->data; + fn(data, regs); + + if (!event->interval) + /* delete one shot timer */ + list_del_init(&event->entry); + else { + /* move interval timer back to list */ + spin_lock(&vt_list->lock); + list_del_init(&event->entry); + list_add_sorted(event, &vt_list->list); + spin_unlock(&vt_list->lock); + } + } +} + +/* + * Handler for the virtual CPU timer. + */ +static void do_cpu_timer_interrupt(struct pt_regs *regs, __u16 error_code) +{ + int cpu; + __u64 next, delta; + struct vtimer_queue *vt_list; + struct vtimer_list *event, *tmp; + struct list_head *ptr; + /* the callback queue */ + struct list_head cb_list; + + INIT_LIST_HEAD(&cb_list); + cpu = smp_processor_id(); + vt_list = &per_cpu(virt_cpu_timer, cpu); + + /* walk timer list, fire all expired events */ + spin_lock(&vt_list->lock); + + if (vt_list->to_expire < VTIMER_MAX_SLICE) + vt_list->offset += vt_list->to_expire; + + list_for_each_entry_safe(event, tmp, &vt_list->list, entry) { + if (event->expires > vt_list->offset) + /* found first unexpired event, leave */ + break; + + /* re-charge interval timer, we have to add the offset */ + if (event->interval) + event->expires = event->interval + vt_list->offset; + + /* move expired timer to the callback queue */ + list_move_tail(&event->entry, &cb_list); + } + spin_unlock(&vt_list->lock); + do_callbacks(&cb_list, regs); + + /* next event is first in list */ + spin_lock(&vt_list->lock); + if (!list_empty(&vt_list->list)) { + ptr = vt_list->list.next; + event = list_entry(ptr, struct vtimer_list, entry); + next = event->expires - vt_list->offset; + + /* add the expired time from this interrupt handler + * and the callback functions + */ + asm volatile ("STPT %0" : "=m" (delta)); + delta = 0xffffffffffffffffLL - delta + 1; + vt_list->offset += delta; + next -= delta; + } else { + vt_list->offset = 0; + next = VTIMER_MAX_SLICE; + } + spin_unlock(&vt_list->lock); + set_vtimer(next); +} + +void init_virt_timer(struct vtimer_list *timer) +{ + timer->magic = VTIMER_MAGIC; + timer->function = NULL; + INIT_LIST_HEAD(&timer->entry); + spin_lock_init(&timer->lock); +} +EXPORT_SYMBOL(init_virt_timer); + +static inline int check_vtimer(struct vtimer_list *timer) +{ + if (timer->magic != VTIMER_MAGIC) + return -EINVAL; + return 0; +} + +static inline int vtimer_pending(struct vtimer_list *timer) +{ + return (!list_empty(&timer->entry)); +} + +/* + * this function should only run on the specified CPU + */ +static void internal_add_vtimer(struct vtimer_list *timer) +{ + unsigned long flags; + __u64 done; + struct vtimer_list *event; + struct vtimer_queue *vt_list; + + vt_list = &per_cpu(virt_cpu_timer, timer->cpu); + spin_lock_irqsave(&vt_list->lock, flags); + + if (timer->cpu != smp_processor_id()) + printk("internal_add_vtimer: BUG, running on wrong CPU"); + + /* if list is empty we only have to set the timer */ + if (list_empty(&vt_list->list)) { + /* reset the offset, this may happen if the last timer was + * just deleted by mod_virt_timer and the interrupt + * didn't happen until here + */ + vt_list->offset = 0; + goto fire; + } + + /* save progress */ + asm volatile ("STPT %0" : "=m" (done)); + + /* calculate completed work */ + done = vt_list->to_expire - done + vt_list->offset; + vt_list->offset = 0; + + list_for_each_entry(event, &vt_list->list, entry) + event->expires -= done; + + fire: + list_add_sorted(timer, &vt_list->list); + + /* get first element, which is the next vtimer slice */ + event = list_entry(vt_list->list.next, struct vtimer_list, entry); + + set_vtimer(event->expires); + spin_unlock_irqrestore(&vt_list->lock, flags); + /* release CPU aquired in prepare_vtimer or mod_virt_timer() */ + put_cpu(); +} + +static inline int prepare_vtimer(struct vtimer_list *timer) +{ + if (check_vtimer(timer) || !timer->function) { + printk("add_virt_timer: uninitialized timer\n"); + return -EINVAL; + } + + if (!timer->expires || timer->expires > VTIMER_MAX_SLICE) { + printk("add_virt_timer: invalid timer expire value!\n"); + return -EINVAL; + } + + if (vtimer_pending(timer)) { + printk("add_virt_timer: timer pending\n"); + return -EBUSY; + } + + timer->cpu = get_cpu(); + return 0; +} + +/* + * add_virt_timer - add an oneshot virtual CPU timer + */ +void add_virt_timer(void *new) +{ + struct vtimer_list *timer; + + timer = (struct vtimer_list *)new; + + if (prepare_vtimer(timer) < 0) + return; + + timer->interval = 0; + internal_add_vtimer(timer); +} +EXPORT_SYMBOL(add_virt_timer); + +/* + * add_virt_timer_int - add an interval virtual CPU timer + */ +void add_virt_timer_periodic(void *new) +{ + struct vtimer_list *timer; + + timer = (struct vtimer_list *)new; + + if (prepare_vtimer(timer) < 0) + return; + + timer->interval = timer->expires; + internal_add_vtimer(timer); +} +EXPORT_SYMBOL(add_virt_timer_periodic); + +/* + * If we change a pending timer the function must be called on the CPU + * where the timer is running on, e.g. by smp_call_function_on() + * + * The original mod_timer adds the timer if it is not pending. For compatibility + * we do the same. The timer will be added on the current CPU as a oneshot timer. + * + * returns whether it has modified a pending timer (1) or not (0) + */ +int mod_virt_timer(struct vtimer_list *timer, __u64 expires) +{ + struct vtimer_queue *vt_list; + unsigned long flags; + int cpu; + + if (check_vtimer(timer) || !timer->function) { + printk("mod_virt_timer: uninitialized timer\n"); + return -EINVAL; + } + + if (!expires || expires > VTIMER_MAX_SLICE) { + printk("mod_virt_timer: invalid expire range\n"); + return -EINVAL; + } + + /* + * This is a common optimization triggered by the + * networking code - if the timer is re-modified + * to be the same thing then just return: + */ + if (timer->expires == expires && vtimer_pending(timer)) + return 1; + + cpu = get_cpu(); + vt_list = &per_cpu(virt_cpu_timer, cpu); + + /* disable interrupts before test if timer is pending */ + spin_lock_irqsave(&vt_list->lock, flags); + + /* if timer isn't pending add it on the current CPU */ + if (!vtimer_pending(timer)) { + spin_unlock_irqrestore(&vt_list->lock, flags); + /* we do not activate an interval timer with mod_virt_timer */ + timer->interval = 0; + timer->expires = expires; + timer->cpu = cpu; + internal_add_vtimer(timer); + return 0; + } + + /* check if we run on the right CPU */ + if (timer->cpu != cpu) { + printk("mod_virt_timer: running on wrong CPU, check your code\n"); + spin_unlock_irqrestore(&vt_list->lock, flags); + put_cpu(); + return -EINVAL; + } + + list_del_init(&timer->entry); + timer->expires = expires; + + /* also change the interval if we have an interval timer */ + if (timer->interval) + timer->interval = expires; + + /* the timer can't expire anymore so we can release the lock */ + spin_unlock_irqrestore(&vt_list->lock, flags); + internal_add_vtimer(timer); + return 1; +} +EXPORT_SYMBOL(mod_virt_timer); + +/* + * delete a virtual timer + * + * returns whether the deleted timer was pending (1) or not (0) + */ +int del_virt_timer(struct vtimer_list *timer) +{ + unsigned long flags; + struct vtimer_queue *vt_list; + + if (check_vtimer(timer)) { + printk("del_virt_timer: timer not initialized\n"); + return -EINVAL; + } + + /* check if timer is pending */ + if (!vtimer_pending(timer)) + return 0; + + vt_list = &per_cpu(virt_cpu_timer, timer->cpu); + spin_lock_irqsave(&vt_list->lock, flags); + + /* we don't interrupt a running timer, just let it expire! */ + list_del_init(&timer->entry); + + /* last timer removed */ + if (list_empty(&vt_list->list)) { + vt_list->to_expire = 0; + vt_list->offset = 0; + } + + spin_unlock_irqrestore(&vt_list->lock, flags); + return 1; +} +EXPORT_SYMBOL(del_virt_timer); + +/* + * Start the virtual CPU timer on the current CPU. + */ +void init_cpu_vtimer(void) +{ + struct vtimer_queue *vt_list; + unsigned long cr0; + + /* kick the virtual timer */ + S390_lowcore.exit_timer = VTIMER_MAX_SLICE; + S390_lowcore.last_update_timer = VTIMER_MAX_SLICE; + asm volatile ("SPT %0" : : "m" (S390_lowcore.last_update_timer)); + asm volatile ("STCK %0" : "=m" (S390_lowcore.last_update_clock)); + __ctl_store(cr0, 0, 0); + cr0 |= 0x400; + __ctl_load(cr0, 0, 0); + + vt_list = &per_cpu(virt_cpu_timer, smp_processor_id()); + INIT_LIST_HEAD(&vt_list->list); + spin_lock_init(&vt_list->lock); + vt_list->to_expire = 0; + vt_list->offset = 0; + vt_list->idle = 0; + +} + +static int vtimer_idle_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + switch (action) { + case CPU_IDLE: + stop_cpu_timer(); + break; + case CPU_NOT_IDLE: + start_cpu_timer(); + break; + } + return NOTIFY_OK; +} + +static struct notifier_block vtimer_idle_nb = { + .notifier_call = vtimer_idle_notify, +}; + +void __init vtime_init(void) +{ + /* request the cpu timer external interrupt */ + if (register_early_external_interrupt(0x1005, do_cpu_timer_interrupt, + &ext_int_info_timer) != 0) + panic("Couldn't request external interrupt 0x1005"); + + if (register_idle_notifier(&vtimer_idle_nb)) + panic("Couldn't register idle notifier"); + + init_cpu_vtimer(); +} + |