Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mattst88...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 9 Apr 2018 16:11:32 +0000 (09:11 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 9 Apr 2018 16:11:32 +0000 (09:11 -0700)
Pull alpha updates from Matt Turner:
 "A few small changes for alpha"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mattst88/alpha:
  alpha: io: reorder barriers to guarantee writeX() and iowriteX() ordering
  alpha: Implement CPU vulnerabilities sysfs functions.
  alpha: rtc: stop validating rtc_time in .read_time
  alpha: rtc: remove unused set_mmss ops

84 files changed:
Documentation/ABI/testing/ima_policy
Documentation/admin-guide/kernel-parameters.txt
Documentation/s390/vfio-ccw.txt
arch/s390/Kconfig
arch/s390/Makefile
arch/s390/boot/compressed/Makefile
arch/s390/boot/compressed/head.S
arch/s390/boot/compressed/misc.c
arch/s390/boot/compressed/vmlinux.lds.S
arch/s390/crypto/aes_s390.c
arch/s390/include/asm/alternative-asm.h [new file with mode: 0644]
arch/s390/include/asm/ccwdev.h
arch/s390/include/asm/chpid.h
arch/s390/include/asm/cio.h
arch/s390/include/asm/cpu_mf.h
arch/s390/include/asm/css_chars.h
arch/s390/include/asm/nospec-branch.h
arch/s390/include/asm/pgalloc.h
arch/s390/include/asm/scsw.h
arch/s390/include/asm/setup.h
arch/s390/include/uapi/asm/dasd.h
arch/s390/kernel/Makefile
arch/s390/kernel/alternative.c
arch/s390/kernel/asm-offsets.c
arch/s390/kernel/early.c
arch/s390/kernel/entry.S
arch/s390/kernel/module.c
arch/s390/kernel/nospec-branch.c
arch/s390/kernel/setup.c
arch/s390/kernel/suspend.c
arch/s390/mm/dump_pagetables.c
arch/s390/mm/pgalloc.c
drivers/char/tpm/tpm-interface.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm2-cmd.c
drivers/char/tpm/tpm_crb.c
drivers/char/tpm/tpm_tis_core.c
drivers/s390/block/dasd.c
drivers/s390/block/dasd_3990_erp.c
drivers/s390/block/dasd_devmap.c
drivers/s390/block/dasd_eckd.c
drivers/s390/char/Makefile
drivers/s390/char/defkeymap.c
drivers/s390/char/keyboard.c
drivers/s390/char/keyboard.h
drivers/s390/char/sclp.c
drivers/s390/char/sclp.h
drivers/s390/char/sclp_early.c
drivers/s390/char/sclp_early_core.c
drivers/s390/char/sclp_sd.c [new file with mode: 0644]
drivers/s390/char/sclp_tty.c
drivers/s390/cio/chp.c
drivers/s390/cio/chp.h
drivers/s390/cio/chsc.c
drivers/s390/cio/chsc.h
drivers/s390/cio/device.c
drivers/s390/cio/device_ops.c
drivers/s390/cio/qdio_main.c
drivers/s390/cio/vfio_ccw_fsm.c
drivers/s390/net/qeth_core_main.c
drivers/tty/Kconfig
drivers/video/Kconfig
drivers/video/console/Kconfig
fs/fuse/inode.c
include/linux/fs.h
include/linux/lsm_hooks.h
include/linux/security.h
include/linux/tpm.h
security/integrity/evm/evm.h
security/integrity/evm/evm_crypto.c
security/integrity/evm/evm_main.c
security/integrity/iint.c
security/integrity/ima/Kconfig
security/integrity/ima/ima.h
security/integrity/ima/ima_api.c
security/integrity/ima/ima_appraise.c
security/integrity/ima/ima_crypto.c
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c
security/integrity/ima/ima_template_lib.c
security/integrity/integrity.h
security/security.c
security/selinux/hooks.c
security/smack/smack_lsm.c

index 2028f2d093b2049ca0a75b265d80dd14446d397a..b8465e00ba5fb7d06e434e9508302b71b1809f5b 100644 (file)
@@ -26,7 +26,7 @@ Description:
                                 [obj_user=] [obj_role=] [obj_type=]]
                        option: [[appraise_type=]] [permit_directio]
 
-               base:   func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK]
+               base:   func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
                                [FIRMWARE_CHECK]
                                [KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
                        mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
index 683145d7b054df49b5994e66bbf94e5a591b40a0..9a3edf7e901ab331e2b005593d15f43d11cae9b3 100644 (file)
 
        ima_policy=     [IMA]
                        The builtin policies to load during IMA setup.
-                       Format: "tcb | appraise_tcb | secure_boot"
+                       Format: "tcb | appraise_tcb | secure_boot |
+                                fail_securely"
 
                        The "tcb" policy measures all programs exec'd, files
                        mmap'd for exec, and all files opened with the read
                        of files (eg. kexec kernel image, kernel modules,
                        firmware, policy, etc) based on file signatures.
 
+                       The "fail_securely" policy forces file signature
+                       verification failure also on privileged mounted
+                       filesystems with the SB_I_UNVERIFIABLE_SIGNATURE
+                       flag.
+
        ima_tcb         [IMA] Deprecated.  Use ima_policy= instead.
                        Load a policy which meets the needs of the Trusted
                        Computing Base.  This means IMA will measure all
index 90b3dfead81bd8390d8fd806129904e50cdc05e8..2be11ad864ff5c2b2bc116dd61d225d88d683d20 100644 (file)
@@ -28,7 +28,7 @@ every detail. More information/reference could be found here:
   https://en.wikipedia.org/wiki/Channel_I/O
 - s390 architecture:
   s390 Principles of Operation manual (IBM Form. No. SA22-7832)
-- The existing Qemu code which implements a simple emulated channel
+- The existing QEMU code which implements a simple emulated channel
   subsystem could also be a good reference. It makes it easier to follow
   the flow.
   qemu/hw/s390x/css.c
@@ -39,22 +39,22 @@ For vfio mediated device framework:
 Motivation of vfio-ccw
 ----------------------
 
-Currently, a guest virtualized via qemu/kvm on s390 only sees
+Typically, a guest virtualized via QEMU/KVM on s390 only sees
 paravirtualized virtio devices via the "Virtio Over Channel I/O
 (virtio-ccw)" transport. This makes virtio devices discoverable via
 standard operating system algorithms for handling channel devices.
 
 However this is not enough. On s390 for the majority of devices, which
 use the standard Channel I/O based mechanism, we also need to provide
-the functionality of passing through them to a Qemu virtual machine.
+the functionality of passing through them to a QEMU virtual machine.
 This includes devices that don't have a virtio counterpart (e.g. tape
 drives) or that have specific characteristics which guests want to
 exploit.
 
 For passing a device to a guest, we want to use the same interface as
-everybody else, namely vfio. Thus, we would like to introduce vfio
-support for channel devices. And we would like to name this new vfio
-device "vfio-ccw".
+everybody else, namely vfio. We implement this vfio support for channel
+devices via the vfio mediated device framework and the subchannel device
+driver "vfio_ccw".
 
 Access patterns of CCW devices
 ------------------------------
@@ -99,7 +99,7 @@ As mentioned above, we realize vfio-ccw with a mdev implementation.
 Channel I/O does not have IOMMU hardware support, so the physical
 vfio-ccw device does not have an IOMMU level translation or isolation.
 
-Sub-channel I/O instructions are all privileged instructions, When
+Subchannel I/O instructions are all privileged instructions. When
 handling the I/O instruction interception, vfio-ccw has the software
 policing and translation how the channel program is programmed before
 it gets sent to hardware.
@@ -121,7 +121,7 @@ devices:
 - The vfio_mdev driver for the mediated vfio ccw device.
   This is provided by the mdev framework. It is a vfio device driver for
   the mdev that created by vfio_ccw.
-  It realize a group of vfio device driver callbacks, adds itself to a
+  It realizes a group of vfio device driver callbacks, adds itself to a
   vfio group, and registers itself to the mdev framework as a mdev
   driver.
   It uses a vfio iommu backend that uses the existing map and unmap
@@ -178,7 +178,7 @@ vfio-ccw I/O region
 
 An I/O region is used to accept channel program request from user
 space and store I/O interrupt result for user space to retrieve. The
-defination of the region is:
+definition of the region is:
 
 struct ccw_io_region {
 #define ORB_AREA_SIZE 12
@@ -198,30 +198,23 @@ irb_area stores the I/O result.
 
 ret_code stores a return code for each access of the region.
 
-vfio-ccw patches overview
--------------------------
+vfio-ccw operation details
+--------------------------
 
-For now, our patches are rebased on the latest mdev implementation.
-vfio-ccw follows what vfio-pci did on the s390 paltform and uses
-vfio-iommu-type1 as the vfio iommu backend. It's a good start to launch
-the code review for vfio-ccw. Note that the implementation is far from
-complete yet; but we'd like to get feedback for the general
-architecture.
+vfio-ccw follows what vfio-pci did on the s390 platform and uses
+vfio-iommu-type1 as the vfio iommu backend.
 
 * CCW translation APIs
-- Description:
-  These introduce a group of APIs (start with 'cp_') to do CCW
-  translation. The CCWs passed in by a user space program are
-  organized with their guest physical memory addresses. These APIs
-  will copy the CCWs into the kernel space, and assemble a runnable
-  kernel channel program by updating the guest physical addresses with
-  their corresponding host physical addresses.
-- Patches:
-  vfio: ccw: introduce channel program interfaces
+  A group of APIs (start with 'cp_') to do CCW translation. The CCWs
+  passed in by a user space program are organized with their guest
+  physical memory addresses. These APIs will copy the CCWs into kernel
+  space, and assemble a runnable kernel channel program by updating the
+  guest physical addresses with their corresponding host physical addresses.
+  Note that we have to use IDALs even for direct-access CCWs, as the
+  referenced memory can be located anywhere, including above 2G.
 
 * vfio_ccw device driver
-- Description:
-  The following patches utilizes the CCW translation APIs and introduce
+  This driver utilizes the CCW translation APIs and introduces
   vfio_ccw, which is the driver for the I/O subchannel devices you want
   to pass through.
   vfio_ccw implements the following vfio ioctls:
@@ -236,20 +229,14 @@ architecture.
   This also provides the SET_IRQ ioctl to setup an event notifier to
   notify the user space program the I/O completion in an asynchronous
   way.
-- Patches:
-  vfio: ccw: basic implementation for vfio_ccw driver
-  vfio: ccw: introduce ccw_io_region
-  vfio: ccw: realize VFIO_DEVICE_GET_REGION_INFO ioctl
-  vfio: ccw: realize VFIO_DEVICE_RESET ioctl
-  vfio: ccw: realize VFIO_DEVICE_G(S)ET_IRQ_INFO ioctls
-
-The user of vfio-ccw is not limited to Qemu, while Qemu is definitely a
+
+The use of vfio-ccw is not limited to QEMU, while QEMU is definitely a
 good example to get understand how these patches work. Here is a little
-bit more detail how an I/O request triggered by the Qemu guest will be
+bit more detail how an I/O request triggered by the QEMU guest will be
 handled (without error handling).
 
 Explanation:
-Q1-Q7: Qemu side process.
+Q1-Q7: QEMU side process.
 K1-K5: Kernel side process.
 
 Q1. Get I/O region info during initialization.
@@ -263,7 +250,7 @@ Q4. Write the guest channel program and ORB to the I/O region.
     K2. Translate the guest channel program to a host kernel space
         channel program, which becomes runnable for a real device.
     K3. With the necessary information contained in the orb passed in
-        by Qemu, issue the ccwchain to the device.
+        by QEMU, issue the ccwchain to the device.
     K4. Return the ssch CC code.
 Q5. Return the CC code to the guest.
 
@@ -271,7 +258,7 @@ Q5. Return the CC code to the guest.
 
     K5. Interrupt handler gets the I/O result and write the result to
         the I/O region.
-    K6. Signal Qemu to retrieve the result.
+    K6. Signal QEMU to retrieve the result.
 Q6. Get the signal and event handler reads out the result from the I/O
     region.
 Q7. Update the irb for the guest.
@@ -289,10 +276,20 @@ More information for DASD and ECKD could be found here:
 https://en.wikipedia.org/wiki/Direct-access_storage_device
 https://en.wikipedia.org/wiki/Count_key_data
 
-Together with the corresponding work in Qemu, we can bring the passed
+Together with the corresponding work in QEMU, we can bring the passed
 through DASD/ECKD device online in a guest now and use it as a block
 device.
 
+While the current code allows the guest to start channel programs via
+START SUBCHANNEL, support for HALT SUBCHANNEL or CLEAR SUBCHANNEL is
+not yet implemented.
+
+vfio-ccw supports classic (command mode) channel I/O only. Transport
+mode (HPF) is not supported.
+
+QDIO subchannels are currently not supported. Classic devices other than
+DASD/ECKD might work, but have not been tested.
+
 Reference
 ---------
 1. ESA/s390 Principles of Operation manual (IBM Form. No. SA22-7832)
index eaee7087886fa290a3b4ca0fbdf379ebf62ee840..32a0d5b958bf5c320d8e22f2e2fb167b443fe632 100644 (file)
@@ -120,6 +120,7 @@ config S390
        select GENERIC_CLOCKEVENTS
        select GENERIC_CPU_AUTOPROBE
        select GENERIC_CPU_DEVICES if !SMP
+       select GENERIC_CPU_VULNERABILITIES
        select GENERIC_FIND_FIRST_BIT
        select GENERIC_SMP_IDLE_THREAD
        select GENERIC_TIME_VSYSCALL
@@ -576,7 +577,7 @@ choice
 config EXPOLINE_OFF
        bool "spectre_v2=off"
 
-config EXPOLINE_MEDIUM
+config EXPOLINE_AUTO
        bool "spectre_v2=auto"
 
 config EXPOLINE_FULL
index 2ced3239cb847239aa5ebcf13d5e727f3057e0f7..c79936d02f7bd428dd8c8d5d64012d1d10ace8bf 100644 (file)
@@ -47,9 +47,6 @@ cflags-$(CONFIG_MARCH_Z14_TUNE)               += -mtune=z14
 
 cflags-y += -Wa,-I$(srctree)/arch/$(ARCH)/include
 
-#KBUILD_IMAGE is necessary for make rpm
-KBUILD_IMAGE   :=arch/s390/boot/image
-
 #
 # Prevent tail-call optimizations, to get clearer backtraces:
 #
@@ -84,7 +81,7 @@ ifdef CONFIG_EXPOLINE
     CC_FLAGS_EXPOLINE += -mfunction-return=thunk
     CC_FLAGS_EXPOLINE += -mindirect-branch-table
     export CC_FLAGS_EXPOLINE
-    cflags-y += $(CC_FLAGS_EXPOLINE)
+    cflags-y += $(CC_FLAGS_EXPOLINE) -DCC_USING_EXPOLINE
   endif
 endif
 
@@ -126,6 +123,9 @@ tools               := arch/s390/tools
 
 all: image bzImage
 
+#KBUILD_IMAGE is necessary for packaging targets like rpm-pkg, deb-pkg...
+KBUILD_IMAGE   := $(boot)/bzImage
+
 install: vmlinux
        $(Q)$(MAKE) $(build)=$(boot) $@
 
index 26d6a94f40f6987d95faf83d455c9a64389436ef..5766f7b9b2710e11c8676eb9dc13e3b2bd8969be 100644 (file)
@@ -29,11 +29,16 @@ LDFLAGS_vmlinux := --oformat $(LD_BFD) -e startup -T
 $(obj)/vmlinux: $(obj)/vmlinux.lds $(OBJECTS)
        $(call if_changed,ld)
 
-sed-sizes := -e 's/^\([0-9a-fA-F]*\) . \(__bss_start\|_end\)$$/\#define SZ\2 0x\1/p'
+TRIM_HEAD_SIZE := 0x11000
 
-quiet_cmd_sizes = GEN $@
+sed-sizes := -e 's/^\([0-9a-fA-F]*\) . \(__bss_start\|_end\)$$/\#define SZ\2 (0x\1 - $(TRIM_HEAD_SIZE))/p'
+
+quiet_cmd_sizes = GEN     $@
       cmd_sizes = $(NM) $< | sed -n $(sed-sizes) > $@
 
+quiet_cmd_trim_head = TRIM    $@
+      cmd_trim_head = tail -c +$$(($(TRIM_HEAD_SIZE) + 1)) $< > $@
+
 $(obj)/sizes.h: vmlinux
        $(call if_changed,sizes)
 
@@ -43,10 +48,13 @@ $(obj)/head.o: $(obj)/sizes.h
 CFLAGS_misc.o += -I$(objtree)/$(obj)
 $(obj)/misc.o: $(obj)/sizes.h
 
-OBJCOPYFLAGS_vmlinux.bin :=  -R .comment -S
-$(obj)/vmlinux.bin: vmlinux
+OBJCOPYFLAGS_vmlinux.bin.full :=  -R .comment -S
+$(obj)/vmlinux.bin.full: vmlinux
        $(call if_changed,objcopy)
 
+$(obj)/vmlinux.bin: $(obj)/vmlinux.bin.full
+       $(call if_changed,trim_head)
+
 vmlinux.bin.all-y := $(obj)/vmlinux.bin
 
 suffix-$(CONFIG_KERNEL_GZIP)  := gz
index 231d1491d431919bcb49fa6034a683862c74b0fa..9f94eca0f467c8bd6aad88b9140e37b96ecb71a6 100644 (file)
@@ -23,12 +23,10 @@ ENTRY(startup_continue)
        aghi    %r15,-160
        brasl   %r14,decompress_kernel
        # Set up registers for memory mover. We move the decompressed image to
-       # 0x11000, starting at offset 0x11000 in the decompressed image so
-       # that code living at 0x11000 in the image will end up at 0x11000 in
-       # memory.
+       # 0x11000, where startup_continue of the decompressed image is supposed
+       # to be.
        lgr     %r4,%r2
        lg      %r2,.Loffset-.LPG1(%r13)
-       la      %r4,0(%r2,%r4)
        lg      %r3,.Lmvsize-.LPG1(%r13)
        lgr     %r5,%r3
        # Move the memory mover someplace safe so it doesn't overwrite itself.
index cecf38b9ec82d50e24fdb068c157a7680274cb75..63838a17e56a1512fe22fe5550f785d6fd1d1826 100644 (file)
@@ -27,8 +27,8 @@
 /* Symbols defined by linker scripts */
 extern char input_data[];
 extern int input_len;
-extern char _text, _end;
-extern char _bss, _ebss;
+extern char _end[];
+extern char _bss[], _ebss[];
 
 static void error(char *m);
 
@@ -144,7 +144,7 @@ unsigned long decompress_kernel(void)
 {
        void *output, *kernel_end;
 
-       output = (void *) ALIGN((unsigned long) &_end + HEAP_SIZE, PAGE_SIZE);
+       output = (void *) ALIGN((unsigned long) _end + HEAP_SIZE, PAGE_SIZE);
        kernel_end = output + SZ__bss_start;
        check_ipl_parmblock((void *) 0, (unsigned long) kernel_end);
 
@@ -166,8 +166,8 @@ unsigned long decompress_kernel(void)
         * Clear bss section. free_mem_ptr and free_mem_end_ptr need to be
         * initialized afterwards since they reside in bss.
         */
-       memset(&_bss, 0, &_ebss - &_bss);
-       free_mem_ptr = (unsigned long) &_end;
+       memset(_bss, 0, _ebss - _bss);
+       free_mem_ptr = (unsigned long) _end;
        free_mem_end_ptr = free_mem_ptr + HEAP_SIZE;
 
        __decompress(input_data, input_len, NULL, NULL, output, 0, NULL, error);
index 8150132b144f75745764faa8bbf1b8c96779a375..d43c2db12d306bd744eaa78df6405b3e0f905183 100644 (file)
@@ -52,6 +52,7 @@ SECTIONS
        /* Sections to be discarded */
        /DISCARD/ : {
                *(.eh_frame)
+               *(__ex_table)
                *(*__ksymtab*)
        }
 }
index d60798737d8669ea447ae0905b98abf64e85aea6..fa9b7dd1a513ffa45d74734444212f4b1e133321 100644 (file)
@@ -1047,6 +1047,7 @@ static struct aead_alg gcm_aes_aead = {
 
 static struct crypto_alg *aes_s390_algs_ptr[5];
 static int aes_s390_algs_num;
+static struct aead_alg *aes_s390_aead_alg;
 
 static int aes_s390_register_alg(struct crypto_alg *alg)
 {
@@ -1065,7 +1066,8 @@ static void aes_s390_fini(void)
        if (ctrblk)
                free_page((unsigned long) ctrblk);
 
-       crypto_unregister_aead(&gcm_aes_aead);
+       if (aes_s390_aead_alg)
+               crypto_unregister_aead(aes_s390_aead_alg);
 }
 
 static int __init aes_s390_init(void)
@@ -1123,6 +1125,7 @@ static int __init aes_s390_init(void)
                ret = crypto_register_aead(&gcm_aes_aead);
                if (ret)
                        goto out_err;
+               aes_s390_aead_alg = &gcm_aes_aead;
        }
 
        return 0;
diff --git a/arch/s390/include/asm/alternative-asm.h b/arch/s390/include/asm/alternative-asm.h
new file mode 100644 (file)
index 0000000..955d620
--- /dev/null
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_S390_ALTERNATIVE_ASM_H
+#define _ASM_S390_ALTERNATIVE_ASM_H
+
+#ifdef __ASSEMBLY__
+
+/*
+ * Check the length of an instruction sequence. The length may not be larger
+ * than 254 bytes and it has to be divisible by 2.
+ */
+.macro alt_len_check start,end
+       .if ( \end - \start ) > 254
+       .error "cpu alternatives does not support instructions blocks > 254 bytes\n"
+       .endif
+       .if ( \end - \start ) % 2
+       .error "cpu alternatives instructions length is odd\n"
+       .endif
+.endm
+
+/*
+ * Issue one struct alt_instr descriptor entry (need to put it into
+ * the section .altinstructions, see below). This entry contains
+ * enough information for the alternatives patching code to patch an
+ * instruction. See apply_alternatives().
+ */
+.macro alt_entry orig_start, orig_end, alt_start, alt_end, feature
+       .long   \orig_start - .
+       .long   \alt_start - .
+       .word   \feature
+       .byte   \orig_end - \orig_start
+       .byte   \alt_end - \alt_start
+.endm
+
+/*
+ * Fill up @bytes with nops. The macro emits 6-byte nop instructions
+ * for the bulk of the area, possibly followed by a 4-byte and/or
+ * a 2-byte nop if the size of the area is not divisible by 6.
+ */
+.macro alt_pad_fill bytes
+       .fill   ( \bytes ) / 6, 6, 0xc0040000
+       .fill   ( \bytes ) % 6 / 4, 4, 0x47000000
+       .fill   ( \bytes ) % 6 % 4 / 2, 2, 0x0700
+.endm
+
+/*
+ * Fill up @bytes with nops. If the number of bytes is larger
+ * than 6, emit a jg instruction to branch over all nops, then
+ * fill an area of size (@bytes - 6) with nop instructions.
+ */
+.macro alt_pad bytes
+       .if ( \bytes > 0 )
+       .if ( \bytes > 6 )
+       jg      . + \bytes
+       alt_pad_fill \bytes - 6
+       .else
+       alt_pad_fill \bytes
+       .endif
+       .endif
+.endm
+
+/*
+ * Define an alternative between two instructions. If @feature is
+ * present, early code in apply_alternatives() replaces @oldinstr with
+ * @newinstr. ".skip" directive takes care of proper instruction padding
+ * in case @newinstr is longer than @oldinstr.
+ */
+.macro ALTERNATIVE oldinstr, newinstr, feature
+       .pushsection .altinstr_replacement,"ax"
+770:   \newinstr
+771:   .popsection
+772:   \oldinstr
+773:   alt_len_check 770b, 771b
+       alt_len_check 772b, 773b
+       alt_pad ( ( 771b - 770b ) - ( 773b - 772b ) )
+774:   .pushsection .altinstructions,"a"
+       alt_entry 772b, 774b, 770b, 771b, \feature
+       .popsection
+.endm
+
+/*
+ * Define an alternative between two instructions. If @feature is
+ * present, early code in apply_alternatives() replaces @oldinstr with
+ * @newinstr. ".skip" directive takes care of proper instruction padding
+ * in case @newinstr is longer than @oldinstr.
+ */
+.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
+       .pushsection .altinstr_replacement,"ax"
+770:   \newinstr1
+771:   \newinstr2
+772:   .popsection
+773:   \oldinstr
+774:   alt_len_check 770b, 771b
+       alt_len_check 771b, 772b
+       alt_len_check 773b, 774b
+       .if ( 771b - 770b > 772b - 771b )
+       alt_pad ( ( 771b - 770b ) - ( 774b - 773b ) )
+       .else
+       alt_pad ( ( 772b - 771b ) - ( 774b - 773b ) )
+       .endif
+775:   .pushsection .altinstructions,"a"
+       alt_entry 773b, 775b, 770b, 771b,\feature1
+       alt_entry 773b, 775b, 771b, 772b,\feature2
+       .popsection
+.endm
+
+#endif /*  __ASSEMBLY__  */
+
+#endif /* _ASM_S390_ALTERNATIVE_ASM_H */
index 633f8da861374df0801a958dcdf87cc127bb0a86..20bce136b2e5b09bcfb170813ee191789a1d2ee4 100644 (file)
@@ -230,5 +230,5 @@ int ccw_device_siosl(struct ccw_device *);
 
 extern void ccw_device_get_schid(struct ccw_device *, struct subchannel_id *);
 
-struct channel_path_desc *ccw_device_get_chp_desc(struct ccw_device *, int);
+struct channel_path_desc_fmt0 *ccw_device_get_chp_desc(struct ccw_device *, int);
 #endif /* _S390_CCWDEV_H_ */
index 4773f747915cab928790475e346a14fca7341b35..20e0d22f29e9b85601706fe3f4ba58670a68c52c 100644 (file)
@@ -9,7 +9,7 @@
 #include <uapi/asm/chpid.h>
 #include <asm/cio.h>
 
-struct channel_path_desc {
+struct channel_path_desc_fmt0 {
        u8 flags;
        u8 lsn;
        u8 desc;
index dc84a0171bb3d1a24c7fc8cd14345891feff9504..847a04262b9c09188474d2bff6d8f14f2735404a 100644 (file)
@@ -227,7 +227,7 @@ struct esw_eadm {
  * a field is valid; a field not being valid is always passed as %0.
  * If a unit check occurred, @ecw may contain sense data; this is retrieved
  * by the common I/O layer itself if the device doesn't support concurrent
- * sense (so that the device driver never needs to perform basic sene itself).
+ * sense (so that the device driver never needs to perform basic sense itself).
  * For unsolicited interrupts, the irb is passed as-is (expect for sense data,
  * if applicable).
  */
index dd08db491b89e149fc7894a5f77578db8fe950d6..f58d17e9dd65872e12d0764c511cbc5955ab7271 100644 (file)
 /* CPU measurement facility support */
 static inline int cpum_cf_avail(void)
 {
-       return MACHINE_HAS_LPP && test_facility(67);
+       return test_facility(40) && test_facility(67);
 }
 
 static inline int cpum_sf_avail(void)
 {
-       return MACHINE_HAS_LPP && test_facility(68);
+       return test_facility(40) && test_facility(68);
 }
 
 
index fb56fa3283a2c35cc79930df704e397265e56383..0563fd3e84585769f7acd0f093f30a48eaab9f54 100644 (file)
@@ -32,8 +32,10 @@ struct css_general_char {
        u32 fcx : 1;     /* bit 88 */
        u32 : 19;
        u32 alt_ssi : 1; /* bit 108 */
-       u32:1;
-       u32 narf:1;      /* bit 110 */
+       u32 : 1;
+       u32 narf : 1;    /* bit 110 */
+       u32 : 12;
+       u32 util_str : 1;/* bit 123 */
 } __packed;
 
 extern struct css_general_char css_general_characteristics;
index 7df48e5cf36f71252ec4e3f9fd51565b71bea345..35bf28fe4c648b7208d73dcd14abba7d0b6168a2 100644 (file)
@@ -6,12 +6,10 @@
 
 #include <linux/types.h>
 
-extern int nospec_call_disable;
-extern int nospec_return_disable;
+extern int nospec_disable;
 
 void nospec_init_branches(void);
-void nospec_call_revert(s32 *start, s32 *end);
-void nospec_return_revert(s32 *start, s32 *end);
+void nospec_revert(s32 *start, s32 *end);
 
 #endif /* __ASSEMBLY__ */
 
index c7b4333d1de0aafe38ecccf606dde62053404be0..f0f9bcf94c03749b0f0030d9de5765cff1597d37 100644 (file)
@@ -151,4 +151,7 @@ void vmem_map_init(void);
 void *vmem_crst_alloc(unsigned long val);
 pte_t *vmem_pte_alloc(void);
 
+unsigned long base_asce_alloc(unsigned long addr, unsigned long num_pages);
+void base_asce_free(unsigned long asce);
+
 #endif /* _S390_PGALLOC_H */
index 79b7ffa91832746110ef1fa3541b1494513d71fe..c00f7b0316288d183d633c5b0c59eae0c2136489 100644 (file)
@@ -390,10 +390,10 @@ static inline int scsw_cmd_is_valid_key(union scsw *scsw)
 }
 
 /**
- * scsw_cmd_is_valid_sctl - check fctl field validity
+ * scsw_cmd_is_valid_sctl - check sctl field validity
  * @scsw: pointer to scsw
  *
- * Return non-zero if the fctl field of the specified command mode scsw is
+ * Return non-zero if the sctl field of the specified command mode scsw is
  * valid, zero otherwise.
  */
 static inline int scsw_cmd_is_valid_sctl(union scsw *scsw)
index 2eb0c8a7b664817759d325c34ec8e53a9bdd6981..124154fdfc9722d34f84ed293a9b6fc31fff370f 100644 (file)
@@ -25,7 +25,6 @@
 #define MACHINE_FLAG_DIAG44    _BITUL(6)
 #define MACHINE_FLAG_EDAT1     _BITUL(7)
 #define MACHINE_FLAG_EDAT2     _BITUL(8)
-#define MACHINE_FLAG_LPP       _BITUL(9)
 #define MACHINE_FLAG_TOPOLOGY  _BITUL(10)
 #define MACHINE_FLAG_TE                _BITUL(11)
 #define MACHINE_FLAG_TLB_LC    _BITUL(12)
@@ -66,7 +65,6 @@ extern void detect_memory_memblock(void);
 #define MACHINE_HAS_DIAG44     (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG44)
 #define MACHINE_HAS_EDAT1      (S390_lowcore.machine_flags & MACHINE_FLAG_EDAT1)
 #define MACHINE_HAS_EDAT2      (S390_lowcore.machine_flags & MACHINE_FLAG_EDAT2)
-#define MACHINE_HAS_LPP                (S390_lowcore.machine_flags & MACHINE_FLAG_LPP)
 #define MACHINE_HAS_TOPOLOGY   (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY)
 #define MACHINE_HAS_TE         (S390_lowcore.machine_flags & MACHINE_FLAG_TE)
 #define MACHINE_HAS_TLB_LC     (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_LC)
index 451c601406b6901e6fd71fddaecbf431eb6715fd..832be5c2584f7018e814c8e5b4c691062c9b5e46 100644 (file)
@@ -68,25 +68,27 @@ typedef struct dasd_information2_t {
 #define DASD_FORMAT_CDL  2
 /*
  * values to be used for dasd_information_t.features
- * 0x00: default features
- * 0x01: readonly (ro)
- * 0x02: use diag discipline (diag)
- * 0x04: set the device initially online (internal use only)
- * 0x08: enable ERP related logging
- * 0x10: allow I/O to fail on lost paths
- * 0x20: allow I/O to fail when a lock was stolen
- * 0x40: give access to raw eckd data
- * 0x80: enable discard support
+ * 0x100: default features
+ * 0x001: readonly (ro)
+ * 0x002: use diag discipline (diag)
+ * 0x004: set the device initially online (internal use only)
+ * 0x008: enable ERP related logging
+ * 0x010: allow I/O to fail on lost paths
+ * 0x020: allow I/O to fail when a lock was stolen
+ * 0x040: give access to raw eckd data
+ * 0x080: enable discard support
+ * 0x100: enable autodisable for IFCC errors (default)
  */
-#define DASD_FEATURE_DEFAULT        0x00
-#define DASD_FEATURE_READONLY       0x01
-#define DASD_FEATURE_USEDIAG        0x02
-#define DASD_FEATURE_INITIAL_ONLINE  0x04
-#define DASD_FEATURE_ERPLOG         0x08
-#define DASD_FEATURE_FAILFAST       0x10
-#define DASD_FEATURE_FAILONSLCK      0x20
-#define DASD_FEATURE_USERAW         0x40
-#define DASD_FEATURE_DISCARD        0x80
+#define DASD_FEATURE_READONLY        0x001
+#define DASD_FEATURE_USEDIAG         0x002
+#define DASD_FEATURE_INITIAL_ONLINE   0x004
+#define DASD_FEATURE_ERPLOG          0x008
+#define DASD_FEATURE_FAILFAST        0x010
+#define DASD_FEATURE_FAILONSLCK       0x020
+#define DASD_FEATURE_USERAW          0x040
+#define DASD_FEATURE_DISCARD         0x080
+#define DASD_FEATURE_PATH_AUTODISABLE 0x100
+#define DASD_FEATURE_DEFAULT         DASD_FEATURE_PATH_AUTODISABLE
 
 #define DASD_PARTN_BITS 2
 
index 7f27e3da9709c1a968bff98f9750d551a7d53bcc..b06a6f79c1ec3b3dba230e6c7a3eadf8e935beea 100644 (file)
@@ -61,11 +61,11 @@ obj-y       += debug.o irq.o ipl.o dis.o diag.o vdso.o als.o
 obj-y  += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o
 obj-y  += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o
 obj-y  += entry.o reipl.o relocate_kernel.o kdebugfs.o alternative.o
+obj-y  += nospec-branch.o
 
 extra-y                                += head.o head64.o vmlinux.lds
 
-obj-$(CONFIG_EXPOLINE)         += nospec-branch.o
-CFLAGS_REMOVE_expoline.o       += $(CC_FLAGS_EXPOLINE)
+CFLAGS_REMOVE_nospec-branch.o  += $(CC_FLAGS_EXPOLINE)
 
 obj-$(CONFIG_MODULES)          += module.o
 obj-$(CONFIG_SMP)              += smp.o
index 22476135f738440a56372b6ac065f7c9cb8b3930..8e1f2aee85efd42df367201e12635c2a7174d587 100644 (file)
@@ -2,6 +2,7 @@
 #include <linux/module.h>
 #include <asm/alternative.h>
 #include <asm/facility.h>
+#include <asm/nospec-branch.h>
 
 #define MAX_PATCH_LEN (255 - 1)
 
@@ -15,29 +16,6 @@ static int __init disable_alternative_instructions(char *str)
 
 early_param("noaltinstr", disable_alternative_instructions);
 
-static int __init nobp_setup_early(char *str)
-{
-       bool enabled;
-       int rc;
-
-       rc = kstrtobool(str, &enabled);
-       if (rc)
-               return rc;
-       if (enabled && test_facility(82))
-               __set_facility(82, S390_lowcore.alt_stfle_fac_list);
-       else
-               __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
-       return 0;
-}
-early_param("nobp", nobp_setup_early);
-
-static int __init nospec_setup_early(char *str)
-{
-       __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
-       return 0;
-}
-early_param("nospec", nospec_setup_early);
-
 struct brcl_insn {
        u16 opc;
        s32 disp;
index 587b195b588dd092ab5ff73fbccd31993ba895d7..cfe2c45c518039e2128c115e1cb98fceba271cf2 100644 (file)
@@ -63,6 +63,7 @@ int main(void)
        OFFSET(__SF_SIE_CONTROL, stack_frame, empty1[0]);
        OFFSET(__SF_SIE_SAVEAREA, stack_frame, empty1[1]);
        OFFSET(__SF_SIE_REASON, stack_frame, empty1[2]);
+       OFFSET(__SF_SIE_FLAGS, stack_frame, empty1[3]);
        BLANK();
        /* timeval/timezone offsets for use by vdso */
        OFFSET(__VDSO_UPD_COUNT, vdso_data, tb_update_count);
index ac707a9f729eac9cd095ac521e9e2dfc9e0673de..b00b515baa53103cb85d21ae15ba0fd119f59664 100644 (file)
@@ -67,7 +67,7 @@ static noinline __init void init_kernel_storage_key(void)
 #if PAGE_DEFAULT_KEY
        unsigned long end_pfn, init_pfn;
 
-       end_pfn = PFN_UP(__pa(&_end));
+       end_pfn = PFN_UP(__pa(_end));
 
        for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++)
                page_set_storage_key(init_pfn << PAGE_SHIFT,
@@ -242,8 +242,6 @@ static __init void detect_machine_facilities(void)
                S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT2;
        if (test_facility(3))
                S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
-       if (test_facility(40))
-               S390_lowcore.machine_flags |= MACHINE_FLAG_LPP;
        if (test_facility(50) && test_facility(73)) {
                S390_lowcore.machine_flags |= MACHINE_FLAG_TE;
                __ctl_set_bit(0, 55);
index a5621ea6d1234f1c3b6c8aec8612bd933f5cc6f3..3f22f139a0413f6880118998bbbb48f280bdad5f 100644 (file)
@@ -11,6 +11,7 @@
 
 #include <linux/init.h>
 #include <linux/linkage.h>
+#include <asm/alternative-asm.h>
 #include <asm/processor.h>
 #include <asm/cache.h>
 #include <asm/ctl_reg.h>
@@ -57,6 +58,8 @@ _CIF_WORK     = (_CIF_MCCK_PENDING | _CIF_ASCE_PRIMARY | \
                   _CIF_ASCE_SECONDARY | _CIF_FPU)
 _PIF_WORK      = (_PIF_PER_TRAP | _PIF_SYSCALL_RESTART)
 
+_LPP_OFFSET    = __LC_LPP
+
 #define BASED(name) name-cleanup_critical(%r13)
 
        .macro  TRACE_IRQS_ON
@@ -162,65 +165,22 @@ _PIF_WORK = (_PIF_PER_TRAP | _PIF_SYSCALL_RESTART)
        .endm
 
        .macro BPOFF
-       .pushsection .altinstr_replacement, "ax"
-660:   .long   0xb2e8c000
-       .popsection
-661:   .long   0x47000000
-       .pushsection .altinstructions, "a"
-       .long 661b - .
-       .long 660b - .
-       .word 82
-       .byte 4
-       .byte 4
-       .popsection
+       ALTERNATIVE "", ".long 0xb2e8c000", 82
        .endm
 
        .macro BPON
-       .pushsection .altinstr_replacement, "ax"
-662:   .long   0xb2e8d000
-       .popsection
-663:   .long   0x47000000
-       .pushsection .altinstructions, "a"
-       .long 663b - .
-       .long 662b - .
-       .word 82
-       .byte 4
-       .byte 4
-       .popsection
+       ALTERNATIVE "", ".long 0xb2e8d000", 82
        .endm
 
        .macro BPENTER tif_ptr,tif_mask
-       .pushsection .altinstr_replacement, "ax"
-662:   .word   0xc004, 0x0000, 0x0000  # 6 byte nop
-       .word   0xc004, 0x0000, 0x0000  # 6 byte nop
-       .popsection
-664:   TSTMSK  \tif_ptr,\tif_mask
-       jz      . + 8
-       .long   0xb2e8d000
-       .pushsection .altinstructions, "a"
-       .long 664b - .
-       .long 662b - .
-       .word 82
-       .byte 12
-       .byte 12
-       .popsection
+       ALTERNATIVE "TSTMSK \tif_ptr,\tif_mask; jz .+8; .long 0xb2e8d000", \
+                   "", 82
        .endm
 
        .macro BPEXIT tif_ptr,tif_mask
        TSTMSK  \tif_ptr,\tif_mask
-       .pushsection .altinstr_replacement, "ax"
-662:   jnz     . + 8
-       .long   0xb2e8d000
-       .popsection
-664:   jz      . + 8
-       .long   0xb2e8c000
-       .pushsection .altinstructions, "a"
-       .long 664b - .
-       .long 662b - .
-       .word 82
-       .byte 8
-       .byte 8
-       .popsection
+       ALTERNATIVE "jz .+8;  .long 0xb2e8c000", \
+                   "jnz .+8; .long 0xb2e8d000", 82
        .endm
 
 #ifdef CONFIG_EXPOLINE
@@ -323,10 +283,8 @@ ENTRY(__switch_to)
        aghi    %r3,__TASK_pid
        mvc     __LC_CURRENT_PID(4,%r0),0(%r3)  # store pid of next
        lmg     %r6,%r15,__SF_GPRS(%r15)        # load gprs of next task
-       TSTMSK  __LC_MACHINE_FLAGS,MACHINE_FLAG_LPP
-       jz      0f
-       .insn   s,0xb2800000,__LC_LPP           # set program parameter
-0:     BR_R1USE_R14
+       ALTERNATIVE "", ".insn s,0xb2800000,_LPP_OFFSET", 40
+       BR_R1USE_R14
 
 .L__critical_start:
 
@@ -339,10 +297,10 @@ ENTRY(__switch_to)
 ENTRY(sie64a)
        stmg    %r6,%r14,__SF_GPRS(%r15)        # save kernel registers
        lg      %r12,__LC_CURRENT
-       stg     %r2,__SF_EMPTY(%r15)            # save control block pointer
-       stg     %r3,__SF_EMPTY+8(%r15)          # save guest register save area
-       xc      __SF_EMPTY+16(8,%r15),__SF_EMPTY+16(%r15) # reason code = 0
-       mvc     __SF_EMPTY+24(8,%r15),__TI_flags(%r12) # copy thread flags
+       stg     %r2,__SF_SIE_CONTROL(%r15)      # save control block pointer
+       stg     %r3,__SF_SIE_SAVEAREA(%r15)     # save guest register save area
+       xc      __SF_SIE_REASON(8,%r15),__SF_SIE_REASON(%r15) # reason code = 0
+       mvc     __SF_SIE_FLAGS(8,%r15),__TI_flags(%r12) # copy thread flags
        TSTMSK  __LC_CPU_FLAGS,_CIF_FPU         # load guest fp/vx registers ?
        jno     .Lsie_load_guest_gprs
        brasl   %r14,load_fpu_regs              # load guest fp/vx regs
@@ -353,18 +311,18 @@ ENTRY(sie64a)
        jz      .Lsie_gmap
        lctlg   %c1,%c1,__GMAP_ASCE(%r14)       # load primary asce
 .Lsie_gmap:
-       lg      %r14,__SF_EMPTY(%r15)           # get control block pointer
+       lg      %r14,__SF_SIE_CONTROL(%r15)     # get control block pointer
        oi      __SIE_PROG0C+3(%r14),1          # we are going into SIE now
        tm      __SIE_PROG20+3(%r14),3          # last exit...
        jnz     .Lsie_skip
        TSTMSK  __LC_CPU_FLAGS,_CIF_FPU
        jo      .Lsie_skip                      # exit if fp/vx regs changed
-       BPEXIT  __SF_EMPTY+24(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
+       BPEXIT  __SF_SIE_FLAGS(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
 .Lsie_entry:
        sie     0(%r14)
 .Lsie_exit:
        BPOFF
-       BPENTER __SF_EMPTY+24(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
+       BPENTER __SF_SIE_FLAGS(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
 .Lsie_skip:
        ni      __SIE_PROG0C+3(%r14),0xfe       # no longer in SIE
        lctlg   %c1,%c1,__LC_USER_ASCE          # load primary asce
@@ -383,7 +341,7 @@ ENTRY(sie64a)
        nopr    7
        .globl sie_exit
 sie_exit:
-       lg      %r14,__SF_EMPTY+8(%r15)         # load guest register save area
+       lg      %r14,__SF_SIE_SAVEAREA(%r15)    # load guest register save area
        stmg    %r0,%r13,0(%r14)                # save guest gprs 0-13
        xgr     %r0,%r0                         # clear guest registers to
        xgr     %r1,%r1                         # prevent speculative use
@@ -392,11 +350,11 @@ sie_exit:
        xgr     %r4,%r4
        xgr     %r5,%r5
        lmg     %r6,%r14,__SF_GPRS(%r15)        # restore kernel registers
-       lg      %r2,__SF_EMPTY+16(%r15)         # return exit reason code
+       lg      %r2,__SF_SIE_REASON(%r15)       # return exit reason code
        BR_R1USE_R14
 .Lsie_fault:
        lghi    %r14,-EFAULT
-       stg     %r14,__SF_EMPTY+16(%r15)        # set exit reason code
+       stg     %r14,__SF_SIE_REASON(%r15)      # set exit reason code
        j       sie_exit
 
        EX_TABLE(.Lrewind_pad6,.Lsie_fault)
@@ -685,7 +643,7 @@ ENTRY(pgm_check_handler)
        slg     %r14,BASED(.Lsie_critical_start)
        clg     %r14,BASED(.Lsie_critical_length)
        jhe     0f
-       lg      %r14,__SF_EMPTY(%r15)           # get control block pointer
+       lg      %r14,__SF_SIE_CONTROL(%r15)     # get control block pointer
        ni      __SIE_PROG0C+3(%r14),0xfe       # no longer in SIE
        lctlg   %c1,%c1,__LC_USER_ASCE          # load primary asce
        larl    %r9,sie_exit                    # skip forward to sie_exit
@@ -1285,10 +1243,8 @@ ENTRY(mcck_int_handler)
 # PSW restart interrupt handler
 #
 ENTRY(restart_int_handler)
-       TSTMSK  __LC_MACHINE_FLAGS,MACHINE_FLAG_LPP
-       jz      0f
-       .insn   s,0xb2800000,__LC_LPP
-0:     stg     %r15,__LC_SAVE_AREA_RESTART
+       ALTERNATIVE "", ".insn s,0xb2800000,_LPP_OFFSET", 40
+       stg     %r15,__LC_SAVE_AREA_RESTART
        lg      %r15,__LC_RESTART_STACK
        aghi    %r15,-__PT_SIZE                 # create pt_regs on stack
        xc      0(__PT_SIZE,%r15),0(%r15)
@@ -1397,8 +1353,8 @@ cleanup_critical:
        clg     %r9,BASED(.Lsie_crit_mcck_length)
        jh      1f
        oi      __LC_CPU_FLAGS+7, _CIF_MCCK_GUEST
-1:     BPENTER __SF_EMPTY+24(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
-       lg      %r9,__SF_EMPTY(%r15)            # get control block pointer
+1:     BPENTER __SF_SIE_FLAGS(%r15),(_TIF_ISOLATE_BP|_TIF_ISOLATE_BP_GUEST)
+       lg      %r9,__SF_SIE_CONTROL(%r15)      # get control block pointer
        ni      __SIE_PROG0C+3(%r9),0xfe        # no longer in SIE
        lctlg   %c1,%c1,__LC_USER_ASCE          # load primary asce
        larl    %r9,sie_exit                    # skip forward to sie_exit
index 1fc6d1ff92d3ec2d564d151b4c8355d08a1714c4..5a83be955c70ed8f9a378ec6a47c61933c2968d9 100644 (file)
@@ -159,7 +159,7 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
        me->core_layout.size += me->arch.got_size;
        me->arch.plt_offset = me->core_layout.size;
        if (me->arch.plt_size) {
-               if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_call_disable)
+               if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable)
                        me->arch.plt_size += PLT_ENTRY_SIZE;
                me->core_layout.size += me->arch.plt_size;
        }
@@ -318,8 +318,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
                                info->plt_offset;
                        ip[0] = 0x0d10e310;     /* basr 1,0  */
                        ip[1] = 0x100a0004;     /* lg   1,10(1) */
-                       if (IS_ENABLED(CONFIG_EXPOLINE) &&
-                           !nospec_call_disable) {
+                       if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable) {
                                unsigned int *ij;
                                ij = me->core_layout.base +
                                        me->arch.plt_offset +
@@ -440,7 +439,7 @@ int module_finalize(const Elf_Ehdr *hdr,
        void *aseg;
 
        if (IS_ENABLED(CONFIG_EXPOLINE) &&
-           !nospec_call_disable && me->arch.plt_size) {
+           !nospec_disable && me->arch.plt_size) {
                unsigned int *ij;
 
                ij = me->core_layout.base + me->arch.plt_offset +
@@ -467,11 +466,11 @@ int module_finalize(const Elf_Ehdr *hdr,
 
                if (IS_ENABLED(CONFIG_EXPOLINE) &&
                    (!strcmp(".nospec_call_table", secname)))
-                       nospec_call_revert(aseg, aseg + s->sh_size);
+                       nospec_revert(aseg, aseg + s->sh_size);
 
                if (IS_ENABLED(CONFIG_EXPOLINE) &&
                    (!strcmp(".nospec_return_table", secname)))
-                       nospec_return_revert(aseg, aseg + s->sh_size);
+                       nospec_revert(aseg, aseg + s->sh_size);
        }
 
        jump_label_apply_nops(me);
index 9aff72d3abda3148a0a6decb358713e6369a43a7..14867ec5f72629c3da78500a969b5d17424a719c 100644 (file)
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/module.h>
+#include <linux/device.h>
 #include <asm/nospec-branch.h>
 
-int nospec_call_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
-int nospec_return_disable = !IS_ENABLED(CONFIG_EXPOLINE_FULL);
+static int __init nobp_setup_early(char *str)
+{
+       bool enabled;
+       int rc;
+
+       rc = kstrtobool(str, &enabled);
+       if (rc)
+               return rc;
+       if (enabled && test_facility(82)) {
+               /*
+                * The user explicitely requested nobp=1, enable it and
+                * disable the expoline support.
+                */
+               __set_facility(82, S390_lowcore.alt_stfle_fac_list);
+               if (IS_ENABLED(CONFIG_EXPOLINE))
+                       nospec_disable = 1;
+       } else {
+               __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+       }
+       return 0;
+}
+early_param("nobp", nobp_setup_early);
+
+static int __init nospec_setup_early(char *str)
+{
+       __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+       return 0;
+}
+early_param("nospec", nospec_setup_early);
+
+static int __init nospec_report(void)
+{
+       if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
+               pr_info("Spectre V2 mitigation: execute trampolines.\n");
+       if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
+               pr_info("Spectre V2 mitigation: limited branch prediction.\n");
+       return 0;
+}
+arch_initcall(nospec_report);
+
+#ifdef CONFIG_SYSFS
+ssize_t cpu_show_spectre_v1(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       return sprintf(buf, "Mitigation: __user pointer sanitization\n");
+}
+
+ssize_t cpu_show_spectre_v2(struct device *dev,
+                           struct device_attribute *attr, char *buf)
+{
+       if (IS_ENABLED(CC_USING_EXPOLINE) && !nospec_disable)
+               return sprintf(buf, "Mitigation: execute trampolines\n");
+       if (__test_facility(82, S390_lowcore.alt_stfle_fac_list))
+               return sprintf(buf, "Mitigation: limited branch prediction.\n");
+       return sprintf(buf, "Vulnerable\n");
+}
+#endif
+
+#ifdef CONFIG_EXPOLINE
+
+int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
 
 static int __init nospectre_v2_setup_early(char *str)
 {
-       nospec_call_disable = 1;
-       nospec_return_disable = 1;
+       nospec_disable = 1;
        return 0;
 }
 early_param("nospectre_v2", nospectre_v2_setup_early);
 
+static int __init spectre_v2_auto_early(void)
+{
+       if (IS_ENABLED(CC_USING_EXPOLINE)) {
+               /*
+                * The kernel has been compiled with expolines.
+                * Keep expolines enabled and disable nobp.
+                */
+               nospec_disable = 0;
+               __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
+       }
+       /*
+        * If the kernel has not been compiled with expolines the
+        * nobp setting decides what is done, this depends on the
+        * CONFIG_KERNEL_NP option and the nobp/nospec parameters.
+        */
+       return 0;
+}
+#ifdef CONFIG_EXPOLINE_AUTO
+early_initcall(spectre_v2_auto_early);
+#endif
+
 static int __init spectre_v2_setup_early(char *str)
 {
        if (str && !strncmp(str, "on", 2)) {
-               nospec_call_disable = 0;
-               nospec_return_disable = 0;
-       }
-       if (str && !strncmp(str, "off", 3)) {
-               nospec_call_disable = 1;
-               nospec_return_disable = 1;
-       }
-       if (str && !strncmp(str, "auto", 4)) {
-               nospec_call_disable = 0;
-               nospec_return_disable = 1;
+               nospec_disable = 0;
+               __clear_facility(82, S390_lowcore.alt_stfle_fac_list);
        }
+       if (str && !strncmp(str, "off", 3))
+               nospec_disable = 1;
+       if (str && !strncmp(str, "auto", 4))
+               spectre_v2_auto_early();
        return 0;
 }
 early_param("spectre_v2", spectre_v2_setup_early);
@@ -79,15 +155,9 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end)
        }
 }
 
-void __init_or_module nospec_call_revert(s32 *start, s32 *end)
-{
-       if (nospec_call_disable)
-               __nospec_revert(start, end);
-}
-
-void __init_or_module nospec_return_revert(s32 *start, s32 *end)
+void __init_or_module nospec_revert(s32 *start, s32 *end)
 {
-       if (nospec_return_disable)
+       if (nospec_disable)
                __nospec_revert(start, end);
 }
 
@@ -95,6 +165,8 @@ extern s32 __nospec_call_start[], __nospec_call_end[];
 extern s32 __nospec_return_start[], __nospec_return_end[];
 void __init nospec_init_branches(void)
 {
-       nospec_call_revert(__nospec_call_start, __nospec_call_end);
-       nospec_return_revert(__nospec_return_start, __nospec_return_end);
+       nospec_revert(__nospec_call_start, __nospec_call_end);
+       nospec_revert(__nospec_return_start, __nospec_return_end);
 }
+
+#endif /* CONFIG_EXPOLINE */
index a6a91f01a17a3846fe9ae87e0dc26834dca5fee3..7b58a712f818c83f9be25a8a0357fc461a12300b 100644 (file)
@@ -221,6 +221,8 @@ static void __init conmode_default(void)
                SET_CONSOLE_SCLP;
 #endif
        }
+       if (IS_ENABLED(CONFIG_VT) && IS_ENABLED(CONFIG_DUMMY_CONSOLE))
+               conswitchp = &dummy_con;
 }
 
 #ifdef CONFIG_CRASH_DUMP
@@ -413,12 +415,12 @@ static void __init setup_resources(void)
        struct memblock_region *reg;
        int j;
 
-       code_resource.start = (unsigned long) &_text;
-       code_resource.end = (unsigned long) &_etext - 1;
-       data_resource.start = (unsigned long) &_etext;
-       data_resource.end = (unsigned long) &_edata - 1;
-       bss_resource.start = (unsigned long) &__bss_start;
-       bss_resource.end = (unsigned long) &__bss_stop - 1;
+       code_resource.start = (unsigned long) _text;
+       code_resource.end = (unsigned long) _etext - 1;
+       data_resource.start = (unsigned long) _etext;
+       data_resource.end = (unsigned long) _edata - 1;
+       bss_resource.start = (unsigned long) __bss_start;
+       bss_resource.end = (unsigned long) __bss_stop - 1;
 
        for_each_memblock(memory, reg) {
                res = memblock_virt_alloc(sizeof(*res), 8);
@@ -667,7 +669,7 @@ static void __init check_initrd(void)
  */
 static void __init reserve_kernel(void)
 {
-       unsigned long start_pfn = PFN_UP(__pa(&_end));
+       unsigned long start_pfn = PFN_UP(__pa(_end));
 
 #ifdef CONFIG_DMA_API_DEBUG
        /*
@@ -888,9 +890,9 @@ void __init setup_arch(char **cmdline_p)
 
        /* Is init_mm really needed? */
        init_mm.start_code = PAGE_OFFSET;
-       init_mm.end_code = (unsigned long) &_etext;
-       init_mm.end_data = (unsigned long) &_edata;
-       init_mm.brk = (unsigned long) &_end;
+       init_mm.end_code = (unsigned long) _etext;
+       init_mm.end_data = (unsigned long) _edata;
+       init_mm.brk = (unsigned long) _end;
 
        parse_early_param();
 #ifdef CONFIG_CRASH_DUMP
index ce329c876d8c16d8405d07cd320d0ebd0cbaccdd..75b7b307946e876eeeea80992fe6212ed613f464 100644 (file)
@@ -153,8 +153,8 @@ int pfn_is_nosave(unsigned long pfn)
 {
        unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin));
        unsigned long nosave_end_pfn = PFN_DOWN(__pa(&__nosave_end));
-       unsigned long end_rodata_pfn = PFN_DOWN(__pa(&__end_rodata)) - 1;
-       unsigned long stext_pfn = PFN_DOWN(__pa(&_stext));
+       unsigned long end_rodata_pfn = PFN_DOWN(__pa(__end_rodata)) - 1;
+       unsigned long stext_pfn = PFN_DOWN(__pa(_stext));
 
        /* Always save lowcore pages (LC protection might be enabled). */
        if (pfn <= LC_PAGES)
index 507f23ba2034b7cc48bdfef14b304e358e5e8615..7cdea2ec51e96c2b9ccfc2157dcc49f51ac99ed3 100644 (file)
@@ -24,8 +24,8 @@ enum address_markers_idx {
 
 static struct addr_marker address_markers[] = {
        [IDENTITY_NR]     = {0, "Identity Mapping"},
-       [KERNEL_START_NR] = {(unsigned long)&_stext, "Kernel Image Start"},
-       [KERNEL_END_NR]   = {(unsigned long)&_end, "Kernel Image End"},
+       [KERNEL_START_NR] = {(unsigned long)_stext, "Kernel Image Start"},
+       [KERNEL_END_NR]   = {(unsigned long)_end, "Kernel Image End"},
        [VMEMMAP_NR]      = {0, "vmemmap Area"},
        [VMALLOC_NR]      = {0, "vmalloc Area"},
        [MODULES_NR]      = {0, "Modules Area"},
index cb364153c43ce204b8736fe1758e7bac8fda2a69..562f729559567e084fade524cbfa386c51cb1283 100644 (file)
@@ -6,8 +6,9 @@
  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
  */
 
-#include <linux/mm.h>
 #include <linux/sysctl.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
 #include <asm/mmu_context.h>
 #include <asm/pgalloc.h>
 #include <asm/gmap.h>
@@ -366,3 +367,293 @@ void tlb_remove_table(struct mmu_gather *tlb, void *table)
        if ((*batch)->nr == MAX_TABLE_BATCH)
                tlb_flush_mmu(tlb);
 }
+
+/*
+ * Base infrastructure required to generate basic asces, region, segment,
+ * and page tables that do not make use of enhanced features like EDAT1.
+ */
+
+static struct kmem_cache *base_pgt_cache;
+
+static unsigned long base_pgt_alloc(void)
+{
+       u64 *table;
+
+       table = kmem_cache_alloc(base_pgt_cache, GFP_KERNEL);
+       if (table)
+               memset64(table, _PAGE_INVALID, PTRS_PER_PTE);
+       return (unsigned long) table;
+}
+
+static void base_pgt_free(unsigned long table)
+{
+       kmem_cache_free(base_pgt_cache, (void *) table);
+}
+
+static unsigned long base_crst_alloc(unsigned long val)
+{
+       unsigned long table;
+
+       table =  __get_free_pages(GFP_KERNEL, CRST_ALLOC_ORDER);
+       if (table)
+               crst_table_init((unsigned long *)table, val);
+       return table;
+}
+
+static void base_crst_free(unsigned long table)
+{
+       free_pages(table, CRST_ALLOC_ORDER);
+}
+
+#define BASE_ADDR_END_FUNC(NAME, SIZE)                                 \
+static inline unsigned long base_##NAME##_addr_end(unsigned long addr, \
+                                                  unsigned long end)   \
+{                                                                      \
+       unsigned long next = (addr + (SIZE)) & ~((SIZE) - 1);           \
+                                                                       \
+       return (next - 1) < (end - 1) ? next : end;                     \
+}
+
+BASE_ADDR_END_FUNC(page,    _PAGE_SIZE)
+BASE_ADDR_END_FUNC(segment, _SEGMENT_SIZE)
+BASE_ADDR_END_FUNC(region3, _REGION3_SIZE)
+BASE_ADDR_END_FUNC(region2, _REGION2_SIZE)
+BASE_ADDR_END_FUNC(region1, _REGION1_SIZE)
+
+static inline unsigned long base_lra(unsigned long address)
+{
+       unsigned long real;
+
+       asm volatile(
+               "       lra     %0,0(%1)\n"
+               : "=d" (real) : "a" (address) : "cc");
+       return real;
+}
+
+static int base_page_walk(unsigned long origin, unsigned long addr,
+                         unsigned long end, int alloc)
+{
+       unsigned long *pte, next;
+
+       if (!alloc)
+               return 0;
+       pte = (unsigned long *) origin;
+       pte += (addr & _PAGE_INDEX) >> _PAGE_SHIFT;
+       do {
+               next = base_page_addr_end(addr, end);
+               *pte = base_lra(addr);
+       } while (pte++, addr = next, addr < end);
+       return 0;
+}
+
+static int base_segment_walk(unsigned long origin, unsigned long addr,
+                            unsigned long end, int alloc)
+{
+       unsigned long *ste, next, table;
+       int rc;
+
+       ste = (unsigned long *) origin;
+       ste += (addr & _SEGMENT_INDEX) >> _SEGMENT_SHIFT;
+       do {
+               next = base_segment_addr_end(addr, end);
+               if (*ste & _SEGMENT_ENTRY_INVALID) {
+                       if (!alloc)
+                               continue;
+                       table = base_pgt_alloc();
+                       if (!table)
+                               return -ENOMEM;
+                       *ste = table | _SEGMENT_ENTRY;
+               }
+               table = *ste & _SEGMENT_ENTRY_ORIGIN;
+               rc = base_page_walk(table, addr, next, alloc);
+               if (rc)
+                       return rc;
+               if (!alloc)
+                       base_pgt_free(table);
+               cond_resched();
+       } while (ste++, addr = next, addr < end);
+       return 0;
+}
+
+static int base_region3_walk(unsigned long origin, unsigned long addr,
+                            unsigned long end, int alloc)
+{
+       unsigned long *rtte, next, table;
+       int rc;
+
+       rtte = (unsigned long *) origin;
+       rtte += (addr & _REGION3_INDEX) >> _REGION3_SHIFT;
+       do {
+               next = base_region3_addr_end(addr, end);
+               if (*rtte & _REGION_ENTRY_INVALID) {
+                       if (!alloc)
+                               continue;
+                       table = base_crst_alloc(_SEGMENT_ENTRY_EMPTY);
+                       if (!table)
+                               return -ENOMEM;
+                       *rtte = table | _REGION3_ENTRY;
+               }
+               table = *rtte & _REGION_ENTRY_ORIGIN;
+               rc = base_segment_walk(table, addr, next, alloc);
+               if (rc)
+                       return rc;
+               if (!alloc)
+                       base_crst_free(table);
+       } while (rtte++, addr = next, addr < end);
+       return 0;
+}
+
+static int base_region2_walk(unsigned long origin, unsigned long addr,
+                            unsigned long end, int alloc)
+{
+       unsigned long *rste, next, table;
+       int rc;
+
+       rste = (unsigned long *) origin;
+       rste += (addr & _REGION2_INDEX) >> _REGION2_SHIFT;
+       do {
+               next = base_region2_addr_end(addr, end);
+               if (*rste & _REGION_ENTRY_INVALID) {
+                       if (!alloc)
+                               continue;
+                       table = base_crst_alloc(_REGION3_ENTRY_EMPTY);
+                       if (!table)
+                               return -ENOMEM;
+                       *rste = table | _REGION2_ENTRY;
+               }
+               table = *rste & _REGION_ENTRY_ORIGIN;
+               rc = base_region3_walk(table, addr, next, alloc);
+               if (rc)
+                       return rc;
+               if (!alloc)
+                       base_crst_free(table);
+       } while (rste++, addr = next, addr < end);
+       return 0;
+}
+
+static int base_region1_walk(unsigned long origin, unsigned long addr,
+                            unsigned long end, int alloc)
+{
+       unsigned long *rfte, next, table;
+       int rc;
+
+       rfte = (unsigned long *) origin;
+       rfte += (addr & _REGION1_INDEX) >> _REGION1_SHIFT;
+       do {
+               next = base_region1_addr_end(addr, end);
+               if (*rfte & _REGION_ENTRY_INVALID) {
+                       if (!alloc)
+                               continue;
+                       table = base_crst_alloc(_REGION2_ENTRY_EMPTY);
+                       if (!table)
+                               return -ENOMEM;
+                       *rfte = table | _REGION1_ENTRY;
+               }
+               table = *rfte & _REGION_ENTRY_ORIGIN;
+               rc = base_region2_walk(table, addr, next, alloc);
+               if (rc)
+                       return rc;
+               if (!alloc)
+                       base_crst_free(table);
+       } while (rfte++, addr = next, addr < end);
+       return 0;
+}
+
+/**
+ * base_asce_free - free asce and tables returned from base_asce_alloc()
+ * @asce: asce to be freed
+ *
+ * Frees all region, segment, and page tables that were allocated with a
+ * corresponding base_asce_alloc() call.
+ */
+void base_asce_free(unsigned long asce)
+{
+       unsigned long table = asce & _ASCE_ORIGIN;
+
+       if (!asce)
+               return;
+       switch (asce & _ASCE_TYPE_MASK) {
+       case _ASCE_TYPE_SEGMENT:
+               base_segment_walk(table, 0, _REGION3_SIZE, 0);
+               break;
+       case _ASCE_TYPE_REGION3:
+               base_region3_walk(table, 0, _REGION2_SIZE, 0);
+               break;
+       case _ASCE_TYPE_REGION2:
+               base_region2_walk(table, 0, _REGION1_SIZE, 0);
+               break;
+       case _ASCE_TYPE_REGION1:
+               base_region1_walk(table, 0, -_PAGE_SIZE, 0);
+               break;
+       }
+       base_crst_free(table);
+}
+
+static int base_pgt_cache_init(void)
+{
+       static DEFINE_MUTEX(base_pgt_cache_mutex);
+       unsigned long sz = _PAGE_TABLE_SIZE;
+
+       if (base_pgt_cache)
+               return 0;
+       mutex_lock(&base_pgt_cache_mutex);
+       if (!base_pgt_cache)
+               base_pgt_cache = kmem_cache_create("base_pgt", sz, sz, 0, NULL);
+       mutex_unlock(&base_pgt_cache_mutex);
+       return base_pgt_cache ? 0 : -ENOMEM;
+}
+
+/**
+ * base_asce_alloc - create kernel mapping without enhanced DAT features
+ * @addr: virtual start address of kernel mapping
+ * @num_pages: number of consecutive pages
+ *
+ * Generate an asce, including all required region, segment and page tables,
+ * that can be used to access the virtual kernel mapping. The difference is
+ * that the returned asce does not make use of any enhanced DAT features like
+ * e.g. large pages. This is required for some I/O functions that pass an
+ * asce, like e.g. some service call requests.
+ *
+ * Note: the returned asce may NEVER be attached to any cpu. It may only be
+ *      used for I/O requests. tlb entries that might result because the
+ *      asce was attached to a cpu won't be cleared.
+ */
+unsigned long base_asce_alloc(unsigned long addr, unsigned long num_pages)
+{
+       unsigned long asce, table, end;
+       int rc;
+
+       if (base_pgt_cache_init())
+               return 0;
+       end = addr + num_pages * PAGE_SIZE;
+       if (end <= _REGION3_SIZE) {
+               table = base_crst_alloc(_SEGMENT_ENTRY_EMPTY);
+               if (!table)
+                       return 0;
+               rc = base_segment_walk(table, addr, end, 1);
+               asce = table | _ASCE_TYPE_SEGMENT | _ASCE_TABLE_LENGTH;
+       } else if (end <= _REGION2_SIZE) {
+               table = base_crst_alloc(_REGION3_ENTRY_EMPTY);
+               if (!table)
+                       return 0;
+               rc = base_region3_walk(table, addr, end, 1);
+               asce = table | _ASCE_TYPE_REGION3 | _ASCE_TABLE_LENGTH;
+       } else if (end <= _REGION1_SIZE) {
+               table = base_crst_alloc(_REGION2_ENTRY_EMPTY);
+               if (!table)
+                       return 0;
+               rc = base_region2_walk(table, addr, end, 1);
+               asce = table | _ASCE_TYPE_REGION2 | _ASCE_TABLE_LENGTH;
+       } else {
+               table = base_crst_alloc(_REGION1_ENTRY_EMPTY);
+               if (!table)
+                       return 0;
+               rc = base_region1_walk(table, addr, end, 1);
+               asce = table | _ASCE_TYPE_REGION1 | _ASCE_TABLE_LENGTH;
+       }
+       if (rc) {
+               base_asce_free(asce);
+               asce = 0;
+       }
+       return asce;
+}
index 9e80a953d6933ea05668636b6abcd444eff72b0a..c43a9e28995eb2f03911504fe125523e86ccd107 100644 (file)
@@ -369,20 +369,40 @@ err_len:
        return -EINVAL;
 }
 
-/**
- * tmp_transmit - Internal kernel interface to transmit TPM commands.
- *
- * @chip: TPM chip to use
- * @buf: TPM command buffer
- * @bufsiz: length of the TPM command buffer
- * @flags: tpm transmit flags - bitmap
- *
- * Return:
- *     0 when the operation is successful.
- *     A negative number for system errors (errno).
- */
-ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
-                    u8 *buf, size_t bufsiz, unsigned int flags)
+static int tpm_request_locality(struct tpm_chip *chip)
+{
+       int rc;
+
+       if (!chip->ops->request_locality)
+               return 0;
+
+       rc = chip->ops->request_locality(chip, 0);
+       if (rc < 0)
+               return rc;
+
+       chip->locality = rc;
+
+       return 0;
+}
+
+static void tpm_relinquish_locality(struct tpm_chip *chip)
+{
+       int rc;
+
+       if (!chip->ops->relinquish_locality)
+               return;
+
+       rc = chip->ops->relinquish_locality(chip, chip->locality);
+       if (rc)
+               dev_err(&chip->dev, "%s: : error %d\n", __func__, rc);
+
+       chip->locality = -1;
+}
+
+static ssize_t tpm_try_transmit(struct tpm_chip *chip,
+                               struct tpm_space *space,
+                               u8 *buf, size_t bufsiz,
+                               unsigned int flags)
 {
        struct tpm_output_header *header = (void *)buf;
        int rc;
@@ -422,8 +442,6 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
        if (!(flags & TPM_TRANSMIT_UNLOCKED))
                mutex_lock(&chip->tpm_mutex);
 
-       if (chip->dev.parent)
-               pm_runtime_get_sync(chip->dev.parent);
 
        if (chip->ops->clk_enable != NULL)
                chip->ops->clk_enable(chip, true);
@@ -431,19 +449,20 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
        /* Store the decision as chip->locality will be changed. */
        need_locality = chip->locality == -1;
 
-       if (!(flags & TPM_TRANSMIT_RAW) &&
-           need_locality && chip->ops->request_locality)  {
-               rc = chip->ops->request_locality(chip, 0);
+       if (!(flags & TPM_TRANSMIT_RAW) && need_locality) {
+               rc = tpm_request_locality(chip);
                if (rc < 0)
                        goto out_no_locality;
-               chip->locality = rc;
        }
 
+       if (chip->dev.parent)
+               pm_runtime_get_sync(chip->dev.parent);
+
        rc = tpm2_prepare_space(chip, space, ordinal, buf);
        if (rc)
                goto out;
 
-       rc = chip->ops->send(chip, (u8 *) buf, count);
+       rc = chip->ops->send(chip, buf, count);
        if (rc < 0) {
                if (rc != -EPIPE)
                        dev_err(&chip->dev,
@@ -480,7 +499,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
        goto out;
 
 out_recv:
-       len = chip->ops->recv(chip, (u8 *) buf, bufsiz);
+       len = chip->ops->recv(chip, buf, bufsiz);
        if (len < 0) {
                rc = len;
                dev_err(&chip->dev,
@@ -499,27 +518,95 @@ out_recv:
        rc = tpm2_commit_space(chip, space, ordinal, buf, &len);
 
 out:
-       if (need_locality && chip->ops->relinquish_locality) {
-               chip->ops->relinquish_locality(chip, chip->locality);
-               chip->locality = -1;
-       }
+       if (chip->dev.parent)
+               pm_runtime_put_sync(chip->dev.parent);
+
+       if (need_locality)
+               tpm_relinquish_locality(chip);
+
 out_no_locality:
        if (chip->ops->clk_enable != NULL)
                chip->ops->clk_enable(chip, false);
 
-       if (chip->dev.parent)
-               pm_runtime_put_sync(chip->dev.parent);
-
        if (!(flags & TPM_TRANSMIT_UNLOCKED))
                mutex_unlock(&chip->tpm_mutex);
        return rc ? rc : len;
 }
 
 /**
- * tmp_transmit_cmd - send a tpm command to the device
+ * tpm_transmit - Internal kernel interface to transmit TPM commands.
+ *
+ * @chip: TPM chip to use
+ * @space: tpm space
+ * @buf: TPM command buffer
+ * @bufsiz: length of the TPM command buffer
+ * @flags: tpm transmit flags - bitmap
+ *
+ * A wrapper around tpm_try_transmit that handles TPM2_RC_RETRY
+ * returns from the TPM and retransmits the command after a delay up
+ * to a maximum wait of TPM2_DURATION_LONG.
+ *
+ * Note: TPM1 never returns TPM2_RC_RETRY so the retry logic is TPM2
+ * only
+ *
+ * Return:
+ *     the length of the return when the operation is successful.
+ *     A negative number for system errors (errno).
+ */
+ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
+                    u8 *buf, size_t bufsiz, unsigned int flags)
+{
+       struct tpm_output_header *header = (struct tpm_output_header *)buf;
+       /* space for header and handles */
+       u8 save[TPM_HEADER_SIZE + 3*sizeof(u32)];
+       unsigned int delay_msec = TPM2_DURATION_SHORT;
+       u32 rc = 0;
+       ssize_t ret;
+       const size_t save_size = min(space ? sizeof(save) : TPM_HEADER_SIZE,
+                                    bufsiz);
+       /* the command code is where the return code will be */
+       u32 cc = be32_to_cpu(header->return_code);
+
+       /*
+        * Subtlety here: if we have a space, the handles will be
+        * transformed, so when we restore the header we also have to
+        * restore the handles.
+        */
+       memcpy(save, buf, save_size);
+
+       for (;;) {
+               ret = tpm_try_transmit(chip, space, buf, bufsiz, flags);
+               if (ret < 0)
+                       break;
+               rc = be32_to_cpu(header->return_code);
+               if (rc != TPM2_RC_RETRY && rc != TPM2_RC_TESTING)
+                       break;
+               /*
+                * return immediately if self test returns test
+                * still running to shorten boot time.
+                */
+               if (rc == TPM2_RC_TESTING && cc == TPM2_CC_SELF_TEST)
+                       break;
+               delay_msec *= 2;
+               if (delay_msec > TPM2_DURATION_LONG) {
+                       if (rc == TPM2_RC_RETRY)
+                               dev_err(&chip->dev, "in retry loop\n");
+                       else
+                               dev_err(&chip->dev,
+                                       "self test is still running\n");
+                       break;
+               }
+               tpm_msleep(delay_msec);
+               memcpy(buf, save, save_size);
+       }
+       return ret;
+}
+/**
+ * tpm_transmit_cmd - send a tpm command to the device
  *    The function extracts tpm out header return code
  *
  * @chip: TPM chip to use
+ * @space: tpm space
  * @buf: TPM command buffer
  * @bufsiz: length of the buffer
  * @min_rsp_body_length: minimum expected length of response body
@@ -532,7 +619,7 @@ out_no_locality:
  *     A positive number for a TPM error.
  */
 ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
-                        const void *buf, size_t bufsiz,
+                        void *buf, size_t bufsiz,
                         size_t min_rsp_body_length, unsigned int flags,
                         const char *desc)
 {
@@ -540,7 +627,7 @@ ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
        int err;
        ssize_t len;
 
-       len = tpm_transmit(chip, space, (u8 *)buf, bufsiz, flags);
+       len = tpm_transmit(chip, space, buf, bufsiz, flags);
        if (len <  0)
                return len;
 
@@ -666,6 +753,8 @@ int tpm_get_timeouts(struct tpm_chip *chip)
                    msecs_to_jiffies(TPM2_DURATION_MEDIUM);
                chip->duration[TPM_LONG] =
                    msecs_to_jiffies(TPM2_DURATION_LONG);
+               chip->duration[TPM_LONG_LONG] =
+                   msecs_to_jiffies(TPM2_DURATION_LONG_LONG);
 
                chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
                return 0;
@@ -754,6 +843,7 @@ int tpm_get_timeouts(struct tpm_chip *chip)
                usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
        chip->duration[TPM_LONG] =
                usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
+       chip->duration[TPM_LONG_LONG] = 0; /* not used under 1.2 */
 
        /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
         * value wrong and apparently reports msecs rather than usecs. So we
@@ -969,6 +1059,10 @@ int tpm_do_selftest(struct tpm_chip *chip)
        loops = jiffies_to_msecs(duration) / delay_msec;
 
        rc = tpm_continue_selftest(chip);
+       if (rc == TPM_ERR_INVALID_POSTINIT) {
+               chip->flags |= TPM_CHIP_FLAG_ALWAYS_POWERED;
+               dev_info(&chip->dev, "TPM not ready (%d)\n", rc);
+       }
        /* This may fail if there was no TPM driver during a suspend/resume
         * cycle; some may return 10 (BAD_ORDINAL), others 28 (FAILEDSELFTEST)
         */
index f895fba4e20d5fd26c82675d3aceb07ab7c0327c..7f2d0f489e9c0a95bc530abfb55306faf716e9db 100644 (file)
@@ -67,7 +67,9 @@ enum tpm_duration {
        TPM_SHORT = 0,
        TPM_MEDIUM = 1,
        TPM_LONG = 2,
+       TPM_LONG_LONG = 3,
        TPM_UNDEFINED,
+       TPM_NUM_DURATIONS = TPM_UNDEFINED,
 };
 
 #define TPM_WARN_RETRY          0x800
@@ -79,15 +81,20 @@ enum tpm_duration {
 #define TPM_HEADER_SIZE                10
 
 enum tpm2_const {
-       TPM2_PLATFORM_PCR       = 24,
-       TPM2_PCR_SELECT_MIN     = ((TPM2_PLATFORM_PCR + 7) / 8),
-       TPM2_TIMEOUT_A          = 750,
-       TPM2_TIMEOUT_B          = 2000,
-       TPM2_TIMEOUT_C          = 200,
-       TPM2_TIMEOUT_D          = 30,
-       TPM2_DURATION_SHORT     = 20,
-       TPM2_DURATION_MEDIUM    = 750,
-       TPM2_DURATION_LONG      = 2000,
+       TPM2_PLATFORM_PCR       =     24,
+       TPM2_PCR_SELECT_MIN     = ((TPM2_PLATFORM_PCR + 7) / 8),
+};
+
+enum tpm2_timeouts {
+       TPM2_TIMEOUT_A          =    750,
+       TPM2_TIMEOUT_B          =   2000,
+       TPM2_TIMEOUT_C          =    200,
+       TPM2_TIMEOUT_D          =     30,
+       TPM2_DURATION_SHORT     =     20,
+       TPM2_DURATION_MEDIUM    =    750,
+       TPM2_DURATION_LONG      =   2000,
+       TPM2_DURATION_LONG_LONG = 300000,
+       TPM2_DURATION_DEFAULT   = 120000,
 };
 
 enum tpm2_structures {
@@ -104,10 +111,12 @@ enum tpm2_return_codes {
        TPM2_RC_HASH            = 0x0083, /* RC_FMT1 */
        TPM2_RC_HANDLE          = 0x008B,
        TPM2_RC_INITIALIZE      = 0x0100, /* RC_VER1 */
+       TPM2_RC_FAILURE         = 0x0101,
        TPM2_RC_DISABLED        = 0x0120,
        TPM2_RC_COMMAND_CODE    = 0x0143,
        TPM2_RC_TESTING         = 0x090A, /* RC_WARN */
        TPM2_RC_REFERENCE_H0    = 0x0910,
+       TPM2_RC_RETRY           = 0x0922,
 };
 
 enum tpm2_algorithms {
@@ -123,6 +132,7 @@ enum tpm2_algorithms {
 
 enum tpm2_command_codes {
        TPM2_CC_FIRST           = 0x011F,
+       TPM2_CC_CREATE_PRIMARY  = 0x0131,
        TPM2_CC_SELF_TEST       = 0x0143,
        TPM2_CC_STARTUP         = 0x0144,
        TPM2_CC_SHUTDOWN        = 0x0145,
@@ -227,7 +237,7 @@ struct tpm_chip {
        unsigned long timeout_c; /* jiffies */
        unsigned long timeout_d; /* jiffies */
        bool timeout_adjusted;
-       unsigned long duration[3]; /* jiffies */
+       unsigned long duration[TPM_NUM_DURATIONS]; /* jiffies */
        bool duration_adjusted;
 
        struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES];
@@ -506,7 +516,7 @@ enum tpm_transmit_flags {
 ssize_t tpm_transmit(struct tpm_chip *chip, struct tpm_space *space,
                     u8 *buf, size_t bufsiz, unsigned int flags);
 ssize_t tpm_transmit_cmd(struct tpm_chip *chip, struct tpm_space *space,
-                        const void *buf, size_t bufsiz,
+                        void *buf, size_t bufsiz,
                         size_t min_rsp_body_length, unsigned int flags,
                         const char *desc);
 int tpm_startup(struct tpm_chip *chip);
index a700f8f9ead797df39de4b5ef946fb5c2c65c95b..96c77c8e7f402818fc629d10e115bd882937a9d6 100644 (file)
@@ -31,10 +31,6 @@ struct tpm2_startup_in {
        __be16  startup_type;
 } __packed;
 
-struct tpm2_self_test_in {
-       u8      full_test;
-} __packed;
-
 struct tpm2_get_tpm_pt_in {
        __be32  cap_id;
        __be32  property_id;
@@ -60,7 +56,6 @@ struct tpm2_get_random_out {
 
 union tpm2_cmd_params {
        struct  tpm2_startup_in         startup_in;
-       struct  tpm2_self_test_in       selftest_in;
        struct  tpm2_get_tpm_pt_in      get_tpm_pt_in;
        struct  tpm2_get_tpm_pt_out     get_tpm_pt_out;
        struct  tpm2_get_random_in      getrandom_in;
@@ -90,6 +85,8 @@ static struct tpm2_hash tpm2_hash_map[] = {
  * of time the chip could take to return the result. The values
  * of the SHORT, MEDIUM, and LONG durations are taken from the
  * PC Client Profile (PTP) specification.
+ * LONG_LONG is for commands that generates keys which empirically
+ * takes longer time on some systems.
  */
 static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
        TPM_UNDEFINED,          /* 11F */
@@ -110,7 +107,7 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
        TPM_UNDEFINED,          /* 12e */
        TPM_UNDEFINED,          /* 12f */
        TPM_UNDEFINED,          /* 130 */
-       TPM_UNDEFINED,          /* 131 */
+       TPM_LONG_LONG,          /* 131 */
        TPM_UNDEFINED,          /* 132 */
        TPM_UNDEFINED,          /* 133 */
        TPM_UNDEFINED,          /* 134 */
@@ -144,7 +141,7 @@ static const u8 tpm2_ordinal_duration[TPM2_CC_LAST - TPM2_CC_FIRST + 1] = {
        TPM_UNDEFINED,          /* 150 */
        TPM_UNDEFINED,          /* 151 */
        TPM_UNDEFINED,          /* 152 */
-       TPM_UNDEFINED,          /* 153 */
+       TPM_LONG_LONG,          /* 153 */
        TPM_UNDEFINED,          /* 154 */
        TPM_UNDEFINED,          /* 155 */
        TPM_UNDEFINED,          /* 156 */
@@ -821,22 +818,12 @@ unsigned long tpm2_calc_ordinal_duration(struct tpm_chip *chip, u32 ordinal)
                duration = chip->duration[index];
 
        if (duration <= 0)
-               duration = 2 * 60 * HZ;
+               duration = msecs_to_jiffies(TPM2_DURATION_DEFAULT);
 
        return duration;
 }
 EXPORT_SYMBOL_GPL(tpm2_calc_ordinal_duration);
 
-#define TPM2_SELF_TEST_IN_SIZE \
-       (sizeof(struct tpm_input_header) + \
-        sizeof(struct tpm2_self_test_in))
-
-static const struct tpm_input_header tpm2_selftest_header = {
-       .tag = cpu_to_be16(TPM2_ST_NO_SESSIONS),
-       .length = cpu_to_be32(TPM2_SELF_TEST_IN_SIZE),
-       .ordinal = cpu_to_be32(TPM2_CC_SELF_TEST)
-};
-
 /**
  * tpm2_do_selftest() - ensure that all self tests have passed
  *
@@ -852,27 +839,24 @@ static const struct tpm_input_header tpm2_selftest_header = {
  */
 static int tpm2_do_selftest(struct tpm_chip *chip)
 {
+       struct tpm_buf buf;
+       int full;
        int rc;
-       unsigned int delay_msec = 10;
-       long duration;
-       struct tpm2_cmd cmd;
 
-       duration = jiffies_to_msecs(
-               tpm2_calc_ordinal_duration(chip, TPM2_CC_SELF_TEST));
-
-       while (1) {
-               cmd.header.in = tpm2_selftest_header;
-               cmd.params.selftest_in.full_test = 0;
-
-               rc = tpm_transmit_cmd(chip, NULL, &cmd, TPM2_SELF_TEST_IN_SIZE,
-                                     0, 0, "continue selftest");
+       for (full = 0; full < 2; full++) {
+               rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_SELF_TEST);
+               if (rc)
+                       return rc;
 
-               if (rc != TPM2_RC_TESTING || delay_msec >= duration)
-                       break;
+               tpm_buf_append_u8(&buf, full);
+               rc = tpm_transmit_cmd(chip, NULL, buf.data, PAGE_SIZE, 0, 0,
+                                     "attempting the self test");
+               tpm_buf_destroy(&buf);
 
-               /* wait longer than before */
-               delay_msec *= 2;
-               tpm_msleep(delay_msec);
+               if (rc == TPM2_RC_TESTING)
+                       rc = TPM2_RC_SUCCESS;
+               if (rc == TPM2_RC_INITIALIZE || rc == TPM2_RC_SUCCESS)
+                       return rc;
        }
 
        return rc;
@@ -1058,10 +1042,8 @@ int tpm2_auto_startup(struct tpm_chip *chip)
                goto out;
 
        rc = tpm2_do_selftest(chip);
-       if (rc != 0 && rc != TPM2_RC_INITIALIZE) {
-               dev_err(&chip->dev, "TPM self test failed\n");
+       if (rc && rc != TPM2_RC_INITIALIZE)
                goto out;
-       }
 
        if (rc == TPM2_RC_INITIALIZE) {
                rc = tpm_startup(chip);
@@ -1069,10 +1051,8 @@ int tpm2_auto_startup(struct tpm_chip *chip)
                        goto out;
 
                rc = tpm2_do_selftest(chip);
-               if (rc) {
-                       dev_err(&chip->dev, "TPM self test failed\n");
+               if (rc)
                        goto out;
-               }
        }
 
        rc = tpm2_get_pcr_allocation(chip);
index 7b3c2a8aa9defc33e4189658e797dbecdb38149f..7f78482cd157b263226261b4ff7166df283c56af 100644 (file)
@@ -112,6 +112,25 @@ struct tpm2_crb_smc {
        u32 smc_func_id;
 };
 
+static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
+                               unsigned long timeout)
+{
+       ktime_t start;
+       ktime_t stop;
+
+       start = ktime_get();
+       stop = ktime_add(start, ms_to_ktime(timeout));
+
+       do {
+               if ((ioread32(reg) & mask) == value)
+                       return true;
+
+               usleep_range(50, 100);
+       } while (ktime_before(ktime_get(), stop));
+
+       return ((ioread32(reg) & mask) == value);
+}
+
 /**
  * crb_go_idle - request tpm crb device to go the idle state
  *
@@ -128,7 +147,7 @@ struct tpm2_crb_smc {
  *
  * Return: 0 always
  */
-static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
+static int crb_go_idle(struct device *dev, struct crb_priv *priv)
 {
        if ((priv->sm == ACPI_TPM2_START_METHOD) ||
            (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
@@ -136,30 +155,17 @@ static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
                return 0;
 
        iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->regs_t->ctrl_req);
-       /* we don't really care when this settles */
 
+       if (!crb_wait_for_reg_32(&priv->regs_t->ctrl_req,
+                                CRB_CTRL_REQ_GO_IDLE/* mask */,
+                                0, /* value */
+                                TPM2_TIMEOUT_C)) {
+               dev_warn(dev, "goIdle timed out\n");
+               return -ETIME;
+       }
        return 0;
 }
 
-static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
-                               unsigned long timeout)
-{
-       ktime_t start;
-       ktime_t stop;
-
-       start = ktime_get();
-       stop = ktime_add(start, ms_to_ktime(timeout));
-
-       do {
-               if ((ioread32(reg) & mask) == value)
-                       return true;
-
-               usleep_range(50, 100);
-       } while (ktime_before(ktime_get(), stop));
-
-       return false;
-}
-
 /**
  * crb_cmd_ready - request tpm crb device to enter ready state
  *
@@ -175,8 +181,7 @@ static bool crb_wait_for_reg_32(u32 __iomem *reg, u32 mask, u32 value,
  *
  * Return: 0 on success -ETIME on timeout;
  */
-static int __maybe_unused crb_cmd_ready(struct device *dev,
-                                       struct crb_priv *priv)
+static int crb_cmd_ready(struct device *dev, struct crb_priv *priv)
 {
        if ((priv->sm == ACPI_TPM2_START_METHOD) ||
            (priv->sm == ACPI_TPM2_COMMAND_BUFFER_WITH_START_METHOD) ||
@@ -195,11 +200,11 @@ static int __maybe_unused crb_cmd_ready(struct device *dev,
        return 0;
 }
 
-static int crb_request_locality(struct tpm_chip *chip, int loc)
+static int __crb_request_locality(struct device *dev,
+                                 struct crb_priv *priv, int loc)
 {
-       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
        u32 value = CRB_LOC_STATE_LOC_ASSIGNED |
-               CRB_LOC_STATE_TPM_REG_VALID_STS;
+                   CRB_LOC_STATE_TPM_REG_VALID_STS;
 
        if (!priv->regs_h)
                return 0;
@@ -207,21 +212,45 @@ static int crb_request_locality(struct tpm_chip *chip, int loc)
        iowrite32(CRB_LOC_CTRL_REQUEST_ACCESS, &priv->regs_h->loc_ctrl);
        if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, value, value,
                                 TPM2_TIMEOUT_C)) {
-               dev_warn(&chip->dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
+               dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
                return -ETIME;
        }
 
        return 0;
 }
 
-static void crb_relinquish_locality(struct tpm_chip *chip, int loc)
+static int crb_request_locality(struct tpm_chip *chip, int loc)
 {
        struct crb_priv *priv = dev_get_drvdata(&chip->dev);
 
+       return __crb_request_locality(&chip->dev, priv, loc);
+}
+
+static int __crb_relinquish_locality(struct device *dev,
+                                    struct crb_priv *priv, int loc)
+{
+       u32 mask = CRB_LOC_STATE_LOC_ASSIGNED |
+                  CRB_LOC_STATE_TPM_REG_VALID_STS;
+       u32 value = CRB_LOC_STATE_TPM_REG_VALID_STS;
+
        if (!priv->regs_h)
-               return;
+               return 0;
 
        iowrite32(CRB_LOC_CTRL_RELINQUISH, &priv->regs_h->loc_ctrl);
+       if (!crb_wait_for_reg_32(&priv->regs_h->loc_state, mask, value,
+                                TPM2_TIMEOUT_C)) {
+               dev_warn(dev, "TPM_LOC_STATE_x.requestAccess timed out\n");
+               return -ETIME;
+       }
+
+       return 0;
+}
+
+static int crb_relinquish_locality(struct tpm_chip *chip, int loc)
+{
+       struct crb_priv *priv = dev_get_drvdata(&chip->dev);
+
+       return __crb_relinquish_locality(&chip->dev, priv, loc);
 }
 
 static u8 crb_status(struct tpm_chip *chip)
@@ -442,6 +471,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
        u32 pa_high, pa_low;
        u64 cmd_pa;
        u32 cmd_size;
+       __le64 __rsp_pa;
        u64 rsp_pa;
        u32 rsp_size;
        int ret;
@@ -475,6 +505,10 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
                        dev_warn(dev, FW_BUG "Bad ACPI memory layout");
        }
 
+       ret = __crb_request_locality(dev, priv, 0);
+       if (ret)
+               return ret;
+
        priv->regs_t = crb_map_res(dev, priv, &io_res, buf->control_address,
                                   sizeof(struct crb_regs_tail));
        if (IS_ERR(priv->regs_t))
@@ -503,8 +537,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
                goto out;
        }
 
-       memcpy_fromio(&rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
-       rsp_pa = le64_to_cpu(rsp_pa);
+       memcpy_fromio(&__rsp_pa, &priv->regs_t->ctrl_rsp_pa, 8);
+       rsp_pa = le64_to_cpu(__rsp_pa);
        rsp_size = crb_fixup_cmd_size(dev, &io_res, rsp_pa,
                                      ioread32(&priv->regs_t->ctrl_rsp_size));
 
@@ -531,6 +565,8 @@ out:
 
        crb_go_idle(dev, priv);
 
+       __crb_relinquish_locality(dev, priv, 0);
+
        return ret;
 }
 
@@ -588,10 +624,14 @@ static int crb_acpi_add(struct acpi_device *device)
        chip->acpi_dev_handle = device->handle;
        chip->flags = TPM_CHIP_FLAG_TPM2;
 
-       rc  = crb_cmd_ready(dev, priv);
+       rc = __crb_request_locality(dev, priv, 0);
        if (rc)
                return rc;
 
+       rc  = crb_cmd_ready(dev, priv);
+       if (rc)
+               goto out;
+
        pm_runtime_get_noresume(dev);
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
@@ -601,12 +641,15 @@ static int crb_acpi_add(struct acpi_device *device)
                crb_go_idle(dev, priv);
                pm_runtime_put_noidle(dev);
                pm_runtime_disable(dev);
-               return rc;
+               goto out;
        }
 
-       pm_runtime_put(dev);
+       pm_runtime_put_sync(dev);
 
-       return 0;
+out:
+       __crb_relinquish_locality(dev, priv, 0);
+
+       return rc;
 }
 
 static int crb_acpi_remove(struct acpi_device *device)
index da074e3db19be5c895f80a403aabb992e9a14d29..5a1f47b43947c80ff3646f6787a4aadb7f224659 100644 (file)
@@ -143,11 +143,13 @@ static bool check_locality(struct tpm_chip *chip, int l)
        return false;
 }
 
-static void release_locality(struct tpm_chip *chip, int l)
+static int release_locality(struct tpm_chip *chip, int l)
 {
        struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
 
        tpm_tis_write8(priv, TPM_ACCESS(l), TPM_ACCESS_ACTIVE_LOCALITY);
+
+       return 0;
 }
 
 static int request_locality(struct tpm_chip *chip, int l)
index b5692a284bd82144dcdf79c6a66826324929e33a..04143c08bd6e8fe13f16b9536053f3c3b0c2d771 100644 (file)
@@ -3918,8 +3918,13 @@ static int dasd_generic_requeue_all_requests(struct dasd_device *device)
                        cqr = refers;
                }
 
-               if (cqr->block)
-                       list_del_init(&cqr->blocklist);
+               /*
+                * _dasd_requeue_request already checked for a valid
+                * blockdevice, no need to check again
+                * all erp requests (cqr->refers) have a cqr->block
+                * pointer copy from the original cqr
+                */
+               list_del_init(&cqr->blocklist);
                cqr->block->base->discipline->free_cp(
                        cqr, (struct request *) cqr->callback_data);
        }
index ee14d8e45c971863c6aef252ecbadba64d365826..ee73b0607e47c7c6fc108aa6c7ffa05924ee3d74 100644 (file)
@@ -2214,15 +2214,28 @@ static void dasd_3990_erp_disable_path(struct dasd_device *device, __u8 lpum)
 {
        int pos = pathmask_to_pos(lpum);
 
+       if (!(device->features & DASD_FEATURE_PATH_AUTODISABLE)) {
+               dev_err(&device->cdev->dev,
+                       "Path %x.%02x (pathmask %02x) is operational despite excessive IFCCs\n",
+                       device->path[pos].cssid, device->path[pos].chpid, lpum);
+               goto out;
+       }
+
        /* no remaining path, cannot disable */
-       if (!(dasd_path_get_opm(device) & ~lpum))
-               return;
+       if (!(dasd_path_get_opm(device) & ~lpum)) {
+               dev_err(&device->cdev->dev,
+                       "Last path %x.%02x (pathmask %02x) is operational despite excessive IFCCs\n",
+                       device->path[pos].cssid, device->path[pos].chpid, lpum);
+               goto out;
+       }
 
        dev_err(&device->cdev->dev,
                "Path %x.%02x (pathmask %02x) is disabled - IFCC threshold exceeded\n",
                device->path[pos].cssid, device->path[pos].chpid, lpum);
        dasd_path_remove_opm(device, lpum);
        dasd_path_add_ifccpm(device, lpum);
+
+out:
        device->path[pos].errorclk = 0;
        atomic_set(&device->path[pos].error_count, 0);
 }
index e7cd28ff1984460540f70fd203326b08f2d5c8f9..b9ebb565ee2c70aa4672d539a4ac2d2236dc7fa8 100644 (file)
@@ -1550,9 +1550,49 @@ dasd_path_threshold_store(struct device *dev, struct device_attribute *attr,
        dasd_put_device(device);
        return count;
 }
-
 static DEVICE_ATTR(path_threshold, 0644, dasd_path_threshold_show,
                   dasd_path_threshold_store);
+
+/*
+ * configure if path is disabled after IFCC/CCC error threshold is
+ * exceeded
+ */
+static ssize_t
+dasd_path_autodisable_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct dasd_devmap *devmap;
+       int flag;
+
+       devmap = dasd_find_busid(dev_name(dev));
+       if (!IS_ERR(devmap))
+               flag = (devmap->features & DASD_FEATURE_PATH_AUTODISABLE) != 0;
+       else
+               flag = (DASD_FEATURE_DEFAULT &
+                       DASD_FEATURE_PATH_AUTODISABLE) != 0;
+       return snprintf(buf, PAGE_SIZE, flag ? "1\n" : "0\n");
+}
+
+static ssize_t
+dasd_path_autodisable_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       unsigned int val;
+       int rc;
+
+       if (kstrtouint(buf, 0, &val) || val > 1)
+               return -EINVAL;
+
+       rc = dasd_set_feature(to_ccwdev(dev),
+                             DASD_FEATURE_PATH_AUTODISABLE, val);
+
+       return rc ? : count;
+}
+
+static DEVICE_ATTR(path_autodisable, 0644,
+                  dasd_path_autodisable_show,
+                  dasd_path_autodisable_store);
 /*
  * interval for IFCC/CCC checks
  * meaning time with no IFCC/CCC error before the error counter
@@ -1623,6 +1663,7 @@ static struct attribute * dasd_attrs[] = {
        &dev_attr_host_access_count.attr,
        &dev_attr_path_masks.attr,
        &dev_attr_path_threshold.attr,
+       &dev_attr_path_autodisable.attr,
        &dev_attr_path_interval.attr,
        &dev_attr_path_reset.attr,
        &dev_attr_hpf.attr,
index 29397a9dba68a37e85b587618249270fa4ba188e..be208e7adcb46087e7fb2436fadf8a737d7c472e 100644 (file)
@@ -214,24 +214,25 @@ static void set_ch_t(struct ch_t *geo, __u32 cyl, __u8 head)
        geo->head |= head;
 }
 
-static int check_XRC(struct ccw1 *ccw, struct DE_eckd_data *data,
+static int set_timestamp(struct ccw1 *ccw, struct DE_eckd_data *data,
                     struct dasd_device *device)
 {
        struct dasd_eckd_private *private = device->private;
        int rc;
 
-       if (!private->rdc_data.facilities.XRC_supported)
+       rc = get_phys_clock(&data->ep_sys_time);
+       /*
+        * Ignore return code if XRC is not supported or
+        * sync clock is switched off
+        */
+       if ((rc && !private->rdc_data.facilities.XRC_supported) ||
+           rc == -EOPNOTSUPP || rc == -EACCES)
                return 0;
 
        /* switch on System Time Stamp - needed for XRC Support */
        data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid'   */
        data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
 
-       rc = get_phys_clock(&data->ep_sys_time);
-       /* Ignore return code if sync clock is switched off. */
-       if (rc == -EOPNOTSUPP || rc == -EACCES)
-               rc = 0;
-
        if (ccw) {
                ccw->count = sizeof(struct DE_eckd_data);
                ccw->flags |= CCW_FLAG_SLI;
@@ -286,12 +287,12 @@ define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
        case DASD_ECKD_CCW_WRITE_KD_MT:
                data->mask.perm = 0x02;
                data->attributes.operation = private->attrib.operation;
-               rc = check_XRC(ccw, data, device);
+               rc = set_timestamp(ccw, data, device);
                break;
        case DASD_ECKD_CCW_WRITE_CKD:
        case DASD_ECKD_CCW_WRITE_CKD_MT:
                data->attributes.operation = DASD_BYPASS_CACHE;
-               rc = check_XRC(ccw, data, device);
+               rc = set_timestamp(ccw, data, device);
                break;
        case DASD_ECKD_CCW_ERASE:
        case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
@@ -299,7 +300,7 @@ define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
                data->mask.perm = 0x3;
                data->mask.auth = 0x1;
                data->attributes.operation = DASD_BYPASS_CACHE;
-               rc = check_XRC(ccw, data, device);
+               rc = set_timestamp(ccw, data, device);
                break;
        case DASD_ECKD_CCW_WRITE_FULL_TRACK:
                data->mask.perm = 0x03;
@@ -310,7 +311,7 @@ define_extent(struct ccw1 *ccw, struct DE_eckd_data *data, unsigned int trk,
                data->mask.perm = 0x02;
                data->attributes.operation = private->attrib.operation;
                data->blk_size = blksize;
-               rc = check_XRC(ccw, data, device);
+               rc = set_timestamp(ccw, data, device);
                break;
        default:
                dev_err(&device->cdev->dev,
@@ -993,7 +994,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
        struct dasd_eckd_private *private, path_private;
        struct dasd_uid *uid;
        char print_path_uid[60], print_device_uid[60];
-       struct channel_path_desc *chp_desc;
+       struct channel_path_desc_fmt0 *chp_desc;
        struct subchannel_id sch_id;
 
        private = device->private;
@@ -3440,7 +3441,7 @@ static int prepare_itcw(struct itcw *itcw,
                dedata->mask.perm = 0x02;
                dedata->attributes.operation = basepriv->attrib.operation;
                dedata->blk_size = blksize;
-               rc = check_XRC(NULL, dedata, basedev);
+               rc = set_timestamp(NULL, dedata, basedev);
                dedata->ga_extended |= 0x42;
                lredata->operation.orientation = 0x0;
                lredata->operation.operation = 0x3F;
index a2b33a22c82a85407d7461346bc5b3169a466328..d049e2d744847d9c8676041ff1cac6033caea0d5 100644 (file)
@@ -23,7 +23,7 @@ CFLAGS_REMOVE_sclp_early_core.o       += $(CC_FLAGS_EXPOLINE)
 
 obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
         sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o sclp_ctl.o \
-        sclp_early.o sclp_early_core.o
+        sclp_early.o sclp_early_core.o sclp_sd.o
 
 obj-$(CONFIG_TN3270) += raw3270.o
 obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
index 98a5c459a1bf3a157d85c475824ec77dbd50b4ba..60845d467a1bc0b82601103ba2b675622086d697 100644 (file)
@@ -9,7 +9,9 @@
 #include <linux/kbd_kern.h>
 #include <linux/kbd_diacr.h>
 
-u_short plain_map[NR_KEYS] = {
+#include "keyboard.h"
+
+u_short ebc_plain_map[NR_KEYS] = {
        0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000,
        0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000,
        0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000, 0xf000,
@@ -85,12 +87,12 @@ static u_short shift_ctrl_map[NR_KEYS] = {
        0xf20a, 0xf108, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200,
 };
 
-ushort *key_maps[MAX_NR_KEYMAPS] = {
-       plain_map, shift_map, NULL, NULL,
+ushort *ebc_key_maps[MAX_NR_KEYMAPS] = {
+       ebc_plain_map, shift_map, NULL, NULL,
        ctrl_map, shift_ctrl_map, NULL,
 };
 
-unsigned int keymap_count = 4;
+unsigned int ebc_keymap_count = 4;
 
 
 /*
@@ -99,7 +101,7 @@ unsigned int keymap_count = 4;
  * the default and allocate dynamically in chunks of 512 bytes.
  */
 
-char func_buf[] = {
+char ebc_func_buf[] = {
        '\033', '[', '[', 'A', 0, 
        '\033', '[', '[', 'B', 0, 
        '\033', '[', '[', 'C', 0, 
@@ -123,37 +125,37 @@ char func_buf[] = {
 };
 
 
-char *funcbufptr = func_buf;
-int funcbufsize = sizeof(func_buf);
-int funcbufleft = 0;          /* space left */
-
-char *func_table[MAX_NR_FUNC] = {
-       func_buf + 0,
-       func_buf + 5,
-       func_buf + 10,
-       func_buf + 15,
-       func_buf + 20,
-       func_buf + 25,
-       func_buf + 31,
-       func_buf + 37,
-       func_buf + 43,
-       func_buf + 49,
-       func_buf + 55,
-       func_buf + 61,
-       func_buf + 67,
-       func_buf + 73,
-       func_buf + 79,
-       func_buf + 85,
-       func_buf + 91,
-       func_buf + 97,
-       func_buf + 103,
-       func_buf + 109,
+char *ebc_funcbufptr = ebc_func_buf;
+int ebc_funcbufsize = sizeof(ebc_func_buf);
+int ebc_funcbufleft;           /* space left */
+
+char *ebc_func_table[MAX_NR_FUNC] = {
+       ebc_func_buf + 0,
+       ebc_func_buf + 5,
+       ebc_func_buf + 10,
+       ebc_func_buf + 15,
+       ebc_func_buf + 20,
+       ebc_func_buf + 25,
+       ebc_func_buf + 31,
+       ebc_func_buf + 37,
+       ebc_func_buf + 43,
+       ebc_func_buf + 49,
+       ebc_func_buf + 55,
+       ebc_func_buf + 61,
+       ebc_func_buf + 67,
+       ebc_func_buf + 73,
+       ebc_func_buf + 79,
+       ebc_func_buf + 85,
+       ebc_func_buf + 91,
+       ebc_func_buf + 97,
+       ebc_func_buf + 103,
+       ebc_func_buf + 109,
        NULL,
 };
 
-struct kbdiacruc accent_table[MAX_DIACR] = {
+struct kbdiacruc ebc_accent_table[MAX_DIACR] = {
        {'^', 'c', 0003},       {'^', 'd', 0004},
        {'^', 'z', 0032},       {'^', 0012, 0000},
 };
 
-unsigned int accent_table_size = 4;
+unsigned int ebc_accent_table_size = 4;
index 5b505fdaedecbef53d964d3cfcf77a333e98059e..db1fbf9b00b5062795528541bcf5eb7d1cb14c9a 100644 (file)
@@ -54,24 +54,24 @@ kbd_alloc(void) {
        kbd = kzalloc(sizeof(struct kbd_data), GFP_KERNEL);
        if (!kbd)
                goto out;
-       kbd->key_maps = kzalloc(sizeof(key_maps), GFP_KERNEL);
+       kbd->key_maps = kzalloc(sizeof(ebc_key_maps), GFP_KERNEL);
        if (!kbd->key_maps)
                goto out_kbd;
-       for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
-               if (key_maps[i]) {
-                       kbd->key_maps[i] = kmemdup(key_maps[i],
+       for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
+               if (ebc_key_maps[i]) {
+                       kbd->key_maps[i] = kmemdup(ebc_key_maps[i],
                                                   sizeof(u_short) * NR_KEYS,
                                                   GFP_KERNEL);
                        if (!kbd->key_maps[i])
                                goto out_maps;
                }
        }
-       kbd->func_table = kzalloc(sizeof(func_table), GFP_KERNEL);
+       kbd->func_table = kzalloc(sizeof(ebc_func_table), GFP_KERNEL);
        if (!kbd->func_table)
                goto out_maps;
-       for (i = 0; i < ARRAY_SIZE(func_table); i++) {
-               if (func_table[i]) {
-                       kbd->func_table[i] = kstrdup(func_table[i],
+       for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++) {
+               if (ebc_func_table[i]) {
+                       kbd->func_table[i] = kstrdup(ebc_func_table[i],
                                                     GFP_KERNEL);
                        if (!kbd->func_table[i])
                                goto out_func;
@@ -81,22 +81,22 @@ kbd_alloc(void) {
                kzalloc(sizeof(fn_handler_fn *) * NR_FN_HANDLER, GFP_KERNEL);
        if (!kbd->fn_handler)
                goto out_func;
-       kbd->accent_table = kmemdup(accent_table,
+       kbd->accent_table = kmemdup(ebc_accent_table,
                                    sizeof(struct kbdiacruc) * MAX_DIACR,
                                    GFP_KERNEL);
        if (!kbd->accent_table)
                goto out_fn_handler;
-       kbd->accent_table_size = accent_table_size;
+       kbd->accent_table_size = ebc_accent_table_size;
        return kbd;
 
 out_fn_handler:
        kfree(kbd->fn_handler);
 out_func:
-       for (i = 0; i < ARRAY_SIZE(func_table); i++)
+       for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++)
                kfree(kbd->func_table[i]);
        kfree(kbd->func_table);
 out_maps:
-       for (i = 0; i < ARRAY_SIZE(key_maps); i++)
+       for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++)
                kfree(kbd->key_maps[i]);
        kfree(kbd->key_maps);
 out_kbd:
@@ -112,10 +112,10 @@ kbd_free(struct kbd_data *kbd)
 
        kfree(kbd->accent_table);
        kfree(kbd->fn_handler);
-       for (i = 0; i < ARRAY_SIZE(func_table); i++)
+       for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++)
                kfree(kbd->func_table[i]);
        kfree(kbd->func_table);
-       for (i = 0; i < ARRAY_SIZE(key_maps); i++)
+       for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++)
                kfree(kbd->key_maps[i]);
        kfree(kbd->key_maps);
        kfree(kbd);
@@ -131,7 +131,7 @@ kbd_ascebc(struct kbd_data *kbd, unsigned char *ascebc)
        int i, j, k;
 
        memset(ascebc, 0x40, 256);
-       for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
+       for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
                keymap = kbd->key_maps[i];
                if (!keymap)
                        continue;
@@ -158,7 +158,7 @@ kbd_ebcasc(struct kbd_data *kbd, unsigned char *ebcasc)
        int i, j, k;
 
        memset(ebcasc, ' ', 256);
-       for (i = 0; i < ARRAY_SIZE(key_maps); i++) {
+       for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
                keymap = kbd->key_maps[i];
                if (!keymap)
                        continue;
index a074d97116287735a49489a99c139bc2c2d94dbd..c467589c7f452fdfc2aeabd509aaddd6086d7713 100644 (file)
 
 struct kbd_data;
 
+extern int ebc_funcbufsize, ebc_funcbufleft;
+extern char *ebc_func_table[MAX_NR_FUNC];
+extern char ebc_func_buf[];
+extern char *ebc_funcbufptr;
+extern unsigned int ebc_keymap_count;
+
+extern struct kbdiacruc ebc_accent_table[];
+extern unsigned int ebc_accent_table_size;
+extern unsigned short *ebc_key_maps[MAX_NR_KEYMAPS];
+extern unsigned short ebc_plain_map[NR_KEYS];
+
 typedef void (fn_handler_fn)(struct kbd_data *);
 
 /*
index e4e2df7a478e36aba8ee63775195b4c97886ead8..e9aa71cdfc44e2fcf476cefff087605ecd7ba84b 100644 (file)
@@ -417,7 +417,7 @@ sclp_dispatch_evbufs(struct sccb_header *sccb)
                reg = NULL;
                list_for_each(l, &sclp_reg_list) {
                        reg = list_entry(l, struct sclp_register, list);
-                       if (reg->receive_mask & (1 << (32 - evbuf->type)))
+                       if (reg->receive_mask & SCLP_EVTYP_MASK(evbuf->type))
                                break;
                        else
                                reg = NULL;
@@ -618,9 +618,12 @@ struct sclp_statechangebuf {
        u16             _zeros : 12;
        u16             mask_length;
        u64             sclp_active_facility_mask;
-       sccb_mask_t     sclp_receive_mask;
-       sccb_mask_t     sclp_send_mask;
-       u32             read_data_function_mask;
+       u8              masks[2 * 1021 + 4];    /* variable length */
+       /*
+        * u8           sclp_receive_mask[mask_length];
+        * u8           sclp_send_mask[mask_length];
+        * u32          read_data_function_mask;
+        */
 } __attribute__((packed));
 
 
@@ -631,14 +634,14 @@ sclp_state_change_cb(struct evbuf_header *evbuf)
        unsigned long flags;
        struct sclp_statechangebuf *scbuf;
 
+       BUILD_BUG_ON(sizeof(struct sclp_statechangebuf) > PAGE_SIZE);
+
        scbuf = (struct sclp_statechangebuf *) evbuf;
-       if (scbuf->mask_length != sizeof(sccb_mask_t))
-               return;
        spin_lock_irqsave(&sclp_lock, flags);
        if (scbuf->validity_sclp_receive_mask)
-               sclp_receive_mask = scbuf->sclp_receive_mask;
+               sclp_receive_mask = sccb_get_recv_mask(scbuf);
        if (scbuf->validity_sclp_send_mask)
-               sclp_send_mask = scbuf->sclp_send_mask;
+               sclp_send_mask = sccb_get_send_mask(scbuf);
        spin_unlock_irqrestore(&sclp_lock, flags);
        if (scbuf->validity_sclp_active_facility_mask)
                sclp.facilities = scbuf->sclp_active_facility_mask;
@@ -748,7 +751,7 @@ EXPORT_SYMBOL(sclp_remove_processed);
 
 /* Prepare init mask request. Called while sclp_lock is locked. */
 static inline void
-__sclp_make_init_req(u32 receive_mask, u32 send_mask)
+__sclp_make_init_req(sccb_mask_t receive_mask, sccb_mask_t send_mask)
 {
        struct init_sccb *sccb;
 
@@ -761,12 +764,15 @@ __sclp_make_init_req(u32 receive_mask, u32 send_mask)
        sclp_init_req.callback = NULL;
        sclp_init_req.callback_data = NULL;
        sclp_init_req.sccb = sccb;
-       sccb->header.length = sizeof(struct init_sccb);
-       sccb->mask_length = sizeof(sccb_mask_t);
-       sccb->receive_mask = receive_mask;
-       sccb->send_mask = send_mask;
-       sccb->sclp_receive_mask = 0;
-       sccb->sclp_send_mask = 0;
+       sccb->header.length = sizeof(*sccb);
+       if (sclp_mask_compat_mode)
+               sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
+       else
+               sccb->mask_length = sizeof(sccb_mask_t);
+       sccb_set_recv_mask(sccb, receive_mask);
+       sccb_set_send_mask(sccb, send_mask);
+       sccb_set_sclp_recv_mask(sccb, 0);
+       sccb_set_sclp_send_mask(sccb, 0);
 }
 
 /* Start init mask request. If calculate is non-zero, calculate the mask as
@@ -822,8 +828,8 @@ sclp_init_mask(int calculate)
                    sccb->header.response_code == 0x20) {
                        /* Successful request */
                        if (calculate) {
-                               sclp_receive_mask = sccb->sclp_receive_mask;
-                               sclp_send_mask = sccb->sclp_send_mask;
+                               sclp_receive_mask = sccb_get_sclp_recv_mask(sccb);
+                               sclp_send_mask = sccb_get_sclp_send_mask(sccb);
                        } else {
                                sclp_receive_mask = 0;
                                sclp_send_mask = 0;
@@ -974,12 +980,18 @@ sclp_check_interface(void)
                irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL);
                spin_lock_irqsave(&sclp_lock, flags);
                del_timer(&sclp_request_timer);
-               if (sclp_init_req.status == SCLP_REQ_DONE &&
-                   sccb->header.response_code == 0x20) {
-                       rc = 0;
-                       break;
-               } else
-                       rc = -EBUSY;
+               rc = -EBUSY;
+               if (sclp_init_req.status == SCLP_REQ_DONE) {
+                       if (sccb->header.response_code == 0x20) {
+                               rc = 0;
+                               break;
+                       } else if (sccb->header.response_code == 0x74f0) {
+                               if (!sclp_mask_compat_mode) {
+                                       sclp_mask_compat_mode = true;
+                                       retry = 0;
+                               }
+                       }
+               }
        }
        unregister_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler);
        spin_unlock_irqrestore(&sclp_lock, flags);
index f41f6e2ca06328cbd36129605852bf552ec7b14a..1fe4918088e71d86e1b2e0dc2547ac4328af3cce 100644 (file)
@@ -18,7 +18,7 @@
 #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3)
 #define SCLP_CONSOLE_PAGES     6
 
-#define SCLP_EVTYP_MASK(T)     (1U << (32 - (T)))
+#define SCLP_EVTYP_MASK(T) (1UL << (sizeof(sccb_mask_t) * BITS_PER_BYTE - (T)))
 
 #define EVTYP_OPCMD            0x01
 #define EVTYP_MSG              0x02
@@ -28,6 +28,7 @@
 #define EVTYP_PMSGCMD          0x09
 #define EVTYP_ASYNC            0x0A
 #define EVTYP_CTLPROGIDENT     0x0B
+#define EVTYP_STORE_DATA       0x0C
 #define EVTYP_ERRNOTIFY                0x18
 #define EVTYP_VT220MSG         0x1A
 #define EVTYP_SDIAS            0x1C
@@ -42,6 +43,7 @@
 #define EVTYP_PMSGCMD_MASK     SCLP_EVTYP_MASK(EVTYP_PMSGCMD)
 #define EVTYP_ASYNC_MASK       SCLP_EVTYP_MASK(EVTYP_ASYNC)
 #define EVTYP_CTLPROGIDENT_MASK        SCLP_EVTYP_MASK(EVTYP_CTLPROGIDENT)
+#define EVTYP_STORE_DATA_MASK  SCLP_EVTYP_MASK(EVTYP_STORE_DATA)
 #define EVTYP_ERRNOTIFY_MASK   SCLP_EVTYP_MASK(EVTYP_ERRNOTIFY)
 #define EVTYP_VT220MSG_MASK    SCLP_EVTYP_MASK(EVTYP_VT220MSG)
 #define EVTYP_SDIAS_MASK       SCLP_EVTYP_MASK(EVTYP_SDIAS)
@@ -85,7 +87,7 @@ enum sclp_pm_event {
 #define SCLP_PANIC_PRIO                1
 #define SCLP_PANIC_PRIO_CLIENT 0
 
-typedef u32 sccb_mask_t;       /* ATTENTION: assumes 32bit mask !!! */
+typedef u64 sccb_mask_t;
 
 struct sccb_header {
        u16     length;
@@ -98,12 +100,53 @@ struct init_sccb {
        struct sccb_header header;
        u16 _reserved;
        u16 mask_length;
-       sccb_mask_t receive_mask;
-       sccb_mask_t send_mask;
-       sccb_mask_t sclp_receive_mask;
-       sccb_mask_t sclp_send_mask;
+       u8 masks[4 * 1021];     /* variable length */
+       /*
+        * u8 receive_mask[mask_length];
+        * u8 send_mask[mask_length];
+        * u8 sclp_receive_mask[mask_length];
+        * u8 sclp_send_mask[mask_length];
+        */
 } __attribute__((packed));
 
+#define SCLP_MASK_SIZE_COMPAT 4
+
+static inline sccb_mask_t sccb_get_mask(u8 *masks, size_t len, int i)
+{
+       sccb_mask_t res = 0;
+
+       memcpy(&res, masks + i * len, min(sizeof(res), len));
+       return res;
+}
+
+static inline void sccb_set_mask(u8 *masks, size_t len, int i, sccb_mask_t val)
+{
+       memset(masks + i * len, 0, len);
+       memcpy(masks + i * len, &val, min(sizeof(val), len));
+}
+
+#define sccb_get_generic_mask(sccb, i)                                 \
+({                                                                     \
+       __typeof__(sccb) __sccb = sccb;                                 \
+                                                                       \
+       sccb_get_mask(__sccb->masks, __sccb->mask_length, i);           \
+})
+#define sccb_get_recv_mask(sccb)       sccb_get_generic_mask(sccb, 0)
+#define sccb_get_send_mask(sccb)       sccb_get_generic_mask(sccb, 1)
+#define sccb_get_sclp_recv_mask(sccb)  sccb_get_generic_mask(sccb, 2)
+#define sccb_get_sclp_send_mask(sccb)  sccb_get_generic_mask(sccb, 3)
+
+#define sccb_set_generic_mask(sccb, i, val)                            \
+({                                                                     \
+       __typeof__(sccb) __sccb = sccb;                                 \
+                                                                       \
+       sccb_set_mask(__sccb->masks, __sccb->mask_length, i, val);      \
+})
+#define sccb_set_recv_mask(sccb, val)      sccb_set_generic_mask(sccb, 0, val)
+#define sccb_set_send_mask(sccb, val)      sccb_set_generic_mask(sccb, 1, val)
+#define sccb_set_sclp_recv_mask(sccb, val)  sccb_set_generic_mask(sccb, 2, val)
+#define sccb_set_sclp_send_mask(sccb, val)  sccb_set_generic_mask(sccb, 3, val)
+
 struct read_cpu_info_sccb {
        struct  sccb_header header;
        u16     nr_configured;
@@ -221,15 +264,17 @@ extern int sclp_init_state;
 extern int sclp_console_pages;
 extern int sclp_console_drop;
 extern unsigned long sclp_console_full;
+extern bool sclp_mask_compat_mode;
 
 extern char sclp_early_sccb[PAGE_SIZE];
 
 void sclp_early_wait_irq(void);
 int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb);
 unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb);
+unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb);
 int sclp_early_set_event_mask(struct init_sccb *sccb,
-                             unsigned long receive_mask,
-                             unsigned long send_mask);
+                             sccb_mask_t receive_mask,
+                             sccb_mask_t send_mask);
 
 /* useful inlines */
 
index 6b1891539c840b723409f03d054125cc50b924a2..9a74abb9224d02fc47041e98a6600265a3bac881 100644 (file)
@@ -249,7 +249,7 @@ static void __init sclp_early_console_detect(struct init_sccb *sccb)
        if (sccb->header.response_code != 0x20)
                return;
 
-       if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK)
+       if (sclp_early_con_check_vt220(sccb))
                sclp.has_vt220 = 1;
 
        if (sclp_early_con_check_linemode(sccb))
index 17b0c67f3e8d0234e0fa16c764f32356d77b9dd5..5f8d9ea69ebdb87aef031dd01220bd5895e3bb89 100644 (file)
 
 char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data);
 int sclp_init_state __section(.data) = sclp_init_state_uninitialized;
+/*
+ * Used to keep track of the size of the event masks. Qemu until version 2.11
+ * only supports 4 and needs a workaround.
+ */
+bool sclp_mask_compat_mode;
 
 void sclp_early_wait_irq(void)
 {
@@ -142,16 +147,24 @@ static void sclp_early_print_vt220(const char *str, unsigned int len)
 }
 
 int sclp_early_set_event_mask(struct init_sccb *sccb,
-                             unsigned long receive_mask,
-                             unsigned long send_mask)
+                             sccb_mask_t receive_mask,
+                             sccb_mask_t send_mask)
 {
+retry:
        memset(sccb, 0, sizeof(*sccb));
        sccb->header.length = sizeof(*sccb);
-       sccb->mask_length = sizeof(sccb_mask_t);
-       sccb->receive_mask = receive_mask;
-       sccb->send_mask = send_mask;
+       if (sclp_mask_compat_mode)
+               sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
+       else
+               sccb->mask_length = sizeof(sccb_mask_t);
+       sccb_set_recv_mask(sccb, receive_mask);
+       sccb_set_send_mask(sccb, send_mask);
        if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb))
                return -EIO;
+       if ((sccb->header.response_code == 0x74f0) && !sclp_mask_compat_mode) {
+               sclp_mask_compat_mode = true;
+               goto retry;
+       }
        if (sccb->header.response_code != 0x20)
                return -EIO;
        return 0;
@@ -159,19 +172,28 @@ int sclp_early_set_event_mask(struct init_sccb *sccb,
 
 unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
 {
-       if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK))
+       if (!(sccb_get_sclp_send_mask(sccb) & EVTYP_OPCMD_MASK))
                return 0;
-       if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
+       if (!(sccb_get_sclp_recv_mask(sccb) & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
                return 0;
        return 1;
 }
 
+unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb)
+{
+       if (sccb_get_sclp_send_mask(sccb) & EVTYP_VT220MSG_MASK)
+               return 1;
+       return 0;
+}
+
 static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
 {
        unsigned long receive_mask, send_mask;
        struct init_sccb *sccb;
        int rc;
 
+       BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE);
+
        *have_linemode = *have_vt220 = 0;
        sccb = (struct init_sccb *) &sclp_early_sccb;
        receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
@@ -180,7 +202,7 @@ static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
        if (rc)
                return rc;
        *have_linemode = sclp_early_con_check_linemode(sccb);
-       *have_vt220 = sccb->send_mask & EVTYP_VT220MSG_MASK;
+       *have_vt220 = !!(sccb_get_send_mask(sccb) & EVTYP_VT220MSG_MASK);
        return rc;
 }
 
diff --git a/drivers/s390/char/sclp_sd.c b/drivers/s390/char/sclp_sd.c
new file mode 100644 (file)
index 0000000..99f41db
--- /dev/null
@@ -0,0 +1,569 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * SCLP Store Data support and sysfs interface
+ *
+ * Copyright IBM Corp. 2017
+ */
+
+#define KMSG_COMPONENT "sclp_sd"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/completion.h>
+#include <linux/kobject.h>
+#include <linux/list.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/async.h>
+#include <linux/export.h>
+#include <linux/mutex.h>
+
+#include <asm/pgalloc.h>
+
+#include "sclp.h"
+
+#define SD_EQ_STORE_DATA       0
+#define SD_EQ_HALT             1
+#define SD_EQ_SIZE             2
+
+#define SD_DI_CONFIG           3
+
+struct sclp_sd_evbuf {
+       struct evbuf_header hdr;
+       u8 eq;
+       u8 di;
+       u8 rflags;
+       u64 :56;
+       u32 id;
+       u16 :16;
+       u8 fmt;
+       u8 status;
+       u64 sat;
+       u64 sa;
+       u32 esize;
+       u32 dsize;
+} __packed;
+
+struct sclp_sd_sccb {
+       struct sccb_header hdr;
+       struct sclp_sd_evbuf evbuf;
+} __packed __aligned(PAGE_SIZE);
+
+/**
+ * struct sclp_sd_data - Result of a Store Data request
+ * @esize_bytes: Resulting esize in bytes
+ * @dsize_bytes: Resulting dsize in bytes
+ * @data: Pointer to data - must be released using vfree()
+ */
+struct sclp_sd_data {
+       size_t esize_bytes;
+       size_t dsize_bytes;
+       void *data;
+};
+
+/**
+ * struct sclp_sd_listener - Listener for asynchronous Store Data response
+ * @list: For enqueueing this struct
+ * @id: Event ID of response to listen for
+ * @completion: Can be used to wait for response
+ * @evbuf: Contains the resulting Store Data response after completion
+ */
+struct sclp_sd_listener {
+       struct list_head list;
+       u32 id;
+       struct completion completion;
+       struct sclp_sd_evbuf evbuf;
+};
+
+/**
+ * struct sclp_sd_file - Sysfs representation of a Store Data entity
+ * @kobj: Kobject
+ * @data_attr: Attribute for accessing data contents
+ * @data_mutex: Mutex to serialize access and updates to @data
+ * @data: Data associated with this entity
+ * @di: DI value associated with this entity
+ */
+struct sclp_sd_file {
+       struct kobject kobj;
+       struct bin_attribute data_attr;
+       struct mutex data_mutex;
+       struct sclp_sd_data data;
+       u8 di;
+};
+#define to_sd_file(x) container_of(x, struct sclp_sd_file, kobj)
+
+static struct kset *sclp_sd_kset;
+static struct sclp_sd_file *config_file;
+
+static LIST_HEAD(sclp_sd_queue);
+static DEFINE_SPINLOCK(sclp_sd_queue_lock);
+
+/**
+ * sclp_sd_listener_add() - Add listener for Store Data responses
+ * @listener: Listener to add
+ */
+static void sclp_sd_listener_add(struct sclp_sd_listener *listener)
+{
+       spin_lock_irq(&sclp_sd_queue_lock);
+       list_add_tail(&listener->list, &sclp_sd_queue);
+       spin_unlock_irq(&sclp_sd_queue_lock);
+}
+
+/**
+ * sclp_sd_listener_remove() - Remove listener for Store Data responses
+ * @listener: Listener to remove
+ */
+static void sclp_sd_listener_remove(struct sclp_sd_listener *listener)
+{
+       spin_lock_irq(&sclp_sd_queue_lock);
+       list_del(&listener->list);
+       spin_unlock_irq(&sclp_sd_queue_lock);
+}
+
+/**
+ * sclp_sd_listener_init() - Initialize a Store Data response listener
+ * @id: Event ID to listen for
+ *
+ * Initialize a listener for asynchronous Store Data responses. This listener
+ * can afterwards be used to wait for a specific response and to retrieve
+ * the associated response data.
+ */
+static void sclp_sd_listener_init(struct sclp_sd_listener *listener, u32 id)
+{
+       memset(listener, 0, sizeof(*listener));
+       listener->id = id;
+       init_completion(&listener->completion);
+}
+
+/**
+ * sclp_sd_receiver() - Receiver for Store Data events
+ * @evbuf_hdr: Header of received events
+ *
+ * Process Store Data events and complete listeners with matching event IDs.
+ */
+static void sclp_sd_receiver(struct evbuf_header *evbuf_hdr)
+{
+       struct sclp_sd_evbuf *evbuf = (struct sclp_sd_evbuf *) evbuf_hdr;
+       struct sclp_sd_listener *listener;
+       int found = 0;
+
+       pr_debug("received event (id=0x%08x)\n", evbuf->id);
+       spin_lock(&sclp_sd_queue_lock);
+       list_for_each_entry(listener, &sclp_sd_queue, list) {
+               if (listener->id != evbuf->id)
+                       continue;
+
+               listener->evbuf = *evbuf;
+               complete(&listener->completion);
+               found = 1;
+               break;
+       }
+       spin_unlock(&sclp_sd_queue_lock);
+
+       if (!found)
+               pr_debug("unsolicited event (id=0x%08x)\n", evbuf->id);
+}
+
+static struct sclp_register sclp_sd_register = {
+       .send_mask = EVTYP_STORE_DATA_MASK,
+       .receive_mask = EVTYP_STORE_DATA_MASK,
+       .receiver_fn = sclp_sd_receiver,
+};
+
+/**
+ * sclp_sd_sync() - Perform Store Data request synchronously
+ * @page: Address of work page - must be below 2GB
+ * @eq: Input EQ value
+ * @di: Input DI value
+ * @sat: Input SAT value
+ * @sa: Input SA value used to specify the address of the target buffer
+ * @dsize_ptr: Optional pointer to input and output DSIZE value
+ * @esize_ptr: Optional pointer to output ESIZE value
+ *
+ * Perform Store Data request with specified parameters and wait for completion.
+ *
+ * Return %0 on success and store resulting DSIZE and ESIZE values in
+ * @dsize_ptr and @esize_ptr (if provided). Return non-zero on error.
+ */
+static int sclp_sd_sync(unsigned long page, u8 eq, u8 di, u64 sat, u64 sa,
+                       u32 *dsize_ptr, u32 *esize_ptr)
+{
+       struct sclp_sd_sccb *sccb = (void *) page;
+       struct sclp_sd_listener listener;
+       struct sclp_sd_evbuf *evbuf;
+       int rc;
+
+       sclp_sd_listener_init(&listener, (u32) (addr_t) sccb);
+       sclp_sd_listener_add(&listener);
+
+       /* Prepare SCCB */
+       memset(sccb, 0, PAGE_SIZE);
+       sccb->hdr.length = sizeof(sccb->hdr) + sizeof(sccb->evbuf);
+       evbuf = &sccb->evbuf;
+       evbuf->hdr.length = sizeof(*evbuf);
+       evbuf->hdr.type = EVTYP_STORE_DATA;
+       evbuf->eq = eq;
+       evbuf->di = di;
+       evbuf->id = listener.id;
+       evbuf->fmt = 1;
+       evbuf->sat = sat;
+       evbuf->sa = sa;
+       if (dsize_ptr)
+               evbuf->dsize = *dsize_ptr;
+
+       /* Perform command */
+       pr_debug("request (eq=%d, di=%d, id=0x%08x)\n", eq, di, listener.id);
+       rc = sclp_sync_request(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
+       pr_debug("request done (rc=%d)\n", rc);
+       if (rc)
+               goto out;
+
+       /* Evaluate response */
+       if (sccb->hdr.response_code == 0x73f0) {
+               pr_debug("event not supported\n");
+               rc = -EIO;
+               goto out_remove;
+       }
+       if (sccb->hdr.response_code != 0x0020 || !(evbuf->hdr.flags & 0x80)) {
+               rc = -EIO;
+               goto out;
+       }
+       if (!(evbuf->rflags & 0x80)) {
+               rc = wait_for_completion_interruptible(&listener.completion);
+               if (rc)
+                       goto out;
+               evbuf = &listener.evbuf;
+       }
+       switch (evbuf->status) {
+       case 0:
+               if (dsize_ptr)
+                       *dsize_ptr = evbuf->dsize;
+               if (esize_ptr)
+                       *esize_ptr = evbuf->esize;
+               pr_debug("success (dsize=%u, esize=%u)\n", evbuf->dsize,
+                        evbuf->esize);
+               break;
+       case 3:
+               rc = -ENOENT;
+               break;
+       default:
+               rc = -EIO;
+               break;
+
+       }
+
+out:
+       if (rc && rc != -ENOENT) {
+               /* Provide some information about what went wrong */
+               pr_warn("Store Data request failed (eq=%d, di=%d, "
+                       "response=0x%04x, flags=0x%02x, status=%d, rc=%d)\n",
+                       eq, di, sccb->hdr.response_code, evbuf->hdr.flags,
+                       evbuf->status, rc);
+       }
+
+out_remove:
+       sclp_sd_listener_remove(&listener);
+
+       return rc;
+}
+
+/**
+ * sclp_sd_store_data() - Obtain data for specified Store Data entity
+ * @result: Resulting data
+ * @di: DI value associated with this entity
+ *
+ * Perform a series of Store Data requests to obtain the size and contents of
+ * the specified Store Data entity.
+ *
+ * Return:
+ *   %0:       Success - result is stored in @result. @result->data must be
+ *            released using vfree() after use.
+ *   %-ENOENT: No data available for this entity
+ *   %<0:      Other error
+ */
+static int sclp_sd_store_data(struct sclp_sd_data *result, u8 di)
+{
+       u32 dsize = 0, esize = 0;
+       unsigned long page, asce = 0;
+       void *data = NULL;
+       int rc;
+
+       page = __get_free_page(GFP_KERNEL | GFP_DMA);
+       if (!page)
+               return -ENOMEM;
+
+       /* Get size */
+       rc = sclp_sd_sync(page, SD_EQ_SIZE, di, 0, 0, &dsize, &esize);
+       if (rc)
+               goto out;
+       if (dsize == 0)
+               goto out_result;
+
+       /* Allocate memory */
+       data = vzalloc((size_t) dsize * PAGE_SIZE);
+       if (!data) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       /* Get translation table for buffer */
+       asce = base_asce_alloc((unsigned long) data, dsize);
+       if (!asce) {
+               vfree(data);
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       /* Get data */
+       rc = sclp_sd_sync(page, SD_EQ_STORE_DATA, di, asce, (u64) data, &dsize,
+                         &esize);
+       if (rc) {
+               /* Cancel running request if interrupted */
+               if (rc == -ERESTARTSYS)
+                       sclp_sd_sync(page, SD_EQ_HALT, di, 0, 0, NULL, NULL);
+               vfree(data);
+               goto out;
+       }
+
+out_result:
+       result->esize_bytes = (size_t) esize * PAGE_SIZE;
+       result->dsize_bytes = (size_t) dsize * PAGE_SIZE;
+       result->data = data;
+
+out:
+       base_asce_free(asce);
+       free_page(page);
+
+       return rc;
+}
+
+/**
+ * sclp_sd_data_reset() - Reset Store Data result buffer
+ * @data: Data buffer to reset
+ *
+ * Reset @data to initial state and release associated memory.
+ */
+static void sclp_sd_data_reset(struct sclp_sd_data *data)
+{
+       vfree(data->data);
+       data->data = NULL;
+       data->dsize_bytes = 0;
+       data->esize_bytes = 0;
+}
+
+/**
+ * sclp_sd_file_release() - Release function for sclp_sd_file object
+ * @kobj: Kobject embedded in sclp_sd_file object
+ */
+static void sclp_sd_file_release(struct kobject *kobj)
+{
+       struct sclp_sd_file *sd_file = to_sd_file(kobj);
+
+       sclp_sd_data_reset(&sd_file->data);
+       kfree(sd_file);
+}
+
+/**
+ * sclp_sd_file_update() - Update contents of sclp_sd_file object
+ * @sd_file: Object to update
+ *
+ * Obtain the current version of data associated with the Store Data entity
+ * @sd_file.
+ *
+ * On success, return %0 and generate a KOBJ_CHANGE event to indicate that the
+ * data may have changed. Return non-zero otherwise.
+ */
+static int sclp_sd_file_update(struct sclp_sd_file *sd_file)
+{
+       const char *name = kobject_name(&sd_file->kobj);
+       struct sclp_sd_data data;
+       int rc;
+
+       rc = sclp_sd_store_data(&data, sd_file->di);
+       if (rc) {
+               if (rc == -ENOENT) {
+                       pr_info("No data is available for the %s data entity\n",
+                                name);
+               }
+               return rc;
+       }
+
+       mutex_lock(&sd_file->data_mutex);
+       sclp_sd_data_reset(&sd_file->data);
+       sd_file->data = data;
+       mutex_unlock(&sd_file->data_mutex);
+
+       pr_info("A %zu-byte %s data entity was retrieved\n", data.dsize_bytes,
+               name);
+       kobject_uevent(&sd_file->kobj, KOBJ_CHANGE);
+
+       return 0;
+}
+
+/**
+ * sclp_sd_file_update_async() - Wrapper for asynchronous update call
+ * @data: Object to update
+ */
+static void sclp_sd_file_update_async(void *data, async_cookie_t cookie)
+{
+       struct sclp_sd_file *sd_file = data;
+
+       sclp_sd_file_update(sd_file);
+}
+
+/**
+ * reload_store() - Store function for "reload" sysfs attribute
+ * @kobj: Kobject of sclp_sd_file object
+ *
+ * Initiate a reload of the data associated with an sclp_sd_file object.
+ */
+static ssize_t reload_store(struct kobject *kobj, struct kobj_attribute *attr,
+                           const char *buf, size_t count)
+{
+       struct sclp_sd_file *sd_file = to_sd_file(kobj);
+
+       sclp_sd_file_update(sd_file);
+
+       return count;
+}
+
+static struct kobj_attribute reload_attr = __ATTR_WO(reload);
+
+static struct attribute *sclp_sd_file_default_attrs[] = {
+       &reload_attr.attr,
+       NULL,
+};
+
+static struct kobj_type sclp_sd_file_ktype = {
+       .sysfs_ops = &kobj_sysfs_ops,
+       .release = sclp_sd_file_release,
+       .default_attrs = sclp_sd_file_default_attrs,
+};
+
+/**
+ * data_read() - Read function for "read" sysfs attribute
+ * @kobj: Kobject of sclp_sd_file object
+ * @buffer: Target buffer
+ * @off: Requested file offset
+ * @size: Requested number of bytes
+ *
+ * Store the requested portion of the Store Data entity contents into the
+ * specified buffer. Return the number of bytes stored on success, or %0
+ * on EOF.
+ */
+static ssize_t data_read(struct file *file, struct kobject *kobj,
+                        struct bin_attribute *attr, char *buffer,
+                        loff_t off, size_t size)
+{
+       struct sclp_sd_file *sd_file = to_sd_file(kobj);
+       size_t data_size;
+       char *data;
+
+       mutex_lock(&sd_file->data_mutex);
+
+       data = sd_file->data.data;
+       data_size = sd_file->data.dsize_bytes;
+       if (!data || off >= data_size) {
+               size = 0;
+       } else {
+               if (off + size > data_size)
+                       size = data_size - off;
+               memcpy(buffer, data + off, size);
+       }
+
+       mutex_unlock(&sd_file->data_mutex);
+
+       return size;
+}
+
+/**
+ * sclp_sd_file_create() - Add a sysfs file representing a Store Data entity
+ * @name: Name of file
+ * @di: DI value associated with this entity
+ *
+ * Create a sysfs directory with the given @name located under
+ *
+ *   /sys/firmware/sclp_sd/
+ *
+ * The files in this directory can be used to access the contents of the Store
+ * Data entity associated with @DI.
+ *
+ * Return pointer to resulting sclp_sd_file object on success, %NULL otherwise.
+ * The object must be freed by calling kobject_put() on the embedded kobject
+ * pointer after use.
+ */
+static __init struct sclp_sd_file *sclp_sd_file_create(const char *name, u8 di)
+{
+       struct sclp_sd_file *sd_file;
+       int rc;
+
+       sd_file = kzalloc(sizeof(*sd_file), GFP_KERNEL);
+       if (!sd_file)
+               return NULL;
+       sd_file->di = di;
+       mutex_init(&sd_file->data_mutex);
+
+       /* Create kobject located under /sys/firmware/sclp_sd/ */
+       sd_file->kobj.kset = sclp_sd_kset;
+       rc = kobject_init_and_add(&sd_file->kobj, &sclp_sd_file_ktype, NULL,
+                                 "%s", name);
+       if (rc) {
+               kobject_put(&sd_file->kobj);
+               return NULL;
+       }
+
+       sysfs_bin_attr_init(&sd_file->data_attr);
+       sd_file->data_attr.attr.name = "data";
+       sd_file->data_attr.attr.mode = 0444;
+       sd_file->data_attr.read = data_read;
+
+       rc = sysfs_create_bin_file(&sd_file->kobj, &sd_file->data_attr);
+       if (rc) {
+               kobject_put(&sd_file->kobj);
+               return NULL;
+       }
+
+       /*
+        * For completeness only - users interested in entity data should listen
+        * for KOBJ_CHANGE instead.
+        */
+       kobject_uevent(&sd_file->kobj, KOBJ_ADD);
+
+       /* Don't let a slow Store Data request delay further initialization */
+       async_schedule(sclp_sd_file_update_async, sd_file);
+
+       return sd_file;
+}
+
+/**
+ * sclp_sd_init() - Initialize sclp_sd support and register sysfs files
+ */
+static __init int sclp_sd_init(void)
+{
+       int rc;
+
+       rc = sclp_register(&sclp_sd_register);
+       if (rc)
+               return rc;
+
+       /* Create kset named "sclp_sd" located under /sys/firmware/ */
+       rc = -ENOMEM;
+       sclp_sd_kset = kset_create_and_add("sclp_sd", NULL, firmware_kobj);
+       if (!sclp_sd_kset)
+               goto err_kset;
+
+       rc = -EINVAL;
+       config_file = sclp_sd_file_create("config", SD_DI_CONFIG);
+       if (!config_file)
+               goto err_config;
+
+       return 0;
+
+err_config:
+       kset_unregister(sclp_sd_kset);
+err_kset:
+       sclp_unregister(&sclp_sd_register);
+
+       return rc;
+}
+device_initcall(sclp_sd_init);
index 9f7b87d6d4349f25c07a2fe421f1a3130efe3903..5aff8b684eb21e8557f253c7ddf18d48420435c1 100644 (file)
@@ -502,7 +502,10 @@ sclp_tty_init(void)
        int i;
        int rc;
 
-       if (!CONSOLE_IS_SCLP)
+       /* z/VM multiplexes the line mode output on the 32xx screen */
+       if (MACHINE_IS_VM && !CONSOLE_IS_SCLP)
+               return 0;
+       if (!sclp.has_linemode)
                return 0;
        driver = alloc_tty_driver(1);
        if (!driver)
index f95b452b8bbcc50ea6d8e0a7cbd4c581348136b5..afbdee74147dd01553ce136bd2a1f3e5f349ad37 100644 (file)
@@ -384,6 +384,28 @@ static ssize_t chp_chid_external_show(struct device *dev,
 }
 static DEVICE_ATTR(chid_external, 0444, chp_chid_external_show, NULL);
 
+static ssize_t util_string_read(struct file *filp, struct kobject *kobj,
+                               struct bin_attribute *attr, char *buf,
+                               loff_t off, size_t count)
+{
+       struct channel_path *chp = to_channelpath(kobj_to_dev(kobj));
+       ssize_t rc;
+
+       mutex_lock(&chp->lock);
+       rc = memory_read_from_buffer(buf, count, &off, chp->desc_fmt3.util_str,
+                                    sizeof(chp->desc_fmt3.util_str));
+       mutex_unlock(&chp->lock);
+
+       return rc;
+}
+static BIN_ATTR_RO(util_string,
+                  sizeof(((struct channel_path_desc_fmt3 *)0)->util_str));
+
+static struct bin_attribute *chp_bin_attrs[] = {
+       &bin_attr_util_string,
+       NULL,
+};
+
 static struct attribute *chp_attrs[] = {
        &dev_attr_status.attr,
        &dev_attr_configure.attr,
@@ -396,6 +418,7 @@ static struct attribute *chp_attrs[] = {
 };
 static struct attribute_group chp_attr_group = {
        .attrs = chp_attrs,
+       .bin_attrs = chp_bin_attrs,
 };
 static const struct attribute_group *chp_attr_groups[] = {
        &chp_attr_group,
@@ -422,7 +445,7 @@ int chp_update_desc(struct channel_path *chp)
 {
        int rc;
 
-       rc = chsc_determine_base_channel_path_desc(chp->chpid, &chp->desc);
+       rc = chsc_determine_fmt0_channel_path_desc(chp->chpid, &chp->desc);
        if (rc)
                return rc;
 
@@ -431,6 +454,7 @@ int chp_update_desc(struct channel_path *chp)
         * hypervisors implement the required chsc commands.
         */
        chsc_determine_fmt1_channel_path_desc(chp->chpid, &chp->desc_fmt1);
+       chsc_determine_fmt3_channel_path_desc(chp->chpid, &chp->desc_fmt3);
        chsc_get_channel_measurement_chars(chp);
 
        return 0;
@@ -506,20 +530,20 @@ out:
  * On success return a newly allocated copy of the channel-path description
  * data associated with the given channel-path ID. Return %NULL on error.
  */
-struct channel_path_desc *chp_get_chp_desc(struct chp_id chpid)
+struct channel_path_desc_fmt0 *chp_get_chp_desc(struct chp_id chpid)
 {
        struct channel_path *chp;
-       struct channel_path_desc *desc;
+       struct channel_path_desc_fmt0 *desc;
 
        chp = chpid_to_chp(chpid);
        if (!chp)
                return NULL;
-       desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
+       desc = kmalloc(sizeof(*desc), GFP_KERNEL);
        if (!desc)
                return NULL;
 
        mutex_lock(&chp->lock);
-       memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
+       memcpy(desc, &chp->desc, sizeof(*desc));
        mutex_unlock(&chp->lock);
        return desc;
 }
index 7e80323cd261e3c189be4fe9e9fab5c6ba6c854c..20259f3fbf453c98bf11a69de568af534b69f210 100644 (file)
@@ -44,8 +44,9 @@ struct channel_path {
        struct chp_id chpid;
        struct mutex lock; /* Serialize access to below members. */
        int state;
-       struct channel_path_desc desc;
+       struct channel_path_desc_fmt0 desc;
        struct channel_path_desc_fmt1 desc_fmt1;
+       struct channel_path_desc_fmt3 desc_fmt3;
        /* Channel-measurement related stuff: */
        int cmg;
        int shared;
@@ -61,7 +62,7 @@ static inline struct channel_path *chpid_to_chp(struct chp_id chpid)
 int chp_get_status(struct chp_id chpid);
 u8 chp_get_sch_opm(struct subchannel *sch);
 int chp_is_registered(struct chp_id chpid);
-struct channel_path_desc *chp_get_chp_desc(struct chp_id chpid);
+struct channel_path_desc_fmt0 *chp_get_chp_desc(struct chp_id chpid);
 void chp_remove_cmg_attr(struct channel_path *chp);
 int chp_add_cmg_attr(struct channel_path *chp);
 int chp_update_desc(struct channel_path *chp);
index c08fc5a8df0c61935c02e282a6ec868d2d0e7630..6652a49a49b17210228fcdd674faa6e81baffd06 100644 (file)
@@ -915,6 +915,8 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
                return -EINVAL;
        if ((rfmt == 2) && !css_general_characteristics.cib)
                return -EINVAL;
+       if ((rfmt == 3) && !css_general_characteristics.util_str)
+               return -EINVAL;
 
        memset(page, 0, PAGE_SIZE);
        scpd_area = page;
@@ -940,43 +942,30 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
 }
 EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);
 
-int chsc_determine_base_channel_path_desc(struct chp_id chpid,
-                                         struct channel_path_desc *desc)
-{
-       struct chsc_scpd *scpd_area;
-       unsigned long flags;
-       int ret;
-
-       spin_lock_irqsave(&chsc_page_lock, flags);
-       scpd_area = chsc_page;
-       ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, scpd_area);
-       if (ret)
-               goto out;
-
-       memcpy(desc, scpd_area->data, sizeof(*desc));
-out:
-       spin_unlock_irqrestore(&chsc_page_lock, flags);
-       return ret;
+#define chsc_det_chp_desc(FMT, c)                                      \
+int chsc_determine_fmt##FMT##_channel_path_desc(                       \
+       struct chp_id chpid, struct channel_path_desc_fmt##FMT *desc)   \
+{                                                                      \
+       struct chsc_scpd *scpd_area;                                    \
+       unsigned long flags;                                            \
+       int ret;                                                        \
+                                                                       \
+       spin_lock_irqsave(&chsc_page_lock, flags);                      \
+       scpd_area = chsc_page;                                          \
+       ret = chsc_determine_channel_path_desc(chpid, 0, FMT, c, 0,     \
+                                              scpd_area);              \
+       if (ret)                                                        \
+               goto out;                                               \
+                                                                       \
+       memcpy(desc, scpd_area->data, sizeof(*desc));                   \
+out:                                                                   \
+       spin_unlock_irqrestore(&chsc_page_lock, flags);                 \
+       return ret;                                                     \
 }
 
-int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid,
-                                         struct channel_path_desc_fmt1 *desc)
-{
-       struct chsc_scpd *scpd_area;
-       unsigned long flags;
-       int ret;
-
-       spin_lock_irqsave(&chsc_page_lock, flags);
-       scpd_area = chsc_page;
-       ret = chsc_determine_channel_path_desc(chpid, 0, 1, 1, 0, scpd_area);
-       if (ret)
-               goto out;
-
-       memcpy(desc, scpd_area->data, sizeof(*desc));
-out:
-       spin_unlock_irqrestore(&chsc_page_lock, flags);
-       return ret;
-}
+chsc_det_chp_desc(0, 0)
+chsc_det_chp_desc(1, 1)
+chsc_det_chp_desc(3, 0)
 
 static void
 chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
index dda5953534b7239292d8c456e0b04af4126179ab..5c9f0dd33f4ee38634d6cb3b0383d364b2c1f310 100644 (file)
@@ -40,6 +40,11 @@ struct channel_path_desc_fmt1 {
        u32 zeros[2];
 } __attribute__ ((packed));
 
+struct channel_path_desc_fmt3 {
+       struct channel_path_desc_fmt1 fmt1_desc;
+       u8 util_str[64];
+};
+
 struct channel_path;
 
 struct css_chsc_char {
@@ -147,10 +152,12 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable);
 int chsc_chp_vary(struct chp_id chpid, int on);
 int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
                                     int c, int m, void *page);
-int chsc_determine_base_channel_path_desc(struct chp_id chpid,
-                                         struct channel_path_desc *desc);
+int chsc_determine_fmt0_channel_path_desc(struct chp_id chpid,
+                                         struct channel_path_desc_fmt0 *desc);
 int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid,
                                          struct channel_path_desc_fmt1 *desc);
+int chsc_determine_fmt3_channel_path_desc(struct chp_id chpid,
+                                         struct channel_path_desc_fmt3 *desc);
 void chsc_chp_online(struct chp_id chpid);
 void chsc_chp_offline(struct chp_id chpid);
 int chsc_get_channel_measurement_chars(struct channel_path *chp);
index f50ea035aa9b3cf3966a39ee14f7e608c5f9ec54..1540229a37bbab129e39b8ef0ad943be237b86b8 100644 (file)
@@ -1073,8 +1073,7 @@ out_schedule:
        return 0;
 }
 
-static int
-io_subchannel_remove (struct subchannel *sch)
+static int io_subchannel_remove(struct subchannel *sch)
 {
        struct io_subchannel_private *io_priv = to_io_private(sch);
        struct ccw_device *cdev;
@@ -1082,14 +1081,12 @@ io_subchannel_remove (struct subchannel *sch)
        cdev = sch_get_cdev(sch);
        if (!cdev)
                goto out_free;
-       io_subchannel_quiesce(sch);
-       /* Set ccw device to not operational and drop reference. */
-       spin_lock_irq(cdev->ccwlock);
+
+       ccw_device_unregister(cdev);
+       spin_lock_irq(sch->lock);
        sch_set_cdev(sch, NULL);
        set_io_private(sch, NULL);
-       cdev->private->state = DEV_STATE_NOT_OPER;
-       spin_unlock_irq(cdev->ccwlock);
-       ccw_device_unregister(cdev);
+       spin_unlock_irq(sch->lock);
 out_free:
        kfree(io_priv);
        sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
@@ -1721,6 +1718,7 @@ static int ccw_device_remove(struct device *dev)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
        struct ccw_driver *cdrv = cdev->drv;
+       struct subchannel *sch;
        int ret;
 
        if (cdrv->remove)
@@ -1746,7 +1744,9 @@ static int ccw_device_remove(struct device *dev)
        ccw_device_set_timeout(cdev, 0);
        cdev->drv = NULL;
        cdev->private->int_class = IRQIO_CIO;
+       sch = to_subchannel(cdev->dev.parent);
        spin_unlock_irq(cdev->ccwlock);
+       io_subchannel_quiesce(sch);
        __disable_cmf(cdev);
 
        return 0;
index 75ce12a24dc2a6a6dfdb756fce5ae6c148de20f6..aecfebb74157ecee8d1346b68745be940aff7fd9 100644 (file)
@@ -460,8 +460,8 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
  * On success return a newly allocated copy of the channel-path description
  * data associated with the given channel path. Return %NULL on error.
  */
-struct channel_path_desc *ccw_device_get_chp_desc(struct ccw_device *cdev,
-                                                 int chp_idx)
+struct channel_path_desc_fmt0 *ccw_device_get_chp_desc(struct ccw_device *cdev,
+                                                      int chp_idx)
 {
        struct subchannel *sch;
        struct chp_id chpid;
index d5b02de02a3af31d55fcef8086c5bad2de98a178..a337281337a71ef9f7a4589036ff62b654c465c2 100644 (file)
@@ -98,22 +98,6 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask,
        return cc;
 }
 
-static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
-{
-       /* all done or next buffer state different */
-       if (ccq == 0 || ccq == 32)
-               return 0;
-       /* no buffer processed */
-       if (ccq == 97)
-               return 1;
-       /* not all buffers processed */
-       if (ccq == 96)
-               return 2;
-       /* notify devices immediately */
-       DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
-       return -EIO;
-}
-
 /**
  * qdio_do_eqbs - extract buffer states for QEBSM
  * @q: queue to manipulate
@@ -128,7 +112,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
 static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
                        int start, int count, int auto_ack)
 {
-       int rc, tmp_count = count, tmp_start = start, nr = q->nr, retried = 0;
+       int tmp_count = count, tmp_start = start, nr = q->nr;
        unsigned int ccq = 0;
 
        qperf_inc(q, eqbs);
@@ -138,34 +122,30 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
 again:
        ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count,
                      auto_ack);
-       rc = qdio_check_ccq(q, ccq);
-       if (!rc)
-               return count - tmp_count;
 
-       if (rc == 1) {
-               DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
-               goto again;
-       }
-
-       if (rc == 2) {
+       switch (ccq) {
+       case 0:
+       case 32:
+               /* all done, or next buffer state different */
+               return count - tmp_count;
+       case 96:
+               /* not all buffers processed */
                qperf_inc(q, eqbs_partial);
                DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS part:%02x",
                        tmp_count);
-               /*
-                * Retry once, if that fails bail out and process the
-                * extracted buffers before trying again.
-                */
-               if (!retried++)
-                       goto again;
-               else
-                       return count - tmp_count;
+               return count - tmp_count;
+       case 97:
+               /* no buffer processed */
+               DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
+               goto again;
+       default:
+               DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
+               DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
+               DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
+               q->handler(q->irq_ptr->cdev, QDIO_ERROR_GET_BUF_STATE, q->nr,
+                          q->first_to_kick, count, q->irq_ptr->int_parm);
+               return 0;
        }
-
-       DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
-       DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
-       q->handler(q->irq_ptr->cdev, QDIO_ERROR_GET_BUF_STATE,
-                  q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
-       return 0;
 }
 
 /**
@@ -185,7 +165,6 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
        unsigned int ccq = 0;
        int tmp_count = count, tmp_start = start;
        int nr = q->nr;
-       int rc;
 
        if (!count)
                return 0;
@@ -195,26 +174,32 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
                nr += q->irq_ptr->nr_input_qs;
 again:
        ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
-       rc = qdio_check_ccq(q, ccq);
-       if (!rc) {
+
+       switch (ccq) {
+       case 0:
+       case 32:
+               /* all done, or active buffer adapter-owned */
                WARN_ON_ONCE(tmp_count);
                return count - tmp_count;
-       }
-
-       if (rc == 1 || rc == 2) {
+       case 96:
+               /* not all buffers processed */
                DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq);
                qperf_inc(q, sqbs_partial);
                goto again;
+       default:
+               DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
+               DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
+               DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
+               q->handler(q->irq_ptr->cdev, QDIO_ERROR_SET_BUF_STATE, q->nr,
+                          q->first_to_kick, count, q->irq_ptr->int_parm);
+               return 0;
        }
-
-       DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
-       DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
-       q->handler(q->irq_ptr->cdev, QDIO_ERROR_SET_BUF_STATE,
-                  q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
-       return 0;
 }
 
-/* returns number of examined buffers and their common state in *state */
+/*
+ * Returns number of examined buffers and their common state in *state.
+ * Requested number of buffers-to-examine must be > 0.
+ */
 static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
                                 unsigned char *state, unsigned int count,
                                 int auto_ack, int merge_pending)
@@ -225,17 +210,23 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
        if (is_qebsm(q))
                return qdio_do_eqbs(q, state, bufnr, count, auto_ack);
 
-       for (i = 0; i < count; i++) {
-               if (!__state) {
-                       __state = q->slsb.val[bufnr];
-                       if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
-                               __state = SLSB_P_OUTPUT_EMPTY;
-               } else if (merge_pending) {
-                       if ((q->slsb.val[bufnr] & __state) != __state)
-                               break;
-               } else if (q->slsb.val[bufnr] != __state)
-                       break;
+       /* get initial state: */
+       __state = q->slsb.val[bufnr];
+       if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
+               __state = SLSB_P_OUTPUT_EMPTY;
+
+       for (i = 1; i < count; i++) {
                bufnr = next_buf(bufnr);
+
+               /* merge PENDING into EMPTY: */
+               if (merge_pending &&
+                   q->slsb.val[bufnr] == SLSB_P_OUTPUT_PENDING &&
+                   __state == SLSB_P_OUTPUT_EMPTY)
+                       continue;
+
+               /* stop if next state differs from initial state: */
+               if (q->slsb.val[bufnr] != __state)
+                       break;
        }
        *state = __state;
        return i;
@@ -502,8 +493,8 @@ static inline void inbound_primed(struct qdio_q *q, int count)
 
 static int get_inbound_buffer_frontier(struct qdio_q *q)
 {
-       int count, stop;
        unsigned char state = 0;
+       int count;
 
        q->timestamp = get_tod_clock_fast();
 
@@ -512,9 +503,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
         * would return 0.
         */
        count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
-       stop = add_buf(q->first_to_check, count);
-
-       if (q->first_to_check == stop)
+       if (!count)
                goto out;
 
        /*
@@ -734,8 +723,8 @@ void qdio_inbound_processing(unsigned long data)
 
 static int get_outbound_buffer_frontier(struct qdio_q *q)
 {
-       int count, stop;
        unsigned char state = 0;
+       int count;
 
        q->timestamp = get_tod_clock_fast();
 
@@ -751,11 +740,11 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
         * would return 0.
         */
        count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
-       stop = add_buf(q->first_to_check, count);
-       if (q->first_to_check == stop)
+       if (!count)
                goto out;
 
-       count = get_buf_states(q, q->first_to_check, &state, count, 0, 1);
+       count = get_buf_states(q, q->first_to_check, &state, count, 0,
+                              q->u.out.use_cq);
        if (!count)
                goto out;
 
index c30420c517b17961e1b0d94f908f557db8c8ee2f..ff6963ad6e393f658feb887f6e8ce5f4eeac62cd 100644 (file)
@@ -124,6 +124,11 @@ static void fsm_io_request(struct vfio_ccw_private *private,
        if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
                orb = (union orb *)io_region->orb_area;
 
+               /* Don't try to build a cp if transport mode is specified. */
+               if (orb->tm.b) {
+                       io_region->ret_code = -EOPNOTSUPP;
+                       goto err_out;
+               }
                io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev),
                                              orb);
                if (io_region->ret_code)
index 19203340f8795be3dd02de136265a76790e0d314..04fefa5bb08d168b7beefeeef62b1190aab84232 100644 (file)
@@ -1369,7 +1369,7 @@ static void qeth_set_multiple_write_queues(struct qeth_card *card)
 static void qeth_update_from_chp_desc(struct qeth_card *card)
 {
        struct ccw_device *ccwdev;
-       struct channel_path_desc *chp_dsc;
+       struct channel_path_desc_fmt0 *chp_dsc;
 
        QETH_DBF_TEXT(SETUP, 2, "chp_desc");
 
index e5041c605fd0807a55b2202e3b5c31b692944c43..0840d27381ea79fa501bee4e26f3f928fa58031f 100644 (file)
@@ -11,7 +11,7 @@ if TTY
 
 config VT
        bool "Virtual terminal" if EXPERT
-       depends on !S390 && !UML
+       depends on !UML
        select INPUT
        default y
        ---help---
index 3c20af999893577c9a1203756a4f2b98f0fe839f..4f950c68605533028b3ae110b9dc0d7ba4ace683 100644 (file)
@@ -3,7 +3,8 @@
 #
 
 menu "Graphics support"
-       depends on HAS_IOMEM
+
+if HAS_IOMEM
 
 config HAVE_FB_ATMEL
        bool
@@ -36,6 +37,8 @@ config VIDEOMODE_HELPERS
 config HDMI
        bool
 
+endif # HAS_IOMEM
+
 if VT
        source "drivers/video/console/Kconfig"
 endif
index 198574b7dbeff4c9beb7f77b686609f1ab078ff5..4110ba7d7ca9070f783c8e23aa55adca9f1247dc 100644 (file)
@@ -8,7 +8,7 @@ config VGA_CONSOLE
        bool "VGA text console" if EXPERT || !X86
        depends on !4xx && !PPC_8xx && !SPARC && !M68K && !PARISC &&  !SUPERH && \
                (!ARM || ARCH_FOOTBRIDGE || ARCH_INTEGRATOR || ARCH_NETWINDER) && \
-               !ARM64 && !ARC && !MICROBLAZE && !OPENRISC && !NDS32
+               !ARM64 && !ARC && !MICROBLAZE && !OPENRISC && !NDS32 && !S390
        default y
        help
          Saying Y here will allow you to use Linux in text mode through a
@@ -84,7 +84,7 @@ config MDA_CONSOLE
 
 config SGI_NEWPORT_CONSOLE
         tristate "SGI Newport Console support"
-        depends on SGI_IP22 
+       depends on SGI_IP22 && HAS_IOMEM
         select FONT_SUPPORT
         help
           Say Y here if you want the console on the Newport aka XL graphics
@@ -152,7 +152,7 @@ config FRAMEBUFFER_CONSOLE_ROTATION
 
 config STI_CONSOLE
         bool "STI text console"
-        depends on PARISC
+       depends on PARISC && HAS_IOMEM
         select FONT_SUPPORT
         default y
         help
index 624f18bbfd2b3430e4a5d67c54cf84af4312a440..ef309958e06073ca0bcc7f8a5b487bf3e42628ba 100644 (file)
@@ -1080,6 +1080,9 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)
        sb->s_maxbytes = MAX_LFS_FILESIZE;
        sb->s_time_gran = 1;
        sb->s_export_op = &fuse_export_operations;
+       sb->s_iflags |= SB_I_IMA_UNVERIFIABLE_SIGNATURE;
+       if (sb->s_user_ns != &init_user_ns)
+               sb->s_iflags |= SB_I_UNTRUSTED_MOUNTER;
 
        file = fget(d.fd);
        err = -EINVAL;
index 0d798052bd8571198aae7f62f6c517862d121b13..1ee7f592e239c3b11587f738b42b06b05291e348 100644 (file)
@@ -1321,6 +1321,8 @@ extern int send_sigurg(struct fown_struct *fown);
 
 /* sb->s_iflags to limit user namespace mounts */
 #define SB_I_USERNS_VISIBLE            0x00000010 /* fstype already mounted */
+#define SB_I_IMA_UNVERIFIABLE_SIGNATURE        0x00000020
+#define SB_I_UNTRUSTED_MOUNTER         0x00000040
 
 /* Possible states of 'frozen' field */
 enum {
index c72c42dbe77b4a1d27bc62df403d62716cbcf25b..9d0b286f3dbaf56c5362327a1bc82ed1a9132a34 100644 (file)
  *     @new points to the new credentials.
  *     @old points to the original credentials.
  *     Transfer data from original creds to new creds
+ * @cred_getsecid:
+ *     Retrieve the security identifier of the cred structure @c
+ *     @c contains the credentials, secid will be placed into @secid.
+ *     In case of failure, @secid will be set to zero.
  * @kernel_act_as:
  *     Set the credentials for a kernel service to act as (subjective context).
  *     @new points to the credentials to be modified.
@@ -1569,6 +1573,7 @@ union security_list_options {
        int (*cred_prepare)(struct cred *new, const struct cred *old,
                                gfp_t gfp);
        void (*cred_transfer)(struct cred *new, const struct cred *old);
+       void (*cred_getsecid)(const struct cred *c, u32 *secid);
        int (*kernel_act_as)(struct cred *new, u32 secid);
        int (*kernel_create_files_as)(struct cred *new, struct inode *inode);
        int (*kernel_module_request)(char *kmod_name);
@@ -1858,6 +1863,7 @@ struct security_hook_heads {
        struct hlist_head cred_free;
        struct hlist_head cred_prepare;
        struct hlist_head cred_transfer;
+       struct hlist_head cred_getsecid;
        struct hlist_head kernel_act_as;
        struct hlist_head kernel_create_files_as;
        struct hlist_head kernel_read_file;
index 4a573c3be93d6a1063eb858f072eecd3c29b0d36..200920f521a1ea03aed601eb5ae69bcdd66aac09 100644 (file)
@@ -322,6 +322,7 @@ int security_cred_alloc_blank(struct cred *cred, gfp_t gfp);
 void security_cred_free(struct cred *cred);
 int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
 void security_transfer_creds(struct cred *new, const struct cred *old);
+void security_cred_getsecid(const struct cred *c, u32 *secid);
 int security_kernel_act_as(struct cred *new, u32 secid);
 int security_kernel_create_files_as(struct cred *new, struct inode *inode);
 int security_kernel_module_request(char *kmod_name);
index bcdd3790e94ddeac42161e55f98dcb284949a2d7..06639fb6ab859b259e9aa02bb5558cfa45e0c478 100644 (file)
@@ -44,7 +44,7 @@ struct tpm_class_ops {
        bool (*update_timeouts)(struct tpm_chip *chip,
                                unsigned long *timeout_cap);
        int (*request_locality)(struct tpm_chip *chip, int loc);
-       void (*relinquish_locality)(struct tpm_chip *chip, int loc);
+       int (*relinquish_locality)(struct tpm_chip *chip, int loc);
        void (*clk_enable)(struct tpm_chip *chip, bool value);
 };
 
index 04825393facbabe59b8f5aa854bc23975669c3bd..45c4a89c02ffd17bd4a2f920e5c3f4e88c5c0e8f 100644 (file)
@@ -31,8 +31,6 @@
                       EVM_ALLOW_METADATA_WRITES)
 
 extern int evm_initialized;
-extern char *evm_hmac;
-extern char *evm_hash;
 
 #define EVM_ATTR_FSUUID                0x0001
 
index 691f3e09154c752d5d547760a3f9cc554e9465a0..a46fba322340b80fac97fb1c6ca1a5b14e128759 100644 (file)
@@ -37,6 +37,9 @@ static DEFINE_MUTEX(mutex);
 
 static unsigned long evm_set_key_flags;
 
+static char * const evm_hmac = "hmac(sha1)";
+static char * const evm_hash = "sha1";
+
 /**
  * evm_set_key() - set EVM HMAC key from the kernel
  * @key: pointer to a buffer with the key data
index a8d5028272702d59bcce310e97a9765d36c78a98..9ea9c19a545c3abbe0dff5e1ea4e8d32f314d471 100644 (file)
 
 int evm_initialized;
 
-static char *integrity_status_msg[] = {
+static const char * const integrity_status_msg[] = {
        "pass", "pass_immutable", "fail", "no_label", "no_xattrs", "unknown"
 };
-char *evm_hmac = "hmac(sha1)";
-char *evm_hash = "sha1";
 int evm_hmac_attrs;
 
 char *evm_config_xattrnames[] = {
@@ -126,6 +124,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
        struct evm_ima_xattr_data *xattr_data = NULL;
        struct evm_ima_xattr_data calc;
        enum integrity_status evm_status = INTEGRITY_PASS;
+       struct inode *inode;
        int rc, xattr_len;
 
        if (iint && (iint->evm_status == INTEGRITY_PASS ||
@@ -180,12 +179,15 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
                                        (const char *)xattr_data, xattr_len,
                                        calc.digest, sizeof(calc.digest));
                if (!rc) {
+                       inode = d_backing_inode(dentry);
+
                        if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG) {
                                if (iint)
                                        iint->flags |= EVM_IMMUTABLE_DIGSIG;
                                evm_status = INTEGRITY_PASS_IMMUTABLE;
-                       } else if (!IS_RDONLY(d_backing_inode(dentry)) &&
-                                  !IS_IMMUTABLE(d_backing_inode(dentry))) {
+                       } else if (!IS_RDONLY(inode) &&
+                                  !(inode->i_sb->s_readonly_remount) &&
+                                  !IS_IMMUTABLE(inode)) {
                                evm_update_evmxattr(dentry, xattr_name,
                                                    xattr_value,
                                                    xattr_value_len);
index 9700e96ab0f05cf27c6dee7bdf3f92fb762d3dab..f266e4b3b7d4614345b8aa6f24806b2f3476b9db 100644 (file)
@@ -79,6 +79,7 @@ static void iint_free(struct integrity_iint_cache *iint)
        iint->ima_mmap_status = INTEGRITY_UNKNOWN;
        iint->ima_bprm_status = INTEGRITY_UNKNOWN;
        iint->ima_read_status = INTEGRITY_UNKNOWN;
+       iint->ima_creds_status = INTEGRITY_UNKNOWN;
        iint->evm_status = INTEGRITY_UNKNOWN;
        iint->measured_pcrs = 0;
        kmem_cache_free(iint_cache, iint);
@@ -158,6 +159,7 @@ static void init_once(void *foo)
        iint->ima_mmap_status = INTEGRITY_UNKNOWN;
        iint->ima_bprm_status = INTEGRITY_UNKNOWN;
        iint->ima_read_status = INTEGRITY_UNKNOWN;
+       iint->ima_creds_status = INTEGRITY_UNKNOWN;
        iint->evm_status = INTEGRITY_UNKNOWN;
        mutex_init(&iint->mutex);
 }
index 35ef69312811377125ff03923c5956ed6ab878af..6a8f67714c831570a674c2bde566191a544906fc 100644 (file)
@@ -10,6 +10,7 @@ config IMA
        select CRYPTO_HASH_INFO
        select TCG_TPM if HAS_IOMEM && !UML
        select TCG_TIS if TCG_TPM && X86
+       select TCG_CRB if TCG_TPM && ACPI
        select TCG_IBMVTPM if TCG_TPM && PPC_PSERIES
        help
          The Trusted Computing Group(TCG) runtime Integrity
index d52b487ad25955fb553813746e0d0c485434221f..35fe91aa1fc95ad6001adccbd85fa3856918879f 100644 (file)
@@ -177,6 +177,7 @@ static inline unsigned long ima_hash_key(u8 *digest)
        hook(FILE_CHECK)                \
        hook(MMAP_CHECK)                \
        hook(BPRM_CHECK)                \
+       hook(CREDS_CHECK)               \
        hook(POST_SETATTR)              \
        hook(MODULE_CHECK)              \
        hook(FIRMWARE_CHECK)            \
@@ -191,8 +192,8 @@ enum ima_hooks {
 };
 
 /* LIM API function definitions */
-int ima_get_action(struct inode *inode, int mask,
-                  enum ima_hooks func, int *pcr);
+int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
+                  int mask, enum ima_hooks func, int *pcr);
 int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
 int ima_collect_measurement(struct integrity_iint_cache *iint,
                            struct file *file, void *buf, loff_t size,
@@ -212,8 +213,8 @@ void ima_free_template_entry(struct ima_template_entry *entry);
 const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
 
 /* IMA policy related functions */
-int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
-                    int flags, int *pcr);
+int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
+                    enum ima_hooks func, int mask, int flags, int *pcr);
 void ima_init_policy(void);
 void ima_update_policy(void);
 void ima_update_policy_flag(void);
index 08fe405338e1fc0443b36ffea07088ece23f8af1..bf88236b7a0bfbb30b550b7e31c35ea1c0ebbaa3 100644 (file)
@@ -158,6 +158,8 @@ err_out:
 /**
  * ima_get_action - appraise & measure decision based on policy.
  * @inode: pointer to inode to measure
+ * @cred: pointer to credentials structure to validate
+ * @secid: secid of the task being validated
  * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXEC,
  *        MAY_APPEND)
  * @func: caller identifier
@@ -166,20 +168,21 @@ err_out:
  * The policy is defined in terms of keypairs:
  *             subj=, obj=, type=, func=, mask=, fsmagic=
  *     subj,obj, and type: are LSM specific.
- *     func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
+ *     func: FILE_CHECK | BPRM_CHECK | CREDS_CHECK | MMAP_CHECK | MODULE_CHECK
  *     mask: contains the permission mask
  *     fsmagic: hex value
  *
  * Returns IMA_MEASURE, IMA_APPRAISE mask.
  *
  */
-int ima_get_action(struct inode *inode, int mask, enum ima_hooks func, int *pcr)
+int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
+                  int mask, enum ima_hooks func, int *pcr)
 {
        int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
 
        flags &= ima_policy_flag;
 
-       return ima_match_policy(inode, func, mask, flags, pcr);
+       return ima_match_policy(inode, cred, secid, func, mask, flags, pcr);
 }
 
 /*
@@ -308,14 +311,17 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
                           const unsigned char *filename)
 {
        struct audit_buffer *ab;
-       char hash[(iint->ima_hash->length * 2) + 1];
+       char *hash;
        const char *algo_name = hash_algo_name[iint->ima_hash->algo];
-       char algo_hash[sizeof(hash) + strlen(algo_name) + 2];
        int i;
 
        if (iint->flags & IMA_AUDITED)
                return;
 
+       hash = kzalloc((iint->ima_hash->length * 2) + 1, GFP_KERNEL);
+       if (!hash)
+               return;
+
        for (i = 0; i < iint->ima_hash->length; i++)
                hex_byte_pack(hash + (i * 2), iint->ima_hash->digest[i]);
        hash[i * 2] = '\0';
@@ -323,18 +329,19 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
        ab = audit_log_start(current->audit_context, GFP_KERNEL,
                             AUDIT_INTEGRITY_RULE);
        if (!ab)
-               return;
+               goto out;
 
        audit_log_format(ab, "file=");
        audit_log_untrustedstring(ab, filename);
-       audit_log_format(ab, " hash=");
-       snprintf(algo_hash, sizeof(algo_hash), "%s:%s", algo_name, hash);
-       audit_log_untrustedstring(ab, algo_hash);
+       audit_log_format(ab, " hash=\"%s:%s\"", algo_name, hash);
 
        audit_log_task_info(ab, current);
        audit_log_end(ab);
 
        iint->flags |= IMA_AUDITED;
+out:
+       kfree(hash);
+       return;
 }
 
 /*
index f2803a40ff82ceb81160ca32d01eaf39263c1079..8bd7a0733e5179eca03febd8524a2c08b142cfde 100644 (file)
@@ -50,11 +50,14 @@ bool is_ima_appraise_enabled(void)
  */
 int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
 {
+       u32 secid;
+
        if (!ima_appraise)
                return 0;
 
-       return ima_match_policy(inode, func, mask, IMA_APPRAISE | IMA_HASH,
-                               NULL);
+       security_task_getsecid(current, &secid);
+       return ima_match_policy(inode, current_cred(), secid, func, mask,
+                               IMA_APPRAISE | IMA_HASH, NULL);
 }
 
 static int ima_fix_xattr(struct dentry *dentry,
@@ -87,6 +90,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
                return iint->ima_mmap_status;
        case BPRM_CHECK:
                return iint->ima_bprm_status;
+       case CREDS_CHECK:
+               return iint->ima_creds_status;
        case FILE_CHECK:
        case POST_SETATTR:
                return iint->ima_file_status;
@@ -107,6 +112,8 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
        case BPRM_CHECK:
                iint->ima_bprm_status = status;
                break;
+       case CREDS_CHECK:
+               iint->ima_creds_status = status;
        case FILE_CHECK:
        case POST_SETATTR:
                iint->ima_file_status = status;
@@ -128,6 +135,9 @@ static void ima_cache_flags(struct integrity_iint_cache *iint,
        case BPRM_CHECK:
                iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
                break;
+       case CREDS_CHECK:
+               iint->flags |= (IMA_CREDS_APPRAISED | IMA_APPRAISED);
+               break;
        case FILE_CHECK:
        case POST_SETATTR:
                iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
@@ -205,7 +215,7 @@ int ima_appraise_measurement(enum ima_hooks func,
                             int xattr_len, int opened)
 {
        static const char op[] = "appraise_data";
-       char *cause = "unknown";
+       const char *cause = "unknown";
        struct dentry *dentry = file_dentry(file);
        struct inode *inode = d_backing_inode(dentry);
        enum integrity_status status = INTEGRITY_UNKNOWN;
@@ -231,16 +241,22 @@ int ima_appraise_measurement(enum ima_hooks func,
        }
 
        status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
-       if ((status != INTEGRITY_PASS) &&
-           (status != INTEGRITY_PASS_IMMUTABLE) &&
-           (status != INTEGRITY_UNKNOWN)) {
-               if ((status == INTEGRITY_NOLABEL)
-                   || (status == INTEGRITY_NOXATTRS))
-                       cause = "missing-HMAC";
-               else if (status == INTEGRITY_FAIL)
-                       cause = "invalid-HMAC";
+       switch (status) {
+       case INTEGRITY_PASS:
+       case INTEGRITY_PASS_IMMUTABLE:
+       case INTEGRITY_UNKNOWN:
+               break;
+       case INTEGRITY_NOXATTRS:        /* No EVM protected xattrs. */
+       case INTEGRITY_NOLABEL:         /* No security.evm xattr. */
+               cause = "missing-HMAC";
                goto out;
+       case INTEGRITY_FAIL:            /* Invalid HMAC/signature. */
+               cause = "invalid-HMAC";
+               goto out;
+       default:
+               WARN_ONCE(true, "Unexpected integrity status %d\n", status);
        }
+
        switch (xattr_value->type) {
        case IMA_XATTR_DIGEST_NG:
                /* first byte contains algorithm id */
@@ -292,23 +308,40 @@ int ima_appraise_measurement(enum ima_hooks func,
        }
 
 out:
-       if (status != INTEGRITY_PASS) {
+       /*
+        * File signatures on some filesystems can not be properly verified.
+        * When such filesystems are mounted by an untrusted mounter or on a
+        * system not willing to accept such a risk, fail the file signature
+        * verification.
+        */
+       if ((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) &&
+           ((inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) ||
+            (iint->flags & IMA_FAIL_UNVERIFIABLE_SIGS))) {
+               status = INTEGRITY_FAIL;
+               cause = "unverifiable-signature";
+               integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
+                                   op, cause, rc, 0);
+       } else if (status != INTEGRITY_PASS) {
+               /* Fix mode, but don't replace file signatures. */
                if ((ima_appraise & IMA_APPRAISE_FIX) &&
                    (!xattr_value ||
                     xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
                        if (!ima_fix_xattr(dentry, iint))
                                status = INTEGRITY_PASS;
-               } else if ((inode->i_size == 0) &&
-                          (iint->flags & IMA_NEW_FILE) &&
-                          (xattr_value &&
-                           xattr_value->type == EVM_IMA_XATTR_DIGSIG)) {
+               }
+
+               /* Permit new files with file signatures, but without data. */
+               if (inode->i_size == 0 && iint->flags & IMA_NEW_FILE &&
+                   xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG) {
                        status = INTEGRITY_PASS;
                }
+
                integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
                                    op, cause, rc, 0);
        } else {
                ima_cache_flags(iint, func);
        }
+
        ima_set_cache_status(iint, func, status);
        return status;
 }
index 205bc69361ea6667febadb403c6cede9a7827a52..4e085a17124f1a87154b675d833941e998271c13 100644 (file)
@@ -73,6 +73,8 @@ int __init ima_init_crypto(void)
                       hash_algo_name[ima_hash_algo], rc);
                return rc;
        }
+       pr_info("Allocated hash algorithm: %s\n",
+               hash_algo_name[ima_hash_algo]);
        return 0;
 }
 
index 2cfb0c71496766b3b69f8700f0bf7f07789ca7c6..74d0bd7e76d7159234c036df6c053532f58ab4a4 100644 (file)
@@ -16,6 +16,9 @@
  *     implements the IMA hooks: ima_bprm_check, ima_file_mmap,
  *     and ima_file_check.
  */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
 #include <linux/module.h>
 #include <linux/file.h>
 #include <linux/binfmts.h>
@@ -25,6 +28,7 @@
 #include <linux/xattr.h>
 #include <linux/ima.h>
 #include <linux/iversion.h>
+#include <linux/fs.h>
 
 #include "ima.h"
 
@@ -167,8 +171,9 @@ void ima_file_free(struct file *file)
        ima_check_last_writer(iint, inode, file);
 }
 
-static int process_measurement(struct file *file, char *buf, loff_t size,
-                              int mask, enum ima_hooks func, int opened)
+static int process_measurement(struct file *file, const struct cred *cred,
+                              u32 secid, char *buf, loff_t size, int mask,
+                              enum ima_hooks func, int opened)
 {
        struct inode *inode = file_inode(file);
        struct integrity_iint_cache *iint = NULL;
@@ -190,7 +195,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
         * bitmask based on the appraise/audit/measurement policy.
         * Included is the appraise submask.
         */
-       action = ima_get_action(inode, mask, func, &pcr);
+       action = ima_get_action(inode, cred, secid, mask, func, &pcr);
        violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
                           (ima_policy_flag & IMA_MEASURE));
        if (!action && !violation_check)
@@ -229,9 +234,18 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
                                 IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
                                 IMA_ACTION_FLAGS);
 
-       if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags))
-               /* reset all flags if ima_inode_setxattr was called */
+       /*
+        * Re-evaulate the file if either the xattr has changed or the
+        * kernel has no way of detecting file change on the filesystem.
+        * (Limited to privileged mounted filesystems.)
+        */
+       if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags) ||
+           ((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) &&
+            !(inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) &&
+            !(action & IMA_FAIL_UNVERIFIABLE_SIGS))) {
                iint->flags &= ~IMA_DONE_MASK;
+               iint->measured_pcrs = 0;
+       }
 
        /* Determine if already appraised/measured based on bitmask
         * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
@@ -324,9 +338,14 @@ out:
  */
 int ima_file_mmap(struct file *file, unsigned long prot)
 {
-       if (file && (prot & PROT_EXEC))
-               return process_measurement(file, NULL, 0, MAY_EXEC,
-                                          MMAP_CHECK, 0);
+       u32 secid;
+
+       if (file && (prot & PROT_EXEC)) {
+               security_task_getsecid(current, &secid);
+               return process_measurement(file, current_cred(), secid, NULL,
+                                          0, MAY_EXEC, MMAP_CHECK, 0);
+       }
+
        return 0;
 }
 
@@ -345,8 +364,18 @@ int ima_file_mmap(struct file *file, unsigned long prot)
  */
 int ima_bprm_check(struct linux_binprm *bprm)
 {
-       return process_measurement(bprm->file, NULL, 0, MAY_EXEC,
-                                  BPRM_CHECK, 0);
+       int ret;
+       u32 secid;
+
+       security_task_getsecid(current, &secid);
+       ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
+                                 MAY_EXEC, BPRM_CHECK, 0);
+       if (ret)
+               return ret;
+
+       security_cred_getsecid(bprm->cred, &secid);
+       return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
+                                  MAY_EXEC, CREDS_CHECK, 0);
 }
 
 /**
@@ -361,7 +390,10 @@ int ima_bprm_check(struct linux_binprm *bprm)
  */
 int ima_file_check(struct file *file, int mask, int opened)
 {
-       return process_measurement(file, NULL, 0,
+       u32 secid;
+
+       security_task_getsecid(current, &secid);
+       return process_measurement(file, current_cred(), secid, NULL, 0,
                                   mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
                                           MAY_APPEND), FILE_CHECK, opened);
 }
@@ -440,6 +472,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
                       enum kernel_read_file_id read_id)
 {
        enum ima_hooks func;
+       u32 secid;
 
        if (!file && read_id == READING_FIRMWARE) {
                if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
@@ -462,7 +495,9 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
        }
 
        func = read_idmap[read_id] ?: FILE_CHECK;
-       return process_measurement(file, buf, size, MAY_READ, func, 0);
+       security_task_getsecid(current, &secid);
+       return process_measurement(file, current_cred(), secid, buf, size,
+                                  MAY_READ, func, 0);
 }
 
 static int __init init_ima(void)
@@ -472,6 +507,16 @@ static int __init init_ima(void)
        ima_init_template_list();
        hash_setup(CONFIG_IMA_DEFAULT_HASH);
        error = ima_init();
+
+       if (error && strcmp(hash_algo_name[ima_hash_algo],
+                           CONFIG_IMA_DEFAULT_HASH) != 0) {
+               pr_info("Allocating %s failed, going to use default hash algorithm %s\n",
+                       hash_algo_name[ima_hash_algo], CONFIG_IMA_DEFAULT_HASH);
+               hash_setup_done = 0;
+               hash_setup(CONFIG_IMA_DEFAULT_HASH);
+               error = ima_init();
+       }
+
        if (!error) {
                ima_initialized = 1;
                ima_update_policy_flag();
index 915f5572c6ffac5cc9703fab6127cc1757092582..d89bebf85421e7134aff233a52545218efabf512 100644 (file)
@@ -96,6 +96,7 @@ static struct ima_rule_entry dont_measure_rules[] __ro_after_init = {
        {.action = DONT_MEASURE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
+       {.action = DONT_MEASURE, .fsmagic = SMACK_MAGIC, .flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE, .fsmagic = CGROUP_SUPER_MAGIC,
         .flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC,
@@ -141,6 +142,7 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = {
        {.action = DONT_APPRAISE, .fsmagic = BINFMTFS_MAGIC, .flags = IMA_FSMAGIC},
        {.action = DONT_APPRAISE, .fsmagic = SECURITYFS_MAGIC, .flags = IMA_FSMAGIC},
        {.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE, .fsmagic = SMACK_MAGIC, .flags = IMA_FSMAGIC},
        {.action = DONT_APPRAISE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC},
        {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC},
        {.action = DONT_APPRAISE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC},
@@ -188,6 +190,7 @@ __setup("ima_tcb", default_measure_policy_setup);
 
 static bool ima_use_appraise_tcb __initdata;
 static bool ima_use_secure_boot __initdata;
+static bool ima_fail_unverifiable_sigs __ro_after_init;
 static int __init policy_setup(char *str)
 {
        char *p;
@@ -201,6 +204,8 @@ static int __init policy_setup(char *str)
                        ima_use_appraise_tcb = true;
                else if (strcmp(p, "secure_boot") == 0)
                        ima_use_secure_boot = true;
+               else if (strcmp(p, "fail_securely") == 0)
+                       ima_fail_unverifiable_sigs = true;
        }
 
        return 1;
@@ -243,16 +248,17 @@ static void ima_lsm_update_rules(void)
  * ima_match_rules - determine whether an inode matches the measure rule.
  * @rule: a pointer to a rule
  * @inode: a pointer to an inode
+ * @cred: a pointer to a credentials structure for user validation
+ * @secid: the secid of the task to be validated
  * @func: LIM hook identifier
  * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
  *
  * Returns true on rule match, false on failure.
  */
 static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
+                           const struct cred *cred, u32 secid,
                            enum ima_hooks func, int mask)
 {
-       struct task_struct *tsk = current;
-       const struct cred *cred = current_cred();
        int i;
 
        if ((rule->flags & IMA_FUNC) &&
@@ -287,7 +293,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
                return false;
        for (i = 0; i < MAX_LSM_RULES; i++) {
                int rc = 0;
-               u32 osid, sid;
+               u32 osid;
                int retried = 0;
 
                if (!rule->lsm[i].rule)
@@ -307,8 +313,7 @@ retry:
                case LSM_SUBJ_USER:
                case LSM_SUBJ_ROLE:
                case LSM_SUBJ_TYPE:
-                       security_task_getsecid(tsk, &sid);
-                       rc = security_filter_rule_match(sid,
+                       rc = security_filter_rule_match(secid,
                                                        rule->lsm[i].type,
                                                        Audit_equal,
                                                        rule->lsm[i].rule,
@@ -341,6 +346,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
                return IMA_MMAP_APPRAISE;
        case BPRM_CHECK:
                return IMA_BPRM_APPRAISE;
+       case CREDS_CHECK:
+               return IMA_CREDS_APPRAISE;
        case FILE_CHECK:
        case POST_SETATTR:
                return IMA_FILE_APPRAISE;
@@ -353,6 +360,9 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
 /**
  * ima_match_policy - decision based on LSM and other conditions
  * @inode: pointer to an inode for which the policy decision is being made
+ * @cred: pointer to a credentials structure for which the policy decision is
+ *        being made
+ * @secid: LSM secid of the task to be validated
  * @func: IMA hook identifier
  * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
  * @pcr: set the pcr to extend
@@ -364,8 +374,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * list when walking it.  Reads are many orders of magnitude more numerous
  * than writes so ima_match_policy() is classical RCU candidate.
  */
-int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
-                    int flags, int *pcr)
+int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
+                    enum ima_hooks func, int mask, int flags, int *pcr)
 {
        struct ima_rule_entry *entry;
        int action = 0, actmask = flags | (flags << 1);
@@ -376,7 +386,7 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
                if (!(entry->action & actmask))
                        continue;
 
-               if (!ima_match_rules(entry, inode, func, mask))
+               if (!ima_match_rules(entry, inode, cred, secid, func, mask))
                        continue;
 
                action |= entry->flags & IMA_ACTION_FLAGS;
@@ -384,7 +394,9 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
                action |= entry->action & IMA_DO_MASK;
                if (entry->action & IMA_APPRAISE) {
                        action |= get_subaction(entry, func);
-                       action ^= IMA_HASH;
+                       action &= ~IMA_HASH;
+                       if (ima_fail_unverifiable_sigs)
+                               action |= IMA_FAIL_UNVERIFIABLE_SIGS;
                }
 
                if (entry->action & IMA_DO_MASK)
@@ -713,6 +725,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
                                entry->func = MMAP_CHECK;
                        else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
                                entry->func = BPRM_CHECK;
+                       else if (strcmp(args[0].from, "CREDS_CHECK") == 0)
+                               entry->func = CREDS_CHECK;
                        else if (strcmp(args[0].from, "KEXEC_KERNEL_CHECK") ==
                                 0)
                                entry->func = KEXEC_KERNEL_CHECK;
index 28af43f63572a7df9ea435656aa924552bbac8a6..5afaa53decc50416ad5963a82ddecb47f4c2f9c5 100644 (file)
@@ -378,16 +378,11 @@ int ima_eventname_ng_init(struct ima_event_data *event_data,
 int ima_eventsig_init(struct ima_event_data *event_data,
                      struct ima_field_data *field_data)
 {
-       enum data_formats fmt = DATA_FMT_HEX;
        struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
-       int xattr_len = event_data->xattr_len;
-       int rc = 0;
 
        if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG))
-               goto out;
+               return 0;
 
-       rc = ima_write_template_field_data(xattr_value, xattr_len, fmt,
-                                          field_data);
-out:
-       return rc;
+       return ima_write_template_field_data(xattr_value, event_data->xattr_len,
+                                            DATA_FMT_HEX, field_data);
 }
index 50a8e3365df7387cb62ee03ad605af1905c1ad97..5e58e02ba8dc97ff1c4a88f5722109f4783d3f62 100644 (file)
 
 /* iint cache flags */
 #define IMA_ACTION_FLAGS       0xff000000
-#define IMA_ACTION_RULE_FLAGS  0x06000000
 #define IMA_DIGSIG_REQUIRED    0x01000000
 #define IMA_PERMIT_DIRECTIO    0x02000000
 #define IMA_NEW_FILE           0x04000000
 #define EVM_IMMUTABLE_DIGSIG   0x08000000
+#define IMA_FAIL_UNVERIFIABLE_SIGS     0x10000000
 
 #define IMA_DO_MASK            (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
                                 IMA_HASH | IMA_APPRAISE_SUBMASK)
 #define IMA_BPRM_APPRAISED     0x00020000
 #define IMA_READ_APPRAISE      0x00040000
 #define IMA_READ_APPRAISED     0x00080000
+#define IMA_CREDS_APPRAISE     0x00100000
+#define IMA_CREDS_APPRAISED    0x00200000
 #define IMA_APPRAISE_SUBMASK   (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
-                                IMA_BPRM_APPRAISE | IMA_READ_APPRAISE)
+                                IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
+                                IMA_CREDS_APPRAISE)
 #define IMA_APPRAISED_SUBMASK  (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
-                                IMA_BPRM_APPRAISED | IMA_READ_APPRAISED)
+                                IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
+                                IMA_CREDS_APPRAISED)
 
 /* iint cache atomic_flags */
 #define IMA_CHANGE_XATTR       0
@@ -121,6 +125,7 @@ struct integrity_iint_cache {
        enum integrity_status ima_mmap_status:4;
        enum integrity_status ima_bprm_status:4;
        enum integrity_status ima_read_status:4;
+       enum integrity_status ima_creds_status:4;
        enum integrity_status evm_status:4;
        struct ima_digest_data *ima_hash;
 };
index 7301902a872171e028749771f6c599a0b39f103c..d2a84cda7e8d4de1d650a5da1d899a5f5caf0960 100644 (file)
@@ -1005,6 +1005,13 @@ void security_transfer_creds(struct cred *new, const struct cred *old)
        call_void_hook(cred_transfer, new, old);
 }
 
+void security_cred_getsecid(const struct cred *c, u32 *secid)
+{
+       *secid = 0;
+       call_void_hook(cred_getsecid, c, secid);
+}
+EXPORT_SYMBOL(security_cred_getsecid);
+
 int security_kernel_act_as(struct cred *new, u32 secid)
 {
        return call_int_hook(kernel_act_as, 0, new, secid);
index 2b8c55e181aea5815327fa637873513ba885f7ed..1eeb70e439d7999646d3fab9c38da5ea60cf1b60 100644 (file)
@@ -3947,6 +3947,11 @@ static void selinux_cred_transfer(struct cred *new, const struct cred *old)
        *tsec = *old_tsec;
 }
 
+static void selinux_cred_getsecid(const struct cred *c, u32 *secid)
+{
+       *secid = cred_sid(c);
+}
+
 /*
  * set the security data for a kernel service
  * - all the creation contexts are set to unlabelled
@@ -6926,6 +6931,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(cred_free, selinux_cred_free),
        LSM_HOOK_INIT(cred_prepare, selinux_cred_prepare),
        LSM_HOOK_INIT(cred_transfer, selinux_cred_transfer),
+       LSM_HOOK_INIT(cred_getsecid, selinux_cred_getsecid),
        LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as),
        LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as),
        LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request),
index 57fef8e04658f4cf67a2d7c4f998df637960c24f..73549007bf9e63d75920376b71671d5bd55ca3f0 100644 (file)
@@ -2049,6 +2049,23 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
        /* cbs copy rule list */
 }
 
+/**
+ * smack_cred_getsecid - get the secid corresponding to a creds structure
+ * @c: the object creds
+ * @secid: where to put the result
+ *
+ * Sets the secid to contain a u32 version of the smack label.
+ */
+static void smack_cred_getsecid(const struct cred *c, u32 *secid)
+{
+       struct smack_known *skp;
+
+       rcu_read_lock();
+       skp = smk_of_task(c->security);
+       *secid = skp->smk_secid;
+       rcu_read_unlock();
+}
+
 /**
  * smack_kernel_act_as - Set the subjective context in a set of credentials
  * @new: points to the set of credentials to be modified.
@@ -3348,6 +3365,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
        if (opt_dentry->d_parent == opt_dentry) {
                switch (sbp->s_magic) {
                case CGROUP_SUPER_MAGIC:
+               case CGROUP2_SUPER_MAGIC:
                        /*
                         * The cgroup filesystem is never mounted,
                         * so there's no opportunity to set the mount
@@ -3391,6 +3409,7 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode)
        switch (sbp->s_magic) {
        case SMACK_MAGIC:
        case CGROUP_SUPER_MAGIC:
+       case CGROUP2_SUPER_MAGIC:
                /*
                 * Casey says that it's a little embarrassing
                 * that the smack file system doesn't do
@@ -4652,6 +4671,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(cred_free, smack_cred_free),
        LSM_HOOK_INIT(cred_prepare, smack_cred_prepare),
        LSM_HOOK_INIT(cred_transfer, smack_cred_transfer),
+       LSM_HOOK_INIT(cred_getsecid, smack_cred_getsecid),
        LSM_HOOK_INIT(kernel_act_as, smack_kernel_act_as),
        LSM_HOOK_INIT(kernel_create_files_as, smack_kernel_create_files_as),
        LSM_HOOK_INIT(task_setpgid, smack_task_setpgid),