Merge branch 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6
Linus Torvalds [Fri, 22 Sep 2006 19:50:35 +0000 (12:50 -0700)]
* 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6: (44 commits)
  [S390] hypfs crashes with invalid mount option.
  [S390] cio: subchannel evaluation function operates without lock
  [S390] cio: always query all paths on path verification.
  [S390] cio: update path groups on logical CHPID changes.
  [S390] cio: subchannels in no-path state.
  [S390] Replace nopav-message on VM.
  [S390] set modalias for ccw bus uevents.
  [S390] Get rid of DBG macro.
  [S390] Use alternative user-copy operations for new hardware.
  [S390] Make user-copy operations run-time configurable.
  [S390] Cleanup in signal handling code.
  [S390] Cleanup in page table related code.
  [S390] Linux API for writing z/VM APPLDATA Monitor records.
  [S390] xpram off by one error.
  [S390] Remove kexec experimental flag.
  [S390] cleanup appldata.
  [S390] fix typo in vmcp.
  [S390] Kernel stack overflow handling.
  [S390] qdio slsb processing state.
  [S390] Missing initialization in common i/o layer.
  ...

99 files changed:
MAINTAINERS
arch/s390/Kconfig
arch/s390/appldata/appldata.h
arch/s390/appldata/appldata_base.c
arch/s390/appldata/appldata_os.c
arch/s390/defconfig
arch/s390/hypfs/hypfs.h
arch/s390/hypfs/hypfs_diag.c
arch/s390/hypfs/hypfs_diag.h
arch/s390/hypfs/inode.c
arch/s390/kernel/Makefile
arch/s390/kernel/entry.S
arch/s390/kernel/entry64.S
arch/s390/kernel/head.S
arch/s390/kernel/head31.S
arch/s390/kernel/head64.S
arch/s390/kernel/ipl.c [new file with mode: 0644]
arch/s390/kernel/kprobes.c [new file with mode: 0644]
arch/s390/kernel/reipl.S
arch/s390/kernel/reipl64.S
arch/s390/kernel/reipl_diag.c [deleted file]
arch/s390/kernel/s390_ksyms.c
arch/s390/kernel/setup.c
arch/s390/kernel/signal.c
arch/s390/kernel/smp.c
arch/s390/kernel/traps.c
arch/s390/kernel/vmlinux.lds.S
arch/s390/lib/Makefile
arch/s390/lib/uaccess.S [deleted file]
arch/s390/lib/uaccess64.S [deleted file]
arch/s390/lib/uaccess_mvcos.c [new file with mode: 0644]
arch/s390/lib/uaccess_std.c [new file with mode: 0644]
arch/s390/mm/cmm.c
arch/s390/mm/fault.c
arch/s390/mm/init.c
drivers/base/hypervisor.c
drivers/s390/Kconfig
drivers/s390/block/dasd.c
drivers/s390/block/dasd_devmap.c
drivers/s390/block/dasd_eer.c
drivers/s390/block/dasd_int.h
drivers/s390/block/xpram.c
drivers/s390/char/Makefile
drivers/s390/char/monwriter.c [new file with mode: 0644]
drivers/s390/char/vmcp.c
drivers/s390/char/vmcp.h
drivers/s390/cio/chsc.c
drivers/s390/cio/cio.c
drivers/s390/cio/css.c
drivers/s390/cio/device.c
drivers/s390/cio/device_fsm.c
drivers/s390/cio/device_ops.c
drivers/s390/cio/device_pgid.c
drivers/s390/cio/qdio.c
drivers/s390/cio/qdio.h
drivers/s390/crypto/Makefile
drivers/s390/crypto/ap_bus.c [new file with mode: 0644]
drivers/s390/crypto/ap_bus.h [new file with mode: 0644]
drivers/s390/crypto/z90common.h [deleted file]
drivers/s390/crypto/z90crypt.h [deleted file]
drivers/s390/crypto/z90hardware.c [deleted file]
drivers/s390/crypto/z90main.c [deleted file]
drivers/s390/crypto/zcrypt_api.c [new file with mode: 0644]
drivers/s390/crypto/zcrypt_api.h [new file with mode: 0644]
drivers/s390/crypto/zcrypt_cca_key.h [new file with mode: 0644]
drivers/s390/crypto/zcrypt_cex2a.c [new file with mode: 0644]
drivers/s390/crypto/zcrypt_cex2a.h [new file with mode: 0644]
drivers/s390/crypto/zcrypt_error.h [new file with mode: 0644]
drivers/s390/crypto/zcrypt_mono.c [new file with mode: 0644]
drivers/s390/crypto/zcrypt_pcica.c [new file with mode: 0644]
drivers/s390/crypto/zcrypt_pcica.h [new file with mode: 0644]
drivers/s390/crypto/zcrypt_pcicc.c [new file with mode: 0644]
drivers/s390/crypto/zcrypt_pcicc.h [new file with mode: 0644]
drivers/s390/crypto/zcrypt_pcixcc.c [new file with mode: 0644]
drivers/s390/crypto/zcrypt_pcixcc.h [new file with mode: 0644]
drivers/s390/s390mach.c
drivers/s390/scsi/zfcp_def.h
drivers/s390/sysinfo.c
include/asm-s390/Kbuild
include/asm-s390/appldata.h [new file with mode: 0644]
include/asm-s390/cio.h
include/asm-s390/dma.h
include/asm-s390/futex.h
include/asm-s390/io.h
include/asm-s390/kdebug.h [new file with mode: 0644]
include/asm-s390/kprobes.h [new file with mode: 0644]
include/asm-s390/lowcore.h
include/asm-s390/monwriter.h [new file with mode: 0644]
include/asm-s390/pgalloc.h
include/asm-s390/pgtable.h
include/asm-s390/processor.h
include/asm-s390/setup.h
include/asm-s390/smp.h
include/asm-s390/uaccess.h
include/asm-s390/unistd.h
include/asm-s390/z90crypt.h [deleted file]
include/asm-s390/zcrypt.h [new file with mode: 0644]
include/linux/mod_devicetable.h
scripts/mod/file2alias.c

index 11b59d2..ed2a83c 100644 (file)
@@ -2452,6 +2452,8 @@ S:      Maintained
 S390
 P:     Martin Schwidefsky
 M:     schwidefsky@de.ibm.com
+P:     Heiko Carstens
+M:     heiko.carstens@de.ibm.com
 M:     linux390@de.ibm.com
 L:     linux-390@vm.marist.edu
 W:     http://www.ibm.com/developerworks/linux/linux390/
index 2f4f70c..b216ca6 100644 (file)
@@ -460,8 +460,7 @@ config S390_HYPFS_FS
          information in an s390 hypervisor environment.
 
 config KEXEC
-       bool "kexec system call (EXPERIMENTAL)"
-       depends on EXPERIMENTAL
+       bool "kexec system call"
        help
          kexec is a system call that implements the ability to shutdown your
          current kernel, and to start another kernel.  It is like a reboot
@@ -487,8 +486,22 @@ source "drivers/net/Kconfig"
 
 source "fs/Kconfig"
 
+menu "Instrumentation Support"
+
 source "arch/s390/oprofile/Kconfig"
 
+config KPROBES
+       bool "Kprobes (EXPERIMENTAL)"
+       depends on EXPERIMENTAL && MODULES
+       help
+         Kprobes allows you to trap at almost any kernel address and
+         execute a callback function.  register_kprobe() establishes
+         a probepoint and specifies the callback.  Kprobes is useful
+         for kernel debugging, non-intrusive instrumentation and testing.
+         If in doubt, say "N".
+
+endmenu
+
 source "arch/s390/Kconfig.debug"
 
 source "security/Kconfig"
index 71d65eb..0429481 100644 (file)
 #define CTL_APPLDATA_NET_SUM   2125
 #define CTL_APPLDATA_PROC      2126
 
-#ifndef CONFIG_64BIT
-
-#define APPLDATA_START_INTERVAL_REC 0x00       /* Function codes for */
-#define APPLDATA_STOP_REC          0x01        /* DIAG 0xDC      */
-#define APPLDATA_GEN_EVENT_RECORD   0x02
-#define APPLDATA_START_CONFIG_REC   0x03
-
-#else
-
-#define APPLDATA_START_INTERVAL_REC 0x80
-#define APPLDATA_STOP_REC          0x81
-#define APPLDATA_GEN_EVENT_RECORD   0x82
-#define APPLDATA_START_CONFIG_REC   0x83
-
-#endif /* CONFIG_64BIT */
-
 #define P_INFO(x...)   printk(KERN_INFO MY_PRINT_NAME " info: " x)
 #define P_ERROR(x...)  printk(KERN_ERR MY_PRINT_NAME " error: " x)
 #define P_WARNING(x...)        printk(KERN_WARNING MY_PRINT_NAME " status: " x)
index a0a94e0..b69ed74 100644 (file)
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/errno.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/smp.h>
 #include <linux/interrupt.h>
 #include <linux/proc_fs.h>
 #include <linux/page-flags.h>
 #include <linux/swap.h>
 #include <linux/pagemap.h>
 #include <linux/sysctl.h>
-#include <asm/timer.h>
-//#include <linux/kernel_stat.h>
 #include <linux/notifier.h>
 #include <linux/cpu.h>
 #include <linux/workqueue.h>
+#include <asm/appldata.h>
+#include <asm/timer.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/smp.h>
 
 #include "appldata.h"
 
 
 #define TOD_MICRO      0x01000                 /* nr. of TOD clock units
                                                   for 1 microsecond */
-
-/*
- * Parameter list for DIAGNOSE X'DC'
- */
-#ifndef CONFIG_64BIT
-struct appldata_parameter_list {
-       u16 diag;               /* The DIAGNOSE code X'00DC'          */
-       u8  function;           /* The function code for the DIAGNOSE */
-       u8  parlist_length;     /* Length of the parameter list       */
-       u32 product_id_addr;    /* Address of the 16-byte product ID  */
-       u16 reserved;
-       u16 buffer_length;      /* Length of the application data buffer  */
-       u32 buffer_addr;        /* Address of the application data buffer */
-};
-#else
-struct appldata_parameter_list {
-       u16 diag;
-       u8  function;
-       u8  parlist_length;
-       u32 unused01;
-       u16 reserved;
-       u16 buffer_length;
-       u32 unused02;
-       u64 product_id_addr;
-       u64 buffer_addr;
-};
-#endif /* CONFIG_64BIT */
-
 /*
  * /proc entries (sysctl)
  */
@@ -181,46 +153,17 @@ static void appldata_work_fn(void *data)
 int appldata_diag(char record_nr, u16 function, unsigned long buffer,
                        u16 length, char *mod_lvl)
 {
-       unsigned long ry;
-       struct appldata_product_id {
-               char prod_nr[7];                        /* product nr. */
-               char prod_fn[2];                        /* product function */
-               char record_nr;                         /* record nr. */
-               char version_nr[2];                     /* version */
-               char release_nr[2];                     /* release */
-               char mod_lvl[2];                        /* modification lvl. */
-       } appldata_product_id = {
-       /* all strings are EBCDIC, record_nr is byte */
+       struct appldata_product_id id = {
                .prod_nr    = {0xD3, 0xC9, 0xD5, 0xE4,
-                               0xE7, 0xD2, 0xD9},      /* "LINUXKR" */
-               .prod_fn    = {0xD5, 0xD3},             /* "NL" */
+                              0xE7, 0xD2, 0xD9},       /* "LINUXKR" */
+               .prod_fn    = 0xD5D3,                   /* "NL" */
                .record_nr  = record_nr,
-               .version_nr = {0xF2, 0xF6},             /* "26" */
-               .release_nr = {0xF0, 0xF1},             /* "01" */
-               .mod_lvl    = {mod_lvl[0], mod_lvl[1]},
-       };
-       struct appldata_parameter_list appldata_parameter_list = {
-                               .diag = 0xDC,
-                               .function = function,
-                               .parlist_length =
-                                       sizeof(appldata_parameter_list),
-                               .buffer_length = length,
-                               .product_id_addr =
-                                       (unsigned long) &appldata_product_id,
-                               .buffer_addr = virt_to_phys((void *) buffer)
+               .version_nr = 0xF2F6,                   /* "26" */
+               .release_nr = 0xF0F1,                   /* "01" */
+               .mod_lvl    = (mod_lvl[0]) << 8 | mod_lvl[1],
        };
 
-       if (!MACHINE_IS_VM)
-               return -ENOSYS;
-       ry = -1;
-       asm volatile(
-                       "diag %1,%0,0xDC\n\t"
-                       : "=d" (ry)
-                       : "d" (&appldata_parameter_list),
-                         "m" (appldata_parameter_list),
-                         "m" (appldata_product_id)
-                       : "cc");
-       return (int) ry;
+       return appldata_asm(&id, function, (void *) buffer, length);
 }
 /************************ timer, work, DIAG <END> ****************************/
 
index 161acc5..76a1552 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/kernel_stat.h>
 #include <linux/netdevice.h>
 #include <linux/sched.h>
+#include <asm/appldata.h>
 #include <asm/smp.h>
 
 #include "appldata.h"
index f1d4591..35da539 100644 (file)
@@ -428,6 +428,7 @@ CONFIG_S390_TAPE_34XX=m
 # CONFIG_VMLOGRDR is not set
 # CONFIG_VMCP is not set
 # CONFIG_MONREADER is not set
+CONFIG_MONWRITER=m
 
 #
 # Cryptographic devices
index ea5567b..f3dbd91 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  fs/hypfs/hypfs.h
+ *  arch/s390/hypfs/hypfs.h
  *    Hypervisor filesystem for Linux on s390.
  *
  *    Copyright (C) IBM Corp. 2006
index 1785bce..75144ef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  fs/hypfs/hypfs_diag.c
+ *  arch/s390/hypfs/hypfs_diag.c
  *    Hypervisor filesystem for Linux on s390. Diag 204 and 224
  *    implementation.
  *
@@ -432,12 +432,14 @@ static int diag204_probe(void)
 
        buf = diag204_get_buffer(INFO_EXT, &pages);
        if (!IS_ERR(buf)) {
-               if (diag204(SUBC_STIB7 | INFO_EXT, pages, buf) >= 0) {
+               if (diag204((unsigned long)SUBC_STIB7 |
+                           (unsigned long)INFO_EXT, pages, buf) >= 0) {
                        diag204_store_sc = SUBC_STIB7;
                        diag204_info_type = INFO_EXT;
                        goto out;
                }
-               if (diag204(SUBC_STIB6 | INFO_EXT, pages, buf) >= 0) {
+               if (diag204((unsigned long)SUBC_STIB6 |
+                           (unsigned long)INFO_EXT, pages, buf) >= 0) {
                        diag204_store_sc = SUBC_STIB7;
                        diag204_info_type = INFO_EXT;
                        goto out;
@@ -452,7 +454,8 @@ static int diag204_probe(void)
                rc = PTR_ERR(buf);
                goto fail_alloc;
        }
-       if (diag204(SUBC_STIB4 | INFO_SIMPLE, pages, buf) >= 0) {
+       if (diag204((unsigned long)SUBC_STIB4 |
+                   (unsigned long)INFO_SIMPLE, pages, buf) >= 0) {
                diag204_store_sc = SUBC_STIB4;
                diag204_info_type = INFO_SIMPLE;
                goto out;
@@ -476,7 +479,8 @@ static void *diag204_store(void)
        buf = diag204_get_buffer(diag204_info_type, &pages);
        if (IS_ERR(buf))
                goto out;
-       if (diag204(diag204_store_sc | diag204_info_type, pages, buf) < 0)
+       if (diag204((unsigned long)diag204_store_sc |
+                   (unsigned long)diag204_info_type, pages, buf) < 0)
                return ERR_PTR(-ENOSYS);
 out:
        return buf;
@@ -531,7 +535,7 @@ __init int hypfs_diag_init(void)
        return rc;
 }
 
-__exit void hypfs_diag_exit(void)
+void hypfs_diag_exit(void)
 {
        diag224_delete_name_table();
        diag204_free_buffer();
index 793dea6..256b384 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  fs/hypfs/hypfs_diag.h
+ *  arch/s390/hypfs_diag.h
  *    Hypervisor filesystem for Linux on s390.
  *
  *    Copyright (C) IBM Corp. 2006
index 18c0919..bdade5f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  fs/hypfs/inode.c
+ *  arch/s390/hypfs/inode.c
  *    Hypervisor filesystem for Linux on s390.
  *
  *    Copyright (C) IBM Corp. 2006
@@ -312,10 +312,12 @@ static void hypfs_kill_super(struct super_block *sb)
 {
        struct hypfs_sb_info *sb_info = sb->s_fs_info;
 
-       hypfs_delete_tree(sb->s_root);
-       hypfs_remove(sb_info->update_file);
-       kfree(sb->s_fs_info);
-       sb->s_fs_info = NULL;
+       if (sb->s_root) {
+               hypfs_delete_tree(sb->s_root);
+               hypfs_remove(sb_info->update_file);
+               kfree(sb->s_fs_info);
+               sb->s_fs_info = NULL;
+       }
        kill_litter_super(sb);
 }
 
index 9a33ed6..aa97897 100644 (file)
@@ -6,7 +6,7 @@ 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 reipl_diag.o
+           semaphore.o s390_ext.o debug.o profile.o irq.o ipl.o
 
 obj-y  += $(if $(CONFIG_64BIT),entry64.o,entry.o)
 obj-y  += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
@@ -24,6 +24,7 @@ obj-$(CONFIG_COMPAT)          += compat_linux.o compat_signal.o \
 
 obj-$(CONFIG_VIRT_TIMER)       += vtime.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
+obj-$(CONFIG_KPROBES)          += kprobes.o
 
 # Kexec part
 S390_KEXEC_OBJS := machine_kexec.o crash.o
index 5b5799a..0c712b7 100644 (file)
@@ -505,6 +505,8 @@ pgm_no_vtime2:
        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
+       tm      SP_PSW+1(%r15),0x01     # kernel per event ?
+       bz      BASED(kernel_per)
        l       %r3,__LC_PGM_ILC         # load program interruption code
        la      %r8,0x7f
        nr      %r8,%r3                  # clear per-event-bit and ilc
@@ -536,6 +538,16 @@ pgm_no_vtime3:
        stosm   __SF_EMPTY(%r15),0x03   # reenable interrupts
        b       BASED(sysc_do_svc)
 
+#
+# per was called from kernel, must be kprobes
+#
+kernel_per:
+       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_leave)  # load adr. of system return
+       br      %r1                     # branch to do_single_step
+
 /*
  * IO interrupt handler routine
  */
index 56f5f61..29bbfba 100644 (file)
@@ -518,6 +518,8 @@ pgm_no_vtime2:
 #endif
        lg      %r9,__LC_THREAD_INFO    # load pointer to thread_info struct
        lg      %r1,__TI_task(%r9)
+       tm      SP_PSW+1(%r15),0x01     # kernel per event ?
+       jz      kernel_per
        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
@@ -553,6 +555,16 @@ pgm_no_vtime3:
        stosm   __SF_EMPTY(%r15),0x03   # reenable interrupts
        j       sysc_do_svc
 
+#
+# per was called from kernel, must be kprobes
+#
+kernel_per:
+       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_leave         # load adr. of system ret, no work
+       jg      do_single_step          # branch to do_single_step
+
 /*
  * IO interrupt handler routine
  */
@@ -815,7 +827,7 @@ restart_go:
  */
 stack_overflow:
        lg      %r15,__LC_PANIC_STACK   # change to panic stack
-       aghi    %r1,-SP_SIZE
+       aghi    %r15,-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
@@ -823,7 +835,7 @@ stack_overflow:
        je      0f
        chi     %r12,__LC_PGM_OLD_PSW
        je      0f
-       la      %r1,__LC_SAVE_AREA+16
+       la      %r1,__LC_SAVE_AREA+32
 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
index adad886..0f1db26 100644 (file)
@@ -272,7 +272,7 @@ iplstart:
 # load parameter file from ipl device
 #
 .Lagain1:
-       l     %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) # ramdisk loc. is temp
+       l     %r2,.Linitrd                     # ramdisk loc. is temp
         bas   %r14,.Lloader                    # load parameter file
         ltr   %r2,%r2                          # got anything ?
         bz    .Lnopf
@@ -280,7 +280,7 @@ iplstart:
        bnh   .Lnotrunc
        la    %r2,895
 .Lnotrunc:
-       l     %r4,INITRD_START+ARCH_OFFSET-PARMAREA(%r12)
+       l     %r4,.Linitrd
        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
@@ -323,14 +323,15 @@ iplstart:
 # load ramdisk from ipl device
 #      
 .Lagain2:
-       l     %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) # addr of ramdisk
+       l     %r2,.Linitrd                     # addr of ramdisk
+       st    %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12)
         bas   %r14,.Lloader                    # load ramdisk
        st    %r2,INITRD_SIZE+ARCH_OFFSET-PARMAREA(%r12) # store size of ramdisk
         ltr   %r2,%r2
         bnz   .Lrdcont
         st    %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12) # no ramdisk found
 .Lrdcont:
-       l     %r2,INITRD_START+ARCH_OFFSET-PARMAREA(%r12)
+       l     %r2,.Linitrd
 
        clc   0(3,%r2),.L_hdr                  # skip HDRx and EOFx 
        bz    .Lagain2
@@ -379,6 +380,7 @@ iplstart:
         l     %r1,.Lstartup
         br    %r1
 
+.Linitrd:.long _end + 0x400000                # default address of initrd
 .Lparm:        .long  PARMAREA
 .Lstartup: .long startup
 .Lcvtab:.long  _ebcasc                         # ebcdic to ascii table
@@ -479,65 +481,6 @@ start:
        .byte 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7 
        .byte 0xf8,0xf9,0xfa,0xfb,0xfc,0xfd,0xfe,0xff
 
-.macro GET_IPL_DEVICE
-.Lget_ipl_device:
-       l     %r1,0xb8                  # get sid
-       sll   %r1,15                    # test if subchannel is enabled
-       srl   %r1,31
-       ltr   %r1,%r1
-       bz    2f-.LPG1(%r13)            # subchannel disabled
-       l     %r1,0xb8
-       la    %r5,.Lipl_schib-.LPG1(%r13)
-       stsch 0(%r5)                    # get schib of subchannel
-       bnz   2f-.LPG1(%r13)            # schib not available
-       tm    5(%r5),0x01               # devno valid?
-       bno   2f-.LPG1(%r13)
-       la    %r6,ipl_parameter_flags-.LPG1(%r13)
-       oi    3(%r6),0x01               # set flag
-       la    %r2,ipl_devno-.LPG1(%r13)
-       mvc   0(2,%r2),6(%r5)           # store devno
-       tm    4(%r5),0x80               # qdio capable device?
-       bno   2f-.LPG1(%r13)
-       oi    3(%r6),0x02               # set flag
-
-       # copy ipl parameters
-
-       lhi   %r0,4096
-       l     %r2,20(%r0)               # get address of parameter list
-       lhi   %r3,IPL_PARMBLOCK_ORIGIN
-       st    %r3,20(%r0)
-       lhi   %r4,1
-       cr    %r2,%r3                   # start parameters < destination ?
-       jl    0f
-       lhi   %r1,1                     # copy direction is upwards
-       j     1f
-0:     lhi   %r1,-1                    # copy direction is downwards
-       ar    %r2,%r0
-       ar    %r3,%r0
-       ar    %r2,%r1
-       ar    %r3,%r1
-1:     mvc   0(1,%r3),0(%r2)           # finally copy ipl parameters
-       ar    %r3,%r1
-       ar    %r2,%r1
-       sr    %r0,%r4
-       jne   1b
-       b     2f-.LPG1(%r13)
-
-       .align 4
-.Lipl_schib:
-       .rept 13
-       .long 0
-       .endr
-
-       .globl ipl_parameter_flags
-ipl_parameter_flags:
-       .long 0
-       .globl ipl_devno
-ipl_devno:
-       .word 0
-2:
-.endm
-
 #ifdef CONFIG_64BIT
 #include "head64.S"
 #else
index a4dc61f..1fa9fa1 100644 (file)
@@ -26,8 +26,8 @@ startup:basr  %r13,0                  # get base
 #
        .org    PARMAREA
        .long   0,0                     # IPL_DEVICE
-       .long   0,RAMDISK_ORIGIN        # INITRD_START
-       .long   0,RAMDISK_SIZE          # INITRD_SIZE
+       .long   0,0                     # INITRD_START
+       .long   0,0                     # INITRD_SIZE
 
        .org    COMMAND_LINE
        .byte   "root=/dev/ram0 ro"
@@ -37,12 +37,23 @@ startup:basr        %r13,0                  # get base
 
 startup_continue:
        basr    %r13,0                  # get base
-.LPG1: GET_IPL_DEVICE
+.LPG1: mvi     __LC_AR_MODE_ID,0       # set ESA flag (mode 0)
        lctl    %c0,%c15,.Lctl-.LPG1(%r13) # load control registers
        l       %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
                                        # move IPL device to lowcore
        mvc     __LC_IPLDEV(4),IPL_DEVICE-PARMAREA(%r12)
+#
+# Setup stack
+#
+       l       %r15,.Linittu-.LPG1(%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
 
+       l       %r14,.Lipl_save_parameters-.LPG1(%r13)
+       basr    %r14,%r14
 #
 # clear bss memory
 #
@@ -114,6 +125,10 @@ startup_continue:
        b       .Lfchunk-.LPG1(%r13)
 
        .align 4
+.Lipl_save_parameters:
+       .long   ipl_save_parameters
+.Linittu:
+       .long   init_thread_union
 .Lpmask:
        .byte   0
 .align 8
@@ -273,7 +288,23 @@ startup_continue:
 .Lbss_end:  .long _end
 .Lparmaddr: .long PARMAREA
 .Lsccbaddr: .long .Lsccb
+
+       .globl ipl_schib
+ipl_schib:
+       .rept 13
+       .long 0
+       .endr
+
+       .globl ipl_flags
+ipl_flags:
+       .long 0
+       .globl ipl_devno
+ipl_devno:
+       .word 0
+
        .org    0x12000
+.globl s390_readinfo_sccb
+s390_readinfo_sccb:
 .Lsccb:
        .hword  0x1000                  # length, one page
        .byte   0x00,0x00,0x00
@@ -302,16 +333,6 @@ startup_continue:
        .globl  _stext
 _stext:        basr    %r13,0                  # get base
 .LPG3:
-#
-# Setup stack
-#
-       l       %r15,.Linittu-.LPG3(%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),0x40            # enable sigp emergency signal
@@ -330,6 +351,5 @@ _stext:     basr    %r13,0                  # get base
 #
        .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
index 9d80c5b..a8bdd96 100644 (file)
@@ -26,8 +26,8 @@ startup:basr  %r13,0                   # get base
 #
        .org   PARMAREA
        .quad  0                        # IPL_DEVICE
-       .quad  RAMDISK_ORIGIN           # INITRD_START
-       .quad  RAMDISK_SIZE             # INITRD_SIZE
+       .quad  0                        # INITRD_START
+       .quad  0                        # INITRD_SIZE
 
        .org   COMMAND_LINE
        .byte  "root=/dev/ram0 ro"
@@ -39,8 +39,8 @@ startup_continue:
        basr  %r13,0                     # get base
 .LPG1:  sll   %r13,1                     # remove high order bit
         srl   %r13,1
-       GET_IPL_DEVICE
         lhi   %r1,1                      # mode 1 = esame
+       mvi   __LC_AR_MODE_ID,1          # set esame flag
         slr   %r0,%r0                    # set cpuid to zero
         sigp  %r1,%r0,0x12               # switch to esame mode
        sam64                            # switch to 64 bit mode
@@ -48,7 +48,18 @@ startup_continue:
        lg    %r12,.Lparmaddr-.LPG1(%r13)# pointer to parameter area
                                         # move IPL device to lowcore
         mvc   __LC_IPLDEV(4),IPL_DEVICE+4-PARMAREA(%r12)
+#
+# 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
 
+       brasl %r14,ipl_save_parameters
 #
 # clear bss memory
 #
@@ -239,6 +250,19 @@ startup_continue:
        oi      7(%r12),0x80            # set IDTE flag
 0:
 
+#
+# find out if we have the MVCOS instruction
+#
+       la      %r1,0f-.LPG1(%r13)      # set program check address
+       stg     %r1,__LC_PGM_NEW_PSW+8
+       .short  0xc800                  # mvcos 0(%r0),0(%r0),%r0
+       .short  0x0000
+       .short  0x0000
+0:     tm      0x8f,0x13               # special-operation exception?
+       bno     1f-.LPG1(%r13)          # if yes, MVCOS is present
+       oi      6(%r12),2               # set MVCOS flag
+1:
+
         lpswe .Lentry-.LPG1(13)         # jump to _stext in primary-space,
                                         # virtual and never return ...
         .align 16
@@ -268,7 +292,22 @@ startup_continue:
 .Lparmaddr:
        .quad   PARMAREA
 
+       .globl ipl_schib
+ipl_schib:
+       .rept 13
+       .long 0
+       .endr
+
+       .globl ipl_flags
+ipl_flags:
+       .long 0
+       .globl ipl_devno
+ipl_devno:
+       .word 0
+
        .org    0x12000
+.globl s390_readinfo_sccb
+s390_readinfo_sccb:
 .Lsccb:
        .hword 0x1000                   # length, one page
        .byte 0x00,0x00,0x00
@@ -297,24 +336,12 @@ startup_continue:
         .globl _stext
 _stext:        basr  %r13,0                    # get base
 .LPG3:
-#
-# 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),0x40             # enable sigp emergency signal
        oi     4(%r15),0x10             # switch on low address proctection
         lctlg  %c0,%c15,0(%r15)
 
-#
         lam    0,15,.Laregs-.LPG3(%r13) # load access regs needed by uaccess
         brasl  %r14,start_kernel        # go to C code
 #
@@ -322,7 +349,7 @@ _stext:     basr  %r13,0                    # get base
 #
         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/ipl.c b/arch/s390/kernel/ipl.c
new file mode 100644 (file)
index 0000000..6555cc4
--- /dev/null
@@ -0,0 +1,942 @@
+/*
+ *  arch/s390/kernel/ipl.c
+ *    ipl/reipl/dump support for Linux on s390.
+ *
+ *    Copyright (C) IBM Corp. 2005,2006
+ *    Author(s): Michael Holzheu <holzheu@de.ibm.com>
+ *              Heiko Carstens <heiko.carstens@de.ibm.com>
+ *              Volker Sameske <sameske@de.ibm.com>
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <asm/smp.h>
+#include <asm/setup.h>
+#include <asm/cpcmd.h>
+#include <asm/cio.h>
+
+#define IPL_PARM_BLOCK_VERSION 0
+
+enum ipl_type {
+       IPL_TYPE_NONE    = 1,
+       IPL_TYPE_UNKNOWN = 2,
+       IPL_TYPE_CCW     = 4,
+       IPL_TYPE_FCP     = 8,
+};
+
+#define IPL_NONE_STR    "none"
+#define IPL_UNKNOWN_STR  "unknown"
+#define IPL_CCW_STR     "ccw"
+#define IPL_FCP_STR     "fcp"
+
+static char *ipl_type_str(enum ipl_type type)
+{
+       switch (type) {
+       case IPL_TYPE_NONE:
+               return IPL_NONE_STR;
+       case IPL_TYPE_CCW:
+               return IPL_CCW_STR;
+       case IPL_TYPE_FCP:
+               return IPL_FCP_STR;
+       case IPL_TYPE_UNKNOWN:
+       default:
+               return IPL_UNKNOWN_STR;
+       }
+}
+
+enum ipl_method {
+       IPL_METHOD_NONE,
+       IPL_METHOD_CCW_CIO,
+       IPL_METHOD_CCW_DIAG,
+       IPL_METHOD_CCW_VM,
+       IPL_METHOD_FCP_RO_DIAG,
+       IPL_METHOD_FCP_RW_DIAG,
+       IPL_METHOD_FCP_RO_VM,
+};
+
+enum shutdown_action {
+       SHUTDOWN_REIPL,
+       SHUTDOWN_DUMP,
+       SHUTDOWN_STOP,
+};
+
+#define SHUTDOWN_REIPL_STR "reipl"
+#define SHUTDOWN_DUMP_STR  "dump"
+#define SHUTDOWN_STOP_STR  "stop"
+
+static char *shutdown_action_str(enum shutdown_action action)
+{
+       switch (action) {
+       case SHUTDOWN_REIPL:
+               return SHUTDOWN_REIPL_STR;
+       case SHUTDOWN_DUMP:
+               return SHUTDOWN_DUMP_STR;
+       case SHUTDOWN_STOP:
+               return SHUTDOWN_STOP_STR;
+       default:
+               BUG();
+       }
+}
+
+enum diag308_subcode  {
+       DIAG308_IPL   = 3,
+       DIAG308_DUMP  = 4,
+       DIAG308_SET   = 5,
+       DIAG308_STORE = 6,
+};
+
+enum diag308_ipl_type {
+       DIAG308_IPL_TYPE_FCP = 0,
+       DIAG308_IPL_TYPE_CCW = 2,
+};
+
+enum diag308_opt {
+       DIAG308_IPL_OPT_IPL  = 0x10,
+       DIAG308_IPL_OPT_DUMP = 0x20,
+};
+
+enum diag308_rc {
+       DIAG308_RC_OK = 1,
+};
+
+static int diag308_set_works = 0;
+
+static int reipl_capabilities = IPL_TYPE_UNKNOWN;
+static enum ipl_type reipl_type = IPL_TYPE_UNKNOWN;
+static enum ipl_method reipl_method = IPL_METHOD_NONE;
+static struct ipl_parameter_block *reipl_block_fcp;
+static struct ipl_parameter_block *reipl_block_ccw;
+
+static int dump_capabilities = IPL_TYPE_NONE;
+static enum ipl_type dump_type = IPL_TYPE_NONE;
+static enum ipl_method dump_method = IPL_METHOD_NONE;
+static struct ipl_parameter_block *dump_block_fcp;
+static struct ipl_parameter_block *dump_block_ccw;
+
+static enum shutdown_action on_panic_action = SHUTDOWN_STOP;
+
+static int diag308(unsigned long subcode, void *addr)
+{
+       register unsigned long _addr asm("0") = (unsigned long)addr;
+       register unsigned long _rc asm("1") = 0;
+
+       asm volatile (
+               "   diag %0,%2,0x308\n"
+               "0: \n"
+               ".section __ex_table,\"a\"\n"
+#ifdef CONFIG_64BIT
+               "   .align 8\n"
+               "   .quad 0b, 0b\n"
+#else
+               "   .align 4\n"
+               "   .long 0b, 0b\n"
+#endif
+               ".previous\n"
+               : "+d" (_addr), "+d" (_rc)
+               : "d" (subcode) : "cc", "memory" );
+
+       return _rc;
+}
+
+/* SYSFS */
+
+#define DEFINE_IPL_ATTR_RO(_prefix, _name, _format, _value)            \
+static ssize_t sys_##_prefix##_##_name##_show(struct subsystem *subsys,        \
+               char *page)                                             \
+{                                                                      \
+       return sprintf(page, _format, _value);                          \
+}                                                                      \
+static struct subsys_attribute sys_##_prefix##_##_name##_attr =                \
+       __ATTR(_name, S_IRUGO, sys_##_prefix##_##_name##_show, NULL);
+
+#define DEFINE_IPL_ATTR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)  \
+static ssize_t sys_##_prefix##_##_name##_show(struct subsystem *subsys,        \
+               char *page)                                             \
+{                                                                      \
+       return sprintf(page, _fmt_out,                                  \
+                       (unsigned long long) _value);                   \
+}                                                                      \
+static ssize_t sys_##_prefix##_##_name##_store(struct subsystem *subsys,\
+               const char *buf, size_t len)                            \
+{                                                                      \
+       unsigned long long value;                                       \
+       if (sscanf(buf, _fmt_in, &value) != 1)                          \
+               return -EINVAL;                                         \
+       _value = value;                                                 \
+       return len;                                                     \
+}                                                                      \
+static struct subsys_attribute sys_##_prefix##_##_name##_attr =                \
+       __ATTR(_name,(S_IRUGO | S_IWUSR),                               \
+                       sys_##_prefix##_##_name##_show,                 \
+                       sys_##_prefix##_##_name##_store);
+
+static void make_attrs_ro(struct attribute **attrs)
+{
+       while (*attrs) {
+               (*attrs)->mode = S_IRUGO;
+               attrs++;
+       }
+}
+
+/*
+ * ipl section
+ */
+
+static enum ipl_type ipl_get_type(void)
+{
+       struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
+
+       if (!(ipl_flags & IPL_DEVNO_VALID))
+               return IPL_TYPE_UNKNOWN;
+       if (!(ipl_flags & IPL_PARMBLOCK_VALID))
+               return IPL_TYPE_CCW;
+       if (ipl->hdr.version > IPL_MAX_SUPPORTED_VERSION)
+               return IPL_TYPE_UNKNOWN;
+       if (ipl->hdr.pbt != DIAG308_IPL_TYPE_FCP)
+               return IPL_TYPE_UNKNOWN;
+       return IPL_TYPE_FCP;
+}
+
+static ssize_t ipl_type_show(struct subsystem *subsys, char *page)
+{
+       return sprintf(page, "%s\n", ipl_type_str(ipl_get_type()));
+}
+
+static struct subsys_attribute sys_ipl_type_attr = __ATTR_RO(ipl_type);
+
+static ssize_t sys_ipl_device_show(struct subsystem *subsys, char *page)
+{
+       struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
+
+       switch (ipl_get_type()) {
+       case IPL_TYPE_CCW:
+               return sprintf(page, "0.0.%04x\n", ipl_devno);
+       case IPL_TYPE_FCP:
+               return sprintf(page, "0.0.%04x\n", ipl->ipl_info.fcp.devno);
+       default:
+               return 0;
+       }
+}
+
+static struct subsys_attribute sys_ipl_device_attr =
+       __ATTR(device, S_IRUGO, sys_ipl_device_show, NULL);
+
+static ssize_t ipl_parameter_read(struct kobject *kobj, char *buf, loff_t off,
+                                 size_t count)
+{
+       unsigned int size = IPL_PARMBLOCK_SIZE;
+
+       if (off > size)
+               return 0;
+       if (off + count > size)
+               count = size - off;
+       memcpy(buf, (void *)IPL_PARMBLOCK_START + off, count);
+       return count;
+}
+
+static struct bin_attribute ipl_parameter_attr = {
+       .attr = {
+               .name = "binary_parameter",
+               .mode = S_IRUGO,
+               .owner = THIS_MODULE,
+       },
+       .size = PAGE_SIZE,
+       .read = &ipl_parameter_read,
+};
+
+static ssize_t ipl_scp_data_read(struct kobject *kobj, char *buf, loff_t off,
+       size_t count)
+{
+       unsigned int size = IPL_PARMBLOCK_START->ipl_info.fcp.scp_data_len;
+       void *scp_data = &IPL_PARMBLOCK_START->ipl_info.fcp.scp_data;
+
+       if (off > size)
+               return 0;
+       if (off + count > size)
+               count = size - off;
+       memcpy(buf, scp_data + off, count);
+       return count;
+}
+
+static struct bin_attribute ipl_scp_data_attr = {
+       .attr = {
+               .name = "scp_data",
+               .mode = S_IRUGO,
+               .owner = THIS_MODULE,
+       },
+       .size = PAGE_SIZE,
+       .read = &ipl_scp_data_read,
+};
+
+/* FCP ipl device attributes */
+
+DEFINE_IPL_ATTR_RO(ipl_fcp, wwpn, "0x%016llx\n", (unsigned long long)
+                  IPL_PARMBLOCK_START->ipl_info.fcp.wwpn);
+DEFINE_IPL_ATTR_RO(ipl_fcp, lun, "0x%016llx\n", (unsigned long long)
+                  IPL_PARMBLOCK_START->ipl_info.fcp.lun);
+DEFINE_IPL_ATTR_RO(ipl_fcp, bootprog, "%lld\n", (unsigned long long)
+                  IPL_PARMBLOCK_START->ipl_info.fcp.bootprog);
+DEFINE_IPL_ATTR_RO(ipl_fcp, br_lba, "%lld\n", (unsigned long long)
+                  IPL_PARMBLOCK_START->ipl_info.fcp.br_lba);
+
+static struct attribute *ipl_fcp_attrs[] = {
+       &sys_ipl_type_attr.attr,
+       &sys_ipl_device_attr.attr,
+       &sys_ipl_fcp_wwpn_attr.attr,
+       &sys_ipl_fcp_lun_attr.attr,
+       &sys_ipl_fcp_bootprog_attr.attr,
+       &sys_ipl_fcp_br_lba_attr.attr,
+       NULL,
+};
+
+static struct attribute_group ipl_fcp_attr_group = {
+       .attrs = ipl_fcp_attrs,
+};
+
+/* CCW ipl device attributes */
+
+static struct attribute *ipl_ccw_attrs[] = {
+       &sys_ipl_type_attr.attr,
+       &sys_ipl_device_attr.attr,
+       NULL,
+};
+
+static struct attribute_group ipl_ccw_attr_group = {
+       .attrs = ipl_ccw_attrs,
+};
+
+/* UNKNOWN ipl device attributes */
+
+static struct attribute *ipl_unknown_attrs[] = {
+       &sys_ipl_type_attr.attr,
+       NULL,
+};
+
+static struct attribute_group ipl_unknown_attr_group = {
+       .attrs = ipl_unknown_attrs,
+};
+
+static decl_subsys(ipl, NULL, NULL);
+
+/*
+ * reipl section
+ */
+
+/* FCP reipl device attributes */
+
+DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%016llx\n",
+                  reipl_block_fcp->ipl_info.fcp.wwpn);
+DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%016llx\n",
+                  reipl_block_fcp->ipl_info.fcp.lun);
+DEFINE_IPL_ATTR_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n",
+                  reipl_block_fcp->ipl_info.fcp.bootprog);
+DEFINE_IPL_ATTR_RW(reipl_fcp, br_lba, "%lld\n", "%lld\n",
+                  reipl_block_fcp->ipl_info.fcp.br_lba);
+DEFINE_IPL_ATTR_RW(reipl_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
+                  reipl_block_fcp->ipl_info.fcp.devno);
+
+static struct attribute *reipl_fcp_attrs[] = {
+       &sys_reipl_fcp_device_attr.attr,
+       &sys_reipl_fcp_wwpn_attr.attr,
+       &sys_reipl_fcp_lun_attr.attr,
+       &sys_reipl_fcp_bootprog_attr.attr,
+       &sys_reipl_fcp_br_lba_attr.attr,
+       NULL,
+};
+
+static struct attribute_group reipl_fcp_attr_group = {
+       .name  = IPL_FCP_STR,
+       .attrs = reipl_fcp_attrs,
+};
+
+/* CCW reipl device attributes */
+
+DEFINE_IPL_ATTR_RW(reipl_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
+       reipl_block_ccw->ipl_info.ccw.devno);
+
+static struct attribute *reipl_ccw_attrs[] = {
+       &sys_reipl_ccw_device_attr.attr,
+       NULL,
+};
+
+static struct attribute_group reipl_ccw_attr_group = {
+       .name  = IPL_CCW_STR,
+       .attrs = reipl_ccw_attrs,
+};
+
+/* reipl type */
+
+static int reipl_set_type(enum ipl_type type)
+{
+       if (!(reipl_capabilities & type))
+               return -EINVAL;
+
+       switch(type) {
+       case IPL_TYPE_CCW:
+               if (MACHINE_IS_VM)
+                       reipl_method = IPL_METHOD_CCW_VM;
+               else
+                       reipl_method = IPL_METHOD_CCW_CIO;
+               break;
+       case IPL_TYPE_FCP:
+               if (diag308_set_works)
+                       reipl_method = IPL_METHOD_FCP_RW_DIAG;
+               else if (MACHINE_IS_VM)
+                       reipl_method = IPL_METHOD_FCP_RO_VM;
+               else
+                       reipl_method = IPL_METHOD_FCP_RO_DIAG;
+               break;
+       default:
+               reipl_method = IPL_METHOD_NONE;
+       }
+       reipl_type = type;
+       return 0;
+}
+
+static ssize_t reipl_type_show(struct subsystem *subsys, char *page)
+{
+       return sprintf(page, "%s\n", ipl_type_str(reipl_type));
+}
+
+static ssize_t reipl_type_store(struct subsystem *subsys, const char *buf,
+                               size_t len)
+{
+       int rc = -EINVAL;
+
+       if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
+               rc = reipl_set_type(IPL_TYPE_CCW);
+       else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
+               rc = reipl_set_type(IPL_TYPE_FCP);
+       return (rc != 0) ? rc : len;
+}
+
+static struct subsys_attribute reipl_type_attr =
+               __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store);
+
+static decl_subsys(reipl, NULL, NULL);
+
+/*
+ * dump section
+ */
+
+/* FCP dump device attributes */
+
+DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n",
+                  dump_block_fcp->ipl_info.fcp.wwpn);
+DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n",
+                  dump_block_fcp->ipl_info.fcp.lun);
+DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
+                  dump_block_fcp->ipl_info.fcp.bootprog);
+DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
+                  dump_block_fcp->ipl_info.fcp.br_lba);
+DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
+                  dump_block_fcp->ipl_info.fcp.devno);
+
+static struct attribute *dump_fcp_attrs[] = {
+       &sys_dump_fcp_device_attr.attr,
+       &sys_dump_fcp_wwpn_attr.attr,
+       &sys_dump_fcp_lun_attr.attr,
+       &sys_dump_fcp_bootprog_attr.attr,
+       &sys_dump_fcp_br_lba_attr.attr,
+       NULL,
+};
+
+static struct attribute_group dump_fcp_attr_group = {
+       .name  = IPL_FCP_STR,
+       .attrs = dump_fcp_attrs,
+};
+
+/* CCW dump device attributes */
+
+DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
+                  dump_block_ccw->ipl_info.ccw.devno);
+
+static struct attribute *dump_ccw_attrs[] = {
+       &sys_dump_ccw_device_attr.attr,
+       NULL,
+};
+
+static struct attribute_group dump_ccw_attr_group = {
+       .name  = IPL_CCW_STR,
+       .attrs = dump_ccw_attrs,
+};
+
+/* dump type */
+
+static int dump_set_type(enum ipl_type type)
+{
+       if (!(dump_capabilities & type))
+               return -EINVAL;
+       switch(type) {
+       case IPL_TYPE_CCW:
+               if (MACHINE_IS_VM)
+                       dump_method = IPL_METHOD_CCW_VM;
+               else
+                       dump_method = IPL_METHOD_CCW_CIO;
+               break;
+       case IPL_TYPE_FCP:
+               dump_method = IPL_METHOD_FCP_RW_DIAG;
+               break;
+       default:
+               dump_method = IPL_METHOD_NONE;
+       }
+       dump_type = type;
+       return 0;
+}
+
+static ssize_t dump_type_show(struct subsystem *subsys, char *page)
+{
+       return sprintf(page, "%s\n", ipl_type_str(dump_type));
+}
+
+static ssize_t dump_type_store(struct subsystem *subsys, const char *buf,
+                              size_t len)
+{
+       int rc = -EINVAL;
+
+       if (strncmp(buf, IPL_NONE_STR, strlen(IPL_NONE_STR)) == 0)
+               rc = dump_set_type(IPL_TYPE_NONE);
+       else if (strncmp(buf, IPL_CCW_STR, strlen(IPL_CCW_STR)) == 0)
+               rc = dump_set_type(IPL_TYPE_CCW);
+       else if (strncmp(buf, IPL_FCP_STR, strlen(IPL_FCP_STR)) == 0)
+               rc = dump_set_type(IPL_TYPE_FCP);
+       return (rc != 0) ? rc : len;
+}
+
+static struct subsys_attribute dump_type_attr =
+               __ATTR(dump_type, 0644, dump_type_show, dump_type_store);
+
+static decl_subsys(dump, NULL, NULL);
+
+#ifdef CONFIG_SMP
+static void dump_smp_stop_all(void)
+{
+       int cpu;
+       preempt_disable();
+       for_each_online_cpu(cpu) {
+               if (cpu == smp_processor_id())
+                       continue;
+               while (signal_processor(cpu, sigp_stop) == sigp_busy)
+                       udelay(10);
+       }
+       preempt_enable();
+}
+#else
+#define dump_smp_stop_all() do { } while (0)
+#endif
+
+/*
+ * Shutdown actions section
+ */
+
+static decl_subsys(shutdown_actions, NULL, NULL);
+
+/* on panic */
+
+static ssize_t on_panic_show(struct subsystem *subsys, char *page)
+{
+       return sprintf(page, "%s\n", shutdown_action_str(on_panic_action));
+}
+
+static ssize_t on_panic_store(struct subsystem *subsys, const char *buf,
+                             size_t len)
+{
+       if (strncmp(buf, SHUTDOWN_REIPL_STR, strlen(SHUTDOWN_REIPL_STR)) == 0)
+               on_panic_action = SHUTDOWN_REIPL;
+       else if (strncmp(buf, SHUTDOWN_DUMP_STR,
+                        strlen(SHUTDOWN_DUMP_STR)) == 0)
+               on_panic_action = SHUTDOWN_DUMP;
+       else if (strncmp(buf, SHUTDOWN_STOP_STR,
+                        strlen(SHUTDOWN_STOP_STR)) == 0)
+               on_panic_action = SHUTDOWN_STOP;
+       else
+               return -EINVAL;
+
+       return len;
+}
+
+static struct subsys_attribute on_panic_attr =
+               __ATTR(on_panic, 0644, on_panic_show, on_panic_store);
+
+static void print_fcp_block(struct ipl_parameter_block *fcp_block)
+{
+       printk(KERN_EMERG "wwpn:      %016llx\n",
+               (unsigned long long)fcp_block->ipl_info.fcp.wwpn);
+       printk(KERN_EMERG "lun:       %016llx\n",
+               (unsigned long long)fcp_block->ipl_info.fcp.lun);
+       printk(KERN_EMERG "bootprog:  %lld\n",
+               (unsigned long long)fcp_block->ipl_info.fcp.bootprog);
+       printk(KERN_EMERG "br_lba:    %lld\n",
+               (unsigned long long)fcp_block->ipl_info.fcp.br_lba);
+       printk(KERN_EMERG "device:    %llx\n",
+               (unsigned long long)fcp_block->ipl_info.fcp.devno);
+       printk(KERN_EMERG "opt:       %x\n", fcp_block->ipl_info.fcp.opt);
+}
+
+void do_reipl(void)
+{
+       struct ccw_dev_id devid;
+       static char buf[100];
+
+       switch (reipl_type) {
+       case IPL_TYPE_CCW:
+               printk(KERN_EMERG "reboot on ccw device: 0.0.%04x\n",
+                       reipl_block_ccw->ipl_info.ccw.devno);
+               break;
+       case IPL_TYPE_FCP:
+               printk(KERN_EMERG "reboot on fcp device:\n");
+               print_fcp_block(reipl_block_fcp);
+               break;
+       default:
+               break;
+       }
+
+       switch (reipl_method) {
+       case IPL_METHOD_CCW_CIO:
+               devid.devno = reipl_block_ccw->ipl_info.ccw.devno;
+               devid.ssid  = 0;
+               reipl_ccw_dev(&devid);
+               break;
+       case IPL_METHOD_CCW_VM:
+               sprintf(buf, "IPL %X", reipl_block_ccw->ipl_info.ccw.devno);
+               cpcmd(buf, NULL, 0, NULL);
+               break;
+       case IPL_METHOD_CCW_DIAG:
+               diag308(DIAG308_SET, reipl_block_ccw);
+               diag308(DIAG308_IPL, NULL);
+               break;
+       case IPL_METHOD_FCP_RW_DIAG:
+               diag308(DIAG308_SET, reipl_block_fcp);
+               diag308(DIAG308_IPL, NULL);
+               break;
+       case IPL_METHOD_FCP_RO_DIAG:
+               diag308(DIAG308_IPL, NULL);
+               break;
+       case IPL_METHOD_FCP_RO_VM:
+               cpcmd("IPL", NULL, 0, NULL);
+               break;
+       case IPL_METHOD_NONE:
+       default:
+               if (MACHINE_IS_VM)
+                       cpcmd("IPL", NULL, 0, NULL);
+               diag308(DIAG308_IPL, NULL);
+               break;
+       }
+       panic("reipl failed!\n");
+}
+
+static void do_dump(void)
+{
+       struct ccw_dev_id devid;
+       static char buf[100];
+
+       switch (dump_type) {
+       case IPL_TYPE_CCW:
+               printk(KERN_EMERG "Automatic dump on ccw device: 0.0.%04x\n",
+                      dump_block_ccw->ipl_info.ccw.devno);
+               break;
+       case IPL_TYPE_FCP:
+               printk(KERN_EMERG "Automatic dump on fcp device:\n");
+               print_fcp_block(dump_block_fcp);
+               break;
+       default:
+               return;
+       }
+
+       switch (dump_method) {
+       case IPL_METHOD_CCW_CIO:
+               dump_smp_stop_all();
+               devid.devno = dump_block_ccw->ipl_info.ccw.devno;
+               devid.ssid  = 0;
+               reipl_ccw_dev(&devid);
+               break;
+       case IPL_METHOD_CCW_VM:
+               dump_smp_stop_all();
+               sprintf(buf, "STORE STATUS");
+               cpcmd(buf, NULL, 0, NULL);
+               sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
+               cpcmd(buf, NULL, 0, NULL);
+               break;
+       case IPL_METHOD_CCW_DIAG:
+               diag308(DIAG308_SET, dump_block_ccw);
+               diag308(DIAG308_DUMP, NULL);
+               break;
+       case IPL_METHOD_FCP_RW_DIAG:
+               diag308(DIAG308_SET, dump_block_fcp);
+               diag308(DIAG308_DUMP, NULL);
+               break;
+       case IPL_METHOD_NONE:
+       default:
+               return;
+       }
+       printk(KERN_EMERG "Dump failed!\n");
+}
+
+/* init functions */
+
+static int __init ipl_register_fcp_files(void)
+{
+       int rc;
+
+       rc = sysfs_create_group(&ipl_subsys.kset.kobj,
+                               &ipl_fcp_attr_group);
+       if (rc)
+               goto out;
+       rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
+                                  &ipl_parameter_attr);
+       if (rc)
+               goto out_ipl_parm;
+       rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
+                                  &ipl_scp_data_attr);
+       if (!rc)
+               goto out;
+
+       sysfs_remove_bin_file(&ipl_subsys.kset.kobj, &ipl_parameter_attr);
+
+out_ipl_parm:
+       sysfs_remove_group(&ipl_subsys.kset.kobj, &ipl_fcp_attr_group);
+out:
+       return rc;
+}
+
+static int __init ipl_init(void)
+{
+       int rc;
+
+       rc = firmware_register(&ipl_subsys);
+       if (rc)
+               return rc;
+       switch (ipl_get_type()) {
+       case IPL_TYPE_CCW:
+               rc = sysfs_create_group(&ipl_subsys.kset.kobj,
+                                       &ipl_ccw_attr_group);
+               break;
+       case IPL_TYPE_FCP:
+               rc = ipl_register_fcp_files();
+               break;
+       default:
+               rc = sysfs_create_group(&ipl_subsys.kset.kobj,
+                                       &ipl_unknown_attr_group);
+               break;
+       }
+       if (rc)
+               firmware_unregister(&ipl_subsys);
+       return rc;
+}
+
+static void __init reipl_probe(void)
+{
+       void *buffer;
+
+       buffer = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!buffer)
+               return;
+       if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK)
+               diag308_set_works = 1;
+       free_page((unsigned long)buffer);
+}
+
+static int __init reipl_ccw_init(void)
+{
+       int rc;
+
+       reipl_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!reipl_block_ccw)
+               return -ENOMEM;
+       rc = sysfs_create_group(&reipl_subsys.kset.kobj, &reipl_ccw_attr_group);
+       if (rc) {
+               free_page((unsigned long)reipl_block_ccw);
+               return rc;
+       }
+       reipl_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
+       reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
+       reipl_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw);
+       reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
+       if (ipl_get_type() == IPL_TYPE_CCW)
+               reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
+       reipl_capabilities |= IPL_TYPE_CCW;
+       return 0;
+}
+
+static int __init reipl_fcp_init(void)
+{
+       int rc;
+
+       if ((!diag308_set_works) && (ipl_get_type() != IPL_TYPE_FCP))
+               return 0;
+       if ((!diag308_set_works) && (ipl_get_type() == IPL_TYPE_FCP))
+               make_attrs_ro(reipl_fcp_attrs);
+
+       reipl_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!reipl_block_fcp)
+               return -ENOMEM;
+       rc = sysfs_create_group(&reipl_subsys.kset.kobj, &reipl_fcp_attr_group);
+       if (rc) {
+               free_page((unsigned long)reipl_block_fcp);
+               return rc;
+       }
+       if (ipl_get_type() == IPL_TYPE_FCP) {
+               memcpy(reipl_block_fcp, IPL_PARMBLOCK_START, PAGE_SIZE);
+       } else {
+               reipl_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
+               reipl_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
+               reipl_block_fcp->hdr.blk0_len =
+                       sizeof(reipl_block_fcp->ipl_info.fcp);
+               reipl_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
+               reipl_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_IPL;
+       }
+       reipl_capabilities |= IPL_TYPE_FCP;
+       return 0;
+}
+
+static int __init reipl_init(void)
+{
+       int rc;
+
+       rc = firmware_register(&reipl_subsys);
+       if (rc)
+               return rc;
+       rc = subsys_create_file(&reipl_subsys, &reipl_type_attr);
+       if (rc) {
+               firmware_unregister(&reipl_subsys);
+               return rc;
+       }
+       rc = reipl_ccw_init();
+       if (rc)
+               return rc;
+       rc = reipl_fcp_init();
+       if (rc)
+               return rc;
+       rc = reipl_set_type(ipl_get_type());
+       if (rc)
+               return rc;
+       return 0;
+}
+
+static int __init dump_ccw_init(void)
+{
+       int rc;
+
+       dump_block_ccw = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!dump_block_ccw)
+               return -ENOMEM;
+       rc = sysfs_create_group(&dump_subsys.kset.kobj, &dump_ccw_attr_group);
+       if (rc) {
+               free_page((unsigned long)dump_block_ccw);
+               return rc;
+       }
+       dump_block_ccw->hdr.len = IPL_PARM_BLK_CCW_LEN;
+       dump_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
+       dump_block_ccw->hdr.blk0_len = sizeof(reipl_block_ccw->ipl_info.ccw);
+       dump_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
+       dump_capabilities |= IPL_TYPE_CCW;
+       return 0;
+}
+
+extern char s390_readinfo_sccb[];
+
+static int __init dump_fcp_init(void)
+{
+       int rc;
+
+       if(!(s390_readinfo_sccb[91] & 0x2))
+               return 0; /* LDIPL DUMP is not installed */
+       if (!diag308_set_works)
+               return 0;
+       dump_block_fcp = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!dump_block_fcp)
+               return -ENOMEM;
+       rc = sysfs_create_group(&dump_subsys.kset.kobj, &dump_fcp_attr_group);
+       if (rc) {
+               free_page((unsigned long)dump_block_fcp);
+               return rc;
+       }
+       dump_block_fcp->hdr.len = IPL_PARM_BLK_FCP_LEN;
+       dump_block_fcp->hdr.version = IPL_PARM_BLOCK_VERSION;
+       dump_block_fcp->hdr.blk0_len = sizeof(dump_block_fcp->ipl_info.fcp);
+       dump_block_fcp->hdr.pbt = DIAG308_IPL_TYPE_FCP;
+       dump_block_fcp->ipl_info.fcp.opt = DIAG308_IPL_OPT_DUMP;
+       dump_capabilities |= IPL_TYPE_FCP;
+       return 0;
+}
+
+#define SHUTDOWN_ON_PANIC_PRIO 0
+
+static int shutdown_on_panic_notify(struct notifier_block *self,
+                                   unsigned long event, void *data)
+{
+       if (on_panic_action == SHUTDOWN_DUMP)
+               do_dump();
+       else if (on_panic_action == SHUTDOWN_REIPL)
+               do_reipl();
+       return NOTIFY_OK;
+}
+
+static struct notifier_block shutdown_on_panic_nb = {
+       .notifier_call = shutdown_on_panic_notify,
+       .priority = SHUTDOWN_ON_PANIC_PRIO
+};
+
+static int __init dump_init(void)
+{
+       int rc;
+
+       rc = firmware_register(&dump_subsys);
+       if (rc)
+               return rc;
+       rc = subsys_create_file(&dump_subsys, &dump_type_attr);
+       if (rc) {
+               firmware_unregister(&dump_subsys);
+               return rc;
+       }
+       rc = dump_ccw_init();
+       if (rc)
+               return rc;
+       rc = dump_fcp_init();
+       if (rc)
+               return rc;
+       dump_set_type(IPL_TYPE_NONE);
+       return 0;
+}
+
+static int __init shutdown_actions_init(void)
+{
+       int rc;
+
+       rc = firmware_register(&shutdown_actions_subsys);
+       if (rc)
+               return rc;
+       rc = subsys_create_file(&shutdown_actions_subsys, &on_panic_attr);
+       if (rc) {
+               firmware_unregister(&shutdown_actions_subsys);
+               return rc;
+       }
+       atomic_notifier_chain_register(&panic_notifier_list,
+                                      &shutdown_on_panic_nb);
+       return 0;
+}
+
+static int __init s390_ipl_init(void)
+{
+       int rc;
+
+       reipl_probe();
+       rc = ipl_init();
+       if (rc)
+               return rc;
+       rc = reipl_init();
+       if (rc)
+               return rc;
+       rc = dump_init();
+       if (rc)
+               return rc;
+       rc = shutdown_actions_init();
+       if (rc)
+               return rc;
+       return 0;
+}
+
+__initcall(s390_ipl_init);
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
new file mode 100644 (file)
index 0000000..ca28fb0
--- /dev/null
@@ -0,0 +1,657 @@
+/*
+ *  Kernel Probes (KProbes)
+ *
+ * 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.
+ *
+ * Copyright (C) IBM Corporation, 2002, 2006
+ *
+ * s390 port, used ppc64 as template. Mike Grundy <grundym@us.ibm.com>
+ */
+
+#include <linux/config.h>
+#include <linux/kprobes.h>
+#include <linux/ptrace.h>
+#include <linux/preempt.h>
+#include <linux/stop_machine.h>
+#include <asm/cacheflush.h>
+#include <asm/kdebug.h>
+#include <asm/sections.h>
+#include <asm/uaccess.h>
+#include <linux/module.h>
+
+DEFINE_PER_CPU(struct kprobe *, current_kprobe) = NULL;
+DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
+
+int __kprobes arch_prepare_kprobe(struct kprobe *p)
+{
+       /* Make sure the probe isn't going on a difficult instruction */
+       if (is_prohibited_opcode((kprobe_opcode_t *) p->addr))
+               return -EINVAL;
+
+       if ((unsigned long)p->addr & 0x01) {
+               printk("Attempt to register kprobe at an unaligned address\n");
+               return -EINVAL;
+               }
+
+       /* Use the get_insn_slot() facility for correctness */
+       if (!(p->ainsn.insn = get_insn_slot()))
+               return -ENOMEM;
+
+       memcpy(p->ainsn.insn, p->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
+
+       get_instruction_type(&p->ainsn);
+       p->opcode = *p->addr;
+       return 0;
+}
+
+int __kprobes is_prohibited_opcode(kprobe_opcode_t *instruction)
+{
+       switch (*(__u8 *) instruction) {
+       case 0x0c:      /* bassm */
+       case 0x0b:      /* bsm   */
+       case 0x83:      /* diag  */
+       case 0x44:      /* ex    */
+               return -EINVAL;
+       }
+       switch (*(__u16 *) instruction) {
+       case 0x0101:    /* pr    */
+       case 0xb25a:    /* bsa   */
+       case 0xb240:    /* bakr  */
+       case 0xb258:    /* bsg   */
+       case 0xb218:    /* pc    */
+       case 0xb228:    /* pt    */
+               return -EINVAL;
+       }
+       return 0;
+}
+
+void __kprobes get_instruction_type(struct arch_specific_insn *ainsn)
+{
+       /* default fixup method */
+       ainsn->fixup = FIXUP_PSW_NORMAL;
+
+       /* save r1 operand */
+       ainsn->reg = (*ainsn->insn & 0xf0) >> 4;
+
+       /* save the instruction length (pop 5-5) in bytes */
+       switch (*(__u8 *) (ainsn->insn) >> 4) {
+       case 0:
+               ainsn->ilen = 2;
+               break;
+       case 1:
+       case 2:
+               ainsn->ilen = 4;
+               break;
+       case 3:
+               ainsn->ilen = 6;
+               break;
+       }
+
+       switch (*(__u8 *) ainsn->insn) {
+       case 0x05:      /* balr */
+       case 0x0d:      /* basr */
+               ainsn->fixup = FIXUP_RETURN_REGISTER;
+               /* if r2 = 0, no branch will be taken */
+               if ((*ainsn->insn & 0x0f) == 0)
+                       ainsn->fixup |= FIXUP_BRANCH_NOT_TAKEN;
+               break;
+       case 0x06:      /* bctr */
+       case 0x07:      /* bcr  */
+               ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN;
+               break;
+       case 0x45:      /* bal  */
+       case 0x4d:      /* bas  */
+               ainsn->fixup = FIXUP_RETURN_REGISTER;
+               break;
+       case 0x47:      /* bc   */
+       case 0x46:      /* bct  */
+       case 0x86:      /* bxh  */
+       case 0x87:      /* bxle */
+               ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN;
+               break;
+       case 0x82:      /* lpsw */
+               ainsn->fixup = FIXUP_NOT_REQUIRED;
+               break;
+       case 0xb2:      /* lpswe */
+               if (*(((__u8 *) ainsn->insn) + 1) == 0xb2) {
+                       ainsn->fixup = FIXUP_NOT_REQUIRED;
+               }
+               break;
+       case 0xa7:      /* bras */
+               if ((*ainsn->insn & 0x0f) == 0x05) {
+                       ainsn->fixup |= FIXUP_RETURN_REGISTER;
+               }
+               break;
+       case 0xc0:
+               if ((*ainsn->insn & 0x0f) == 0x00  /* larl  */
+                       || (*ainsn->insn & 0x0f) == 0x05) /* brasl */
+               ainsn->fixup |= FIXUP_RETURN_REGISTER;
+               break;
+       case 0xeb:
+               if (*(((__u8 *) ainsn->insn) + 5 ) == 0x44 ||   /* bxhg  */
+                       *(((__u8 *) ainsn->insn) + 5) == 0x45) {/* bxleg */
+                       ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN;
+               }
+               break;
+       case 0xe3:      /* bctg */
+               if (*(((__u8 *) ainsn->insn) + 5) == 0x46) {
+                       ainsn->fixup = FIXUP_BRANCH_NOT_TAKEN;
+               }
+               break;
+       }
+}
+
+static int __kprobes swap_instruction(void *aref)
+{
+       struct ins_replace_args *args = aref;
+       int err = -EFAULT;
+
+       asm volatile(
+               "0: mvc  0(2,%2),0(%3)\n"
+               "1: la   %0,0\n"
+               "2:\n"
+               EX_TABLE(0b,2b)
+               : "+d" (err), "=m" (*args->ptr)
+               : "a" (args->ptr), "a" (&args->new), "m" (args->new));
+       return err;
+}
+
+void __kprobes arch_arm_kprobe(struct kprobe *p)
+{
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+       unsigned long status = kcb->kprobe_status;
+       struct ins_replace_args args;
+
+       args.ptr = p->addr;
+       args.old = p->opcode;
+       args.new = BREAKPOINT_INSTRUCTION;
+
+       kcb->kprobe_status = KPROBE_SWAP_INST;
+       stop_machine_run(swap_instruction, &args, NR_CPUS);
+       kcb->kprobe_status = status;
+}
+
+void __kprobes arch_disarm_kprobe(struct kprobe *p)
+{
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+       unsigned long status = kcb->kprobe_status;
+       struct ins_replace_args args;
+
+       args.ptr = p->addr;
+       args.old = BREAKPOINT_INSTRUCTION;
+       args.new = p->opcode;
+
+       kcb->kprobe_status = KPROBE_SWAP_INST;
+       stop_machine_run(swap_instruction, &args, NR_CPUS);
+       kcb->kprobe_status = status;
+}
+
+void __kprobes arch_remove_kprobe(struct kprobe *p)
+{
+       mutex_lock(&kprobe_mutex);
+       free_insn_slot(p->ainsn.insn);
+       mutex_unlock(&kprobe_mutex);
+}
+
+static void __kprobes prepare_singlestep(struct kprobe *p, struct pt_regs *regs)
+{
+       per_cr_bits kprobe_per_regs[1];
+
+       memset(kprobe_per_regs, 0, sizeof(per_cr_bits));
+       regs->psw.addr = (unsigned long)p->ainsn.insn | PSW_ADDR_AMODE;
+
+       /* Set up the per control reg info, will pass to lctl */
+       kprobe_per_regs[0].em_instruction_fetch = 1;
+       kprobe_per_regs[0].starting_addr = (unsigned long)p->ainsn.insn;
+       kprobe_per_regs[0].ending_addr = (unsigned long)p->ainsn.insn + 1;
+
+       /* Set the PER control regs, turns on single step for this address */
+       __ctl_load(kprobe_per_regs, 9, 11);
+       regs->psw.mask |= PSW_MASK_PER;
+       regs->psw.mask &= ~(PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK);
+}
+
+static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+       kcb->prev_kprobe.kp = kprobe_running();
+       kcb->prev_kprobe.status = kcb->kprobe_status;
+       kcb->prev_kprobe.kprobe_saved_imask = kcb->kprobe_saved_imask;
+       memcpy(kcb->prev_kprobe.kprobe_saved_ctl, kcb->kprobe_saved_ctl,
+                                       sizeof(kcb->kprobe_saved_ctl));
+}
+
+static void __kprobes restore_previous_kprobe(struct kprobe_ctlblk *kcb)
+{
+       __get_cpu_var(current_kprobe) = kcb->prev_kprobe.kp;
+       kcb->kprobe_status = kcb->prev_kprobe.status;
+       kcb->kprobe_saved_imask = kcb->prev_kprobe.kprobe_saved_imask;
+       memcpy(kcb->kprobe_saved_ctl, kcb->prev_kprobe.kprobe_saved_ctl,
+                                       sizeof(kcb->kprobe_saved_ctl));
+}
+
+static void __kprobes set_current_kprobe(struct kprobe *p, struct pt_regs *regs,
+                                               struct kprobe_ctlblk *kcb)
+{
+       __get_cpu_var(current_kprobe) = p;
+       /* Save the interrupt and per flags */
+       kcb->kprobe_saved_imask = regs->psw.mask &
+           (PSW_MASK_PER | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK);
+       /* Save the control regs that govern PER */
+       __ctl_store(kcb->kprobe_saved_ctl, 9, 11);
+}
+
+/* Called with kretprobe_lock held */
+void __kprobes arch_prepare_kretprobe(struct kretprobe *rp,
+                                       struct pt_regs *regs)
+{
+       struct kretprobe_instance *ri;
+
+       if ((ri = get_free_rp_inst(rp)) != NULL) {
+               ri->rp = rp;
+               ri->task = current;
+               ri->ret_addr = (kprobe_opcode_t *) regs->gprs[14];
+
+               /* Replace the return addr with trampoline addr */
+               regs->gprs[14] = (unsigned long)&kretprobe_trampoline;
+
+               add_rp_inst(ri);
+       } else {
+               rp->nmissed++;
+       }
+}
+
+static int __kprobes kprobe_handler(struct pt_regs *regs)
+{
+       struct kprobe *p;
+       int ret = 0;
+       unsigned long *addr = (unsigned long *)
+               ((regs->psw.addr & PSW_ADDR_INSN) - 2);
+       struct kprobe_ctlblk *kcb;
+
+       /*
+        * We don't want to be preempted for the entire
+        * duration of kprobe processing
+        */
+       preempt_disable();
+       kcb = get_kprobe_ctlblk();
+
+       /* Check we're not actually recursing */
+       if (kprobe_running()) {
+               p = get_kprobe(addr);
+               if (p) {
+                       if (kcb->kprobe_status == KPROBE_HIT_SS &&
+                           *p->ainsn.insn == BREAKPOINT_INSTRUCTION) {
+                               regs->psw.mask &= ~PSW_MASK_PER;
+                               regs->psw.mask |= kcb->kprobe_saved_imask;
+                               goto no_kprobe;
+                       }
+                       /* We have reentered the kprobe_handler(), since
+                        * another probe was hit while within the handler.
+                        * We here save the original kprobes variables and
+                        * just single step on the instruction of the new probe
+                        * without calling any user handlers.
+                        */
+                       save_previous_kprobe(kcb);
+                       set_current_kprobe(p, regs, kcb);
+                       kprobes_inc_nmissed_count(p);
+                       prepare_singlestep(p, regs);
+                       kcb->kprobe_status = KPROBE_REENTER;
+                       return 1;
+               } else {
+                       p = __get_cpu_var(current_kprobe);
+                       if (p->break_handler && p->break_handler(p, regs)) {
+                               goto ss_probe;
+                       }
+               }
+               goto no_kprobe;
+       }
+
+       p = get_kprobe(addr);
+       if (!p) {
+               if (*addr != BREAKPOINT_INSTRUCTION) {
+                       /*
+                        * The breakpoint instruction was removed right
+                        * after we hit it.  Another cpu has removed
+                        * either a probepoint or a debugger breakpoint
+                        * at this address.  In either case, no further
+                        * handling of this interrupt is appropriate.
+                        *
+                        */
+                       ret = 1;
+               }
+               /* Not one of ours: let kernel handle it */
+               goto no_kprobe;
+       }
+
+       kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+       set_current_kprobe(p, regs, kcb);
+       if (p->pre_handler && p->pre_handler(p, regs))
+               /* handler has already set things up, so skip ss setup */
+               return 1;
+
+ss_probe:
+       prepare_singlestep(p, regs);
+       kcb->kprobe_status = KPROBE_HIT_SS;
+       return 1;
+
+no_kprobe:
+       preempt_enable_no_resched();
+       return ret;
+}
+
+/*
+ * Function return probe trampoline:
+ *     - init_kprobes() establishes a probepoint here
+ *     - When the probed function returns, this probe
+ *             causes the handlers to fire
+ */
+void __kprobes kretprobe_trampoline_holder(void)
+{
+       asm volatile(".global kretprobe_trampoline\n"
+                    "kretprobe_trampoline: bcr 0,0\n");
+}
+
+/*
+ * Called when the probe at kretprobe trampoline is hit
+ */
+int __kprobes trampoline_probe_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       struct kretprobe_instance *ri = NULL;
+       struct hlist_head *head;
+       struct hlist_node *node, *tmp;
+       unsigned long flags, orig_ret_address = 0;
+       unsigned long trampoline_address = (unsigned long)&kretprobe_trampoline;
+
+       spin_lock_irqsave(&kretprobe_lock, flags);
+       head = kretprobe_inst_table_head(current);
+
+       /*
+        * It is possible to have multiple instances associated with a given
+        * task either because an multiple functions in the call path
+        * have a return probe installed on them, and/or more then one return
+        * return probe was registered for a target function.
+        *
+        * We can handle this because:
+        *     - instances are always inserted at the head of the list
+        *     - when multiple return probes are registered for the same
+        *       function, the first instance's ret_addr will point to the
+        *       real return address, and all the rest will point to
+        *       kretprobe_trampoline
+        */
+       hlist_for_each_entry_safe(ri, node, tmp, head, hlist) {
+               if (ri->task != current)
+                       /* another task is sharing our hash bucket */
+                       continue;
+
+               if (ri->rp && ri->rp->handler)
+                       ri->rp->handler(ri, regs);
+
+               orig_ret_address = (unsigned long)ri->ret_addr;
+               recycle_rp_inst(ri);
+
+               if (orig_ret_address != trampoline_address) {
+                       /*
+                        * This is the real return address. Any other
+                        * instances associated with this task are for
+                        * other calls deeper on the call stack
+                        */
+                       break;
+               }
+       }
+       BUG_ON(!orig_ret_address || (orig_ret_address == trampoline_address));
+       regs->psw.addr = orig_ret_address | PSW_ADDR_AMODE;
+
+       reset_current_kprobe();
+       spin_unlock_irqrestore(&kretprobe_lock, flags);
+       preempt_enable_no_resched();
+
+       /*
+        * By returning a non-zero value, we are telling
+        * kprobe_handler() that we don't want the post_handler
+        * to run (and have re-enabled preemption)
+        */
+       return 1;
+}
+
+/*
+ * Called after single-stepping.  p->addr is the address of the
+ * instruction whose first byte has been replaced by the "breakpoint"
+ * instruction.  To avoid the SMP problems that can occur when we
+ * temporarily put back the original opcode to single-step, we
+ * single-stepped a copy of the instruction.  The address of this
+ * copy is p->ainsn.insn.
+ */
+static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs)
+{
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+       regs->psw.addr &= PSW_ADDR_INSN;
+
+       if (p->ainsn.fixup & FIXUP_PSW_NORMAL)
+               regs->psw.addr = (unsigned long)p->addr +
+                               ((unsigned long)regs->psw.addr -
+                                (unsigned long)p->ainsn.insn);
+
+       if (p->ainsn.fixup & FIXUP_BRANCH_NOT_TAKEN)
+               if ((unsigned long)regs->psw.addr -
+                   (unsigned long)p->ainsn.insn == p->ainsn.ilen)
+                       regs->psw.addr = (unsigned long)p->addr + p->ainsn.ilen;
+
+       if (p->ainsn.fixup & FIXUP_RETURN_REGISTER)
+               regs->gprs[p->ainsn.reg] = ((unsigned long)p->addr +
+                                               (regs->gprs[p->ainsn.reg] -
+                                               (unsigned long)p->ainsn.insn))
+                                               | PSW_ADDR_AMODE;
+
+       regs->psw.addr |= PSW_ADDR_AMODE;
+       /* turn off PER mode */
+       regs->psw.mask &= ~PSW_MASK_PER;
+       /* Restore the original per control regs */
+       __ctl_load(kcb->kprobe_saved_ctl, 9, 11);
+       regs->psw.mask |= kcb->kprobe_saved_imask;
+}
+
+static int __kprobes post_kprobe_handler(struct pt_regs *regs)
+{
+       struct kprobe *cur = kprobe_running();
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+       if (!cur)
+               return 0;
+
+       if ((kcb->kprobe_status != KPROBE_REENTER) && cur->post_handler) {
+               kcb->kprobe_status = KPROBE_HIT_SSDONE;
+               cur->post_handler(cur, regs, 0);
+       }
+
+       resume_execution(cur, regs);
+
+       /*Restore back the original saved kprobes variables and continue. */
+       if (kcb->kprobe_status == KPROBE_REENTER) {
+               restore_previous_kprobe(kcb);
+               goto out;
+       }
+       reset_current_kprobe();
+out:
+       preempt_enable_no_resched();
+
+       /*
+        * if somebody else is singlestepping across a probe point, psw mask
+        * will have PER set, in which case, continue the remaining processing
+        * of do_single_step, as if this is not a probe hit.
+        */
+       if (regs->psw.mask & PSW_MASK_PER) {
+               return 0;
+       }
+
+       return 1;
+}
+
+static int __kprobes kprobe_fault_handler(struct pt_regs *regs, int trapnr)
+{
+       struct kprobe *cur = kprobe_running();
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+       const struct exception_table_entry *entry;
+
+       switch(kcb->kprobe_status) {
+       case KPROBE_SWAP_INST:
+               /* We are here because the instruction replacement failed */
+               return 0;
+       case KPROBE_HIT_SS:
+       case KPROBE_REENTER:
+               /*
+                * We are here because the instruction being single
+                * stepped caused a page fault. We reset the current
+                * kprobe and the nip points back to the probe address
+                * and allow the page fault handler to continue as a
+                * normal page fault.
+                */
+               regs->psw.addr = (unsigned long)cur->addr | PSW_ADDR_AMODE;
+               regs->psw.mask &= ~PSW_MASK_PER;
+               regs->psw.mask |= kcb->kprobe_saved_imask;
+               if (kcb->kprobe_status == KPROBE_REENTER)
+                       restore_previous_kprobe(kcb);
+               else
+                       reset_current_kprobe();
+               preempt_enable_no_resched();
+               break;
+       case KPROBE_HIT_ACTIVE:
+       case KPROBE_HIT_SSDONE:
+               /*
+                * We increment the nmissed count for accounting,
+                * we can also use npre/npostfault count for accouting
+                * these specific fault cases.
+                */
+               kprobes_inc_nmissed_count(cur);
+
+               /*
+                * We come here because instructions in the pre/post
+                * handler caused the page_fault, this could happen
+                * if handler tries to access user space by
+                * copy_from_user(), get_user() etc. Let the
+                * user-specified handler try to fix it first.
+                */
+               if (cur->fault_handler && cur->fault_handler(cur, regs, trapnr))
+                       return 1;
+
+               /*
+                * In case the user-specified fault handler returned
+                * zero, try to fix up.
+                */
+               entry = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
+               if (entry) {
+                       regs->psw.addr = entry->fixup | PSW_ADDR_AMODE;
+                       return 1;
+               }
+
+               /*
+                * fixup_exception() could not handle it,
+                * Let do_page_fault() fix it.
+                */
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
+
+/*
+ * Wrapper routine to for handling exceptions.
+ */
+int __kprobes kprobe_exceptions_notify(struct notifier_block *self,
+                                      unsigned long val, void *data)
+{
+       struct die_args *args = (struct die_args *)data;
+       int ret = NOTIFY_DONE;
+
+       switch (val) {
+       case DIE_BPT:
+               if (kprobe_handler(args->regs))
+                       ret = NOTIFY_STOP;
+               break;
+       case DIE_SSTEP:
+               if (post_kprobe_handler(args->regs))
+                       ret = NOTIFY_STOP;
+               break;
+       case DIE_TRAP:
+       case DIE_PAGE_FAULT:
+               /* kprobe_running() needs smp_processor_id() */
+               preempt_disable();
+               if (kprobe_running() &&
+                   kprobe_fault_handler(args->regs, args->trapnr))
+                       ret = NOTIFY_STOP;
+               preempt_enable();
+               break;
+       default:
+               break;
+       }
+       return ret;
+}
+
+int __kprobes setjmp_pre_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       struct jprobe *jp = container_of(p, struct jprobe, kp);
+       unsigned long addr;
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+
+       memcpy(&kcb->jprobe_saved_regs, regs, sizeof(struct pt_regs));
+
+       /* setup return addr to the jprobe handler routine */
+       regs->psw.addr = (unsigned long)(jp->entry) | PSW_ADDR_AMODE;
+
+       /* r14 is the function return address */
+       kcb->jprobe_saved_r14 = (unsigned long)regs->gprs[14];
+       /* r15 is the stack pointer */
+       kcb->jprobe_saved_r15 = (unsigned long)regs->gprs[15];
+       addr = (unsigned long)kcb->jprobe_saved_r15;
+
+       memcpy(kcb->jprobes_stack, (kprobe_opcode_t *) addr,
+              MIN_STACK_SIZE(addr));
+       return 1;
+}
+
+void __kprobes jprobe_return(void)
+{
+       asm volatile(".word 0x0002");
+}
+
+void __kprobes jprobe_return_end(void)
+{
+       asm volatile("bcr 0,0");
+}
+
+int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
+{
+       struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
+       unsigned long stack_addr = (unsigned long)(kcb->jprobe_saved_r15);
+
+       /* Put the regs back */
+       memcpy(regs, &kcb->jprobe_saved_regs, sizeof(struct pt_regs));
+       /* put the stack back */
+       memcpy((kprobe_opcode_t *) stack_addr, kcb->jprobes_stack,
+              MIN_STACK_SIZE(stack_addr));
+       preempt_enable_no_resched();
+       return 1;
+}
+
+static struct kprobe trampoline_p = {
+       .addr = (kprobe_opcode_t *) & kretprobe_trampoline,
+       .pre_handler = trampoline_probe_handler
+};
+
+int __init arch_init_kprobes(void)
+{
+       return register_kprobe(&trampoline_p);
+}
index 658e5ac..4562cdb 100644 (file)
@@ -8,13 +8,30 @@
 
 #include <asm/lowcore.h>
 
-               .globl  do_reipl
-do_reipl:      basr    %r13,0
+               .globl  do_reipl_asm
+do_reipl_asm:  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)
+
+               # switch off lowcore protection
+
+.Lpg1:         stctl   %c0,%c0,.Lctlsave1-.Lpg0(%r13)
+               stctl   %c0,%c0,.Lctlsave2-.Lpg0(%r13)
+               ni      .Lctlsave1-.Lpg0(%r13),0xef
+               lctl    %c0,%c0,.Lctlsave1-.Lpg0(%r13)
+
+               # do store status of all registers
+
+               stm     %r0,%r15,__LC_GPREGS_SAVE_AREA
+               stctl   %c0,%c15,__LC_CREGS_SAVE_AREA
+               mvc     __LC_CREGS_SAVE_AREA(4),.Lctlsave2-.Lpg0(%r13)
+               stam    %a0,%a15,__LC_AREGS_SAVE_AREA
+               stpx    __LC_PREFIX_SAVE_AREA
+               stckc   .Lclkcmp-.Lpg0(%r13)
+               mvc     __LC_CLOCK_COMP_SAVE_AREA(8),.Lclkcmp-.Lpg0(%r13)
+               stpt    __LC_CPU_TIMER_SAVE_AREA
+               st      %r13, __LC_PSW_SAVE_AREA+4
+
+               lctl    %c6,%c6,.Lall-.Lpg0(%r13)
                 lr      %r1,%r2
                mvc     __LC_PGM_NEW_PSW(8),.Lpcnew-.Lpg0(%r13)
                 stsch   .Lschib-.Lpg0(%r13)                                    
@@ -46,9 +63,11 @@ do_reipl:    basr    %r13,0
 .Ldisab:       st      %r14,.Ldispsw+4-.Lpg0(%r13)
                lpsw    .Ldispsw-.Lpg0(%r13)
                 .align         8
+.Lclkcmp:      .quad   0x0000000000000000
 .Lall:         .long   0xff000000
 .Lnull:                .long   0x00000000
-.Lctlsave:      .long   0x00000000
+.Lctlsave1:    .long   0x00000000
+.Lctlsave2:    .long   0x00000000
                 .align         8
 .Lnewpsw:      .long   0x00080000,0x80000000+.Lpg1
 .Lpcnew:       .long   0x00080000,0x80000000+.Lecs
index 4d090d6..95bd1e2 100644 (file)
@@ -8,13 +8,30 @@
  */
 
 #include <asm/lowcore.h>
-               .globl  do_reipl
-do_reipl:      basr    %r13,0
-.Lpg0:         lpswe   .Lnewpsw-.Lpg0(%r13)
+               .globl  do_reipl_asm
+do_reipl_asm:  basr    %r13,0
+
+               # do store status of all registers
+
+.Lpg0:         stg     %r1,.Lregsave-.Lpg0(%r13)
+               lghi    %r1,0x1000
+               stmg    %r0,%r15,__LC_GPREGS_SAVE_AREA-0x1000(%r1)
+               lg      %r0,.Lregsave-.Lpg0(%r13)
+               stg     %r0,__LC_GPREGS_SAVE_AREA-0x1000+8(%r1)
+               stctg   %c0,%c15,__LC_CREGS_SAVE_AREA-0x1000(%r1)
+               stam    %a0,%a15,__LC_AREGS_SAVE_AREA-0x1000(%r1)
+               stpx    __LC_PREFIX_SAVE_AREA-0x1000(%r1)
+               stfpc   __LC_FP_CREG_SAVE_AREA-0x1000(%r1)
+               stckc   .Lclkcmp-.Lpg0(%r13)
+               mvc     __LC_CLOCK_COMP_SAVE_AREA-0x1000(8,%r1),.Lclkcmp-.Lpg0(%r13)
+               stpt    __LC_CPU_TIMER_SAVE_AREA-0x1000(%r1)
+               stg     %r13, __LC_PSW_SAVE_AREA-0x1000+8(%r1)
+
+               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)
+               stctg   %c0,%c0,.Lregsave-.Lpg0(%r13)
+               ni      .Lregsave+4-.Lpg0(%r13),0xef
+               lctlg   %c0,%c0,.Lregsave-.Lpg0(%r13)
                 lgr     %r1,%r2
                mvc     __LC_PGM_NEW_PSW(16),.Lpcnew-.Lpg0(%r13)
                 stsch   .Lschib-.Lpg0(%r13)                                    
@@ -50,8 +67,9 @@ do_reipl:     basr    %r13,0
                st     %r14,.Ldispsw+12-.Lpg0(%r13)
                lpswe   .Ldispsw-.Lpg0(%r13)
                 .align         8
+.Lclkcmp:      .quad   0x0000000000000000
 .Lall:         .quad   0x00000000ff000000
-.Lctlsave:      .quad   0x0000000000000000
+.Lregsave:     .quad   0x0000000000000000
 .Lnull:                .long   0x0000000000000000
                 .align         16
 /*
@@ -92,5 +110,3 @@ do_reipl:    basr    %r13,0
                .long   0x00000000,0x00000000
                .long   0x00000000,0x00000000
        
-
-       
diff --git a/arch/s390/kernel/reipl_diag.c b/arch/s390/kernel/reipl_diag.c
deleted file mode 100644 (file)
index 1f33951..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * This file contains the implementation of the
- * Linux re-IPL support
- *
- * (C) Copyright IBM Corp. 2005
- *
- * Author(s): Volker Sameske (sameske@de.ibm.com)
- *
- */
-
-#include <linux/kernel.h>
-
-static unsigned int reipl_diag_rc1;
-static unsigned int reipl_diag_rc2;
-
-/*
- * re-IPL the system using the last used IPL parameters
- */
-void reipl_diag(void)
-{
-        asm volatile (
-               "   la   %%r4,0\n"
-               "   la   %%r5,0\n"
-                "   diag %%r4,%2,0x308\n"
-                "0:\n"
-               "   st   %%r4,%0\n"
-               "   st   %%r5,%1\n"
-                ".section __ex_table,\"a\"\n"
-#ifdef CONFIG_64BIT
-                "   .align 8\n"
-                "   .quad 0b, 0b\n"
-#else
-                "   .align 4\n"
-                "   .long 0b, 0b\n"
-#endif
-                ".previous\n"
-                : "=m" (reipl_diag_rc1), "=m" (reipl_diag_rc2)
-               : "d" (3) : "cc", "4", "5" );
-}
index c73a454..9f19e83 100644 (file)
@@ -25,12 +25,6 @@ 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);
 
 /*
index c902f05..e3d9325 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/kernel_stat.h>
 #include <linux/device.h>
 #include <linux/notifier.h>
+#include <linux/pfn.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/sections.h>
 
 /*
+ * User copy operations.
+ */
+struct uaccess_ops uaccess;
+EXPORT_SYMBOL_GPL(uaccess);
+
+/*
  * Machine setup..
  */
 unsigned int console_mode = 0;
@@ -284,16 +291,9 @@ void (*_machine_power_off)(void) = machine_power_off_smp;
 /*
  * Reboot, halt and power_off routines for non SMP.
  */
-extern void reipl(unsigned long devno);
-extern void reipl_diag(void);
 static void do_machine_restart_nonsmp(char * __unused)
 {
-       reipl_diag();
-
-       if (MACHINE_IS_VM)
-               cpcmd ("IPL", NULL, 0, NULL);
-       else
-               reipl (0x10000 | S390_lowcore.ipl_device);
+       do_reipl();
 }
 
 static void do_machine_halt_nonsmp(void)
@@ -501,13 +501,47 @@ setup_memory(void)
         * 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;
+       start_pfn = PFN_UP(__pa(&_end));
+       end_pfn = max_pfn = PFN_DOWN(memory_end);
 
        /* Initialize storage key for kernel pages */
        for (init_pfn = 0 ; init_pfn < start_pfn; init_pfn++)
                page_set_storage_key(init_pfn << PAGE_SHIFT, PAGE_DEFAULT_KEY);
 
+#ifdef CONFIG_BLK_DEV_INITRD
+       /*
+        * Move the initrd in case the bitmap of the bootmem allocater
+        * would overwrite it.
+        */
+
+       if (INITRD_START && INITRD_SIZE) {
+               unsigned long bmap_size;
+               unsigned long start;
+
+               bmap_size = bootmem_bootmap_pages(end_pfn - start_pfn + 1);
+               bmap_size = PFN_PHYS(bmap_size);
+
+               if (PFN_PHYS(start_pfn) + bmap_size > INITRD_START) {
+                       start = PFN_PHYS(start_pfn) + bmap_size + PAGE_SIZE;
+
+                       if (start + INITRD_SIZE > memory_end) {
+                               printk("initrd extends beyond end of memory "
+                                      "(0x%08lx > 0x%08lx)\n"
+                                      "disabling initrd\n",
+                                      start + INITRD_SIZE, memory_end);
+                               INITRD_START = INITRD_SIZE = 0;
+                       } else {
+                               printk("Moving initrd (0x%08lx -> 0x%08lx, "
+                                      "size: %ld)\n",
+                                      INITRD_START, start, INITRD_SIZE);
+                               memmove((void *) start, (void *) INITRD_START,
+                                       INITRD_SIZE);
+                               INITRD_START = start;
+                       }
+               }
+       }
+#endif
+
        /*
         * Initialize the boot-time allocator (with low memory only):
         */
@@ -559,7 +593,7 @@ setup_memory(void)
        reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size);
 
 #ifdef CONFIG_BLK_DEV_INITRD
-       if (INITRD_START) {
+       if (INITRD_START && INITRD_SIZE) {
                if (INITRD_START + INITRD_SIZE <= memory_end) {
                        reserve_bootmem(INITRD_START, INITRD_SIZE);
                        initrd_start = INITRD_START;
@@ -613,6 +647,11 @@ setup_arch(char **cmdline_p)
 
        memory_end = memory_size;
 
+       if (MACHINE_HAS_MVCOS)
+               memcpy(&uaccess, &uaccess_mvcos, sizeof(uaccess));
+       else
+               memcpy(&uaccess, &uaccess_std, sizeof(uaccess));
+
        parse_early_param();
 
 #ifndef CONFIG_64BIT
@@ -720,214 +759,3 @@ struct seq_operations cpuinfo_op = {
        .show   = show_cpuinfo,
 };
 
-#define DEFINE_IPL_ATTR(_name, _format, _value)                        \
-static ssize_t ipl_##_name##_show(struct subsystem *subsys,    \
-               char *page)                                     \
-{                                                              \
-       return sprintf(page, _format, _value);                  \
-}                                                              \
-static struct subsys_attribute ipl_##_name##_attr =            \
-       __ATTR(_name, S_IRUGO, ipl_##_name##_show, NULL);
-
-DEFINE_IPL_ATTR(wwpn, "0x%016llx\n", (unsigned long long)
-               IPL_PARMBLOCK_START->fcp.wwpn);
-DEFINE_IPL_ATTR(lun, "0x%016llx\n", (unsigned long long)
-               IPL_PARMBLOCK_START->fcp.lun);
-DEFINE_IPL_ATTR(bootprog, "%lld\n", (unsigned long long)
-               IPL_PARMBLOCK_START->fcp.bootprog);
-DEFINE_IPL_ATTR(br_lba, "%lld\n", (unsigned long long)
-               IPL_PARMBLOCK_START->fcp.br_lba);
-
-enum ipl_type_type {
-       ipl_type_unknown,
-       ipl_type_ccw,
-       ipl_type_fcp,
-};
-
-static enum ipl_type_type
-get_ipl_type(void)
-{
-       struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
-
-       if (!IPL_DEVNO_VALID)
-               return ipl_type_unknown;
-       if (!IPL_PARMBLOCK_VALID)
-               return ipl_type_ccw;
-       if (ipl->hdr.header.version > IPL_MAX_SUPPORTED_VERSION)
-               return ipl_type_unknown;
-       if (ipl->fcp.pbt != IPL_TYPE_FCP)
-               return ipl_type_unknown;
-       return ipl_type_fcp;
-}
-
-static ssize_t
-ipl_type_show(struct subsystem *subsys, char *page)
-{
-       switch (get_ipl_type()) {
-       case ipl_type_ccw:
-               return sprintf(page, "ccw\n");
-       case ipl_type_fcp:
-               return sprintf(page, "fcp\n");
-       default:
-               return sprintf(page, "unknown\n");
-       }
-}
-
-static struct subsys_attribute ipl_type_attr = __ATTR_RO(ipl_type);
-
-static ssize_t
-ipl_device_show(struct subsystem *subsys, char *page)
-{
-       struct ipl_parameter_block *ipl = IPL_PARMBLOCK_START;
-
-       switch (get_ipl_type()) {
-       case ipl_type_ccw:
-               return sprintf(page, "0.0.%04x\n", ipl_devno);
-       case ipl_type_fcp:
-               return sprintf(page, "0.0.%04x\n", ipl->fcp.devno);
-       default:
-               return 0;
-       }
-}
-
-static struct subsys_attribute ipl_device_attr =
-       __ATTR(device, S_IRUGO, ipl_device_show, NULL);
-
-static struct attribute *ipl_fcp_attrs[] = {
-       &ipl_type_attr.attr,
-       &ipl_device_attr.attr,
-       &ipl_wwpn_attr.attr,
-       &ipl_lun_attr.attr,
-       &ipl_bootprog_attr.attr,
-       &ipl_br_lba_attr.attr,
-       NULL,
-};
-
-static struct attribute_group ipl_fcp_attr_group = {
-       .attrs = ipl_fcp_attrs,
-};
-
-static struct attribute *ipl_ccw_attrs[] = {
-       &ipl_type_attr.attr,
-       &ipl_device_attr.attr,
-       NULL,
-};
-
-static struct attribute_group ipl_ccw_attr_group = {
-       .attrs = ipl_ccw_attrs,
-};
-
-static struct attribute *ipl_unknown_attrs[] = {
-       &ipl_type_attr.attr,
-       NULL,
-};
-
-static struct attribute_group ipl_unknown_attr_group = {
-       .attrs = ipl_unknown_attrs,
-};
-
-static ssize_t
-ipl_parameter_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
-{
-       unsigned int size = IPL_PARMBLOCK_SIZE;
-
-       if (off > size)
-               return 0;
-       if (off + count > size)
-               count = size - off;
-
-       memcpy(buf, (void *) IPL_PARMBLOCK_START + off, count);
-       return count;
-}
-
-static struct bin_attribute ipl_parameter_attr = {
-       .attr = {
-               .name = "binary_parameter",
-               .mode = S_IRUGO,
-               .owner = THIS_MODULE,
-       },
-       .size = PAGE_SIZE,
-       .read = &ipl_parameter_read,
-};
-
-static ssize_t
-ipl_scp_data_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
-{
-       unsigned int size =  IPL_PARMBLOCK_START->fcp.scp_data_len;
-       void *scp_data = &IPL_PARMBLOCK_START->fcp.scp_data;
-
-       if (off > size)
-               return 0;
-       if (off + count > size)
-               count = size - off;
-
-       memcpy(buf, scp_data + off, count);
-       return count;
-}
-
-static struct bin_attribute ipl_scp_data_attr = {
-       .attr = {
-               .name = "scp_data",
-               .mode = S_IRUGO,
-               .owner = THIS_MODULE,
-       },
-       .size = PAGE_SIZE,
-       .read = &ipl_scp_data_read,
-};
-
-static decl_subsys(ipl, NULL, NULL);
-
-static int ipl_register_fcp_files(void)
-{
-       int rc;
-
-       rc = sysfs_create_group(&ipl_subsys.kset.kobj,
-                               &ipl_fcp_attr_group);
-       if (rc)
-               goto out;
-       rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
-                                  &ipl_parameter_attr);
-       if (rc)
-               goto out_ipl_parm;
-       rc = sysfs_create_bin_file(&ipl_subsys.kset.kobj,
-                                  &ipl_scp_data_attr);
-       if (!rc)
-               goto out;
-
-       sysfs_remove_bin_file(&ipl_subsys.kset.kobj, &ipl_parameter_attr);
-
-out_ipl_parm:
-       sysfs_remove_group(&ipl_subsys.kset.kobj, &ipl_fcp_attr_group);
-out:
-       return rc;
-}
-
-static int __init
-ipl_device_sysfs_register(void) {
-       int rc;
-
-       rc = firmware_register(&ipl_subsys);
-       if (rc)
-               goto out;
-
-       switch (get_ipl_type()) {
-       case ipl_type_ccw:
-               rc = sysfs_create_group(&ipl_subsys.kset.kobj,
-                                       &ipl_ccw_attr_group);
-               break;
-       case ipl_type_fcp:
-               rc = ipl_register_fcp_files();
-               break;
-       default:
-               rc = sysfs_create_group(&ipl_subsys.kset.kobj,
-                                       &ipl_unknown_attr_group);
-               break;
-       }
-
-       if (rc)
-               firmware_unregister(&ipl_subsys);
-out:
-       return rc;
-}
-
-__initcall(ipl_device_sysfs_register);
index a887b68..642095e 100644 (file)
@@ -114,29 +114,26 @@ sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
 static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
 {
        unsigned long old_mask = regs->psw.mask;
-       int err;
-  
+       _sigregs user_sregs;
+
        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, &regs->psw,
-                            sizeof(sregs->regs.psw)+sizeof(sregs->regs.gprs));
+       memcpy(&user_sregs.regs.psw, &regs->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;
+       memcpy(&user_sregs.regs.acrs, current->thread.acrs,
+              sizeof(sregs->regs.acrs));
        /* 
         * We have to store the fp registers to current->thread.fp_regs
         * to merge them with the emulated registers.
         */
        save_fp_regs(&current->thread.fp_regs);
-       return __copy_to_user(&sregs->fpregs, &current->thread.fp_regs,
-                             sizeof(s390_fp_regs));
+       memcpy(&user_sregs.fpregs, &current->thread.fp_regs,
+              sizeof(s390_fp_regs));
+       return __copy_to_user(sregs, &user_sregs, sizeof(_sigregs));
 }
 
 /* Returns positive number on error */
@@ -144,27 +141,25 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
 {
        unsigned long old_mask = regs->psw.mask;
        int err;
+       _sigregs user_sregs;
 
        /* Alwys make any pending restarted system call return -EINTR */
        current_thread_info()->restart_block.fn = do_no_restart_syscall;
 
-       err = __copy_from_user(&regs->psw, &sregs->regs.psw,
-                              sizeof(sregs->regs.psw)+sizeof(sregs->regs.gprs));
+       err = __copy_from_user(&user_sregs, sregs, sizeof(_sigregs));
        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(&current->thread.acrs, &sregs->regs.acrs,
-                              sizeof(sregs->regs.acrs));
-       if (err)
-               return err;
+       memcpy(&regs->psw, &user_sregs.regs.psw, sizeof(sregs->regs.psw) +
+              sizeof(sregs->regs.gprs));
+       memcpy(&current->thread.acrs, &user_sregs.regs.acrs,
+              sizeof(sregs->regs.acrs));
        restore_access_regs(current->thread.acrs);
 
-       err = __copy_from_user(&current->thread.fp_regs, &sregs->fpregs,
-                              sizeof(s390_fp_regs));
+       memcpy(&current->thread.fp_regs, &user_sregs.fpregs,
+              sizeof(s390_fp_regs));
        current->thread.fp_regs.fpc &= FPC_VALID_MASK;
-       if (err)
-               return err;
 
        restore_fp_regs(&current->thread.fp_regs);
        regs->trap = -1;        /* disable syscall checks */
@@ -457,6 +452,7 @@ void do_signal(struct pt_regs *regs)
                case -ERESTART_RESTARTBLOCK:
                        regs->gprs[2] = -EINTR;
                }
+               regs->trap = -1;        /* Don't deal with this again. */
        }
 
        /* Get signal to deliver.  When running under ptrace, at this point
index 8e03219..b2e6f4c 100644 (file)
@@ -59,9 +59,6 @@ static struct task_struct *current_set[NR_CPUS];
 extern char vmhalt_cmd[];
 extern char vmpoff_cmd[];
 
-extern void reipl(unsigned long devno);
-extern void reipl_diag(void);
-
 static void smp_ext_bitcall(int, ec_bit_sig);
 static void smp_ext_bitcall_others(ec_bit_sig);
 
@@ -279,12 +276,7 @@ static void do_machine_restart(void * __unused)
         * interrupted by an external interrupt and s390irq
         * locks are always held disabled).
         */
-       reipl_diag();
-
-       if (MACHINE_IS_VM)
-               cpcmd ("IPL", NULL, 0, NULL);
-       else
-               reipl (0x10000 | S390_lowcore.ipl_device);
+       do_reipl();
 }
 
 void machine_restart_smp(char * __unused) 
index bde1d1d..c4982c9 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/kallsyms.h>
 #include <linux/reboot.h>
+#include <linux/kprobes.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -39,6 +40,7 @@
 #include <asm/s390_ext.h>
 #include <asm/lowcore.h>
 #include <asm/debug.h>
+#include <asm/kdebug.h>
 
 /* Called from entry.S only */
 extern void handle_per_exception(struct pt_regs *regs);
@@ -74,6 +76,20 @@ static int kstack_depth_to_print = 12;
 static int kstack_depth_to_print = 20;
 #endif /* CONFIG_64BIT */
 
+ATOMIC_NOTIFIER_HEAD(s390die_chain);
+
+int register_die_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&s390die_chain, nb);
+}
+EXPORT_SYMBOL(register_die_notifier);
+
+int unregister_die_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&s390die_chain, nb);
+}
+EXPORT_SYMBOL(unregister_die_notifier);
+
 /*
  * For show_trace we have tree different stack to consider:
  *   - the panic stack which is used if the kernel stack has overflown
@@ -305,8 +321,9 @@ report_user_fault(long interruption_code, struct pt_regs *regs)
 #endif
 }
 
-static void inline do_trap(long interruption_code, int signr, char *str,
-                           struct pt_regs *regs, siginfo_t *info)
+static void __kprobes 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
@@ -315,6 +332,10 @@ static void inline do_trap(long interruption_code, int signr, char *str,
         if (regs->psw.mask & PSW_MASK_PSTATE)
                local_irq_enable();
 
+       if (notify_die(DIE_TRAP, str, regs, interruption_code,
+                               interruption_code, signr) == NOTIFY_STOP)
+               return;
+
         if (regs->psw.mask & PSW_MASK_PSTATE) {
                 struct task_struct *tsk = current;
 
@@ -336,8 +357,12 @@ static inline void __user *get_check_address(struct pt_regs *regs)
        return (void __user *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN);
 }
 
-void do_single_step(struct pt_regs *regs)
+void __kprobes do_single_step(struct pt_regs *regs)
 {
+       if (notify_die(DIE_SSTEP, "sstep", regs, 0, 0,
+                                       SIGTRAP) == NOTIFY_STOP){
+               return;
+       }
        if ((current->ptrace & PT_PTRACED) != 0)
                force_sig(SIGTRAP, current);
 }
index ff5f7bb..af9e69a 100644 (file)
@@ -24,6 +24,7 @@ SECTIONS
        *(.text)
        SCHED_TEXT
        LOCK_TEXT
+       KPROBES_TEXT
        *(.fixup)
        *(.gnu.warning)
        } = 0x0700
@@ -117,7 +118,7 @@ SECTIONS
 
   /* Sections to be discarded */
   /DISCARD/ : {
-       *(.exitcall.exit)
+       *(.exit.text) *(.exit.data) *(.exitcall.exit)
        }
 
   /* Stabs debugging sections.  */
index e05d087..c42ffed 100644 (file)
@@ -4,6 +4,6 @@
 
 EXTRA_AFLAGS := -traditional
 
-lib-y += delay.o string.o
-lib-y += $(if $(CONFIG_64BIT),uaccess64.o,uaccess.o)
+lib-y += delay.o string.o uaccess_std.o
+lib-$(CONFIG_64BIT) += uaccess_mvcos.o
 lib-$(CONFIG_SMP) += spinlock.o
diff --git a/arch/s390/lib/uaccess.S b/arch/s390/lib/uaccess.S
deleted file mode 100644 (file)
index 8372752..0000000
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- *  arch/s390/lib/uaccess.S
- *    __copy_{from|to}_user functions.
- *
- *  s390
- *    Copyright (C) 2000,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
- *
- *  These functions have standard call interface
- */
-
-#include <linux/errno.h>
-#include <asm/lowcore.h>
-#include <asm/asm-offsets.h>
-
-        .text
-        .align 4
-        .globl __copy_from_user_asm
-       # %r2 = to, %r3 = n, %r4 = from
-__copy_from_user_asm:
-       slr     %r0,%r0
-0:     mvcp    0(%r3,%r2),0(%r4),%r0
-       jnz     1f
-       slr     %r2,%r2
-       br      %r14
-1:     la      %r2,256(%r2)
-       la      %r4,256(%r4)
-       ahi     %r3,-256
-2:     mvcp    0(%r3,%r2),0(%r4),%r0
-       jnz     1b
-3:     slr     %r2,%r2
-       br      %r14
-4:     lhi     %r0,-4096
-       lr      %r5,%r4
-       slr     %r5,%r0
-       nr      %r5,%r0         # %r5 = (%r4 + 4096) & -4096
-       slr     %r5,%r4         # %r5 = #bytes to next user page boundary
-       clr     %r3,%r5         # copy crosses next page boundary ?
-       jnh     6f              # no, the current page faulted
-       # move with the reduced length which is < 256
-5:     mvcp    0(%r5,%r2),0(%r4),%r0
-       slr     %r3,%r5
-6:     lr      %r2,%r3
-       br      %r14
-        .section __ex_table,"a"
-       .long   0b,4b
-       .long   2b,4b
-       .long   5b,6b
-        .previous
-
-        .align 4
-        .text
-        .globl __copy_to_user_asm
-       # %r2 = from, %r3 = n, %r4 = to
-__copy_to_user_asm:
-       slr     %r0,%r0
-0:     mvcs    0(%r3,%r4),0(%r2),%r0
-       jnz     1f
-       slr     %r2,%r2
-       br      %r14
-1:     la      %r2,256(%r2)
-       la      %r4,256(%r4)
-       ahi     %r3,-256
-2:     mvcs    0(%r3,%r4),0(%r2),%r0
-       jnz     1b
-3:     slr     %r2,%r2
-       br      %r14
-4:     lhi     %r0,-4096
-       lr      %r5,%r4
-       slr     %r5,%r0
-       nr      %r5,%r0         # %r5 = (%r4 + 4096) & -4096
-       slr     %r5,%r4         # %r5 = #bytes to next user page boundary
-       clr     %r3,%r5         # copy crosses next page boundary ?
-       jnh     6f              # no, the current page faulted
-       # move with the reduced length which is < 256
-5:     mvcs    0(%r5,%r4),0(%r2),%r0
-       slr     %r3,%r5
-6:     lr      %r2,%r3
-       br      %r14
-        .section __ex_table,"a"
-       .long   0b,4b
-       .long   2b,4b
-       .long   5b,6b
-        .previous
-
-        .align 4
-        .text
-        .globl __copy_in_user_asm
-       # %r2 = from, %r3 = n, %r4 = to
-__copy_in_user_asm:
-       ahi     %r3,-1
-       jo      6f
-       sacf    256
-       bras    %r1,4f
-0:     ahi     %r3,257
-1:     mvc     0(1,%r4),0(%r2)
-       la      %r2,1(%r2)
-       la      %r4,1(%r4)
-       ahi     %r3,-1
-       jnz     1b
-2:     lr      %r2,%r3
-       br      %r14
-3:     mvc     0(256,%r4),0(%r2)
-       la      %r2,256(%r2)
-       la      %r4,256(%r4)
-4:     ahi     %r3,-256
-       jnm     3b
-5:     ex      %r3,4(%r1)
-       sacf    0
-6:     slr     %r2,%r2
-       br      %r14
-        .section __ex_table,"a"
-       .long   1b,2b
-       .long   3b,0b
-       .long   5b,0b
-        .previous
-
-        .align 4
-        .text
-        .globl __clear_user_asm
-       # %r2 = to, %r3 = n
-__clear_user_asm:
-       bras    %r5,0f
-       .long   empty_zero_page
-0:     l       %r5,0(%r5)
-       slr     %r0,%r0
-1:     mvcs    0(%r3,%r2),0(%r5),%r0
-       jnz     2f
-       slr     %r2,%r2
-       br      %r14
-2:     la      %r2,256(%r2)
-       ahi     %r3,-256
-3:     mvcs    0(%r3,%r2),0(%r5),%r0
-       jnz     2b
-4:     slr     %r2,%r2
-       br      %r14
-5:     lhi     %r0,-4096
-       lr      %r4,%r2
-       slr     %r4,%r0
-       nr      %r4,%r0         # %r4 = (%r2 + 4096) & -4096
-       slr     %r4,%r2         # %r4 = #bytes to next user page boundary
-       clr     %r3,%r4         # clear crosses next page boundary ?
-       jnh     7f              # no, the current page faulted
-       # clear with the reduced length which is < 256
-6:     mvcs    0(%r4,%r2),0(%r5),%r0
-       slr     %r3,%r4
-7:     lr      %r2,%r3
-       br      %r14
-        .section __ex_table,"a"
-       .long   1b,5b
-       .long   3b,5b
-       .long   6b,7b
-        .previous
-
-        .align 4
-        .text
-        .globl __strncpy_from_user_asm
-       # %r2 = count, %r3 = dst, %r4 = src
-__strncpy_from_user_asm:
-       lhi     %r0,0
-       lr      %r1,%r4
-       la      %r4,0(%r4)      # clear high order bit from %r4
-       la      %r2,0(%r2,%r4)  # %r2 points to first byte after string
-       sacf    256
-0:     srst    %r2,%r1
-       jo      0b
-       sacf    0
-       lr      %r1,%r2
-       jh      1f              # \0 found in string ?
-       ahi     %r1,1           # include \0 in copy
-1:     slr     %r1,%r4         # %r1 = copy length (without \0)
-       slr     %r2,%r4         # %r2 = return length (including \0)
-2:     mvcp    0(%r1,%r3),0(%r4),%r0
-       jnz     3f
-       br      %r14
-3:     la      %r3,256(%r3)
-       la      %r4,256(%r4)
-       ahi     %r1,-256
-       mvcp    0(%r1,%r3),0(%r4),%r0
-       jnz     3b
-       br      %r14
-4:     sacf    0
-       lhi     %r2,-EFAULT
-       br      %r14
-       .section __ex_table,"a"
-       .long   0b,4b
-       .previous
-
-        .align 4
-        .text
-        .globl __strnlen_user_asm
-       # %r2 = count, %r3 = src
-__strnlen_user_asm:
-       lhi     %r0,0
-       lr      %r1,%r3
-       la      %r3,0(%r3)      # clear high order bit from %r4
-       la      %r2,0(%r2,%r3)  # %r2 points to first byte after string
-       sacf    256
-0:     srst    %r2,%r1
-       jo      0b
-       sacf    0
-       ahi     %r2,1           # strnlen_user result includes the \0
-                               # or return count+1 if \0 not found
-       slr     %r2,%r3
-       br      %r14
-2:     sacf    0
-       slr     %r2,%r2         # return 0 on exception
-       br      %r14
-       .section __ex_table,"a"
-       .long   0b,2b
-       .previous
diff --git a/arch/s390/lib/uaccess64.S b/arch/s390/lib/uaccess64.S
deleted file mode 100644 (file)
index 1f755be..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- *  arch/s390x/lib/uaccess.S
- *    __copy_{from|to}_user functions.
- *
- *  s390
- *    Copyright (C) 2000,2002 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Authors(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
- *
- *  These functions have standard call interface
- */
-
-#include <linux/errno.h>
-#include <asm/lowcore.h>
-#include <asm/asm-offsets.h>
-
-        .text
-        .align 4
-        .globl __copy_from_user_asm
-       # %r2 = to, %r3 = n, %r4 = from
-__copy_from_user_asm:
-       slgr    %r0,%r0
-0:     mvcp    0(%r3,%r2),0(%r4),%r0
-       jnz     1f
-       slgr    %r2,%r2
-       br      %r14
-1:     la      %r2,256(%r2)
-       la      %r4,256(%r4)
-       aghi    %r3,-256
-2:     mvcp    0(%r3,%r2),0(%r4),%r0
-       jnz     1b
-3:     slgr    %r2,%r2
-       br      %r14
-4:     lghi    %r0,-4096
-       lgr     %r5,%r4
-       slgr    %r5,%r0
-       ngr     %r5,%r0         # %r5 = (%r4 + 4096) & -4096
-       slgr    %r5,%r4         # %r5 = #bytes to next user page boundary
-       clgr    %r3,%r5         # copy crosses next page boundary ?
-       jnh     6f              # no, the current page faulted
-       # move with the reduced length which is < 256
-5:     mvcp    0(%r5,%r2),0(%r4),%r0
-       slgr    %r3,%r5
-6:     lgr     %r2,%r3
-       br      %r14
-        .section __ex_table,"a"
-       .quad   0b,4b
-       .quad   2b,4b
-       .quad   5b,6b
-        .previous
-
-        .align 4
-        .text
-        .globl __copy_to_user_asm
-       # %r2 = from, %r3 = n, %r4 = to
-__copy_to_user_asm:
-       slgr    %r0,%r0
-0:     mvcs    0(%r3,%r4),0(%r2),%r0
-       jnz     1f
-       slgr    %r2,%r2
-       br      %r14
-1:     la      %r2,256(%r2)
-       la      %r4,256(%r4)
-       aghi    %r3,-256
-2:     mvcs    0(%r3,%r4),0(%r2),%r0
-       jnz     1b
-3:     slgr    %r2,%r2
-       br      %r14
-4:     lghi    %r0,-4096
-       lgr     %r5,%r4
-       slgr    %r5,%r0
-       ngr     %r5,%r0         # %r5 = (%r4 + 4096) & -4096
-       slgr    %r5,%r4         # %r5 = #bytes to next user page boundary
-       clgr    %r3,%r5         # copy crosses next page boundary ?
-       jnh     6f              # no, the current page faulted
-       # move with the reduced length which is < 256
-5:     mvcs    0(%r5,%r4),0(%r2),%r0
-       slgr    %r3,%r5
-6:     lgr     %r2,%r3
-       br      %r14
-        .section __ex_table,"a"
-       .quad   0b,4b
-       .quad   2b,4b
-       .quad   5b,6b
-        .previous
-
-        .align 4
-        .text
-        .globl __copy_in_user_asm
-       # %r2 = from, %r3 = n, %r4 = to
-__copy_in_user_asm:
-       aghi    %r3,-1
-       jo      6f
-       sacf    256
-       bras    %r1,4f
-0:     aghi    %r3,257
-1:     mvc     0(1,%r4),0(%r2)
-       la      %r2,1(%r2)
-       la      %r4,1(%r4)
-       aghi    %r3,-1
-       jnz     1b
-2:     lgr     %r2,%r3
-       br      %r14
-3:     mvc     0(256,%r4),0(%r2)
-       la      %r2,256(%r2)
-       la      %r4,256(%r4)
-4:     aghi    %r3,-256
-       jnm     3b
-5:     ex      %r3,4(%r1)
-       sacf    0
-6:     slgr    %r2,%r2
-       br      14
-        .section __ex_table,"a"
-       .quad   1b,2b
-       .quad   3b,0b
-       .quad   5b,0b
-        .previous
-
-        .align 4
-        .text
-        .globl __clear_user_asm
-       # %r2 = to, %r3 = n
-__clear_user_asm:
-       slgr    %r0,%r0
-       larl    %r5,empty_zero_page
-1:     mvcs    0(%r3,%r2),0(%r5),%r0
-       jnz     2f
-       slgr    %r2,%r2
-       br      %r14
-2:     la      %r2,256(%r2)
-       aghi    %r3,-256
-3:     mvcs    0(%r3,%r2),0(%r5),%r0
-       jnz     2b
-4:     slgr    %r2,%r2
-       br      %r14
-5:     lghi    %r0,-4096
-       lgr     %r4,%r2
-       slgr    %r4,%r0
-       ngr     %r4,%r0         # %r4 = (%r2 + 4096) & -4096
-       slgr    %r4,%r2         # %r4 = #bytes to next user page boundary
-       clgr    %r3,%r4         # clear crosses next page boundary ?
-       jnh     7f              # no, the current page faulted
-       # clear with the reduced length which is < 256
-6:     mvcs    0(%r4,%r2),0(%r5),%r0
-       slgr    %r3,%r4
-7:     lgr     %r2,%r3
-       br      %r14
-        .section __ex_table,"a"
-       .quad   1b,5b
-       .quad   3b,5b
-       .quad   6b,7b
-        .previous
-
-        .align 4
-        .text
-        .globl __strncpy_from_user_asm
-       # %r2 = count, %r3 = dst, %r4 = src
-__strncpy_from_user_asm:
-       lghi    %r0,0
-       lgr     %r1,%r4
-       la      %r2,0(%r2,%r4)  # %r2 points to first byte after string
-       sacf    256
-0:     srst    %r2,%r1
-       jo      0b
-       sacf    0
-       lgr     %r1,%r2
-       jh      1f              # \0 found in string ?
-       aghi    %r1,1           # include \0 in copy
-1:     slgr    %r1,%r4         # %r1 = copy length (without \0)
-       slgr    %r2,%r4         # %r2 = return length (including \0)
-2:     mvcp    0(%r1,%r3),0(%r4),%r0
-       jnz     3f
-       br      %r14
-3:     la      %r3,256(%r3)
-       la      %r4,256(%r4)
-       aghi    %r1,-256
-       mvcp    0(%r1,%r3),0(%r4),%r0
-       jnz     3b
-       br      %r14
-4:     sacf    0
-       lghi    %r2,-EFAULT
-       br      %r14
-       .section __ex_table,"a"
-       .quad   0b,4b
-       .previous
-
-        .align 4
-        .text
-        .globl __strnlen_user_asm
-       # %r2 = count, %r3 = src
-__strnlen_user_asm:
-       lghi    %r0,0
-       lgr     %r1,%r3
-       la      %r2,0(%r2,%r3)  # %r2 points to first byte after string
-       sacf    256
-0:     srst    %r2,%r1
-       jo      0b
-       sacf    0
-       aghi    %r2,1           # strnlen_user result includes the \0
-                               # or return count+1 if \0 not found
-       slgr    %r2,%r3
-       br      %r14
-2:     sacf    0
-       slgr    %r2,%r2         # return 0 on exception
-       br      %r14
-       .section __ex_table,"a"
-       .quad   0b,2b
-       .previous
diff --git a/arch/s390/lib/uaccess_mvcos.c b/arch/s390/lib/uaccess_mvcos.c
new file mode 100644 (file)
index 0000000..86c96d6
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ *  arch/s390/lib/uaccess_mvcos.c
+ *
+ *  Optimized user space space access functions based on mvcos.
+ *
+ *    Copyright (C) IBM Corp. 2006
+ *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ *              Gerald Schaefer (gerald.schaefer@de.ibm.com)
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+#include <asm/futex.h>
+
+#ifndef __s390x__
+#define AHI    "ahi"
+#define ALR    "alr"
+#define CLR    "clr"
+#define LHI    "lhi"
+#define SLR    "slr"
+#else
+#define AHI    "aghi"
+#define ALR    "algr"
+#define CLR    "clgr"
+#define LHI    "lghi"
+#define SLR    "slgr"
+#endif
+
+size_t copy_from_user_mvcos(size_t size, const void __user *ptr, void *x)
+{
+       register unsigned long reg0 asm("0") = 0x81UL;
+       unsigned long tmp1, tmp2;
+
+       tmp1 = -4096UL;
+       asm volatile(
+               "0: .insn ss,0xc80000000000,0(%0,%2),0(%1),0\n"
+               "   jz    4f\n"
+               "1:"ALR"  %0,%3\n"
+               "  "SLR"  %1,%3\n"
+               "  "SLR"  %2,%3\n"
+               "   j     0b\n"
+               "2: la    %4,4095(%1)\n"/* %4 = ptr + 4095 */
+               "   nr    %4,%3\n"      /* %4 = (ptr + 4095) & -4096 */
+               "  "SLR"  %4,%1\n"
+               "  "CLR"  %0,%4\n"      /* copy crosses next page boundary? */
+               "   jnh   5f\n"
+               "3: .insn ss,0xc80000000000,0(%4,%2),0(%1),0\n"
+               "  "SLR"  %0,%4\n"
+               "   j     5f\n"
+               "4:"SLR"  %0,%0\n"
+               "5: \n"
+               EX_TABLE(0b,2b) EX_TABLE(3b,5b)
+               : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
+               : "d" (reg0) : "cc", "memory");
+       return size;
+}
+
+size_t copy_to_user_mvcos(size_t size, void __user *ptr, const void *x)
+{
+       register unsigned long reg0 asm("0") = 0x810000UL;
+       unsigned long tmp1, tmp2;
+
+       tmp1 = -4096UL;
+       asm volatile(
+               "0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
+               "   jz    4f\n"
+               "1:"ALR"  %0,%3\n"
+               "  "SLR"  %1,%3\n"
+               "  "SLR"  %2,%3\n"
+               "   j     0b\n"
+               "2: la    %4,4095(%1)\n"/* %4 = ptr + 4095 */
+               "   nr    %4,%3\n"      /* %4 = (ptr + 4095) & -4096 */
+               "  "SLR"  %4,%1\n"
+               "  "CLR"  %0,%4\n"      /* copy crosses next page boundary? */
+               "   jnh   5f\n"
+               "3: .insn ss,0xc80000000000,0(%4,%1),0(%2),0\n"
+               "  "SLR"  %0,%4\n"
+               "   j     5f\n"
+               "4:"SLR"  %0,%0\n"
+               "5: \n"
+               EX_TABLE(0b,2b) EX_TABLE(3b,5b)
+               : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
+               : "d" (reg0) : "cc", "memory");
+       return size;
+}
+
+size_t copy_in_user_mvcos(size_t size, void __user *to, const void __user *from)
+{
+       register unsigned long reg0 asm("0") = 0x810081UL;
+       unsigned long tmp1, tmp2;
+
+       tmp1 = -4096UL;
+       /* FIXME: copy with reduced length. */
+       asm volatile(
+               "0: .insn ss,0xc80000000000,0(%0,%1),0(%2),0\n"
+               "   jz    2f\n"
+               "1:"ALR"  %0,%3\n"
+               "  "SLR"  %1,%3\n"
+               "  "SLR"  %2,%3\n"
+               "   j     0b\n"
+               "2:"SLR"  %0,%0\n"
+               "3: \n"
+               EX_TABLE(0b,3b)
+               : "+a" (size), "+a" (to), "+a" (from), "+a" (tmp1), "=a" (tmp2)
+               : "d" (reg0) : "cc", "memory");
+       return size;
+}
+
+size_t clear_user_mvcos(size_t size, void __user *to)
+{
+       register unsigned long reg0 asm("0") = 0x810000UL;
+       unsigned long tmp1, tmp2;
+
+       tmp1 = -4096UL;
+       asm volatile(
+               "0: .insn ss,0xc80000000000,0(%0,%1),0(%4),0\n"
+               "   jz    4f\n"
+               "1:"ALR"  %0,%2\n"
+               "  "SLR"  %1,%2\n"
+               "   j     0b\n"
+               "2: la    %3,4095(%1)\n"/* %4 = to + 4095 */
+               "   nr    %3,%2\n"      /* %4 = (to + 4095) & -4096 */
+               "  "SLR"  %3,%1\n"
+               "  "CLR"  %0,%3\n"      /* copy crosses next page boundary? */
+               "   jnh   5f\n"
+               "3: .insn ss,0xc80000000000,0(%3,%1),0(%4),0\n"
+               "  "SLR"  %0,%3\n"
+               "   j     5f\n"
+               "4:"SLR"  %0,%0\n"
+               "5: \n"
+               EX_TABLE(0b,2b) EX_TABLE(3b,5b)
+               : "+a" (size), "+a" (to), "+a" (tmp1), "=a" (tmp2)
+               : "a" (empty_zero_page), "d" (reg0) : "cc", "memory");
+       return size;
+}
+
+extern size_t copy_from_user_std_small(size_t, const void __user *, void *);
+extern size_t copy_to_user_std_small(size_t, void __user *, const void *);
+extern size_t strnlen_user_std(size_t, const char __user *);
+extern size_t strncpy_from_user_std(size_t, const char __user *, char *);
+extern int futex_atomic_op(int, int __user *, int, int *);
+extern int futex_atomic_cmpxchg(int __user *, int, int);
+
+struct uaccess_ops uaccess_mvcos = {
+       .copy_from_user = copy_from_user_mvcos,
+       .copy_from_user_small = copy_from_user_std_small,
+       .copy_to_user = copy_to_user_mvcos,
+       .copy_to_user_small = copy_to_user_std_small,
+       .copy_in_user = copy_in_user_mvcos,
+       .clear_user = clear_user_mvcos,
+       .strnlen_user = strnlen_user_std,
+       .strncpy_from_user = strncpy_from_user_std,
+       .futex_atomic_op = futex_atomic_op,
+       .futex_atomic_cmpxchg = futex_atomic_cmpxchg,
+};
diff --git a/arch/s390/lib/uaccess_std.c b/arch/s390/lib/uaccess_std.c
new file mode 100644 (file)
index 0000000..9a4d4a2
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ *  arch/s390/lib/uaccess_std.c
+ *
+ *  Standard user space access functions based on mvcp/mvcs and doing
+ *  interesting things in the secondary space mode.
+ *
+ *    Copyright (C) IBM Corp. 2006
+ *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com),
+ *              Gerald Schaefer (gerald.schaefer@de.ibm.com)
+ */
+
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <asm/uaccess.h>
+#include <asm/futex.h>
+
+#ifndef __s390x__
+#define AHI    "ahi"
+#define ALR    "alr"
+#define CLR    "clr"
+#define LHI    "lhi"
+#define SLR    "slr"
+#else
+#define AHI    "aghi"
+#define ALR    "algr"
+#define CLR    "clgr"
+#define LHI    "lghi"
+#define SLR    "slgr"
+#endif
+
+size_t copy_from_user_std(size_t size, const void __user *ptr, void *x)
+{
+       unsigned long tmp1, tmp2;
+
+       tmp1 = -256UL;
+       asm volatile(
+               "0: mvcp  0(%0,%2),0(%1),%3\n"
+               "   jz    5f\n"
+               "1:"ALR"  %0,%3\n"
+               "   la    %1,256(%1)\n"
+               "   la    %2,256(%2)\n"
+               "2: mvcp  0(%0,%2),0(%1),%3\n"
+               "   jnz   1b\n"
+               "   j     5f\n"
+               "3: la    %4,255(%1)\n" /* %4 = ptr + 255 */
+               "  "LHI"  %3,-4096\n"
+               "   nr    %4,%3\n"      /* %4 = (ptr + 255) & -4096 */
+               "  "SLR"  %4,%1\n"
+               "  "CLR"  %0,%4\n"      /* copy crosses next page boundary? */
+               "   jnh   6f\n"
+               "4: mvcp  0(%4,%2),0(%1),%3\n"
+               "  "SLR"  %0,%4\n"
+               "   j     6f\n"
+               "5:"SLR"  %0,%0\n"
+               "6: \n"
+               EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
+               : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
+               : : "cc", "memory");
+       return size;
+}
+
+size_t copy_from_user_std_small(size_t size, const void __user *ptr, void *x)
+{
+       unsigned long tmp1, tmp2;
+
+       tmp1 = 0UL;
+       asm volatile(
+               "0: mvcp  0(%0,%2),0(%1),%3\n"
+               "  "SLR"  %0,%0\n"
+               "   j     3f\n"
+               "1: la    %4,255(%1)\n" /* %4 = ptr + 255 */
+               "  "LHI"  %3,-4096\n"
+               "   nr    %4,%3\n"      /* %4 = (ptr + 255) & -4096 */
+               "  "SLR"  %4,%1\n"
+               "  "CLR"  %0,%4\n"      /* copy crosses next page boundary? */
+               "   jnh   3f\n"
+               "2: mvcp  0(%4,%2),0(%1),%3\n"
+               "  "SLR"  %0,%4\n"
+               "3:\n"
+               EX_TABLE(0b,1b) EX_TABLE(2b,3b)
+               : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
+               : : "cc", "memory");
+       return size;
+}
+
+size_t copy_to_user_std(size_t size, void __user *ptr, const void *x)
+{
+       unsigned long tmp1, tmp2;
+
+       tmp1 = -256UL;
+       asm volatile(
+               "0: mvcs  0(%0,%1),0(%2),%3\n"
+               "   jz    5f\n"
+               "1:"ALR"  %0,%3\n"
+               "   la    %1,256(%1)\n"
+               "   la    %2,256(%2)\n"
+               "2: mvcs  0(%0,%1),0(%2),%3\n"
+               "   jnz   1b\n"
+               "   j     5f\n"
+               "3: la    %4,255(%1)\n" /* %4 = ptr + 255 */
+               "  "LHI"  %3,-4096\n"
+               "   nr    %4,%3\n"      /* %4 = (ptr + 255) & -4096 */
+               "  "SLR"  %4,%1\n"
+               "  "CLR"  %0,%4\n"      /* copy crosses next page boundary? */
+               "   jnh   6f\n"
+               "4: mvcs  0(%4,%1),0(%2),%3\n"
+               "  "SLR"  %0,%4\n"
+               "   j     6f\n"
+               "5:"SLR"  %0,%0\n"
+               "6: \n"
+               EX_TABLE(0b,3b) EX_TABLE(2b,3b) EX_TABLE(4b,6b)
+               : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
+               : : "cc", "memory");
+       return size;
+}
+
+size_t copy_to_user_std_small(size_t size, void __user *ptr, const void *x)
+{
+       unsigned long tmp1, tmp2;
+
+       tmp1 = 0UL;
+       asm volatile(
+               "0: mvcs  0(%0,%1),0(%2),%3\n"
+               "  "SLR"  %0,%0\n"
+               "   j     3f\n"
+               "1: la    %4,255(%1)\n" /* ptr + 255 */
+               "  "LHI"  %3,-4096\n"
+               "   nr    %4,%3\n"      /* (ptr + 255) & -4096UL */
+               "  "SLR"  %4,%1\n"
+               "  "CLR"  %0,%4\n"      /* copy crosses next page boundary? */
+               "   jnh   3f\n"
+               "2: mvcs  0(%4,%1),0(%2),%3\n"
+               "  "SLR"  %0,%4\n"
+               "3:\n"
+               EX_TABLE(0b,1b) EX_TABLE(2b,3b)
+               : "+a" (size), "+a" (ptr), "+a" (x), "+a" (tmp1), "=a" (tmp2)
+               : : "cc", "memory");
+       return size;
+}
+
+size_t copy_in_user_std(size_t size, void __user *to, const void __user *from)
+{
+       unsigned long tmp1;
+
+       asm volatile(
+               "  "AHI"  %0,-1\n"
+               "   jo    5f\n"
+               "   sacf  256\n"
+               "   bras  %3,3f\n"
+               "0:"AHI"  %0,257\n"
+               "1: mvc   0(1,%1),0(%2)\n"
+               "   la    %1,1(%1)\n"
+               "   la    %2,1(%2)\n"
+               "  "AHI"  %0,-1\n"
+               "   jnz   1b\n"
+               "   j     5f\n"
+               "2: mvc   0(256,%1),0(%2)\n"
+               "   la    %1,256(%1)\n"
+               "   la    %2,256(%2)\n"
+               "3:"AHI"  %0,-256\n"
+               "   jnm   2b\n"
+               "4: ex    %0,1b-0b(%3)\n"
+               "   sacf  0\n"
+               "5: "SLR"  %0,%0\n"
+               "6:\n"
+               EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
+               : "+a" (size), "+a" (to), "+a" (from), "=a" (tmp1)
+               : : "cc", "memory");
+       return size;
+}
+
+size_t clear_user_std(size_t size, void __user *to)
+{
+       unsigned long tmp1, tmp2;
+
+       asm volatile(
+               "  "AHI"  %0,-1\n"
+               "   jo    5f\n"
+               "   sacf  256\n"
+               "   bras  %3,3f\n"
+               "   xc    0(1,%1),0(%1)\n"
+               "0:"AHI"  %0,257\n"
+               "   la    %2,255(%1)\n" /* %2 = ptr + 255 */
+               "   srl   %2,12\n"
+               "   sll   %2,12\n"      /* %2 = (ptr + 255) & -4096 */
+               "  "SLR"  %2,%1\n"
+               "  "CLR"  %0,%2\n"      /* clear crosses next page boundary? */
+               "   jnh   5f\n"
+               "  "AHI"  %2,-1\n"
+               "1: ex    %2,0(%3)\n"
+               "  "AHI"  %2,1\n"
+               "  "SLR"  %0,%2\n"
+               "   j     5f\n"
+               "2: xc    0(256,%1),0(%1)\n"
+               "   la    %1,256(%1)\n"
+               "3:"AHI"  %0,-256\n"
+               "   jnm   2b\n"
+               "4: ex    %0,0(%3)\n"
+               "   sacf  0\n"
+               "5: "SLR"  %0,%0\n"
+               "6:\n"
+               EX_TABLE(1b,6b) EX_TABLE(2b,0b) EX_TABLE(4b,0b)
+               : "+a" (size), "+a" (to), "=a" (tmp1), "=a" (tmp2)
+               : : "cc", "memory");
+       return size;
+}
+
+size_t strnlen_user_std(size_t size, const char __user *src)
+{
+       register unsigned long reg0 asm("0") = 0UL;
+       unsigned long tmp1, tmp2;
+
+       asm volatile(
+               "   la    %2,0(%1)\n"
+               "   la    %3,0(%0,%1)\n"
+               "  "SLR"  %0,%0\n"
+               "   sacf  256\n"
+               "0: srst  %3,%2\n"
+               "   jo    0b\n"
+               "   la    %0,1(%3)\n"   /* strnlen_user results includes \0 */
+               "  "SLR"  %0,%1\n"
+               "1: sacf  0\n"
+               EX_TABLE(0b,1b)
+               : "+a" (size), "+a" (src), "=a" (tmp1), "=a" (tmp2)
+               : "d" (reg0) : "cc", "memory");
+       return size;
+}
+
+size_t strncpy_from_user_std(size_t size, const char __user *src, char *dst)
+{
+       register unsigned long reg0 asm("0") = 0UL;
+       unsigned long tmp1, tmp2;
+
+       asm volatile(
+               "   la    %3,0(%1)\n"
+               "   la    %4,0(%0,%1)\n"
+               "   sacf  256\n"
+               "0: srst  %4,%3\n"
+               "   jo    0b\n"
+               "   sacf  0\n"
+               "   la    %0,0(%4)\n"
+               "   jh    1f\n"         /* found \0 in string ? */
+               "  "AHI"  %4,1\n"       /* include \0 in copy */
+               "1:"SLR"  %0,%1\n"      /* %0 = return length (without \0) */
+               "  "SLR"  %4,%1\n"      /* %4 = copy length (including \0) */
+               "2: mvcp  0(%4,%2),0(%1),%5\n"
+               "   jz    9f\n"
+               "3:"AHI"  %4,-256\n"
+               "   la    %1,256(%1)\n"
+               "   la    %2,256(%2)\n"
+               "4: mvcp  0(%4,%2),0(%1),%5\n"
+               "   jnz   3b\n"
+               "   j     9f\n"
+               "7: sacf  0\n"
+               "8:"LHI"  %0,%6\n"
+               "9:\n"
+               EX_TABLE(0b,7b) EX_TABLE(2b,8b) EX_TABLE(4b,8b)
+               : "+a" (size), "+a" (src), "+d" (dst), "=a" (tmp1), "=a" (tmp2)
+               : "d" (reg0), "K" (-EFAULT) : "cc", "memory");
+       return size;
+}
+
+#define __futex_atomic_op(insn, ret, oldval, newval, uaddr, oparg)     \
+       asm volatile(                                                   \
+               "   sacf  256\n"                                        \
+               "0: l     %1,0(%6)\n"                                   \
+               "1:"insn                                                \
+               "2: cs    %1,%2,0(%6)\n"                                \
+               "3: jl    1b\n"                                         \
+               "   lhi   %0,0\n"                                       \
+               "4: sacf  0\n"                                          \
+               EX_TABLE(0b,4b) EX_TABLE(2b,4b) EX_TABLE(3b,4b)         \
+               : "=d" (ret), "=&d" (oldval), "=&d" (newval),           \
+                 "=m" (*uaddr)                                         \
+               : "0" (-EFAULT), "d" (oparg), "a" (uaddr),              \
+                 "m" (*uaddr) : "cc");
+
+int futex_atomic_op(int op, int __user *uaddr, int oparg, int *old)
+{
+       int oldval = 0, newval, ret;
+
+       inc_preempt_count();
+
+       switch (op) {
+       case FUTEX_OP_SET:
+               __futex_atomic_op("lr %2,%5\n",
+                                 ret, oldval, newval, uaddr, oparg);
+               break;
+       case FUTEX_OP_ADD:
+               __futex_atomic_op("lr %2,%1\nar %2,%5\n",
+                                 ret, oldval, newval, uaddr, oparg);
+               break;
+       case FUTEX_OP_OR:
+               __futex_atomic_op("lr %2,%1\nor %2,%5\n",
+                                 ret, oldval, newval, uaddr, oparg);
+               break;
+       case FUTEX_OP_ANDN:
+               __futex_atomic_op("lr %2,%1\nnr %2,%5\n",
+                                 ret, oldval, newval, uaddr, oparg);
+               break;
+       case FUTEX_OP_XOR:
+               __futex_atomic_op("lr %2,%1\nxr %2,%5\n",
+                                 ret, oldval, newval, uaddr, oparg);
+               break;
+       default:
+               ret = -ENOSYS;
+       }
+       dec_preempt_count();
+       *old = oldval;
+       return ret;
+}
+
+int futex_atomic_cmpxchg(int __user *uaddr, int oldval, int newval)
+{
+       int ret;
+
+       asm volatile(
+               "   sacf 256\n"
+               "   cs   %1,%4,0(%5)\n"
+               "0: lr   %0,%1\n"
+               "1: sacf 0\n"
+               EX_TABLE(0b,1b)
+               : "=d" (ret), "+d" (oldval), "=m" (*uaddr)
+               : "0" (-EFAULT), "d" (newval), "a" (uaddr), "m" (*uaddr)
+               : "cc", "memory" );
+       return ret;
+}
+
+struct uaccess_ops uaccess_std = {
+       .copy_from_user = copy_from_user_std,
+       .copy_from_user_small = copy_from_user_std_small,
+       .copy_to_user = copy_to_user_std,
+       .copy_to_user_small = copy_to_user_std_small,
+       .copy_in_user = copy_in_user_std,
+       .clear_user = clear_user_std,
+       .strnlen_user = strnlen_user_std,
+       .strncpy_from_user = strncpy_from_user_std,
+       .futex_atomic_op = futex_atomic_op,
+       .futex_atomic_cmpxchg = futex_atomic_cmpxchg,
+};
index ceea51c..786a44d 100644 (file)
@@ -53,22 +53,6 @@ static void cmm_timer_fn(unsigned long);
 static void cmm_set_timer(void);
 
 static long
-cmm_strtoul(const char *cp, char **endp)
-{
-       unsigned int base = 10;
-
-       if (*cp == '0') {
-               base = 8;
-               cp++;
-               if ((*cp == 'x' || *cp == 'X') && isxdigit(cp[1])) {
-                       base = 16;
-                       cp++;
-               }
-       }
-       return simple_strtoul(cp, endp, base);
-}
-
-static long
 cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list)
 {
        struct cmm_page_array *pa;
@@ -276,7 +260,7 @@ cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
                        return -EFAULT;
                buf[sizeof(buf) - 1] = '\0';
                cmm_skip_blanks(buf, &p);
-               pages = cmm_strtoul(p, &p);
+               pages = simple_strtoul(p, &p, 0);
                if (ctl == &cmm_table[0])
                        cmm_set_pages(pages);
                else
@@ -317,9 +301,9 @@ cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
                        return -EFAULT;
                buf[sizeof(buf) - 1] = '\0';
                cmm_skip_blanks(buf, &p);
-               pages = cmm_strtoul(p, &p);
+               pages = simple_strtoul(p, &p, 0);
                cmm_skip_blanks(p, &p);
-               seconds = cmm_strtoul(p, &p);
+               seconds = simple_strtoul(p, &p, 0);
                cmm_set_timeout(pages, seconds);
        } else {
                len = sprintf(buf, "%ld %ld\n",
@@ -382,24 +366,24 @@ cmm_smsg_target(char *from, char *msg)
        if (strncmp(msg, "SHRINK", 6) == 0) {
                if (!cmm_skip_blanks(msg + 6, &msg))
                        return;
-               pages = cmm_strtoul(msg, &msg);
+               pages = simple_strtoul(msg, &msg, 0);
                cmm_skip_blanks(msg, &msg);
                if (*msg == '\0')
                        cmm_set_pages(pages);
        } else if (strncmp(msg, "RELEASE", 7) == 0) {
                if (!cmm_skip_blanks(msg + 7, &msg))
                        return;
-               pages = cmm_strtoul(msg, &msg);
+               pages = simple_strtoul(msg, &msg, 0);
                cmm_skip_blanks(msg, &msg);
                if (*msg == '\0')
                        cmm_add_timed_pages(pages);
        } else if (strncmp(msg, "REUSE", 5) == 0) {
                if (!cmm_skip_blanks(msg + 5, &msg))
                        return;
-               pages = cmm_strtoul(msg, &msg);
+               pages = simple_strtoul(msg, &msg, 0);
                if (!cmm_skip_blanks(msg, &msg))
                        return;
-               seconds = cmm_strtoul(msg, &msg);
+               seconds = simple_strtoul(msg, &msg, 0);
                cmm_skip_blanks(msg, &msg);
                if (*msg == '\0')
                        cmm_set_timeout(pages, seconds);
index 7cd8257..44f0cda 100644 (file)
 #include <linux/console.h>
 #include <linux/module.h>
 #include <linux/hardirq.h>
+#include <linux/kprobes.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
+#include <asm/kdebug.h>
 
 #ifndef CONFIG_64BIT
 #define __FAIL_ADDR_MASK 0x7ffff000
@@ -48,6 +50,38 @@ extern int sysctl_userprocess_debug;
 
 extern void die(const char *,struct pt_regs *,long);
 
+#ifdef CONFIG_KPROBES
+ATOMIC_NOTIFIER_HEAD(notify_page_fault_chain);
+int register_page_fault_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_register(&notify_page_fault_chain, nb);
+}
+
+int unregister_page_fault_notifier(struct notifier_block *nb)
+{
+       return atomic_notifier_chain_unregister(&notify_page_fault_chain, nb);
+}
+
+static inline int notify_page_fault(enum die_val val, const char *str,
+                       struct pt_regs *regs, long err, int trap, int sig)
+{
+       struct die_args args = {
+               .regs = regs,
+               .str = str,
+               .err = err,
+               .trapnr = trap,
+               .signr = sig
+       };
+       return atomic_notifier_call_chain(&notify_page_fault_chain, val, &args);
+}
+#else
+static inline int notify_page_fault(enum die_val val, const char *str,
+                       struct pt_regs *regs, long err, int trap, int sig)
+{
+       return NOTIFY_DONE;
+}
+#endif
+
 extern spinlock_t timerlist_lock;
 
 /*
@@ -159,7 +193,7 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code,
  *   11       Page translation     ->  Not present       (nullification)
  *   3b       Region third trans.  ->  Not present       (nullification)
  */
-static inline void
+static inline void __kprobes
 do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection)
 {
         struct task_struct *tsk;
@@ -173,6 +207,10 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int is_protection)
         tsk = current;
         mm = tsk->mm;
        
+       if (notify_page_fault(DIE_PAGE_FAULT, "page fault", regs, error_code, 14,
+                                       SIGSEGV) == NOTIFY_STOP)
+               return;
+
        /* 
          * Check for low-address protection.  This needs to be treated
         * as a special case because the translation exception code 
index 6e6b6de..cfd9b8f 100644 (file)
@@ -108,16 +108,23 @@ void __init paging_init(void)
         unsigned long pgdir_k = (__pa(swapper_pg_dir) & PAGE_MASK) | _KERNSEG_TABLE;
         static const int ssm_mask = 0x04000000L;
        unsigned long ro_start_pfn, ro_end_pfn;
+       unsigned long zones_size[MAX_NR_ZONES];
 
        ro_start_pfn = PFN_DOWN((unsigned long)&__start_rodata);
        ro_end_pfn = PFN_UP((unsigned long)&__end_rodata);
 
+       memset(zones_size, 0, sizeof(zones_size));
+       zones_size[ZONE_DMA] = max_low_pfn;
+       free_area_init_node(0, &contig_page_data, zones_size,
+                           __pa(PAGE_OFFSET) >> PAGE_SHIFT,
+                           zholes_size);
+
        /* unmap whole virtual address space */
        
         pg_dir = swapper_pg_dir;
 
-       for (i=0;i<KERNEL_PGD_PTRS;i++) 
-               pmd_clear((pmd_t*)pg_dir++);
+       for (i = 0; i < PTRS_PER_PGD; i++)
+               pmd_clear((pmd_t *) pg_dir++);
                
        /*
         * map whole physical memory to virtual memory (identity mapping) 
@@ -131,10 +138,7 @@ void __init paging_init(void)
                  */
                pg_table = (pte_t *) alloc_bootmem_pages(PAGE_SIZE);
 
-                pg_dir->pgd0 =  (_PAGE_TABLE | __pa(pg_table));
-                pg_dir->pgd1 =  (_PAGE_TABLE | (__pa(pg_table)+1024));
-                pg_dir->pgd2 =  (_PAGE_TABLE | (__pa(pg_table)+2048));
-                pg_dir->pgd3 =  (_PAGE_TABLE | (__pa(pg_table)+3072));
+               pmd_populate_kernel(&init_mm, (pmd_t *) pg_dir, pg_table);
                 pg_dir++;
 
                 for (tmp = 0 ; tmp < PTRS_PER_PTE ; tmp++,pg_table++) {
@@ -143,8 +147,8 @@ void __init paging_init(void)
                        else
                                pte = pfn_pte(pfn, PAGE_KERNEL);
                         if (pfn >= max_low_pfn)
-                                pte_clear(&init_mm, 0, &pte);
-                        set_pte(pg_table, pte);
+                               pte_val(pte) = _PAGE_TYPE_EMPTY;
+                       set_pte(pg_table, pte);
                         pfn++;
                 }
         }
@@ -159,16 +163,6 @@ void __init paging_init(void)
                             : : "m" (pgdir_k), "m" (ssm_mask));
 
         local_flush_tlb();
-
-       {
-               unsigned long zones_size[MAX_NR_ZONES];
-
-               memset(zones_size, 0, sizeof(zones_size));
-               zones_size[ZONE_DMA] = max_low_pfn;
-               free_area_init_node(0, &contig_page_data, zones_size,
-                                   __pa(PAGE_OFFSET) >> PAGE_SHIFT,
-                                   zholes_size);
-       }
         return;
 }
 
@@ -236,10 +230,8 @@ void __init paging_init(void)
                                        pte = pfn_pte(pfn, __pgprot(_PAGE_RO));
                                else
                                        pte = pfn_pte(pfn, PAGE_KERNEL);
-                                if (pfn >= max_low_pfn) {
-                                        pte_clear(&init_mm, 0, &pte); 
-                                        continue;
-                                }
+                               if (pfn >= max_low_pfn)
+                                       pte_val(pte) = _PAGE_TYPE_EMPTY;
                                 set_pte(pt_dir, pte);
                                 pfn++;
                         }
index 0c85e9d..7080b41 100644 (file)
@@ -1,8 +1,9 @@
 /*
  * hypervisor.c - /sys/hypervisor subsystem.
  *
- * This file is released under the GPLv2
+ * Copyright (C) IBM Corp. 2006
  *
+ * This file is released under the GPLv2
  */
 
 #include <linux/kobject.h>
index 4d36208..ae89b9b 100644 (file)
@@ -213,17 +213,35 @@ config MONREADER
        help
          Character device driver for reading z/VM monitor service records
 
+config MONWRITER
+       tristate "API for writing z/VM monitor service records"
+       default "m"
+       help
+         Character device driver for writing z/VM monitor service records
+
 endmenu
 
 menu "Cryptographic devices"
 
-config Z90CRYPT
+config ZCRYPT
        tristate "Support for PCI-attached cryptographic adapters"
-        default "m"
-        help
+       select ZCRYPT_MONOLITHIC if ZCRYPT="y"
+       default "m"
+       help
          Select this option if you want to use a PCI-attached cryptographic
-         adapter like the PCI Cryptographic Accelerator (PCICA) or the PCI
-         Cryptographic Coprocessor (PCICC).  This option is also available
-         as a module called z90crypt.ko.
+         adapter like:
+         + PCI Cryptographic Accelerator (PCICA)
+         + PCI Cryptographic Coprocessor (PCICC)
+         + PCI-X Cryptographic Coprocessor (PCIXCC)
+         + Crypto Express2 Coprocessor (CEX2C)
+         + Crypto Express2 Accelerator (CEX2A)
+
+config ZCRYPT_MONOLITHIC
+       bool "Monolithic zcrypt module"
+       depends on ZCRYPT="m"
+       help
+         Select this option if you want to have a single module z90crypt.ko
+         that contains all parts of the crypto device driver (ap bus,
+         request router and all the card drivers).
 
 endmenu
index 25c1ef6..d0647d1 100644 (file)
@@ -184,7 +184,7 @@ dasd_state_known_to_basic(struct dasd_device * device)
        device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2,
                                            8 * sizeof (long));
        debug_register_view(device->debug_area, &debug_sprintf_view);
-       debug_set_level(device->debug_area, DBF_EMERG);
+       debug_set_level(device->debug_area, DBF_WARNING);
        DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
 
        device->state = DASD_STATE_BASIC;
@@ -893,7 +893,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
 
        device = (struct dasd_device *) cqr->device;
        if (device == NULL ||
-           device != dasd_device_from_cdev(cdev) ||
+           device != dasd_device_from_cdev_locked(cdev) ||
            strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
                MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
                        cdev->dev.bus_id);
@@ -970,7 +970,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
        /* first of all check for state change pending interrupt */
        mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
        if ((irb->scsw.dstat & mask) == mask) {
-               device = dasd_device_from_cdev(cdev);
+               device = dasd_device_from_cdev_locked(cdev);
                if (!IS_ERR(device)) {
                        dasd_handle_state_change_pending(device);
                        dasd_put_device(device);
@@ -2169,7 +2169,7 @@ dasd_init(void)
                goto failed;
        }
        debug_register_view(dasd_debug_area, &debug_sprintf_view);
-       debug_set_level(dasd_debug_area, DBF_EMERG);
+       debug_set_level(dasd_debug_area, DBF_WARNING);
 
        DBF_EVENT(DBF_EMERG, "%s", "debug area created");
 
index 9af02c7..91cf971 100644 (file)
@@ -258,8 +258,12 @@ dasd_parse_keyword( char *parsestring ) {
                 return residual_str;
         }
        if (strncmp("nopav", parsestring, length) == 0) {
-               dasd_nopav = 1;
-               MESSAGE(KERN_INFO, "%s", "disable PAV mode");
+               if (MACHINE_IS_VM)
+                       MESSAGE(KERN_INFO, "%s", "'nopav' not supported on VM");
+               else {
+                       dasd_nopav = 1;
+                       MESSAGE(KERN_INFO, "%s", "disable PAV mode");
+               }
                return residual_str;
        }
        if (strncmp("fixedbuffers", parsestring, length) == 0) {
@@ -523,17 +527,17 @@ dasd_create_device(struct ccw_device *cdev)
 {
        struct dasd_devmap *devmap;
        struct dasd_device *device;
+       unsigned long flags;
        int rc;
 
        devmap = dasd_devmap_from_cdev(cdev);
        if (IS_ERR(devmap))
                return (void *) devmap;
-       cdev->dev.driver_data = devmap;
 
        device = dasd_alloc_device();
        if (IS_ERR(device))
                return device;
-       atomic_set(&device->ref_count, 2);
+       atomic_set(&device->ref_count, 3);
 
        spin_lock(&dasd_devmap_lock);
        if (!devmap->device) {
@@ -552,6 +556,11 @@ dasd_create_device(struct ccw_device *cdev)
                dasd_free_device(device);
                return ERR_PTR(rc);
        }
+
+       spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+       cdev->dev.driver_data = device;
+       spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+
        return device;
 }
 
@@ -569,6 +578,7 @@ dasd_delete_device(struct dasd_device *device)
 {
        struct ccw_device *cdev;
        struct dasd_devmap *devmap;
+       unsigned long flags;
 
        /* First remove device pointer from devmap. */
        devmap = dasd_find_busid(device->cdev->dev.bus_id);
@@ -582,9 +592,16 @@ dasd_delete_device(struct dasd_device *device)
        devmap->device = NULL;
        spin_unlock(&dasd_devmap_lock);
 
-       /* Drop ref_count by 2, one for the devmap reference and
-        * one for the passed reference. */
-       atomic_sub(2, &device->ref_count);
+       /* Disconnect dasd_device structure from ccw_device structure. */
+       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+       device->cdev->dev.driver_data = NULL;
+       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+
+       /*
+        * Drop ref_count by 3, one for the devmap reference, one for
+        * the cdev reference and one for the passed reference.
+        */
+       atomic_sub(3, &device->ref_count);
 
        /* Wait for reference counter to drop to zero. */
        wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
@@ -593,9 +610,6 @@ dasd_delete_device(struct dasd_device *device)
        cdev = device->cdev;
        device->cdev = NULL;
 
-       /* Disconnect dasd_devmap structure from ccw_device structure. */
-       cdev->dev.driver_data = NULL;
-
        /* Put ccw_device structure. */
        put_device(&cdev->dev);
 
@@ -615,21 +629,32 @@ dasd_put_device_wake(struct dasd_device *device)
 
 /*
  * Return dasd_device structure associated with cdev.
+ * This function needs to be called with the ccw device
+ * lock held. It can be used from interrupt context.
+ */
+struct dasd_device *
+dasd_device_from_cdev_locked(struct ccw_device *cdev)
+{
+       struct dasd_device *device = cdev->dev.driver_data;
+
+       if (!device)
+               return ERR_PTR(-ENODEV);
+       dasd_get_device(device);
+       return device;
+}
+
+/*
+ * Return dasd_device structure associated with cdev.
  */
 struct dasd_device *
 dasd_device_from_cdev(struct ccw_device *cdev)
 {
-       struct dasd_devmap *devmap;
        struct dasd_device *device;
+       unsigned long flags;
 
-       device = ERR_PTR(-ENODEV);
-       spin_lock(&dasd_devmap_lock);
-       devmap = cdev->dev.driver_data;
-       if (devmap && devmap->device) {
-               device = devmap->device;
-               dasd_get_device(device);
-       }
-       spin_unlock(&dasd_devmap_lock);
+       spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+       device = dasd_device_from_cdev_locked(cdev);
+       spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
        return device;
 }
 
@@ -730,16 +755,17 @@ static ssize_t
 dasd_discipline_show(struct device *dev, struct device_attribute *attr,
                     char *buf)
 {
-       struct dasd_devmap *devmap;
-       char *dname;
+       struct dasd_device *device;
+       ssize_t len;
 
-       spin_lock(&dasd_devmap_lock);
-       dname = "none";
-       devmap = dev->driver_data;
-       if (devmap && devmap->device && devmap->device->discipline)
-               dname = devmap->device->discipline->name;
-       spin_unlock(&dasd_devmap_lock);
-       return snprintf(buf, PAGE_SIZE, "%s\n", dname);
+       device = dasd_device_from_cdev(to_ccwdev(dev));
+       if (!IS_ERR(device) && device->discipline) {
+               len = snprintf(buf, PAGE_SIZE, "%s\n",
+                              device->discipline->name);
+               dasd_put_device(device);
+       } else
+               len = snprintf(buf, PAGE_SIZE, "none\n");
+       return len;
 }
 
 static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
index da65f1b..e0bf30e 100644 (file)
@@ -678,7 +678,7 @@ int __init dasd_eer_init(void)
        return 0;
 }
 
-void __exit dasd_eer_exit(void)
+void dasd_eer_exit(void)
 {
        WARN_ON(misc_deregister(&dasd_eer_dev) != 0);
 }
index 3ccf06d..9f52004 100644 (file)
@@ -534,6 +534,7 @@ int dasd_add_sysfs_files(struct ccw_device *);
 void dasd_remove_sysfs_files(struct ccw_device *);
 
 struct dasd_device *dasd_device_from_cdev(struct ccw_device *);
+struct dasd_device *dasd_device_from_cdev_locked(struct ccw_device *);
 struct dasd_device *dasd_device_from_devindex(int);
 
 int dasd_parse(void);
index ca7d51f..cab2c73 100644 (file)
@@ -453,7 +453,7 @@ static int __init xpram_init(void)
                PRINT_WARN("No expanded memory available\n");
                return -ENODEV;
        }
-       xpram_pages = xpram_highest_page_index();
+       xpram_pages = xpram_highest_page_index() + 1;
        PRINT_INFO("  %u pages expanded memory found (%lu KB).\n",
                   xpram_pages, (unsigned long) xpram_pages*4);
        rc = xpram_setup_sizes(xpram_pages);
index 0c0162f..c3e97b4 100644 (file)
@@ -28,3 +28,4 @@ obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o
 obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o
 obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o
 obj-$(CONFIG_MONREADER) += monreader.o
+obj-$(CONFIG_MONWRITER) += monwriter.o
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
new file mode 100644 (file)
index 0000000..1e3939a
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * drivers/s390/char/monwriter.c
+ *
+ * Character device driver for writing z/VM *MONITOR service records.
+ *
+ * Copyright (C) IBM Corp. 2006
+ *
+ * Author(s): Melissa Howland <Melissa.Howland@us.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/ctype.h>
+#include <linux/poll.h>
+#include <asm/uaccess.h>
+#include <asm/ebcdic.h>
+#include <asm/io.h>
+#include <asm/appldata.h>
+#include <asm/monwriter.h>
+
+#define MONWRITE_MAX_DATALEN   4024
+
+static int mon_max_bufs = 255;
+
+struct mon_buf {
+       struct list_head list;
+       struct monwrite_hdr hdr;
+       int diag_done;
+       char *data;
+};
+
+struct mon_private {
+       struct list_head list;
+       struct monwrite_hdr hdr;
+       size_t hdr_to_read;
+       size_t data_to_read;
+       struct mon_buf *current_buf;
+       int mon_buf_count;
+};
+
+/*
+ * helper functions
+ */
+
+static int monwrite_diag(struct monwrite_hdr *myhdr, char *buffer, int fcn)
+{
+       struct appldata_product_id id;
+       int rc;
+
+       strcpy(id.prod_nr, "LNXAPPL");
+       id.prod_fn = myhdr->applid;
+       id.record_nr = myhdr->record_num;
+       id.version_nr = myhdr->version;
+       id.release_nr = myhdr->release;
+       id.mod_lvl = myhdr->mod_level;
+       rc = appldata_asm(&id, fcn, (void *) buffer, myhdr->datalen);
+       if (rc <= 0)
+               return rc;
+       if (rc == 5)
+               return -EPERM;
+       printk("DIAG X'DC' error with return code: %i\n", rc);
+       return -EINVAL;
+}
+
+static inline struct mon_buf *monwrite_find_hdr(struct mon_private *monpriv,
+                                               struct monwrite_hdr *monhdr)
+{
+       struct mon_buf *entry, *next;
+
+       list_for_each_entry_safe(entry, next, &monpriv->list, list)
+               if (entry->hdr.applid == monhdr->applid &&
+                   entry->hdr.record_num == monhdr->record_num &&
+                   entry->hdr.version == monhdr->version &&
+                   entry->hdr.release == monhdr->release &&
+                   entry->hdr.mod_level == monhdr->mod_level)
+                       return entry;
+       return NULL;
+}
+
+static int monwrite_new_hdr(struct mon_private *monpriv)
+{
+       struct monwrite_hdr *monhdr = &monpriv->hdr;
+       struct mon_buf *monbuf;
+       int rc;
+
+       if (monhdr->datalen > MONWRITE_MAX_DATALEN ||
+           monhdr->mon_function > MONWRITE_START_CONFIG ||
+           monhdr->hdrlen != sizeof(struct monwrite_hdr))
+               return -EINVAL;
+       monbuf = monwrite_find_hdr(monpriv, monhdr);
+       if (monbuf) {
+               if (monhdr->mon_function == MONWRITE_STOP_INTERVAL) {
+                       monhdr->datalen = monbuf->hdr.datalen;
+                       rc = monwrite_diag(monhdr, monbuf->data,
+                                          APPLDATA_STOP_REC);
+                       list_del(&monbuf->list);
+                       monpriv->mon_buf_count--;
+                       kfree(monbuf->data);
+                       kfree(monbuf);
+                       monbuf = NULL;
+               }
+       } else {
+               if (monpriv->mon_buf_count >= mon_max_bufs)
+                       return -ENOSPC;
+               monbuf = kzalloc(sizeof(struct mon_buf), GFP_KERNEL);
+               if (!monbuf)
+                       return -ENOMEM;
+               monbuf->data = kzalloc(monbuf->hdr.datalen,
+                                      GFP_KERNEL | GFP_DMA);
+               if (!monbuf->data) {
+                       kfree(monbuf);
+                       return -ENOMEM;
+               }
+               monbuf->hdr = *monhdr;
+               list_add_tail(&monbuf->list, &monpriv->list);
+               monpriv->mon_buf_count++;
+       }
+       monpriv->current_buf = monbuf;
+       return 0;
+}
+
+static int monwrite_new_data(struct mon_private *monpriv)
+{
+       struct monwrite_hdr *monhdr = &monpriv->hdr;
+       struct mon_buf *monbuf = monpriv->current_buf;
+       int rc = 0;
+
+       switch (monhdr->mon_function) {
+       case MONWRITE_START_INTERVAL:
+               if (!monbuf->diag_done) {
+                       rc = monwrite_diag(monhdr, monbuf->data,
+                                          APPLDATA_START_INTERVAL_REC);
+                       monbuf->diag_done = 1;
+               }
+               break;
+       case MONWRITE_START_CONFIG:
+               if (!monbuf->diag_done) {
+                       rc = monwrite_diag(monhdr, monbuf->data,
+                                          APPLDATA_START_CONFIG_REC);
+                       monbuf->diag_done = 1;
+               }
+               break;
+       case MONWRITE_GEN_EVENT:
+               rc = monwrite_diag(monhdr, monbuf->data,
+                                  APPLDATA_GEN_EVENT_REC);
+               list_del(&monpriv->current_buf->list);
+               kfree(monpriv->current_buf->data);
+               kfree(monpriv->current_buf);
+               monpriv->current_buf = NULL;
+               break;
+       default:
+               /* monhdr->mon_function is checked in monwrite_new_hdr */
+               BUG();
+       }
+       return rc;
+}
+
+/*
+ * file operations
+ */
+
+static int monwrite_open(struct inode *inode, struct file *filp)
+{
+       struct mon_private *monpriv;
+
+       monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
+       if (!monpriv)
+               return -ENOMEM;
+       INIT_LIST_HEAD(&monpriv->list);
+       monpriv->hdr_to_read = sizeof(monpriv->hdr);
+       filp->private_data = monpriv;
+       return nonseekable_open(inode, filp);
+}
+
+static int monwrite_close(struct inode *inode, struct file *filp)
+{
+       struct mon_private *monpriv = filp->private_data;
+       struct mon_buf *entry, *next;
+
+       list_for_each_entry_safe(entry, next, &monpriv->list, list) {
+               if (entry->hdr.mon_function != MONWRITE_GEN_EVENT)
+                       monwrite_diag(&entry->hdr, entry->data,
+                                     APPLDATA_STOP_REC);
+               monpriv->mon_buf_count--;
+               list_del(&entry->list);
+               kfree(entry->data);
+               kfree(entry);
+       }
+       kfree(monpriv);
+       return 0;
+}
+
+static ssize_t monwrite_write(struct file *filp, const char __user *data,
+                             size_t count, loff_t *ppos)
+{
+       struct mon_private *monpriv = filp->private_data;
+       size_t len, written;
+       void *to;
+       int rc;
+
+       for (written = 0; written < count; ) {
+               if (monpriv->hdr_to_read) {
+                       len = min(count - written, monpriv->hdr_to_read);
+                       to = (char *) &monpriv->hdr +
+                               sizeof(monpriv->hdr) - monpriv->hdr_to_read;
+                       if (copy_from_user(to, data + written, len)) {
+                               rc = -EFAULT;
+                               goto out_error;
+                       }
+                       monpriv->hdr_to_read -= len;
+                       written += len;
+                       if (monpriv->hdr_to_read > 0)
+                               continue;
+                       rc = monwrite_new_hdr(monpriv);
+                       if (rc)
+                               goto out_error;
+                       monpriv->data_to_read = monpriv->current_buf ?
+                               monpriv->current_buf->hdr.datalen : 0;
+               }
+
+               if (monpriv->data_to_read) {
+                       len = min(count - written, monpriv->data_to_read);
+                       to = monpriv->current_buf->data +
+                               monpriv->hdr.datalen - monpriv->data_to_read;
+                       if (copy_from_user(to, data + written, len)) {
+                               rc = -EFAULT;
+                               goto out_error;
+                       }
+                       monpriv->data_to_read -= len;
+                       written += len;
+                       if (monpriv->data_to_read > 0)
+                               continue;
+                       rc = monwrite_new_data(monpriv);
+                       if (rc)
+                               goto out_error;
+               }
+               monpriv->hdr_to_read = sizeof(monpriv->hdr);
+       }
+       return written;
+
+out_error:
+       monpriv->data_to_read = 0;
+       monpriv->hdr_to_read = sizeof(struct monwrite_hdr);
+       return rc;
+}
+
+static struct file_operations monwrite_fops = {
+       .owner   = THIS_MODULE,
+       .open    = &monwrite_open,
+       .release = &monwrite_close,
+       .write   = &monwrite_write,
+};
+
+static struct miscdevice mon_dev = {
+       .name   = "monwriter",
+       .fops   = &monwrite_fops,
+       .minor  = MISC_DYNAMIC_MINOR,
+};
+
+/*
+ * module init/exit
+ */
+
+static int __init mon_init(void)
+{
+       if (MACHINE_IS_VM)
+               return misc_register(&mon_dev);
+       else
+               return -ENODEV;
+}
+
+static void __exit mon_exit(void)
+{
+       WARN_ON(misc_deregister(&mon_dev) != 0);
+}
+
+module_init(mon_init);
+module_exit(mon_exit);
+
+module_param_named(max_bufs, mon_max_bufs, int, 0644);
+MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers"
+                "that can be active at one time");
+
+MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>");
+MODULE_DESCRIPTION("Character device driver for writing z/VM "
+                  "APPLDATA monitor records.");
+MODULE_LICENSE("GPL");
index 19762f3..1678b6c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2004,2005 IBM Corporation
- * Interface implementation for communication with the v/VM control program
+ * Interface implementation for communication with the z/VM control program
  * Author(s): Christian Borntraeger <cborntra@de.ibm.com>
  *
  *
index 87389e7..8a5975f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2004, 2005 IBM Corporation
- * Interface implementation for communication with the v/VM control program
+ * Interface implementation for communication with the z/VM control program
  * Version 1.0
  * Author(s): Christian Borntraeger <cborntra@de.ibm.com>
  *
index c28444a..3bb4e47 100644 (file)
@@ -256,7 +256,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
        /* trigger path verification. */
        if (sch->driver && sch->driver->verify)
                sch->driver->verify(&sch->dev);
-       else if (sch->vpm == mask)
+       else if (sch->lpm == mask)
                goto out_unreg;
 out_unlock:
        spin_unlock_irq(&sch->lock);
@@ -378,6 +378,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
 
        if (chp_mask == 0) {
                spin_unlock_irq(&sch->lock);
+               put_device(&sch->dev);
                return 0;
        }
        old_lpm = sch->lpm;
@@ -392,7 +393,7 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
 
        spin_unlock_irq(&sch->lock);
        put_device(&sch->dev);
-       return (res_data->fla_mask == 0xffff) ? -ENODEV : 0;
+       return 0;
 }
 
 
index 89320c1..2e2882d 100644 (file)
 #include <linux/device.h>
 #include <linux/kernel_stat.h>
 #include <linux/interrupt.h>
-
 #include <asm/cio.h>
 #include <asm/delay.h>
 #include <asm/irq.h>
-
+#include <asm/setup.h>
 #include "airq.h"
 #include "cio.h"
 #include "css.h"
@@ -192,7 +191,7 @@ cio_start_key (struct subchannel *sch,      /* subchannel structure */
        sch->orb.pfch = sch->options.prefetch == 0;
        sch->orb.spnd = sch->options.suspend;
        sch->orb.ssic = sch->options.suspend && sch->options.inter;
-       sch->orb.lpm = (lpm != 0) ? (lpm & sch->opm) : sch->lpm;
+       sch->orb.lpm = (lpm != 0) ? lpm : sch->lpm;
 #ifdef CONFIG_64BIT
        /*
         * for 64 bit we always support 64 bit IDAWs with 4k page size only
@@ -570,10 +569,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
        sch->opm = 0xff;
        if (!cio_is_console(sch->schid))
                chsc_validate_chpids(sch);
-       sch->lpm = sch->schib.pmcw.pim &
-               sch->schib.pmcw.pam &
-               sch->schib.pmcw.pom &
-               sch->opm;
+       sch->lpm = sch->schib.pmcw.pam & sch->opm;
 
        CIO_DEBUG(KERN_INFO, 0,
                  "Detected device %04x on subchannel 0.%x.%04X"
@@ -841,14 +837,26 @@ __clear_subchannel_easy(struct subchannel_id schid)
        return -EBUSY;
 }
 
-extern void do_reipl(unsigned long devno);
-static int
-__shutdown_subchannel_easy(struct subchannel_id schid, void *data)
+struct sch_match_id {
+       struct subchannel_id schid;
+       struct ccw_dev_id devid;
+       int rc;
+};
+
+static int __shutdown_subchannel_easy_and_match(struct subchannel_id schid,
+       void *data)
 {
        struct schib schib;
+       struct sch_match_id *match_id = data;
 
        if (stsch_err(schid, &schib))
                return -ENXIO;
+       if (match_id && schib.pmcw.dnv &&
+               (schib.pmcw.dev == match_id->devid.devno) &&
+               (schid.ssid == match_id->devid.ssid)) {
+               match_id->schid = schid;
+               match_id->rc = 0;
+       }
        if (!schib.pmcw.ena)
                return 0;
        switch(__disable_subchannel_easy(schid, &schib)) {
@@ -864,18 +872,71 @@ __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
        return 0;
 }
 
-void
-clear_all_subchannels(void)
+static int clear_all_subchannels_and_match(struct ccw_dev_id *devid,
+       struct subchannel_id *schid)
 {
+       struct sch_match_id match_id;
+
+       match_id.devid = *devid;
+       match_id.rc = -ENODEV;
        local_irq_disable();
-       for_each_subchannel(__shutdown_subchannel_easy, NULL);
+       for_each_subchannel(__shutdown_subchannel_easy_and_match, &match_id);
+       if (match_id.rc == 0)
+               *schid = match_id.schid;
+       return match_id.rc;
 }
 
+
+void clear_all_subchannels(void)
+{
+       local_irq_disable();
+       for_each_subchannel(__shutdown_subchannel_easy_and_match, NULL);
+}
+
+extern void do_reipl_asm(__u32 schid);
+
 /* Make sure all subchannels are quiet before we re-ipl an lpar. */
-void
-reipl(unsigned long devno)
+void reipl_ccw_dev(struct ccw_dev_id *devid)
 {
-       clear_all_subchannels();
+       struct subchannel_id schid;
+
+       if (clear_all_subchannels_and_match(devid, &schid))
+               panic("IPL Device not found\n");
        cio_reset_channel_paths();
-       do_reipl(devno);
+       do_reipl_asm(*((__u32*)&schid));
+}
+
+extern struct schib ipl_schib;
+
+/*
+ * ipl_save_parameters gets called very early. It is not allowed to access
+ * anything in the bss section at all. The bss section is not cleared yet,
+ * but may contain some ipl parameters written by the firmware.
+ * These parameters (if present) are copied to 0x2000.
+ * To avoid corruption of the ipl parameters, all variables used by this
+ * function must reside on the stack or in the data section.
+ */
+void ipl_save_parameters(void)
+{
+       struct subchannel_id schid;
+       unsigned int *ipl_ptr;
+       void *src, *dst;
+
+       schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID;
+       if (!schid.one)
+               return;
+       if (stsch(schid, &ipl_schib))
+               return;
+       if (!ipl_schib.pmcw.dnv)
+               return;
+       ipl_devno = ipl_schib.pmcw.dev;
+       ipl_flags |= IPL_DEVNO_VALID;
+       if (!ipl_schib.pmcw.qf)
+               return;
+       ipl_flags |= IPL_PARMBLOCK_VALID;
+       ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR;
+       src = (void *)(unsigned long)*ipl_ptr;
+       dst = (void *)IPL_PARMBLOCK_ORIGIN;
+       memmove(dst, src, PAGE_SIZE);
+       *ipl_ptr = IPL_PARMBLOCK_ORIGIN;
 }
index 13eeea3..7086a74 100644 (file)
@@ -182,136 +182,141 @@ get_subchannel_by_schid(struct subchannel_id schid)
        return dev ? to_subchannel(dev) : NULL;
 }
 
-
-static inline int
-css_get_subchannel_status(struct subchannel *sch, struct subchannel_id schid)
+static inline int css_get_subchannel_status(struct subchannel *sch)
 {
        struct schib schib;
-       int cc;
 
-       cc = stsch(schid, &schib);
-       if (cc)
-               return CIO_GONE;
-       if (!schib.pmcw.dnv)
+       if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
                return CIO_GONE;
-       if (sch && sch->schib.pmcw.dnv &&
-           (schib.pmcw.dev != sch->schib.pmcw.dev))
+       if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
                return CIO_REVALIDATE;
-       if (sch && !sch->lpm)
+       if (!sch->lpm)
                return CIO_NO_PATH;
        return CIO_OPER;
 }
-       
-static int
-css_evaluate_subchannel(struct subchannel_id schid, int slow)
+
+static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
 {
        int event, ret, disc;
-       struct subchannel *sch;
        unsigned long flags;
+       enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action;
 
-       sch = get_subchannel_by_schid(schid);
-       disc = sch ? device_is_disconnected(sch) : 0;
+       spin_lock_irqsave(&sch->lock, flags);
+       disc = device_is_disconnected(sch);
        if (disc && slow) {
-               if (sch)
-                       put_device(&sch->dev);
-               return 0; /* Already processed. */
+               /* Disconnected devices are evaluated directly only.*/
+               spin_unlock_irqrestore(&sch->lock, flags);
+               return 0;
        }
-       /*
-        * We've got a machine check, so running I/O won't get an interrupt.
-        * Kill any pending timers.
-        */
-       if (sch)
-               device_kill_pending_timer(sch);
+       /* No interrupt after machine check - kill pending timers. */
+       device_kill_pending_timer(sch);
        if (!disc && !slow) {
-               if (sch)
-                       put_device(&sch->dev);
-               return -EAGAIN; /* Will be done on the slow path. */
+               /* Non-disconnected devices are evaluated on the slow path. */
+               spin_unlock_irqrestore(&sch->lock, flags);
+               return -EAGAIN;
        }
-       event = css_get_subchannel_status(sch, schid);
+       event = css_get_subchannel_status(sch);
        CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
-                     schid.ssid, schid.sch_no, event,
-                     sch?(disc?"disconnected":"normal"):"unknown",
-                     slow?"slow":"fast");
+                     sch->schid.ssid, sch->schid.sch_no, event,
+                     disc ? "disconnected" : "normal",
+                     slow ? "slow" : "fast");
+       /* Analyze subchannel status. */
+       action = NONE;
        switch (event) {
        case CIO_NO_PATH:
-       case CIO_GONE:
-               if (!sch) {
-                       /* Never used this subchannel. Ignore. */
-                       ret = 0;
+               if (disc) {
+                       /* Check if paths have become available. */
+                       action = REPROBE;
                        break;
                }
-               if (disc && (event == CIO_NO_PATH)) {
-                       /*
-                        * Uargh, hack again. Because we don't get a machine
-                        * check on configure on, our path bookkeeping can
-                        * be out of date here (it's fine while we only do
-                        * logical varying or get chsc machine checks). We
-                        * need to force reprobing or we might miss devices
-                        * coming operational again. It won't do harm in real
-                        * no path situations.
-                        */
-                       spin_lock_irqsave(&sch->lock, flags);
-                       device_trigger_reprobe(sch);
+               /* fall through */
+       case CIO_GONE:
+               /* Prevent unwanted effects when opening lock. */
+               cio_disable_subchannel(sch);
+               device_set_disconnected(sch);
+               /* Ask driver what to do with device. */
+               action = UNREGISTER;
+               if (sch->driver && sch->driver->notify) {
                        spin_unlock_irqrestore(&sch->lock, flags);
-                       ret = 0;
-                       break;
-               }
-               if (sch->driver && sch->driver->notify &&
-                   sch->driver->notify(&sch->dev, event)) {
-                       cio_disable_subchannel(sch);
-                       device_set_disconnected(sch);
-                       ret = 0;
-                       break;
+                       ret = sch->driver->notify(&sch->dev, event);
+                       spin_lock_irqsave(&sch->lock, flags);
+                       if (ret)
+                               action = NONE;
                }
-               /*
-                * Unregister subchannel.
-                * The device will be killed automatically.
-                */
-               cio_disable_subchannel(sch);
-               css_sch_device_unregister(sch);
-               /* Reset intparm to zeroes. */
-               sch->schib.pmcw.intparm = 0;
-               cio_modify(sch);
-               put_device(&sch->dev);
-               ret = 0;
                break;
        case CIO_REVALIDATE:
-               /* 
-                * Revalidation machine check. Sick.
-                * We don't notify the driver since we have to throw the device
-                * away in any case.
-                */
-               if (!disc) {
-                       css_sch_device_unregister(sch);
-                       /* Reset intparm to zeroes. */
-                       sch->schib.pmcw.intparm = 0;
-                       cio_modify(sch);
-                       put_device(&sch->dev);
-                       ret = css_probe_device(schid);
-               } else {
-                       /*
-                        * We can't immediately deregister the disconnected
-                        * device since it might block.
-                        */
-                       spin_lock_irqsave(&sch->lock, flags);
-                       device_trigger_reprobe(sch);
-                       spin_unlock_irqrestore(&sch->lock, flags);
-                       ret = 0;
-               }
+               /* Device will be removed, so no notify necessary. */
+               if (disc)
+                       /* Reprobe because immediate unregister might block. */
+                       action = REPROBE;
+               else
+                       action = UNREGISTER_PROBE;
                break;
        case CIO_OPER:
-               if (disc) {
-                       spin_lock_irqsave(&sch->lock, flags);
+               if (disc)
                        /* Get device operational again. */
-                       device_trigger_reprobe(sch);
-                       spin_unlock_irqrestore(&sch->lock, flags);
-               }
-               ret = sch ? 0 : css_probe_device(schid);
+                       action = REPROBE;
+               break;
+       }
+       /* Perform action. */
+       ret = 0;
+       switch (action) {
+       case UNREGISTER:
+       case UNREGISTER_PROBE:
+               /* Unregister device (will use subchannel lock). */
+               spin_unlock_irqrestore(&sch->lock, flags);
+               css_sch_device_unregister(sch);
+               spin_lock_irqsave(&sch->lock, flags);
+
+               /* Reset intparm to zeroes. */
+               sch->schib.pmcw.intparm = 0;
+               cio_modify(sch);
+
+               /* Probe if necessary. */
+               if (action == UNREGISTER_PROBE)
+                       ret = css_probe_device(sch->schid);
+               break;
+       case REPROBE:
+               device_trigger_reprobe(sch);
                break;
        default:
-               BUG();
-               ret = 0;
+               break;
+       }
+       spin_unlock_irqrestore(&sch->lock, flags);
+
+       return ret;
+}
+
+static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
+{
+       struct schib schib;
+
+       if (!slow) {
+               /* Will be done on the slow path. */
+               return -EAGAIN;
        }
+       if (stsch(schid, &schib) || !schib.pmcw.dnv) {
+               /* Unusable - ignore. */
+               return 0;
+       }
+       CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, "
+                        "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER);
+
+       return css_probe_device(schid);
+}
+
+static int css_evaluate_subchannel(struct subchannel_id schid, int slow)
+{
+       struct subchannel *sch;
+       int ret;
+
+       sch = get_subchannel_by_schid(schid);
+       if (sch) {
+               ret = css_evaluate_known_subchannel(sch, slow);
+               put_device(&sch->dev);
+       } else
+               ret = css_evaluate_new_subchannel(schid, slow);
+
        return ret;
 }
 
index 646da56..6889456 100644 (file)
@@ -52,53 +52,81 @@ ccw_bus_match (struct device * dev, struct device_driver * drv)
        return 1;
 }
 
-/*
- * Hotplugging interface for ccw devices.
- * Heavily modeled on pci and usb hotplug.
- */
-static int
-ccw_uevent (struct device *dev, char **envp, int num_envp,
-            char *buffer, int buffer_size)
+/* Store modalias string delimited by prefix/suffix string into buffer with
+ * specified size. Return length of resulting string (excluding trailing '\0')
+ * even if string doesn't fit buffer (snprintf semantics). */
+static int snprint_alias(char *buf, size_t size, const char *prefix,
+                        struct ccw_device_id *id, const char *suffix)
 {
-       struct ccw_device *cdev = to_ccwdev(dev);
-       int i = 0;
-       int length = 0;
+       int len;
 
-       if (!cdev)
-               return -ENODEV;
+       len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type,
+                      id->cu_model);
+       if (len > size)
+               return len;
+       buf += len;
+       size -= len;
 
-       /* what we want to pass to /sbin/hotplug */
+       if (id->dev_type != 0)
+               len += snprintf(buf, size, "dt%04Xdm%02X%s", id->dev_type,
+                               id->dev_model, suffix);
+       else
+               len += snprintf(buf, size, "dtdm%s", suffix);
 
-       envp[i++] = buffer;
-       length += scnprintf(buffer, buffer_size - length, "CU_TYPE=%04X",
-                          cdev->id.cu_type);
-       if ((buffer_size - length <= 0) || (i >= num_envp))
-               return -ENOMEM;
-       ++length;
-       buffer += length;
+       return len;
+}
 
+/* Set up environment variables for ccw device uevent. Return 0 on success,
+ * non-zero otherwise. */
+static int ccw_uevent(struct device *dev, char **envp, int num_envp,
+                     char *buffer, int buffer_size)
+{
+       struct ccw_device *cdev = to_ccwdev(dev);
+       struct ccw_device_id *id = &(cdev->id);
+       int i = 0;
+       int len;
+
+       /* CU_TYPE= */
+       len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1;
+       if (len > buffer_size || i >= num_envp)
+               return -ENOMEM;
        envp[i++] = buffer;
-       length += scnprintf(buffer, buffer_size - length, "CU_MODEL=%02X",
-                          cdev->id.cu_model);
-       if ((buffer_size - length <= 0) || (i >= num_envp))
+       buffer += len;
+       buffer_size -= len;
+
+       /* CU_MODEL= */
+       len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1;
+       if (len > buffer_size || i >= num_envp)
                return -ENOMEM;
-       ++length;
-       buffer += length;
+       envp[i++] = buffer;
+       buffer += len;
+       buffer_size -= len;
 
        /* The next two can be zero, that's ok for us */
-       envp[i++] = buffer;
-       length += scnprintf(buffer, buffer_size - length, "DEV_TYPE=%04X",
-                          cdev->id.dev_type);
-       if ((buffer_size - length <= 0) || (i >= num_envp))
+       /* DEV_TYPE= */
+       len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1;
+       if (len > buffer_size || i >= num_envp)
                return -ENOMEM;
-       ++length;
-       buffer += length;
+       envp[i++] = buffer;
+       buffer += len;
+       buffer_size -= len;
 
+       /* DEV_MODEL= */
+       len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X",
+                       (unsigned char) id->dev_model) + 1;
+       if (len > buffer_size || i >= num_envp)
+               return -ENOMEM;
        envp[i++] = buffer;
-       length += scnprintf(buffer, buffer_size - length, "DEV_MODEL=%02X",
-                          cdev->id.dev_model);
-       if ((buffer_size - length <= 0) || (i >= num_envp))
+       buffer += len;
+       buffer_size -= len;
+
+       /* MODALIAS=  */
+       len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1;
+       if (len > buffer_size || i >= num_envp)
                return -ENOMEM;
+       envp[i++] = buffer;
+       buffer += len;
+       buffer_size -= len;
 
        envp[i] = NULL;
 
@@ -251,16 +279,11 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
        struct ccw_device_id *id = &(cdev->id);
-       int ret;
+       int len;
 
-       ret = sprintf(buf, "ccw:t%04Xm%02X",
-                       id->cu_type, id->cu_model);
-       if (id->dev_type != 0)
-               ret += sprintf(buf + ret, "dt%04Xdm%02X\n",
-                               id->dev_type, id->dev_model);
-       else
-               ret += sprintf(buf + ret, "dtdm\n");
-       return ret;
+       len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1;
+
+       return len > PAGE_SIZE ? PAGE_SIZE : len;
 }
 
 static ssize_t
index 35e162b..dace46f 100644 (file)
@@ -232,10 +232,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
         */
        old_lpm = sch->lpm;
        stsch(sch->schid, &sch->schib);
-       sch->lpm = sch->schib.pmcw.pim &
-               sch->schib.pmcw.pam &
-               sch->schib.pmcw.pom &
-               sch->opm;
+       sch->lpm = sch->schib.pmcw.pam & sch->opm;
        /* Check since device may again have become not operational. */
        if (!sch->schib.pmcw.dnv)
                state = DEV_STATE_NOT_OPER;
@@ -267,6 +264,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
                        notify = 1;
                }
                /* fill out sense information */
+               memset(&cdev->id, 0, sizeof(cdev->id));
                cdev->id.cu_type   = cdev->private->senseid.cu_type;
                cdev->id.cu_model  = cdev->private->senseid.cu_model;
                cdev->id.dev_type  = cdev->private->senseid.dev_type;
@@ -454,8 +452,8 @@ ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
                return;
        }
        /* Start Path Group verification. */
-       sch->vpm = 0;   /* Start with no path groups set. */
        cdev->private->state = DEV_STATE_VERIFY;
+       cdev->private->flags.doverify = 0;
        ccw_device_verify_start(cdev);
 }
 
@@ -555,7 +553,19 @@ ccw_device_nopath_notify(void *data)
 void
 ccw_device_verify_done(struct ccw_device *cdev, int err)
 {
-       cdev->private->flags.doverify = 0;
+       struct subchannel *sch;
+
+       sch = to_subchannel(cdev->dev.parent);
+       /* Update schib - pom may have changed. */
+       stsch(sch->schid, &sch->schib);
+       /* Update lpm with verified path mask. */
+       sch->lpm = sch->vpm;
+       /* Repeat path verification? */
+       if (cdev->private->flags.doverify) {
+               cdev->private->flags.doverify = 0;
+               ccw_device_verify_start(cdev);
+               return;
+       }
        switch (err) {
        case -EOPNOTSUPP: /* path grouping not supported, just set online. */
                cdev->private->options.pgroup = 0;
@@ -613,6 +623,7 @@ ccw_device_online(struct ccw_device *cdev)
        if (!cdev->private->options.pgroup) {
                /* Start initial path verification. */
                cdev->private->state = DEV_STATE_VERIFY;
+               cdev->private->flags.doverify = 0;
                ccw_device_verify_start(cdev);
                return 0;
        }
@@ -659,7 +670,6 @@ ccw_device_offline(struct ccw_device *cdev)
        /* Are we doing path grouping? */
        if (!cdev->private->options.pgroup) {
                /* No, set state offline immediately. */
-               sch->vpm = 0;
                ccw_device_done(cdev, DEV_STATE_OFFLINE);
                return 0;
        }
@@ -780,6 +790,7 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
        }
        /* Device is idle, we can do the path verification. */
        cdev->private->state = DEV_STATE_VERIFY;
+       cdev->private->flags.doverify = 0;
        ccw_device_verify_start(cdev);
 }
 
@@ -1042,9 +1053,9 @@ ccw_device_wait4io_timeout(struct ccw_device *cdev, enum dev_event dev_event)
 }
 
 static void
-ccw_device_wait4io_verify(struct ccw_device *cdev, enum dev_event dev_event)
+ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event)
 {
-       /* When the I/O has terminated, we have to start verification. */
+       /* Start verification after current task finished. */
        cdev->private->flags.doverify = 1;
 }
 
@@ -1110,10 +1121,7 @@ device_trigger_reprobe(struct subchannel *sch)
         * The pim, pam, pom values may not be accurate, but they are the best
         * we have before performing device selection :/
         */
-       sch->lpm = sch->schib.pmcw.pim &
-               sch->schib.pmcw.pam &
-               sch->schib.pmcw.pom &
-               sch->opm;
+       sch->lpm = sch->schib.pmcw.pam & sch->opm;
        /* Re-set some bits in the pmcw that were lost. */
        sch->schib.pmcw.isc = 3;
        sch->schib.pmcw.csense = 1;
@@ -1237,7 +1245,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_online_notoper,
                [DEV_EVENT_INTERRUPT]   = ccw_device_verify_irq,
                [DEV_EVENT_TIMEOUT]     = ccw_device_onoff_timeout,
-               [DEV_EVENT_VERIFY]      = ccw_device_nop,
+               [DEV_EVENT_VERIFY]      = ccw_device_delay_verify,
        },
        [DEV_STATE_ONLINE] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_online_notoper,
@@ -1280,7 +1288,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_online_notoper,
                [DEV_EVENT_INTERRUPT]   = ccw_device_wait4io_irq,
                [DEV_EVENT_TIMEOUT]     = ccw_device_wait4io_timeout,
-               [DEV_EVENT_VERIFY]      = ccw_device_wait4io_verify,
+               [DEV_EVENT_VERIFY]      = ccw_device_delay_verify,
        },
        [DEV_STATE_QUIESCE] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_quiesce_done,
@@ -1293,7 +1301,7 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_nop,
                [DEV_EVENT_INTERRUPT]   = ccw_device_start_id,
                [DEV_EVENT_TIMEOUT]     = ccw_device_bug,
-               [DEV_EVENT_VERIFY]      = ccw_device_nop,
+               [DEV_EVENT_VERIFY]      = ccw_device_start_id,
        },
        [DEV_STATE_DISCONNECTED_SENSE_ID] = {
                [DEV_EVENT_NOTOPER]     = ccw_device_recog_notoper,
index 9e3de0b..93a897e 100644 (file)
@@ -96,6 +96,12 @@ ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
        ret = cio_set_options (sch, flags);
        if (ret)
                return ret;
+       /* Adjust requested path mask to excluded varied off paths. */
+       if (lpm) {
+               lpm &= sch->opm;
+               if (lpm == 0)
+                       return -EACCES;
+       }
        ret = cio_start_key (sch, cpa, lpm, key);
        if (ret == 0)
                cdev->private->intparm = intparm;
@@ -250,7 +256,7 @@ ccw_device_get_path_mask(struct ccw_device *cdev)
        if (!sch)
                return 0;
        else
-               return sch->vpm;
+               return sch->lpm;
 }
 
 static void
@@ -304,7 +310,7 @@ __ccw_device_retry_loop(struct ccw_device *cdev, struct ccw1 *ccw, long magic, _
        sch = to_subchannel(cdev->dev.parent);
        do {
                ret = cio_start (sch, ccw, lpm);
-               if ((ret == -EBUSY) || (ret == -EACCES)) {
+               if (ret == -EBUSY) {
                        /* Try again later. */
                        spin_unlock_irq(&sch->lock);
                        msleep(10);
@@ -433,6 +439,13 @@ read_conf_data_lpm (struct ccw_device *cdev, void **buffer, int *length, __u8 lp
        if (!ciw || ciw->cmd == 0)
                return -EOPNOTSUPP;
 
+       /* Adjust requested path mask to excluded varied off paths. */
+       if (lpm) {
+               lpm &= sch->opm;
+               if (lpm == 0)
+                       return -EACCES;
+       }
+
        rcd_ccw = kzalloc(sizeof(struct ccw1), GFP_KERNEL | GFP_DMA);
        if (!rcd_ccw)
                return -ENOMEM;
index 1693a10..8ca2d07 100644 (file)
@@ -245,18 +245,17 @@ __ccw_device_do_pgid(struct ccw_device *cdev, __u8 func)
        memset(&cdev->private->irb, 0, sizeof(struct irb));
 
        /* Try multiple times. */
-       ret = -ENODEV;
+       ret = -EACCES;
        if (cdev->private->iretry > 0) {
                cdev->private->iretry--;
                ret = cio_start (sch, cdev->private->iccws,
                                 cdev->private->imask);
-               /* ret is 0, -EBUSY, -EACCES or -ENODEV */
-               if ((ret != -EACCES) && (ret != -ENODEV))
+               /* We expect an interrupt in case of success or busy
+                * indication. */
+               if ((ret == 0) || (ret == -EBUSY))
                        return ret;
        }
-       /* PGID command failed on this path. Switch it off. */
-       sch->lpm &= ~cdev->private->imask;
-       sch->vpm &= ~cdev->private->imask;
+       /* PGID command failed on this path. */
        CIO_MSG_EVENT(2, "SPID - Device %04x on Subchannel "
                      "0.%x.%04x, lpm %02X, became 'not operational'\n",
                      cdev->private->devno, sch->schid.ssid,
@@ -286,18 +285,17 @@ static int __ccw_device_do_nop(struct ccw_device *cdev)
        memset(&cdev->private->irb, 0, sizeof(struct irb));
 
        /* Try multiple times. */
-       ret = -ENODEV;
+       ret = -EACCES;
        if (cdev->private->iretry > 0) {
                cdev->private->iretry--;
                ret = cio_start (sch, cdev->private->iccws,
                                 cdev->private->imask);
-               /* ret is 0, -EBUSY, -EACCES or -ENODEV */
-               if ((ret != -EACCES) && (ret != -ENODEV))
+               /* We expect an interrupt in case of success or busy
+                * indication. */
+               if ((ret == 0) || (ret == -EBUSY))
                        return ret;
        }
-       /* nop command failed on this path. Switch it off. */
-       sch->lpm &= ~cdev->private->imask;
-       sch->vpm &= ~cdev->private->imask;
+       /* nop command failed on this path. */
        CIO_MSG_EVENT(2, "NOP - Device %04x on Subchannel "
                      "0.%x.%04x, lpm %02X, became 'not operational'\n",
                      cdev->private->devno, sch->schid.ssid,
@@ -372,27 +370,32 @@ static void
 __ccw_device_verify_start(struct ccw_device *cdev)
 {
        struct subchannel *sch;
-       __u8 imask, func;
+       __u8 func;
        int ret;
 
        sch = to_subchannel(cdev->dev.parent);
-       while (sch->vpm != sch->lpm) {
-               /* Find first unequal bit in vpm vs. lpm */
-               for (imask = 0x80; imask != 0; imask >>= 1)
-                       if ((sch->vpm & imask) != (sch->lpm & imask))
-                               break;
-               cdev->private->imask = imask;
+       /* Repeat for all paths. */
+       for (; cdev->private->imask; cdev->private->imask >>= 1,
+                                    cdev->private->iretry = 5) {
+               if ((cdev->private->imask & sch->schib.pmcw.pam) == 0)
+                       /* Path not available, try next. */
+                       continue;
                if (cdev->private->options.pgroup) {
-                       func = (sch->vpm & imask) ?
-                               SPID_FUNC_RESIGN : SPID_FUNC_ESTABLISH;
+                       if (sch->opm & cdev->private->imask)
+                               func = SPID_FUNC_ESTABLISH;
+                       else
+                               func = SPID_FUNC_RESIGN;
                        ret = __ccw_device_do_pgid(cdev, func);
                } else
                        ret = __ccw_device_do_nop(cdev);
+               /* We expect an interrupt in case of success or busy
+                * indication. */
                if (ret == 0 || ret == -EBUSY)
                        return;
-               cdev->private->iretry = 5;
+               /* Permanent path failure, try next. */
        }
-       ccw_device_verify_done(cdev, (sch->lpm != 0) ? 0 : -ENODEV);
+       /* Done with all paths. */
+       ccw_device_verify_done(cdev, (sch->vpm != 0) ? 0 : -ENODEV);
 }
                
 /*
@@ -421,14 +424,14 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
        else
                ret = __ccw_device_check_nop(cdev);
        memset(&cdev->private->irb, 0, sizeof(struct irb));
+
        switch (ret) {
        /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
        case 0:
-               /* Establish or Resign Path Group done. Update vpm. */
-               if ((sch->lpm & cdev->private->imask) != 0)
-                       sch->vpm |= cdev->private->imask;
-               else
-                       sch->vpm &= ~cdev->private->imask;
+               /* Path verification ccw finished successfully, update lpm. */
+               sch->vpm |= sch->opm & cdev->private->imask;
+               /* Go on with next path. */
+               cdev->private->imask >>= 1;
                cdev->private->iretry = 5;
                __ccw_device_verify_start(cdev);
                break;
@@ -441,6 +444,10 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
                        cdev->private->options.pgroup = 0;
                else
                        cdev->private->flags.pgid_single = 1;
+               /* Retry */
+               sch->vpm = 0;
+               cdev->private->imask = 0x80;
+               cdev->private->iretry = 5;
                /* fall through. */
        case -EAGAIN:           /* Try again. */
                __ccw_device_verify_start(cdev);
@@ -449,8 +456,7 @@ ccw_device_verify_irq(struct ccw_device *cdev, enum dev_event dev_event)
                ccw_device_verify_done(cdev, -ETIME);
                break;
        case -EACCES:           /* channel is not operational. */
-               sch->lpm &= ~cdev->private->imask;
-               sch->vpm &= ~cdev->private->imask;
+               cdev->private->imask >>= 1;
                cdev->private->iretry = 5;
                __ccw_device_verify_start(cdev);
                break;
@@ -463,19 +469,17 @@ ccw_device_verify_start(struct ccw_device *cdev)
        struct subchannel *sch = to_subchannel(cdev->dev.parent);
 
        cdev->private->flags.pgid_single = 0;
+       cdev->private->imask = 0x80;
        cdev->private->iretry = 5;
-       /*
-        * Update sch->lpm with current values to catch paths becoming
-        * available again.
-        */
+
+       /* Start with empty vpm. */
+       sch->vpm = 0;
+
+       /* Get current pam. */
        if (stsch(sch->schid, &sch->schib)) {
                ccw_device_verify_done(cdev, -ENODEV);
                return;
        }
-       sch->lpm = sch->schib.pmcw.pim &
-               sch->schib.pmcw.pam &
-               sch->schib.pmcw.pom &
-               sch->opm;
        __ccw_device_verify_start(cdev);
 }
 
@@ -524,7 +528,6 @@ ccw_device_disband_irq(struct ccw_device *cdev, enum dev_event dev_event)
        switch (ret) {
        /* 0, -ETIME, -EAGAIN, -EOPNOTSUPP or -EACCES */
        case 0:                 /* disband successful. */
-               sch->vpm = 0;
                ccw_device_disband_done(cdev, ret);
                break;
        case -EOPNOTSUPP:
index 7c93a87..cde822d 100644 (file)
@@ -115,7 +115,7 @@ qdio_min(int a,int b)
 static inline __u64 
 qdio_get_micros(void)
 {
-        return (get_clock() >> 10); /* time>>12 is microseconds */
+       return (get_clock() >> 12); /* time>>12 is microseconds */
 }
 
 /* 
@@ -1129,7 +1129,7 @@ out:
 
 #ifdef QDIO_USE_PROCESSING_STATE
        if (last_position>=0)
-               set_slsb(q, &last_position, SLSB_P_INPUT_NOT_INIT, &count);
+               set_slsb(q, &last_position, SLSB_P_INPUT_PROCESSING, &count);
 #endif /* QDIO_USE_PROCESSING_STATE */
 
        QDIO_DBF_HEX4(0,trace,&q->first_to_check,sizeof(int));
index ceb3ab3..1245693 100644 (file)
@@ -191,49 +191,49 @@ enum qdio_irq_states {
 #if QDIO_VERBOSE_LEVEL>8
 #define QDIO_PRINT_STUPID(x...) printk( KERN_DEBUG QDIO_PRINTK_HEADER x)
 #else
-#define QDIO_PRINT_STUPID(x...)
+#define QDIO_PRINT_STUPID(x...) do { } while (0)
 #endif
 
 #if QDIO_VERBOSE_LEVEL>7
 #define QDIO_PRINT_ALL(x...) printk( QDIO_PRINTK_HEADER x)
 #else
-#define QDIO_PRINT_ALL(x...)
+#define QDIO_PRINT_ALL(x...) do { } while (0)
 #endif
 
 #if QDIO_VERBOSE_LEVEL>6
 #define QDIO_PRINT_INFO(x...) printk( QDIO_PRINTK_HEADER x)
 #else
-#define QDIO_PRINT_INFO(x...)
+#define QDIO_PRINT_INFO(x...) do { } while (0)
 #endif
 
 #if QDIO_VERBOSE_LEVEL>5
 #define QDIO_PRINT_WARN(x...) printk( QDIO_PRINTK_HEADER x)
 #else
-#define QDIO_PRINT_WARN(x...)
+#define QDIO_PRINT_WARN(x...) do { } while (0)
 #endif
 
 #if QDIO_VERBOSE_LEVEL>4
 #define QDIO_PRINT_ERR(x...) printk( QDIO_PRINTK_HEADER x)
 #else
-#define QDIO_PRINT_ERR(x...)
+#define QDIO_PRINT_ERR(x...) do { } while (0)
 #endif
 
 #if QDIO_VERBOSE_LEVEL>3
 #define QDIO_PRINT_CRIT(x...) printk( QDIO_PRINTK_HEADER x)
 #else
-#define QDIO_PRINT_CRIT(x...)
+#define QDIO_PRINT_CRIT(x...) do { } while (0)
 #endif
 
 #if QDIO_VERBOSE_LEVEL>2
 #define QDIO_PRINT_ALERT(x...) printk( QDIO_PRINTK_HEADER x)
 #else
-#define QDIO_PRINT_ALERT(x...)
+#define QDIO_PRINT_ALERT(x...) do { } while (0)
 #endif
 
 #if QDIO_VERBOSE_LEVEL>1
 #define QDIO_PRINT_EMERG(x...) printk( QDIO_PRINTK_HEADER x)
 #else
-#define QDIO_PRINT_EMERG(x...)
+#define QDIO_PRINT_EMERG(x...) do { } while (0)
 #endif
 
 #define HEXDUMP16(importance,header,ptr) \
index 15edebb..f0a12d2 100644 (file)
@@ -2,5 +2,16 @@
 # S/390 crypto devices
 #
 
-z90crypt-objs := z90main.o z90hardware.o
-obj-$(CONFIG_Z90CRYPT) += z90crypt.o
+ifdef CONFIG_ZCRYPT_MONOLITHIC
+
+z90crypt-objs := zcrypt_mono.o ap_bus.o zcrypt_api.o \
+               zcrypt_pcica.o zcrypt_pcicc.o zcrypt_pcixcc.o zcrypt_cex2a.o
+obj-$(CONFIG_ZCRYPT) += z90crypt.o
+
+else
+
+ap-objs := ap_bus.o
+obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o
+obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o
+
+endif
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
new file mode 100644 (file)
index 0000000..6ed0985
--- /dev/null
@@ -0,0 +1,1221 @@
+/*
+ * linux/drivers/s390/crypto/ap_bus.c
+ *
+ * Copyright (C) 2006 IBM Corporation
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *           Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *           Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * Adjunct processor bus.
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/notifier.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <asm/s390_rdev.h>
+
+#include "ap_bus.h"
+
+/* Some prototypes. */
+static void ap_scan_bus(void *);
+static void ap_poll_all(unsigned long);
+static void ap_poll_timeout(unsigned long);
+static int ap_poll_thread_start(void);
+static void ap_poll_thread_stop(void);
+
+/**
+ * Module description.
+ */
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Adjunct Processor Bus driver, "
+                  "Copyright 2006 IBM Corporation");
+MODULE_LICENSE("GPL");
+
+/**
+ * Module parameter
+ */
+int ap_domain_index = -1;      /* Adjunct Processor Domain Index */
+module_param_named(domain, ap_domain_index, int, 0000);
+MODULE_PARM_DESC(domain, "domain index for ap devices");
+EXPORT_SYMBOL(ap_domain_index);
+
+static int ap_thread_flag = 1;
+module_param_named(poll_thread, ap_thread_flag, int, 0000);
+MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on).");
+
+static struct device *ap_root_device = NULL;
+
+/**
+ * Workqueue & timer for bus rescan.
+ */
+static struct workqueue_struct *ap_work_queue;
+static struct timer_list ap_config_timer;
+static int ap_config_time = AP_CONFIG_TIME;
+static DECLARE_WORK(ap_config_work, ap_scan_bus, NULL);
+
+/**
+ * Tasklet & timer for AP request polling.
+ */
+static struct timer_list ap_poll_timer = TIMER_INITIALIZER(ap_poll_timeout,0,0);
+static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
+static atomic_t ap_poll_requests = ATOMIC_INIT(0);
+static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
+static struct task_struct *ap_poll_kthread = NULL;
+static DEFINE_MUTEX(ap_poll_thread_mutex);
+
+/**
+ * Test if ap instructions are available.
+ *
+ * Returns 0 if the ap instructions are installed.
+ */
+static inline int ap_instructions_available(void)
+{
+       register unsigned long reg0 asm ("0") = AP_MKQID(0,0);
+       register unsigned long reg1 asm ("1") = -ENODEV;
+       register unsigned long reg2 asm ("2") = 0UL;
+
+       asm volatile(
+               "   .long 0xb2af0000\n"         /* PQAP(TAPQ) */
+               "0: la    %1,0\n"
+               "1:\n"
+               EX_TABLE(0b, 1b)
+               : "+d" (reg0), "+d" (reg1), "+d" (reg2) : : "cc" );
+       return reg1;
+}
+
+/**
+ * Test adjunct processor queue.
+ * @qid: the ap queue number
+ * @queue_depth: pointer to queue depth value
+ * @device_type: pointer to device type value
+ *
+ * Returns ap queue status structure.
+ */
+static inline struct ap_queue_status
+ap_test_queue(ap_qid_t qid, int *queue_depth, int *device_type)
+{
+       register unsigned long reg0 asm ("0") = qid;
+       register struct ap_queue_status reg1 asm ("1");
+       register unsigned long reg2 asm ("2") = 0UL;
+
+       asm volatile(".long 0xb2af0000"         /* PQAP(TAPQ) */
+                    : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");
+       *device_type = (int) (reg2 >> 24);
+       *queue_depth = (int) (reg2 & 0xff);
+       return reg1;
+}
+
+/**
+ * Reset adjunct processor queue.
+ * @qid: the ap queue number
+ *
+ * Returns ap queue status structure.
+ */
+static inline struct ap_queue_status ap_reset_queue(ap_qid_t qid)
+{
+       register unsigned long reg0 asm ("0") = qid | 0x01000000UL;
+       register struct ap_queue_status reg1 asm ("1");
+       register unsigned long reg2 asm ("2") = 0UL;
+
+       asm volatile(
+               ".long 0xb2af0000"              /* PQAP(RAPQ) */
+               : "+d" (reg0), "=d" (reg1), "+d" (reg2) : : "cc");
+       return reg1;
+}
+
+/**
+ * Send message to adjunct processor queue.
+ * @qid: the ap queue number
+ * @psmid: the program supplied message identifier
+ * @msg: the message text
+ * @length: the message length
+ *
+ * Returns ap queue status structure.
+ *
+ * Condition code 1 on NQAP can't happen because the L bit is 1.
+ *
+ * Condition code 2 on NQAP also means the send is incomplete,
+ * because a segment boundary was reached. The NQAP is repeated.
+ */
+static inline struct ap_queue_status
+__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
+{
+       typedef struct { char _[length]; } msgblock;
+       register unsigned long reg0 asm ("0") = qid | 0x40000000UL;
+       register struct ap_queue_status reg1 asm ("1");
+       register unsigned long reg2 asm ("2") = (unsigned long) msg;
+       register unsigned long reg3 asm ("3") = (unsigned long) length;
+       register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32);
+       register unsigned long reg5 asm ("5") = (unsigned int) psmid;
+
+       asm volatile (
+               "0: .long 0xb2ad0042\n"         /* DQAP */
+               "   brc   2,0b"
+               : "+d" (reg0), "=d" (reg1), "+d" (reg2), "+d" (reg3)
+               : "d" (reg4), "d" (reg5), "m" (*(msgblock *) msg)
+               : "cc" );
+       return reg1;
+}
+
+int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
+{
+       struct ap_queue_status status;
+
+       status = __ap_send(qid, psmid, msg, length);
+       switch (status.response_code) {
+       case AP_RESPONSE_NORMAL:
+               return 0;
+       case AP_RESPONSE_Q_FULL:
+               return -EBUSY;
+       default:        /* Device is gone. */
+               return -ENODEV;
+       }
+}
+EXPORT_SYMBOL(ap_send);
+
+/*
+ * Receive message from adjunct processor queue.
+ * @qid: the ap queue number
+ * @psmid: pointer to program supplied message identifier
+ * @msg: the message text
+ * @length: the message length
+ *
+ * Returns ap queue status structure.
+ *
+ * Condition code 1 on DQAP means the receive has taken place
+ * but only partially. The response is incomplete, hence the
+ * DQAP is repeated.
+ *
+ * Condition code 2 on DQAP also means the receive is incomplete,
+ * this time because a segment boundary was reached. Again, the
+ * DQAP is repeated.
+ *
+ * Note that gpr2 is used by the DQAP instruction to keep track of
+ * any 'residual' length, in case the instruction gets interrupted.
+ * Hence it gets zeroed before the instruction.
+ */
+static inline struct ap_queue_status
+__ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length)
+{
+       typedef struct { char _[length]; } msgblock;
+       register unsigned long reg0 asm("0") = qid | 0x80000000UL;
+       register struct ap_queue_status reg1 asm ("1");
+       register unsigned long reg2 asm("2") = 0UL;
+       register unsigned long reg4 asm("4") = (unsigned long) msg;
+       register unsigned long reg5 asm("5") = (unsigned long) length;
+       register unsigned long reg6 asm("6") = 0UL;
+       register unsigned long reg7 asm("7") = 0UL;
+
+
+       asm volatile(
+               "0: .long 0xb2ae0064\n"
+               "   brc   6,0b\n"
+               : "+d" (reg0), "=d" (reg1), "+d" (reg2),
+               "+d" (reg4), "+d" (reg5), "+d" (reg6), "+d" (reg7),
+               "=m" (*(msgblock *) msg) : : "cc" );
+       *psmid = (((unsigned long long) reg6) << 32) + reg7;
+       return reg1;
+}
+
+int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length)
+{
+       struct ap_queue_status status;
+
+       status = __ap_recv(qid, psmid, msg, length);
+       switch (status.response_code) {
+       case AP_RESPONSE_NORMAL:
+               return 0;
+       case AP_RESPONSE_NO_PENDING_REPLY:
+               if (status.queue_empty)
+                       return -ENOENT;
+               return -EBUSY;
+       default:
+               return -ENODEV;
+       }
+}
+EXPORT_SYMBOL(ap_recv);
+
+/**
+ * Check if an AP queue is available. The test is repeated for
+ * AP_MAX_RESET times.
+ * @qid: the ap queue number
+ * @queue_depth: pointer to queue depth value
+ * @device_type: pointer to device type value
+ */
+static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type)
+{
+       struct ap_queue_status status;
+       int t_depth, t_device_type, rc, i;
+
+       rc = -EBUSY;
+       for (i = 0; i < AP_MAX_RESET; i++) {
+               status = ap_test_queue(qid, &t_depth, &t_device_type);
+               switch (status.response_code) {
+               case AP_RESPONSE_NORMAL:
+                       *queue_depth = t_depth + 1;
+                       *device_type = t_device_type;
+                       rc = 0;
+                       break;
+               case AP_RESPONSE_Q_NOT_AVAIL:
+                       rc = -ENODEV;
+                       break;
+               case AP_RESPONSE_RESET_IN_PROGRESS:
+                       break;
+               case AP_RESPONSE_DECONFIGURED:
+                       rc = -ENODEV;
+                       break;
+               case AP_RESPONSE_CHECKSTOPPED:
+                       rc = -ENODEV;
+                       break;
+               case AP_RESPONSE_BUSY:
+                       break;
+               default:
+                       BUG();
+               }
+               if (rc != -EBUSY)
+                       break;
+               if (i < AP_MAX_RESET - 1)
+                       udelay(5);
+       }
+       return rc;
+}
+
+/**
+ * Reset an AP queue and wait for it to become available again.
+ * @qid: the ap queue number
+ */
+static int ap_init_queue(ap_qid_t qid)
+{
+       struct ap_queue_status status;
+       int rc, dummy, i;
+
+       rc = -ENODEV;
+       status = ap_reset_queue(qid);
+       for (i = 0; i < AP_MAX_RESET; i++) {
+               switch (status.response_code) {
+               case AP_RESPONSE_NORMAL:
+                       if (status.queue_empty)
+                               rc = 0;
+                       break;
+               case AP_RESPONSE_Q_NOT_AVAIL:
+               case AP_RESPONSE_DECONFIGURED:
+               case AP_RESPONSE_CHECKSTOPPED:
+                       i = AP_MAX_RESET;       /* return with -ENODEV */
+                       break;
+               case AP_RESPONSE_RESET_IN_PROGRESS:
+               case AP_RESPONSE_BUSY:
+               default:
+                       break;
+               }
+               if (rc != -ENODEV)
+                       break;
+               if (i < AP_MAX_RESET - 1) {
+                       udelay(5);
+                       status = ap_test_queue(qid, &dummy, &dummy);
+               }
+       }
+       return rc;
+}
+
+/**
+ * AP device related attributes.
+ */
+static ssize_t ap_hwtype_show(struct device *dev,
+                             struct device_attribute *attr, char *buf)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->device_type);
+}
+static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL);
+
+static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->queue_depth);
+}
+static DEVICE_ATTR(depth, 0444, ap_depth_show, NULL);
+
+static ssize_t ap_request_count_show(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       int rc;
+
+       spin_lock_bh(&ap_dev->lock);
+       rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->total_request_count);
+       spin_unlock_bh(&ap_dev->lock);
+       return rc;
+}
+
+static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL);
+
+static ssize_t ap_modalias_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "ap:t%02X", to_ap_dev(dev)->device_type);
+}
+
+static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);
+
+static struct attribute *ap_dev_attrs[] = {
+       &dev_attr_hwtype.attr,
+       &dev_attr_depth.attr,
+       &dev_attr_request_count.attr,
+       &dev_attr_modalias.attr,
+       NULL
+};
+static struct attribute_group ap_dev_attr_group = {
+       .attrs = ap_dev_attrs
+};
+
+/**
+ * AP bus driver registration/unregistration.
+ */
+static int ap_bus_match(struct device *dev, struct device_driver *drv)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       struct ap_driver *ap_drv = to_ap_drv(drv);
+       struct ap_device_id *id;
+
+       /**
+        * Compare device type of the device with the list of
+        * supported types of the device_driver.
+        */
+       for (id = ap_drv->ids; id->match_flags; id++) {
+               if ((id->match_flags & AP_DEVICE_ID_MATCH_DEVICE_TYPE) &&
+                   (id->dev_type != ap_dev->device_type))
+                       continue;
+               return 1;
+       }
+       return 0;
+}
+
+/**
+ * uevent function for AP devices. It sets up a single environment
+ * variable DEV_TYPE which contains the hardware device type.
+ */
+static int ap_uevent (struct device *dev, char **envp, int num_envp,
+                      char *buffer, int buffer_size)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       int length;
+
+       if (!ap_dev)
+               return -ENODEV;
+
+       /* Set up DEV_TYPE environment variable. */
+       envp[0] = buffer;
+       length = scnprintf(buffer, buffer_size, "DEV_TYPE=%04X",
+                          ap_dev->device_type);
+       if (buffer_size - length <= 0)
+               return -ENOMEM;
+       envp[1] = 0;
+       return 0;
+}
+
+static struct bus_type ap_bus_type = {
+       .name = "ap",
+       .match = &ap_bus_match,
+       .uevent = &ap_uevent,
+};
+
+static int ap_device_probe(struct device *dev)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       struct ap_driver *ap_drv = to_ap_drv(dev->driver);
+       int rc;
+
+       ap_dev->drv = ap_drv;
+       rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
+       if (rc)
+               ap_dev->unregistered = 1;
+       return rc;
+}
+
+/**
+ * Flush all requests from the request/pending queue of an AP device.
+ * @ap_dev: pointer to the AP device.
+ */
+static inline void __ap_flush_queue(struct ap_device *ap_dev)
+{
+       struct ap_message *ap_msg, *next;
+
+       list_for_each_entry_safe(ap_msg, next, &ap_dev->pendingq, list) {
+               list_del_init(&ap_msg->list);
+               ap_dev->pendingq_count--;
+               ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+       }
+       list_for_each_entry_safe(ap_msg, next, &ap_dev->requestq, list) {
+               list_del_init(&ap_msg->list);
+               ap_dev->requestq_count--;
+               ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+       }
+}
+
+void ap_flush_queue(struct ap_device *ap_dev)
+{
+       spin_lock_bh(&ap_dev->lock);
+       __ap_flush_queue(ap_dev);
+       spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_flush_queue);
+
+static int ap_device_remove(struct device *dev)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       struct ap_driver *ap_drv = ap_dev->drv;
+
+       spin_lock_bh(&ap_dev->lock);
+       __ap_flush_queue(ap_dev);
+       /**
+        * set ->unregistered to 1 while holding the lock. This prevents
+        * new messages to be put on the queue from now on.
+        */
+       ap_dev->unregistered = 1;
+       spin_unlock_bh(&ap_dev->lock);
+       if (ap_drv->remove)
+               ap_drv->remove(ap_dev);
+       return 0;
+}
+
+int ap_driver_register(struct ap_driver *ap_drv, struct module *owner,
+                      char *name)
+{
+       struct device_driver *drv = &ap_drv->driver;
+
+       drv->bus = &ap_bus_type;
+       drv->probe = ap_device_probe;
+       drv->remove = ap_device_remove;
+       drv->owner = owner;
+       drv->name = name;
+       return driver_register(drv);
+}
+EXPORT_SYMBOL(ap_driver_register);
+
+void ap_driver_unregister(struct ap_driver *ap_drv)
+{
+       driver_unregister(&ap_drv->driver);
+}
+EXPORT_SYMBOL(ap_driver_unregister);
+
+/**
+ * AP bus attributes.
+ */
+static ssize_t ap_domain_show(struct bus_type *bus, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", ap_domain_index);
+}
+
+static BUS_ATTR(ap_domain, 0444, ap_domain_show, NULL);
+
+static ssize_t ap_config_time_show(struct bus_type *bus, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", ap_config_time);
+}
+
+static ssize_t ap_config_time_store(struct bus_type *bus,
+                                   const char *buf, size_t count)
+{
+       int time;
+
+       if (sscanf(buf, "%d\n", &time) != 1 || time < 5 || time > 120)
+               return -EINVAL;
+       ap_config_time = time;
+       if (!timer_pending(&ap_config_timer) ||
+           !mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ)) {
+               ap_config_timer.expires = jiffies + ap_config_time * HZ;
+               add_timer(&ap_config_timer);
+       }
+       return count;
+}
+
+static BUS_ATTR(config_time, 0644, ap_config_time_show, ap_config_time_store);
+
+static ssize_t ap_poll_thread_show(struct bus_type *bus, char *buf)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", ap_poll_kthread ? 1 : 0);
+}
+
+static ssize_t ap_poll_thread_store(struct bus_type *bus,
+                                   const char *buf, size_t count)
+{
+       int flag, rc;
+
+       if (sscanf(buf, "%d\n", &flag) != 1)
+               return -EINVAL;
+       if (flag) {
+               rc = ap_poll_thread_start();
+               if (rc)
+                       return rc;
+       }
+       else
+               ap_poll_thread_stop();
+       return count;
+}
+
+static BUS_ATTR(poll_thread, 0644, ap_poll_thread_show, ap_poll_thread_store);
+
+static struct bus_attribute *const ap_bus_attrs[] = {
+       &bus_attr_ap_domain,
+       &bus_attr_config_time,
+       &bus_attr_poll_thread,
+       NULL
+};
+
+/**
+ * Pick one of the 16 ap domains.
+ */
+static inline int ap_select_domain(void)
+{
+       int queue_depth, device_type, count, max_count, best_domain;
+       int rc, i, j;
+
+       /**
+        * We want to use a single domain. Either the one specified with
+        * the "domain=" parameter or the domain with the maximum number
+        * of devices.
+        */
+       if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS)
+               /* Domain has already been selected. */
+               return 0;
+       best_domain = -1;
+       max_count = 0;
+       for (i = 0; i < AP_DOMAINS; i++) {
+               count = 0;
+               for (j = 0; j < AP_DEVICES; j++) {
+                       ap_qid_t qid = AP_MKQID(j, i);
+                       rc = ap_query_queue(qid, &queue_depth, &device_type);
+                       if (rc)
+                               continue;
+                       count++;
+               }
+               if (count > max_count) {
+                       max_count = count;
+                       best_domain = i;
+               }
+       }
+       if (best_domain >= 0){
+               ap_domain_index = best_domain;
+               return 0;
+       }
+       return -ENODEV;
+}
+
+/**
+ * Find the device type if query queue returned a device type of 0.
+ * @ap_dev: pointer to the AP device.
+ */
+static int ap_probe_device_type(struct ap_device *ap_dev)
+{
+       static unsigned char msg[] = {
+               0x00,0x06,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x01,0x00,0x43,0x43,0x41,0x2d,0x41,0x50,
+               0x50,0x4c,0x20,0x20,0x20,0x01,0x01,0x01,
+               0x00,0x00,0x00,0x00,0x50,0x4b,0x00,0x00,
+               0x00,0x00,0x01,0x1c,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x05,0xb8,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x70,0x00,0x41,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x54,0x32,0x01,0x00,0xa0,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0xb8,0x05,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x0a,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+               0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x00,
+               0x49,0x43,0x53,0x46,0x20,0x20,0x20,0x20,
+               0x50,0x4b,0x0a,0x00,0x50,0x4b,0x43,0x53,
+               0x2d,0x31,0x2e,0x32,0x37,0x00,0x11,0x22,
+               0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
+               0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,
+               0x99,0x00,0x11,0x22,0x33,0x44,0x55,0x66,
+               0x77,0x88,0x99,0x00,0x11,0x22,0x33,0x44,
+               0x55,0x66,0x77,0x88,0x99,0x00,0x11,0x22,
+               0x33,0x44,0x55,0x66,0x77,0x88,0x99,0x00,
+               0x11,0x22,0x33,0x5d,0x00,0x5b,0x00,0x77,
+               0x88,0x1e,0x00,0x00,0x57,0x00,0x00,0x00,
+               0x00,0x04,0x00,0x00,0x4f,0x00,0x00,0x00,
+               0x03,0x02,0x00,0x00,0x40,0x01,0x00,0x01,
+               0xce,0x02,0x68,0x2d,0x5f,0xa9,0xde,0x0c,
+               0xf6,0xd2,0x7b,0x58,0x4b,0xf9,0x28,0x68,
+               0x3d,0xb4,0xf4,0xef,0x78,0xd5,0xbe,0x66,
+               0x63,0x42,0xef,0xf8,0xfd,0xa4,0xf8,0xb0,
+               0x8e,0x29,0xc2,0xc9,0x2e,0xd8,0x45,0xb8,
+               0x53,0x8c,0x6f,0x4e,0x72,0x8f,0x6c,0x04,
+               0x9c,0x88,0xfc,0x1e,0xc5,0x83,0x55,0x57,
+               0xf7,0xdd,0xfd,0x4f,0x11,0x36,0x95,0x5d,
+       };
+       struct ap_queue_status status;
+       unsigned long long psmid;
+       char *reply;
+       int rc, i;
+
+       reply = (void *) get_zeroed_page(GFP_KERNEL);
+       if (!reply) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       status = __ap_send(ap_dev->qid, 0x0102030405060708ULL,
+                          msg, sizeof(msg));
+       if (status.response_code != AP_RESPONSE_NORMAL) {
+               rc = -ENODEV;
+               goto out_free;
+       }
+
+       /* Wait for the test message to complete. */
+       for (i = 0; i < 6; i++) {
+               mdelay(300);
+               status = __ap_recv(ap_dev->qid, &psmid, reply, 4096);
+               if (status.response_code == AP_RESPONSE_NORMAL &&
+                   psmid == 0x0102030405060708ULL)
+                       break;
+       }
+       if (i < 6) {
+               /* Got an answer. */
+               if (reply[0] == 0x00 && reply[1] == 0x86)
+                       ap_dev->device_type = AP_DEVICE_TYPE_PCICC;
+               else
+                       ap_dev->device_type = AP_DEVICE_TYPE_PCICA;
+               rc = 0;
+       } else
+               rc = -ENODEV;
+
+out_free:
+       free_page((unsigned long) reply);
+out:
+       return rc;
+}
+
+/**
+ * Scan the ap bus for new devices.
+ */
+static int __ap_scan_bus(struct device *dev, void *data)
+{
+       return to_ap_dev(dev)->qid == (ap_qid_t)(unsigned long) data;
+}
+
+static void ap_device_release(struct device *dev)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+
+       kfree(ap_dev);
+}
+
+static void ap_scan_bus(void *data)
+{
+       struct ap_device *ap_dev;
+       struct device *dev;
+       ap_qid_t qid;
+       int queue_depth, device_type;
+       int rc, i;
+
+       if (ap_select_domain() != 0)
+               return;
+       for (i = 0; i < AP_DEVICES; i++) {
+               qid = AP_MKQID(i, ap_domain_index);
+               dev = bus_find_device(&ap_bus_type, NULL,
+                                     (void *)(unsigned long)qid,
+                                     __ap_scan_bus);
+               if (dev) {
+                       put_device(dev);
+                       continue;
+               }
+               rc = ap_query_queue(qid, &queue_depth, &device_type);
+               if (rc)
+                       continue;
+               rc = ap_init_queue(qid);
+               if (rc)
+                       continue;
+               ap_dev = kzalloc(sizeof(*ap_dev), GFP_KERNEL);
+               if (!ap_dev)
+                       break;
+               ap_dev->qid = qid;
+               ap_dev->queue_depth = queue_depth;
+               spin_lock_init(&ap_dev->lock);
+               INIT_LIST_HEAD(&ap_dev->pendingq);
+               INIT_LIST_HEAD(&ap_dev->requestq);
+               if (device_type == 0)
+                       ap_probe_device_type(ap_dev);
+               else
+                       ap_dev->device_type = device_type;
+
+               ap_dev->device.bus = &ap_bus_type;
+               ap_dev->device.parent = ap_root_device;
+               snprintf(ap_dev->device.bus_id, BUS_ID_SIZE, "card%02x",
+                        AP_QID_DEVICE(ap_dev->qid));
+               ap_dev->device.release = ap_device_release;
+               rc = device_register(&ap_dev->device);
+               if (rc) {
+                       kfree(ap_dev);
+                       continue;
+               }
+               /* Add device attributes. */
+               rc = sysfs_create_group(&ap_dev->device.kobj,
+                                       &ap_dev_attr_group);
+               if (rc)
+                       device_unregister(&ap_dev->device);
+       }
+}
+
+static void
+ap_config_timeout(unsigned long ptr)
+{
+       queue_work(ap_work_queue, &ap_config_work);
+       ap_config_timer.expires = jiffies + ap_config_time * HZ;
+       add_timer(&ap_config_timer);
+}
+
+/**
+ * Set up the timer to run the poll tasklet
+ */
+static inline void ap_schedule_poll_timer(void)
+{
+       if (timer_pending(&ap_poll_timer))
+               return;
+       mod_timer(&ap_poll_timer, jiffies + AP_POLL_TIME);
+}
+
+/**
+ * Receive pending reply messages from an AP device.
+ * @ap_dev: pointer to the AP device
+ * @flags: pointer to control flags, bit 2^0 is set if another poll is
+ *        required, bit 2^1 is set if the poll timer needs to get armed
+ * Returns 0 if the device is still present, -ENODEV if not.
+ */
+static inline int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags)
+{
+       struct ap_queue_status status;
+       struct ap_message *ap_msg;
+
+       if (ap_dev->queue_count <= 0)
+               return 0;
+       status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid,
+                          ap_dev->reply->message, ap_dev->reply->length);
+       switch (status.response_code) {
+       case AP_RESPONSE_NORMAL:
+               atomic_dec(&ap_poll_requests);
+               ap_dev->queue_count--;
+               list_for_each_entry(ap_msg, &ap_dev->pendingq, list) {
+                       if (ap_msg->psmid != ap_dev->reply->psmid)
+                               continue;
+                       list_del_init(&ap_msg->list);
+                       ap_dev->pendingq_count--;
+                       ap_dev->drv->receive(ap_dev, ap_msg, ap_dev->reply);
+                       break;
+               }
+               if (ap_dev->queue_count > 0)
+                       *flags |= 1;
+               break;
+       case AP_RESPONSE_NO_PENDING_REPLY:
+               if (status.queue_empty) {
+                       /* The card shouldn't forget requests but who knows. */
+                       ap_dev->queue_count = 0;
+                       list_splice_init(&ap_dev->pendingq, &ap_dev->requestq);
+                       ap_dev->requestq_count += ap_dev->pendingq_count;
+                       ap_dev->pendingq_count = 0;
+               } else
+                       *flags |= 2;
+               break;
+       default:
+               return -ENODEV;
+       }
+       return 0;
+}
+
+/**
+ * Send messages from the request queue to an AP device.
+ * @ap_dev: pointer to the AP device
+ * @flags: pointer to control flags, bit 2^0 is set if another poll is
+ *        required, bit 2^1 is set if the poll timer needs to get armed
+ * Returns 0 if the device is still present, -ENODEV if not.
+ */
+static inline int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
+{
+       struct ap_queue_status status;
+       struct ap_message *ap_msg;
+
+       if (ap_dev->requestq_count <= 0 ||
+           ap_dev->queue_count >= ap_dev->queue_depth)
+               return 0;
+       /* Start the next request on the queue. */
+       ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
+       status = __ap_send(ap_dev->qid, ap_msg->psmid,
+                          ap_msg->message, ap_msg->length);
+       switch (status.response_code) {
+       case AP_RESPONSE_NORMAL:
+               atomic_inc(&ap_poll_requests);
+               ap_dev->queue_count++;
+               list_move_tail(&ap_msg->list, &ap_dev->pendingq);
+               ap_dev->requestq_count--;
+               ap_dev->pendingq_count++;
+               if (ap_dev->queue_count < ap_dev->queue_depth &&
+                   ap_dev->requestq_count > 0)
+                       *flags |= 1;
+               *flags |= 2;
+               break;
+       case AP_RESPONSE_Q_FULL:
+               *flags |= 2;
+               break;
+       case AP_RESPONSE_MESSAGE_TOO_BIG:
+               return -EINVAL;
+       default:
+               return -ENODEV;
+       }
+       return 0;
+}
+
+/**
+ * Poll AP device for pending replies and send new messages. If either
+ * ap_poll_read or ap_poll_write returns -ENODEV unregister the device.
+ * @ap_dev: pointer to the bus device
+ * @flags: pointer to control flags, bit 2^0 is set if another poll is
+ *        required, bit 2^1 is set if the poll timer needs to get armed
+ * Returns 0.
+ */
+static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags)
+{
+       int rc;
+
+       rc = ap_poll_read(ap_dev, flags);
+       if (rc)
+               return rc;
+       return ap_poll_write(ap_dev, flags);
+}
+
+/**
+ * Queue a message to a device.
+ * @ap_dev: pointer to the AP device
+ * @ap_msg: the message to be queued
+ */
+static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+       struct ap_queue_status status;
+
+       if (list_empty(&ap_dev->requestq) &&
+           ap_dev->queue_count < ap_dev->queue_depth) {
+               status = __ap_send(ap_dev->qid, ap_msg->psmid,
+                                  ap_msg->message, ap_msg->length);
+               switch (status.response_code) {
+               case AP_RESPONSE_NORMAL:
+                       list_add_tail(&ap_msg->list, &ap_dev->pendingq);
+                       atomic_inc(&ap_poll_requests);
+                       ap_dev->pendingq_count++;
+                       ap_dev->queue_count++;
+                       ap_dev->total_request_count++;
+                       break;
+               case AP_RESPONSE_Q_FULL:
+                       list_add_tail(&ap_msg->list, &ap_dev->requestq);
+                       ap_dev->requestq_count++;
+                       ap_dev->total_request_count++;
+                       return -EBUSY;
+               case AP_RESPONSE_MESSAGE_TOO_BIG:
+                       ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL));
+                       return -EINVAL;
+               default:        /* Device is gone. */
+                       ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+                       return -ENODEV;
+               }
+       } else {
+               list_add_tail(&ap_msg->list, &ap_dev->requestq);
+               ap_dev->requestq_count++;
+               ap_dev->total_request_count++;
+               return -EBUSY;
+       }
+       ap_schedule_poll_timer();
+       return 0;
+}
+
+void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+       unsigned long flags;
+       int rc;
+
+       spin_lock_bh(&ap_dev->lock);
+       if (!ap_dev->unregistered) {
+               /* Make room on the queue by polling for finished requests. */
+               rc = ap_poll_queue(ap_dev, &flags);
+               if (!rc)
+                       rc = __ap_queue_message(ap_dev, ap_msg);
+               if (!rc)
+                       wake_up(&ap_poll_wait);
+       } else {
+               ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV));
+               rc = 0;
+       }
+       spin_unlock_bh(&ap_dev->lock);
+       if (rc == -ENODEV)
+               device_unregister(&ap_dev->device);
+}
+EXPORT_SYMBOL(ap_queue_message);
+
+/**
+ * Cancel a crypto request. This is done by removing the request
+ * from the devive pendingq or requestq queue. Note that the
+ * request stays on the AP queue. When it finishes the message
+ * reply will be discarded because the psmid can't be found.
+ * @ap_dev: AP device that has the message queued
+ * @ap_msg: the message that is to be removed
+ */
+void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+       struct ap_message *tmp;
+
+       spin_lock_bh(&ap_dev->lock);
+       if (!list_empty(&ap_msg->list)) {
+               list_for_each_entry(tmp, &ap_dev->pendingq, list)
+                       if (tmp->psmid == ap_msg->psmid) {
+                               ap_dev->pendingq_count--;
+                               goto found;
+                       }
+               ap_dev->requestq_count--;
+       found:
+               list_del_init(&ap_msg->list);
+       }
+       spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_cancel_message);
+
+/**
+ * AP receive polling for finished AP requests
+ */
+static void ap_poll_timeout(unsigned long unused)
+{
+       tasklet_schedule(&ap_tasklet);
+}
+
+/**
+ * Poll all AP devices on the bus in a round robin fashion. Continue
+ * polling until bit 2^0 of the control flags is not set. If bit 2^1
+ * of the control flags has been set arm the poll timer.
+ */
+static int __ap_poll_all(struct device *dev, void *data)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       int rc;
+
+       spin_lock(&ap_dev->lock);
+       if (!ap_dev->unregistered) {
+               rc = ap_poll_queue(to_ap_dev(dev), (unsigned long *) data);
+       } else
+               rc = 0;
+       spin_unlock(&ap_dev->lock);
+       if (rc)
+               device_unregister(&ap_dev->device);
+       return 0;
+}
+
+static void ap_poll_all(unsigned long dummy)
+{
+       unsigned long flags;
+
+       do {
+               flags = 0;
+               bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all);
+       } while (flags & 1);
+       if (flags & 2)
+               ap_schedule_poll_timer();
+}
+
+/**
+ * AP bus poll thread. The purpose of this thread is to poll for
+ * finished requests in a loop if there is a "free" cpu - that is
+ * a cpu that doesn't have anything better to do. The polling stops
+ * as soon as there is another task or if all messages have been
+ * delivered.
+ */
+static int ap_poll_thread(void *data)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long flags;
+       int requests;
+
+       set_user_nice(current, -20);
+       while (1) {
+               if (need_resched()) {
+                       schedule();
+                       continue;
+               }
+               add_wait_queue(&ap_poll_wait, &wait);
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (kthread_should_stop())
+                       break;
+               requests = atomic_read(&ap_poll_requests);
+               if (requests <= 0)
+                       schedule();
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&ap_poll_wait, &wait);
+
+               local_bh_disable();
+               flags = 0;
+               bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all);
+               local_bh_enable();
+       }
+       set_current_state(TASK_RUNNING);
+       remove_wait_queue(&ap_poll_wait, &wait);
+       return 0;
+}
+
+static int ap_poll_thread_start(void)
+{
+       int rc;
+
+       mutex_lock(&ap_poll_thread_mutex);
+       if (!ap_poll_kthread) {
+               ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
+               rc = IS_ERR(ap_poll_kthread) ? PTR_ERR(ap_poll_kthread) : 0;
+               if (rc)
+                       ap_poll_kthread = NULL;
+       }
+       else
+               rc = 0;
+       mutex_unlock(&ap_poll_thread_mutex);
+       return rc;
+}
+
+static void ap_poll_thread_stop(void)
+{
+       mutex_lock(&ap_poll_thread_mutex);
+       if (ap_poll_kthread) {
+               kthread_stop(ap_poll_kthread);
+               ap_poll_kthread = NULL;
+       }
+       mutex_unlock(&ap_poll_thread_mutex);
+}
+
+/**
+ * The module initialization code.
+ */
+int __init ap_module_init(void)
+{
+       int rc, i;
+
+       if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) {
+               printk(KERN_WARNING "Invalid param: domain = %d. "
+                      " Not loading.\n", ap_domain_index);
+               return -EINVAL;
+       }
+       if (ap_instructions_available() != 0) {
+               printk(KERN_WARNING "AP instructions not installed.\n");
+               return -ENODEV;
+       }
+
+       /* Create /sys/bus/ap. */
+       rc = bus_register(&ap_bus_type);
+       if (rc)
+               goto out;
+       for (i = 0; ap_bus_attrs[i]; i++) {
+               rc = bus_create_file(&ap_bus_type, ap_bus_attrs[i]);
+               if (rc)
+                       goto out_bus;
+       }
+
+       /* Create /sys/devices/ap. */
+       ap_root_device = s390_root_dev_register("ap");
+       rc = IS_ERR(ap_root_device) ? PTR_ERR(ap_root_device) : 0;
+       if (rc)
+               goto out_bus;
+
+       ap_work_queue = create_singlethread_workqueue("kapwork");
+       if (!ap_work_queue) {
+               rc = -ENOMEM;
+               goto out_root;
+       }
+
+       if (ap_select_domain() == 0)
+               ap_scan_bus(NULL);
+
+       /* Setup the ap bus rescan timer. */
+       init_timer(&ap_config_timer);
+       ap_config_timer.function = ap_config_timeout;
+       ap_config_timer.data = 0;
+       ap_config_timer.expires = jiffies + ap_config_time * HZ;
+       add_timer(&ap_config_timer);
+
+       /* Start the low priority AP bus poll thread. */
+       if (ap_thread_flag) {
+               rc = ap_poll_thread_start();
+               if (rc)
+                       goto out_work;
+       }
+
+       return 0;
+
+out_work:
+       del_timer_sync(&ap_config_timer);
+       del_timer_sync(&ap_poll_timer);
+       destroy_workqueue(ap_work_queue);
+out_root:
+       s390_root_dev_unregister(ap_root_device);
+out_bus:
+       while (i--)
+               bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
+       bus_unregister(&ap_bus_type);
+out:
+       return rc;
+}
+
+static int __ap_match_all(struct device *dev, void *data)
+{
+       return 1;
+}
+
+/**
+ * The module termination code
+ */
+void ap_module_exit(void)
+{
+       int i;
+       struct device *dev;
+
+       ap_poll_thread_stop();
+       del_timer_sync(&ap_config_timer);
+       del_timer_sync(&ap_poll_timer);
+       destroy_workqueue(ap_work_queue);
+       s390_root_dev_unregister(ap_root_device);
+       while ((dev = bus_find_device(&ap_bus_type, NULL, NULL,
+                   __ap_match_all)))
+       {
+               device_unregister(dev);
+               put_device(dev);
+       }
+       for (i = 0; ap_bus_attrs[i]; i++)
+               bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
+       bus_unregister(&ap_bus_type);
+}
+
+#ifndef CONFIG_ZCRYPT_MONOLITHIC
+module_init(ap_module_init);
+module_exit(ap_module_exit);
+#endif
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
new file mode 100644 (file)
index 0000000..83b69c0
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * linux/drivers/s390/crypto/ap_bus.h
+ *
+ * Copyright (C) 2006 IBM Corporation
+ * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
+ *           Martin Schwidefsky <schwidefsky@de.ibm.com>
+ *           Ralph Wuerthner <rwuerthn@de.ibm.com>
+ *
+ * Adjunct processor bus header file.
+ *
+ * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _AP_BUS_H_
+#define _AP_BUS_H_
+
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/types.h>
+
+#define AP_DEVICES 64          /* Number of AP devices. */
+#define AP_DOMAINS 16          /* Number of AP domains. */
+#define AP_MAX_RESET 90                /* Maximum number of resets. */
+#define AP_CONFIG_TIME 30      /* Time in seconds between AP bus rescans. */
+#define AP_POLL_TIME 1         /* Time in ticks between receive polls. */
+
+extern int ap_domain_index;
+
+/**
+ * The ap_qid_t identifier of an ap queue. It contains a
+ * 6 bit device index and a 4 bit queue index (domain).
+ */
+typedef unsigned int ap_qid_t;
+
+#define AP_MKQID(_device,_queue) (((_device) & 63) << 8 | ((_queue) & 15))
+#define AP_QID_DEVICE(_qid) (((_qid) >> 8) & 63)
+#define AP_QID_QUEUE(_qid) ((_qid) & 15)
+
+/**
+ * The ap queue status word is returned by all three AP functions
+ * (PQAP, NQAP and DQAP).  There's a set of flags in the first
+ * byte, followed by a 1 byte response code.
+ */
+struct ap_queue_status {
+       unsigned int queue_empty        : 1;
+       unsigned int replies_waiting    : 1;
+       unsigned int queue_full         : 1;
+       unsigned int pad1               : 5;
+       unsigned int response_code      : 8;
+       unsigned int pad2               : 16;
+};
+
+#define AP_RESPONSE_NORMAL             0x00
+#define AP_RESPONSE_Q_NOT_AVAIL                0x01
+#define AP_RESPONSE_RESET_IN_PROGRESS  0x02
+#define AP_RESPONSE_DECONFIGURED       0x03
+#define AP_RESPONSE_CHECKSTOPPED       0x04
+#define AP_RESPONSE_BUSY               0x05
+#define AP_RESPONSE_Q_FULL             0x10
+#define AP_RESPONSE_NO_PENDING_REPLY   0x10
+#define AP_RESPONSE_INDEX_TOO_BIG      0x11
+#define AP_RESPONSE_NO_FIRST_PART      0x13
+#define AP_RESPONSE_MESSAGE_TOO_BIG    0x15
+
+/**
+ * Known device types
+ */
+#define AP_DEVICE_TYPE_PCICC   3
+#define AP_DEVICE_TYPE_PCICA   4
+#define AP_DEVICE_TYPE_PCIXCC  5
+#define AP_DEVICE_TYPE_CEX2A   6
+#define AP_DEVICE_TYPE_CEX2C   7
+
+struct ap_device;
+struct ap_message;
+
+struct ap_driver {
+       struct device_driver driver;
+       struct ap_device_id *ids;
+
+       int (*probe)(struct ap_device *);
+       void (*remove)(struct ap_device *);
+       /* receive is called from tasklet context */
+       void (*receive)(struct ap_device *, struct ap_message *,
+                       struct ap_message *);
+};
+
+#define to_ap_drv(x) container_of((x), struct ap_driver, driver)
+
+int ap_driver_register(struct ap_driver *, struct module *, char *);
+void ap_driver_unregister(struct ap_driver *);
+
+struct ap_device {
+       struct device device;
+       struct ap_driver *drv;          /* Pointer to AP device driver. */
+       spinlock_t lock;                /* Per device lock. */
+
+       ap_qid_t qid;                   /* AP queue id. */
+       int queue_depth;                /* AP queue depth.*/
+       int device_type;                /* AP device type. */
+       int unregistered;               /* marks AP device as unregistered */
+
+       int queue_count;                /* # messages currently on AP queue. */
+
+       struct list_head pendingq;      /* List of message sent to AP queue. */
+       int pendingq_count;             /* # requests on pendingq list. */
+       struct list_head requestq;      /* List of message yet to be sent. */
+       int requestq_count;             /* # requests on requestq list. */
+       int total_request_count;        /* # requests ever for this AP device. */
+
+       struct ap_message *reply;       /* Per device reply message. */
+
+       void *private;                  /* ap driver private pointer. */
+};
+
+#define to_ap_dev(x) container_of((x), struct ap_device, device)
+
+struct ap_message {
+       struct list_head list;          /* Request queueing. */
+       unsigned long long psmid;       /* Message id. */
+       void *message;                  /* Pointer to message buffer. */
+       size_t length;                  /* Message length. */
+
+       void *private;                  /* ap driver private pointer. */
+};
+
+#define AP_DEVICE(dt)                                  \
+       .dev_type=(dt),                                 \
+       .match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE,
+
+/**
+ * Note: don't use ap_send/ap_recv after using ap_queue_message
+ * for the first time. Otherwise the ap message queue will get
+ * confused.
+ */
+int ap_send(ap_qid_t, unsigned long long, void *, size_t);
+int ap_recv(ap_qid_t, unsigned long long *, void *, size_t);
+
+void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
+void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
+void ap_flush_queue(struct ap_device *ap_dev);
+
+int ap_module_init(void);
+void ap_module_exit(void);
+
+#endif /* _AP_BUS_H_ */
diff --git a/drivers/s390/crypto/z90common.h b/drivers/s390/crypto/z90common.h
deleted file mode 100644 (file)
index dbbcda3..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- *  linux/drivers/s390/crypto/z90common.h
- *
- *  z90crypt 1.3.3
- *
- *  Copyright (C)  2001, 2005 IBM Corporation
- *  Author(s): Robert Burroughs (burrough@us.ibm.com)
- *             Eric Rossman (edrossma@us.ibm.com)
- *
- *  Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
- *
- * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#ifndef _Z90COMMON_H_
-#define _Z90COMMON_H_
-
-
-#define RESPBUFFSIZE 256
-#define PCI_FUNC_KEY_DECRYPT 0x5044
-#define PCI_FUNC_KEY_ENCRYPT 0x504B
-extern int ext_bitlens;
-
-enum devstat {
-       DEV_GONE,
-       DEV_ONLINE,
-       DEV_QUEUE_FULL,
-       DEV_EMPTY,
-       DEV_NO_WORK,
-       DEV_BAD_MESSAGE,
-       DEV_TSQ_EXCEPTION,
-       DEV_RSQ_EXCEPTION,
-       DEV_SEN_EXCEPTION,
-       DEV_REC_EXCEPTION
-};
-
-enum hdstat {
-       HD_NOT_THERE,
-       HD_BUSY,
-       HD_DECONFIGURED,
-       HD_CHECKSTOPPED,
-       HD_ONLINE,
-       HD_TSQ_EXCEPTION
-};
-
-#define Z90C_NO_DEVICES                1
-#define Z90C_AMBIGUOUS_DOMAIN  2
-#define Z90C_INCORRECT_DOMAIN  3
-#define ENOTINIT               4
-
-#define SEN_BUSY        7
-#define SEN_USER_ERROR  8
-#define SEN_QUEUE_FULL 11
-#define SEN_NOT_AVAIL  16
-#define SEN_PAD_ERROR  17
-#define SEN_RETRY      18
-#define SEN_RELEASED   24
-
-#define REC_EMPTY       4
-#define REC_BUSY        6
-#define REC_OPERAND_INV         8
-#define REC_OPERAND_SIZE 9
-#define REC_EVEN_MOD   10
-#define REC_NO_WORK    11
-#define REC_HARDWAR_ERR        12
-#define REC_NO_RESPONSE        13
-#define REC_RETRY_DEV  14
-#define REC_USER_GONE  15
-#define REC_BAD_MESSAGE        16
-#define REC_INVALID_PAD        17
-#define REC_USE_PCICA  18
-
-#define WRONG_DEVICE_TYPE 20
-
-#define REC_FATAL_ERROR 32
-#define SEN_FATAL_ERROR 33
-#define TSQ_FATAL_ERROR 34
-#define RSQ_FATAL_ERROR 35
-
-#define Z90CRYPT_NUM_TYPES     6
-#define PCICA          0
-#define PCICC          1
-#define PCIXCC_MCL2    2
-#define PCIXCC_MCL3    3
-#define CEX2C          4
-#define CEX2A          5
-#define NILDEV         -1
-#define ANYDEV         -1
-#define PCIXCC_UNK     -2
-
-enum hdevice_type {
-       PCICC_HW  = 3,
-       PCICA_HW  = 4,
-       PCIXCC_HW = 5,
-       CEX2A_HW  = 6,
-       CEX2C_HW  = 7
-};
-
-struct CPRBX {
-       unsigned short cprb_len;
-       unsigned char  cprb_ver_id;
-       unsigned char  pad_000[3];
-       unsigned char  func_id[2];
-       unsigned char  cprb_flags[4];
-       unsigned int   req_parml;
-       unsigned int   req_datal;
-       unsigned int   rpl_msgbl;
-       unsigned int   rpld_parml;
-       unsigned int   rpl_datal;
-       unsigned int   rpld_datal;
-       unsigned int   req_extbl;
-       unsigned char  pad_001[4];
-       unsigned int   rpld_extbl;
-       unsigned char  req_parmb[16];
-       unsigned char  req_datab[16];
-       unsigned char  rpl_parmb[16];
-       unsigned char  rpl_datab[16];
-       unsigned char  req_extb[16];
-       unsigned char  rpl_extb[16];
-       unsigned short ccp_rtcode;
-       unsigned short ccp_rscode;
-       unsigned int   mac_data_len;
-       unsigned char  logon_id[8];
-       unsigned char  mac_value[8];
-       unsigned char  mac_content_flgs;
-       unsigned char  pad_002;
-       unsigned short domain;
-       unsigned char  pad_003[12];
-       unsigned char  pad_004[36];
-};
-
-#ifndef DEV_NAME
-#define DEV_NAME       "z90crypt"
-#endif
-#define PRINTK(fmt, args...) \
-       printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-#define PRINTKN(fmt, args...) \
-       printk(KERN_DEBUG DEV_NAME ": " fmt, ## args)
-#define PRINTKW(fmt, args...) \
-       printk(KERN_WARNING DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-#define PRINTKC(fmt, args...) \
-       printk(KERN_CRIT DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-
-#ifdef Z90CRYPT_DEBUG
-#define PDEBUG(fmt, args...) \
-       printk(KERN_DEBUG DEV_NAME ": %s -> " fmt, __FUNCTION__ , ## args)
-#else
-#define PDEBUG(fmt, args...) do {} while (0)
-#endif
-
-#define UMIN(a,b) ((a) < (b) ? (a) : (b))
-#define IS_EVEN(x) ((x) == (2 * ((x) / 2)))
-
-#endif