Commit 9c6a019c authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 updates from Martin Schwidefsky:
 "The big one is support for fake NUMA, splitting a really large machine
  in more manageable piece improves performance in some cases, e.g. for
  a KVM host.

  The FICON Link Incident handling has been improved, this helps the
  operator to identify degraded or non-operational FICON connections.

  The save and restore of floating point and vector registers has been
  overhauled to allow the future use of vector registers in the kernel.

  A few small enhancement, magic sys-requests for the vt220 console via
  SCLP, some more assembler code has been converted to C, the PCI error
  handling is improved.

  And the usual cleanup and bug fixing"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (59 commits)
  s390/jump_label: Use %*ph to print small buffers
  s390/sclp_vt220: support magic sysrequests
  s390/ctrlchar: improve handling of magic sysrequests
  s390/numa: remove superfluous ARCH_WANT defines
  s390/3270: redraw screen on unsolicited device end
  s390/dcssblk: correct out of bounds array indexes
  s390/mm: simplify page table alloc/free code
  s390/pci: move debug messages to debugfs
  s390/nmi: initialize control register 0 earlier
  s390/zcrypt: use msleep() instead of mdelay()
  s390/hmcdrv: fix interrupt registration
  s390/setup: fix novx parameter
  s390/uaccess: remove uaccess_primary kernel parameter
  s390: remove unneeded sizeof(void *) comparisons
  s390/facilities: remove transactional-execution bits
  s390/numa: re-add DIE sched_domain_topology_level
  s390/dasd: enhance CUIR scope detection
  s390/dasd: fix failing path verification
  s390/vdso: emit a GNU hash
  s390/numa: make core to node mapping data dynamic
  ...
parents 7c019191 e4ec7351
...@@ -5907,7 +5907,6 @@ F: arch/powerpc/kvm/ ...@@ -5907,7 +5907,6 @@ F: arch/powerpc/kvm/
KERNEL VIRTUAL MACHINE for s390 (KVM/s390) KERNEL VIRTUAL MACHINE for s390 (KVM/s390)
M: Christian Borntraeger <borntraeger@de.ibm.com> M: Christian Borntraeger <borntraeger@de.ibm.com>
M: Cornelia Huck <cornelia.huck@de.ibm.com> M: Cornelia Huck <cornelia.huck@de.ibm.com>
M: linux390@de.ibm.com
L: linux-s390@vger.kernel.org L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/ W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported S: Supported
...@@ -8718,7 +8717,6 @@ F: drivers/video/fbdev/savage/ ...@@ -8718,7 +8717,6 @@ F: drivers/video/fbdev/savage/
S390 S390
M: Martin Schwidefsky <schwidefsky@de.ibm.com> M: Martin Schwidefsky <schwidefsky@de.ibm.com>
M: Heiko Carstens <heiko.carstens@de.ibm.com> M: Heiko Carstens <heiko.carstens@de.ibm.com>
M: linux390@de.ibm.com
L: linux-s390@vger.kernel.org L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/ W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported S: Supported
...@@ -8746,7 +8744,6 @@ F: block/partitions/ibm.c ...@@ -8746,7 +8744,6 @@ F: block/partitions/ibm.c
S390 NETWORK DRIVERS S390 NETWORK DRIVERS
M: Ursula Braun <ursula.braun@de.ibm.com> M: Ursula Braun <ursula.braun@de.ibm.com>
M: linux390@de.ibm.com
L: linux-s390@vger.kernel.org L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/ W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported S: Supported
...@@ -8763,7 +8760,6 @@ F: drivers/pci/hotplug/s390_pci_hpc.c ...@@ -8763,7 +8760,6 @@ F: drivers/pci/hotplug/s390_pci_hpc.c
S390 ZCRYPT DRIVER S390 ZCRYPT DRIVER
M: Ingo Tuchscherer <ingo.tuchscherer@de.ibm.com> M: Ingo Tuchscherer <ingo.tuchscherer@de.ibm.com>
M: linux390@de.ibm.com
L: linux-s390@vger.kernel.org L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/ W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported S: Supported
...@@ -8771,7 +8767,6 @@ F: drivers/s390/crypto/ ...@@ -8771,7 +8767,6 @@ F: drivers/s390/crypto/
S390 ZFCP DRIVER S390 ZFCP DRIVER
M: Steffen Maier <maier@linux.vnet.ibm.com> M: Steffen Maier <maier@linux.vnet.ibm.com>
M: linux390@de.ibm.com
L: linux-s390@vger.kernel.org L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/ W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported S: Supported
...@@ -8779,7 +8774,6 @@ F: drivers/s390/scsi/zfcp_* ...@@ -8779,7 +8774,6 @@ F: drivers/s390/scsi/zfcp_*
S390 IUCV NETWORK LAYER S390 IUCV NETWORK LAYER
M: Ursula Braun <ursula.braun@de.ibm.com> M: Ursula Braun <ursula.braun@de.ibm.com>
M: linux390@de.ibm.com
L: linux-s390@vger.kernel.org L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/ W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported S: Supported
......
...@@ -6,3 +6,4 @@ obj-$(CONFIG_S390_HYPFS_FS) += hypfs/ ...@@ -6,3 +6,4 @@ obj-$(CONFIG_S390_HYPFS_FS) += hypfs/
obj-$(CONFIG_APPLDATA_BASE) += appldata/ obj-$(CONFIG_APPLDATA_BASE) += appldata/
obj-y += net/ obj-y += net/
obj-$(CONFIG_PCI) += pci/ obj-$(CONFIG_PCI) += pci/
obj-$(CONFIG_NUMA) += numa/
...@@ -99,18 +99,22 @@ config S390 ...@@ -99,18 +99,22 @@ config S390
select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
select ARCH_SAVE_PAGE_KEYS if HIBERNATION select ARCH_SAVE_PAGE_KEYS if HIBERNATION
select ARCH_SUPPORTS_ATOMIC_RMW select ARCH_SUPPORTS_ATOMIC_RMW
select ARCH_SUPPORTS_NUMA_BALANCING
select ARCH_USE_CMPXCHG_LOCKREF select ARCH_USE_CMPXCHG_LOCKREF
select ARCH_WANTS_PROT_NUMA_PROT_NONE
select ARCH_WANT_IPC_PARSE_VERSION select ARCH_WANT_IPC_PARSE_VERSION
select BUILDTIME_EXTABLE_SORT select BUILDTIME_EXTABLE_SORT
select CLONE_BACKWARDS2 select CLONE_BACKWARDS2
select DYNAMIC_FTRACE if FUNCTION_TRACER select DYNAMIC_FTRACE if FUNCTION_TRACER
select GENERIC_CLOCKEVENTS select GENERIC_CLOCKEVENTS
select GENERIC_CPU_AUTOPROBE
select GENERIC_CPU_DEVICES if !SMP select GENERIC_CPU_DEVICES if !SMP
select GENERIC_FIND_FIRST_BIT select GENERIC_FIND_FIRST_BIT
select GENERIC_SMP_IDLE_THREAD select GENERIC_SMP_IDLE_THREAD
select GENERIC_TIME_VSYSCALL select GENERIC_TIME_VSYSCALL
select HAVE_ALIGNED_STRUCT_PAGE if SLUB select HAVE_ALIGNED_STRUCT_PAGE if SLUB
select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_EARLY_PFN_TO_NID
select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL
select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_SECCOMP_FILTER
select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRACEHOOK
...@@ -153,6 +157,7 @@ config S390 ...@@ -153,6 +157,7 @@ config S390
select VIRT_CPU_ACCOUNTING select VIRT_CPU_ACCOUNTING
select VIRT_TO_BUS select VIRT_TO_BUS
config SCHED_OMIT_FRAME_POINTER config SCHED_OMIT_FRAME_POINTER
def_bool y def_bool y
...@@ -385,6 +390,76 @@ config HOTPLUG_CPU ...@@ -385,6 +390,76 @@ config HOTPLUG_CPU
config SCHED_SMT config SCHED_SMT
def_bool n def_bool n
# Some NUMA nodes have memory ranges that span
# other nodes. Even though a pfn is valid and
# between a node's start and end pfns, it may not
# reside on that node. See memmap_init_zone()
# for details. <- They meant memory holes!
config NODES_SPAN_OTHER_NODES
def_bool NUMA
config NUMA
bool "NUMA support"
depends on SMP && 64BIT && SCHED_TOPOLOGY
default n
help
Enable NUMA support
This option adds NUMA support to the kernel.
An operation mode can be selected by appending
numa=<method> to the kernel command line.
The default behaviour is identical to appending numa=plain to
the command line. This will create just one node with all
available memory and all CPUs in it.
config NODES_SHIFT
int "Maximum NUMA nodes (as a power of 2)"
range 1 10
depends on NUMA
default "4"
help
Specify the maximum number of NUMA nodes available on the target
system. Increases memory reserved to accommodate various tables.
menu "Select NUMA modes"
depends on NUMA
config NUMA_EMU
bool "NUMA emulation"
default y
help
Numa emulation mode will split the available system memory into
equal chunks which then are distributed over the configured number
of nodes in a round-robin manner.
The number of fake nodes is limited by the number of available memory
chunks (i.e. memory size / fake size) and the number of supported
nodes in the kernel.
The CPUs are assigned to the nodes in a way that partially respects
the original machine topology (if supported by the machine).
Fair distribution of the CPUs is not guaranteed.
config EMU_SIZE
hex "NUMA emulation memory chunk size"
default 0x10000000
range 0x400000 0x100000000
depends on NUMA_EMU
help
Select the default size by which the memory is chopped and then
assigned to emulated NUMA nodes.
This can be overridden by specifying
emu_size=<n>
on the kernel command line where also suffixes K, M, G, and T are
supported.
endmenu
config SCHED_MC config SCHED_MC
def_bool n def_bool n
......
...@@ -33,6 +33,8 @@ mflags-$(CONFIG_MARCH_Z196) := -march=z196 ...@@ -33,6 +33,8 @@ mflags-$(CONFIG_MARCH_Z196) := -march=z196
mflags-$(CONFIG_MARCH_ZEC12) := -march=zEC12 mflags-$(CONFIG_MARCH_ZEC12) := -march=zEC12
mflags-$(CONFIG_MARCH_Z13) := -march=z13 mflags-$(CONFIG_MARCH_Z13) := -march=z13
export CC_FLAGS_MARCH := $(mflags-y)
aflags-y += $(mflags-y) aflags-y += $(mflags-y)
cflags-y += $(mflags-y) cflags-y += $(mflags-y)
......
...@@ -13,6 +13,7 @@ CONFIG_TASK_IO_ACCOUNTING=y ...@@ -13,6 +13,7 @@ CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_FAST_NO_HZ=y CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y CONFIG_IKCONFIG_PROC=y
CONFIG_NUMA_BALANCING=y
CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y CONFIG_CPUSETS=y
...@@ -50,6 +51,7 @@ CONFIG_LIVEPATCH=y ...@@ -50,6 +51,7 @@ CONFIG_LIVEPATCH=y
CONFIG_MARCH_Z196=y CONFIG_MARCH_Z196=y
CONFIG_TUNE_ZEC12=y CONFIG_TUNE_ZEC12=y
CONFIG_NR_CPUS=256 CONFIG_NR_CPUS=256
CONFIG_NUMA=y
CONFIG_PREEMPT=y CONFIG_PREEMPT=y
CONFIG_HZ_100=y CONFIG_HZ_100=y
CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTPLUG=y
......
...@@ -13,6 +13,7 @@ CONFIG_TASK_IO_ACCOUNTING=y ...@@ -13,6 +13,7 @@ CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_FAST_NO_HZ=y CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y CONFIG_IKCONFIG_PROC=y
CONFIG_NUMA_BALANCING=y
CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y CONFIG_CPUSETS=y
...@@ -49,6 +50,7 @@ CONFIG_DEFAULT_DEADLINE=y ...@@ -49,6 +50,7 @@ CONFIG_DEFAULT_DEADLINE=y
CONFIG_MARCH_Z196=y CONFIG_MARCH_Z196=y
CONFIG_TUNE_ZEC12=y CONFIG_TUNE_ZEC12=y
CONFIG_NR_CPUS=256 CONFIG_NR_CPUS=256
CONFIG_NUMA=y
CONFIG_HZ_100=y CONFIG_HZ_100=y
CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTPLUG=y
CONFIG_MEMORY_HOTREMOVE=y CONFIG_MEMORY_HOTREMOVE=y
......
...@@ -13,6 +13,8 @@ CONFIG_TASK_IO_ACCOUNTING=y ...@@ -13,6 +13,8 @@ CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_RCU_FAST_NO_HZ=y CONFIG_RCU_FAST_NO_HZ=y
CONFIG_IKCONFIG=y CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y CONFIG_IKCONFIG_PROC=y
CONFIG_NUMA_BALANCING=y
# CONFIG_NUMA_BALANCING_DEFAULT_ENABLED is not set
CONFIG_CGROUP_FREEZER=y CONFIG_CGROUP_FREEZER=y
CONFIG_CGROUP_DEVICE=y CONFIG_CGROUP_DEVICE=y
CONFIG_CPUSETS=y CONFIG_CPUSETS=y
...@@ -48,6 +50,7 @@ CONFIG_LIVEPATCH=y ...@@ -48,6 +50,7 @@ CONFIG_LIVEPATCH=y
CONFIG_MARCH_Z196=y CONFIG_MARCH_Z196=y
CONFIG_TUNE_ZEC12=y CONFIG_TUNE_ZEC12=y
CONFIG_NR_CPUS=512 CONFIG_NR_CPUS=512
CONFIG_NUMA=y
CONFIG_HZ_100=y CONFIG_HZ_100=y
CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTPLUG=y
CONFIG_MEMORY_HOTREMOVE=y CONFIG_MEMORY_HOTREMOVE=y
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <crypto/algapi.h> #include <crypto/algapi.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpufeature.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include "crypt_s390.h" #include "crypt_s390.h"
...@@ -976,7 +977,7 @@ static void __exit aes_s390_fini(void) ...@@ -976,7 +977,7 @@ static void __exit aes_s390_fini(void)
crypto_unregister_alg(&aes_alg); crypto_unregister_alg(&aes_alg);
} }
module_init(aes_s390_init); module_cpu_feature_match(MSA, aes_s390_init);
module_exit(aes_s390_fini); module_exit(aes_s390_fini);
MODULE_ALIAS_CRYPTO("aes-all"); MODULE_ALIAS_CRYPTO("aes-all");
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpufeature.h>
#include <linux/crypto.h> #include <linux/crypto.h>
#include <crypto/algapi.h> #include <crypto/algapi.h>
#include <crypto/des.h> #include <crypto/des.h>
...@@ -616,7 +617,7 @@ static void __exit des_s390_exit(void) ...@@ -616,7 +617,7 @@ static void __exit des_s390_exit(void)
crypto_unregister_alg(&des_alg); crypto_unregister_alg(&des_alg);
} }
module_init(des_s390_init); module_cpu_feature_match(MSA, des_s390_init);
module_exit(des_s390_exit); module_exit(des_s390_exit);
MODULE_ALIAS_CRYPTO("des"); MODULE_ALIAS_CRYPTO("des");
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <crypto/internal/hash.h> #include <crypto/internal/hash.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpufeature.h>
#include "crypt_s390.h" #include "crypt_s390.h"
...@@ -158,7 +159,7 @@ static void __exit ghash_mod_exit(void) ...@@ -158,7 +159,7 @@ static void __exit ghash_mod_exit(void)
crypto_unregister_shash(&ghash_alg); crypto_unregister_shash(&ghash_alg);
} }
module_init(ghash_mod_init); module_cpu_feature_match(MSA, ghash_mod_init);
module_exit(ghash_mod_exit); module_exit(ghash_mod_exit);
MODULE_ALIAS_CRYPTO("ghash"); MODULE_ALIAS_CRYPTO("ghash");
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/cpufeature.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/debug.h> #include <asm/debug.h>
...@@ -914,6 +915,5 @@ static void __exit prng_exit(void) ...@@ -914,6 +915,5 @@ static void __exit prng_exit(void)
} }
} }
module_cpu_feature_match(MSA, prng_init);
module_init(prng_init);
module_exit(prng_exit); module_exit(prng_exit);
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <crypto/internal/hash.h> #include <crypto/internal/hash.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpufeature.h>
#include <crypto/sha.h> #include <crypto/sha.h>
#include "crypt_s390.h" #include "crypt_s390.h"
...@@ -100,7 +101,7 @@ static void __exit sha1_s390_fini(void) ...@@ -100,7 +101,7 @@ static void __exit sha1_s390_fini(void)
crypto_unregister_shash(&alg); crypto_unregister_shash(&alg);
} }
module_init(sha1_s390_init); module_cpu_feature_match(MSA, sha1_s390_init);
module_exit(sha1_s390_fini); module_exit(sha1_s390_fini);
MODULE_ALIAS_CRYPTO("sha1"); MODULE_ALIAS_CRYPTO("sha1");
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <crypto/internal/hash.h> #include <crypto/internal/hash.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpufeature.h>
#include <crypto/sha.h> #include <crypto/sha.h>
#include "crypt_s390.h" #include "crypt_s390.h"
...@@ -140,7 +141,7 @@ static void __exit sha256_s390_fini(void) ...@@ -140,7 +141,7 @@ static void __exit sha256_s390_fini(void)
crypto_unregister_shash(&sha256_alg); crypto_unregister_shash(&sha256_alg);
} }
module_init(sha256_s390_init); module_cpu_feature_match(MSA, sha256_s390_init);
module_exit(sha256_s390_fini); module_exit(sha256_s390_fini);
MODULE_ALIAS_CRYPTO("sha256"); MODULE_ALIAS_CRYPTO("sha256");
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/cpufeature.h>
#include "sha.h" #include "sha.h"
#include "crypt_s390.h" #include "crypt_s390.h"
...@@ -148,7 +149,7 @@ static void __exit fini(void) ...@@ -148,7 +149,7 @@ static void __exit fini(void)
crypto_unregister_shash(&sha384_alg); crypto_unregister_shash(&sha384_alg);
} }
module_init(init); module_cpu_feature_match(MSA, init);
module_exit(fini); module_exit(fini);
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
......
/*
* Module interface for CPU features
*
* Copyright IBM Corp. 2015
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
*/
#ifndef __ASM_S390_CPUFEATURE_H
#define __ASM_S390_CPUFEATURE_H
#include <asm/elf.h>
/* Hardware features on Linux on z Systems are indicated by facility bits that
* are mapped to the so-called machine flags. Particular machine flags are
* then used to define ELF hardware capabilities; most notably hardware flags
* that are essential for user space / glibc.
*
* Restrict the set of exposed CPU features to ELF hardware capabilities for
* now. Additional machine flags can be indicated by values larger than
* MAX_ELF_HWCAP_FEATURES.
*/
#define MAX_ELF_HWCAP_FEATURES (8 * sizeof(elf_hwcap))
#define MAX_CPU_FEATURES MAX_ELF_HWCAP_FEATURES
#define cpu_feature(feat) ilog2(HWCAP_S390_ ## feat)
int cpu_have_feature(unsigned int nr);
#endif /* __ASM_S390_CPUFEATURE_H */
...@@ -46,6 +46,8 @@ static inline void __ctl_clear_bit(unsigned int cr, unsigned int bit) ...@@ -46,6 +46,8 @@ static inline void __ctl_clear_bit(unsigned int cr, unsigned int bit)
__ctl_load(reg, cr, cr); __ctl_load(reg, cr, cr);
} }
void __ctl_set_vx(void);
void smp_ctl_set_bit(int cr, int bit); void smp_ctl_set_bit(int cr, int bit);
void smp_ctl_clear_bit(int cr, int bit); void smp_ctl_clear_bit(int cr, int bit);
......
/*
* General floating pointer and vector register helpers
*
* Copyright IBM Corp. 2015
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
*/
#ifndef _ASM_S390_FPU_INTERNAL_H
#define _ASM_S390_FPU_INTERNAL_H
#define FPU_USE_VX 1 /* Vector extension is active */
#ifndef __ASSEMBLY__
#include <linux/errno.h>
#include <linux/string.h>
#include <asm/linkage.h>
#include <asm/ctl_reg.h>
#include <asm/sigcontext.h>
struct fpu {
__u32 fpc; /* Floating-point control */
__u32 flags;
union {
void *regs;
freg_t *fprs; /* Floating-point register save area */
__vector128 *vxrs; /* Vector register save area */
};
};
void save_fpu_regs(void);
#define is_vx_fpu(fpu) (!!((fpu)->flags & FPU_USE_VX))
#define is_vx_task(tsk) (!!((tsk)->thread.fpu.flags & FPU_USE_VX))
/* VX array structure for address operand constraints in inline assemblies */
struct vx_array { __vector128 _[__NUM_VXRS]; };
static inline int test_fp_ctl(u32 fpc)
{
u32 orig_fpc;
int rc;
asm volatile(
" efpc %1\n"
" sfpc %2\n"
"0: sfpc %1\n"
" la %0,0\n"
"1:\n"
EX_TABLE(0b,1b)
: "=d" (rc), "=d" (orig_fpc)
: "d" (fpc), "0" (-EINVAL));
return rc;
}
static inline void save_vx_regs_safe(__vector128 *vxrs)
{
unsigned long cr0, flags;
flags = arch_local_irq_save();
__ctl_store(cr0, 0, 0);
__ctl_set_bit(0, 17);
__ctl_set_bit(0, 18);
asm volatile(
" la 1,%0\n"
" .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */
" .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */
: "=Q" (*(struct vx_array *) vxrs) : : "1");
__ctl_load(cr0, 0, 0);
arch_local_irq_restore(flags);
}
static inline void convert_vx_to_fp(freg_t *fprs, __vector128 *vxrs)
{
int i;
for (i = 0; i < __NUM_FPRS; i++)
fprs[i] = *(freg_t *)(vxrs + i);
}
static inline void convert_fp_to_vx(__vector128 *vxrs, freg_t *fprs)
{
int i;
for (i = 0; i < __NUM_FPRS; i++)
*(freg_t *)(vxrs + i) = fprs[i];
}
static inline void fpregs_store(_s390_fp_regs *fpregs, struct fpu *fpu)
{
fpregs->pad = 0;
if (is_vx_fpu(fpu))
convert_vx_to_fp((freg_t *)&fpregs->fprs, fpu->vxrs);
else
memcpy((freg_t *)&fpregs->fprs, fpu->fprs,
sizeof(fpregs->fprs));
}
static inline void fpregs_load(_s390_fp_regs *fpregs, struct fpu *fpu)
{
if (is_vx_fpu(fpu))
convert_fp_to_vx(fpu->vxrs, (freg_t *)&fpregs->fprs);
else
memcpy(fpu->fprs, (freg_t *)&fpregs->fprs,
sizeof(fpregs->fprs));
}
#endif
#endif /* _ASM_S390_FPU_INTERNAL_H */
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/kvm.h> #include <linux/kvm.h>
#include <asm/debug.h> #include <asm/debug.h>
#include <asm/cpu.h> #include <asm/cpu.h>
#include <asm/fpu-internal.h>
#include <asm/isc.h> #include <asm/isc.h>
#define KVM_MAX_VCPUS 64 #define KVM_MAX_VCPUS 64
...@@ -501,10 +502,9 @@ struct kvm_guestdbg_info_arch { ...@@ -501,10 +502,9 @@ struct kvm_guestdbg_info_arch {
struct kvm_vcpu_arch { struct kvm_vcpu_arch {
struct kvm_s390_sie_block *sie_block; struct kvm_s390_sie_block *sie_block;
s390_fp_regs host_fpregs;
unsigned int host_acrs[NUM_ACRS]; unsigned int host_acrs[NUM_ACRS];
s390_fp_regs guest_fpregs; struct fpu host_fpregs;
struct kvm_s390_vregs *host_vregs; struct fpu guest_fpregs;
struct kvm_s390_local_interrupt local_int; struct kvm_s390_local_interrupt local_int;
struct hrtimer ckc_timer; struct hrtimer ckc_timer;
struct kvm_s390_pgm_info pgm; struct kvm_s390_pgm_info pgm;
......
...@@ -6,4 +6,26 @@ ...@@ -6,4 +6,26 @@
#define __ALIGN .align 4, 0x07 #define __ALIGN .align 4, 0x07
#define __ALIGN_STR __stringify(__ALIGN) #define __ALIGN_STR __stringify(__ALIGN)
#ifndef __ASSEMBLY__
/*
* Helper macro for exception table entries
*/
#define EX_TABLE(_fault, _target) \
".section __ex_table,\"a\"\n" \
".align 4\n" \
".long (" #_fault ") - .\n" \
".long (" #_target ") - .\n" \
".previous\n"
#else /* __ASSEMBLY__ */
#define EX_TABLE(_fault, _target) \
.section __ex_table,"a" ; \
.align 4 ; \
.long (_fault) - . ; \
.long (_target) - . ; \
.previous
#endif /* __ASSEMBLY__ */
#endif #endif
/*
* NUMA support for s390
*
* Copyright IBM Corp. 2015
*/
#ifndef _ASM_S390_MMZONE_H
#define _ASM_S390_MMZONE_H
#ifdef CONFIG_NUMA
extern struct pglist_data *node_data[];
#define NODE_DATA(nid) (node_data[nid])
#endif /* CONFIG_NUMA */
#endif /* _ASM_S390_MMZONE_H */
/*
* NUMA support for s390
*
* Declare the NUMA core code structures and functions.
*
* Copyright IBM Corp. 2015
*/
#ifndef _ASM_S390_NUMA_H
#define _ASM_S390_NUMA_H
#ifdef CONFIG_NUMA
#include <linux/numa.h>
#include <linux/cpumask.h>
void numa_setup(void);
int numa_pfn_to_nid(unsigned long pfn);
int __node_distance(int a, int b);
void numa_update_cpu_topology(void);
extern cpumask_var_t node_to_cpumask_map[MAX_NUMNODES];
extern int numa_debug_enabled;
#else
static inline void numa_setup(void) { }
static inline void numa_update_cpu_topology(void) { }
static inline int numa_pfn_to_nid(unsigned long pfn)
{
return 0;
}
#endif /* CONFIG_NUMA */
#endif /* _ASM_S390_NUMA_H */
...@@ -170,7 +170,11 @@ static inline void zpci_exit_slot(struct zpci_dev *zdev) {} ...@@ -170,7 +170,11 @@ static inline void zpci_exit_slot(struct zpci_dev *zdev) {}
#endif /* CONFIG_HOTPLUG_PCI_S390 */ #endif /* CONFIG_HOTPLUG_PCI_S390 */
/* Helpers */ /* Helpers */
struct zpci_dev *get_zdev(struct pci_dev *); static inline struct zpci_dev *to_zpci(struct pci_dev *pdev)
{
return pdev->sysdata;
}
struct zpci_dev *get_zdev_by_fid(u32); struct zpci_dev *get_zdev_by_fid(u32);
/* DMA */ /* DMA */
...@@ -188,4 +192,20 @@ void zpci_debug_init_device(struct zpci_dev *); ...@@ -188,4 +192,20 @@ void zpci_debug_init_device(struct zpci_dev *);
void zpci_debug_exit_device(struct zpci_dev *); void zpci_debug_exit_device(struct zpci_dev *);
void zpci_debug_info(struct zpci_dev *, struct seq_file *); void zpci_debug_info(struct zpci_dev *, struct seq_file *);
#ifdef CONFIG_NUMA
/* Returns the node based on PCI bus */
static inline int __pcibus_to_node(const struct pci_bus *bus)
{
return NUMA_NO_NODE;
}
static inline const struct cpumask *
cpumask_of_pcibus(const struct pci_bus *bus)
{
return cpu_online_mask;
}
#endif /* CONFIG_NUMA */
#endif #endif
...@@ -576,6 +576,19 @@ static inline int pte_same(pte_t a, pte_t b) ...@@ -576,6 +576,19 @@ static inline int pte_same(pte_t a, pte_t b)
return pte_val(a) == pte_val(b); return pte_val(a) == pte_val(b);
} }
#ifdef CONFIG_NUMA_BALANCING
static inline int pte_protnone(pte_t pte)
{
return pte_present(pte) && !(pte_val(pte) & _PAGE_READ);
}
static inline int pmd_protnone(pmd_t pmd)
{
/* pmd_large(pmd) implies pmd_present(pmd) */
return pmd_large(pmd) && !(pmd_val(pmd) & _SEGMENT_ENTRY_READ);
}
#endif
static inline pgste_t pgste_get_lock(pte_t *ptep) static inline pgste_t pgste_get_lock(pte_t *ptep)
{ {
unsigned long new = 0; unsigned long new = 0;
......
...@@ -14,10 +14,12 @@ ...@@ -14,10 +14,12 @@
#define CIF_MCCK_PENDING 0 /* machine check handling is pending */ #define CIF_MCCK_PENDING 0 /* machine check handling is pending */
#define CIF_ASCE 1 /* user asce needs fixup / uaccess */ #define CIF_ASCE 1 /* user asce needs fixup / uaccess */
#define CIF_NOHZ_DELAY 2 /* delay HZ disable for a tick */ #define CIF_NOHZ_DELAY 2 /* delay HZ disable for a tick */
#define CIF_FPU 3 /* restore vector registers */
#define _CIF_MCCK_PENDING (1<<CIF_MCCK_PENDING) #define _CIF_MCCK_PENDING (1<<CIF_MCCK_PENDING)
#define _CIF_ASCE (1<<CIF_ASCE) #define _CIF_ASCE (1<<CIF_ASCE)
#define _CIF_NOHZ_DELAY (1<<CIF_NOHZ_DELAY) #define _CIF_NOHZ_DELAY (1<<CIF_NOHZ_DELAY)
#define _CIF_FPU (1<<CIF_FPU)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
...@@ -28,6 +30,7 @@ ...@@ -28,6 +30,7 @@
#include <asm/ptrace.h> #include <asm/ptrace.h>
#include <asm/setup.h> #include <asm/setup.h>
#include <asm/runtime_instr.h> #include <asm/runtime_instr.h>
#include <asm/fpu-internal.h>
static inline void set_cpu_flag(int flag) static inline void set_cpu_flag(int flag)
{ {
...@@ -85,7 +88,7 @@ typedef struct { ...@@ -85,7 +88,7 @@ typedef struct {
* Thread structure * Thread structure
*/ */
struct thread_struct { struct thread_struct {
s390_fp_regs fp_regs; struct fpu fpu; /* FP and VX register save area */
unsigned int acrs[NUM_ACRS]; unsigned int acrs[NUM_ACRS];
unsigned long ksp; /* kernel stack pointer */ unsigned long ksp; /* kernel stack pointer */
mm_segment_t mm_segment; mm_segment_t mm_segment;
...@@ -101,7 +104,6 @@ struct thread_struct { ...@@ -101,7 +104,6 @@ struct thread_struct {
struct runtime_instr_cb *ri_cb; struct runtime_instr_cb *ri_cb;
int ri_signum; int ri_signum;
unsigned char trap_tdb[256]; /* Transaction abort diagnose block */ unsigned char trap_tdb[256]; /* Transaction abort diagnose block */
__vector128 *vxrs; /* Vector register save area */
}; };
/* Flag to disable transactions. */ /* Flag to disable transactions. */
...@@ -230,6 +232,17 @@ static inline void __load_psw_mask (unsigned long mask) ...@@ -230,6 +232,17 @@ static inline void __load_psw_mask (unsigned long mask)
: "=&d" (addr), "=Q" (psw) : "Q" (psw) : "memory", "cc"); : "=&d" (addr), "=Q" (psw) : "Q" (psw) : "memory", "cc");
} }
/*
* Extract current PSW mask
*/
static inline unsigned long __extract_psw(void)
{
unsigned int reg1, reg2;
asm volatile("epsw %0,%1" : "=d" (reg1), "=a" (reg2));
return (((unsigned long) reg1) << 32) | ((unsigned long) reg2);
}
/* /*
* Rewind PSW instruction address by specified number of bytes. * Rewind PSW instruction address by specified number of bytes.
*/ */
...@@ -336,25 +349,6 @@ extern void memcpy_absolute(void *, void *, size_t); ...@@ -336,25 +349,6 @@ extern void memcpy_absolute(void *, void *, size_t);
memcpy_absolute(&(dest), &__tmp, sizeof(__tmp)); \ memcpy_absolute(&(dest), &__tmp, sizeof(__tmp)); \
} }
/*
* Helper macro for exception table entries
*/
#define EX_TABLE(_fault, _target) \
".section __ex_table,\"a\"\n" \
".align 4\n" \
".long (" #_fault ") - .\n" \
".long (" #_target ") - .\n" \
".previous\n"
#else /* __ASSEMBLY__ */
#define EX_TABLE(_fault, _target) \
.section __ex_table,"a" ; \
.align 4 ; \
.long (_fault) - . ; \
.long (_target) - . ; \
.previous
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* __ASM_S390_PROCESSOR_H */ #endif /* __ASM_S390_PROCESSOR_H */
...@@ -79,6 +79,6 @@ int sclp_pci_configure(u32 fid); ...@@ -79,6 +79,6 @@ int sclp_pci_configure(u32 fid);
int sclp_pci_deconfigure(u32 fid); int sclp_pci_deconfigure(u32 fid);
int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode); int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode);
void sclp_early_detect(void); void sclp_early_detect(void);
long _sclp_print_early(const char *); int _sclp_print_early(const char *);
#endif /* _ASM_S390_SCLP_H */ #endif /* _ASM_S390_SCLP_H */
...@@ -8,139 +8,12 @@ ...@@ -8,139 +8,12 @@
#define __ASM_SWITCH_TO_H #define __ASM_SWITCH_TO_H
#include <linux/thread_info.h> #include <linux/thread_info.h>
#include <asm/fpu-internal.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
extern struct task_struct *__switch_to(void *, void *); extern struct task_struct *__switch_to(void *, void *);
extern void update_cr_regs(struct task_struct *task); extern void update_cr_regs(struct task_struct *task);
static inline int test_fp_ctl(u32 fpc)
{
u32 orig_fpc;
int rc;
asm volatile(
" efpc %1\n"
" sfpc %2\n"
"0: sfpc %1\n"
" la %0,0\n"
"1:\n"
EX_TABLE(0b,1b)
: "=d" (rc), "=d" (orig_fpc)
: "d" (fpc), "0" (-EINVAL));
return rc;
}
static inline void save_fp_ctl(u32 *fpc)
{
asm volatile(
" stfpc %0\n"
: "+Q" (*fpc));
}
static inline int restore_fp_ctl(u32 *fpc)
{
int rc;
asm volatile(
" lfpc %1\n"
"0: la %0,0\n"
"1:\n"
EX_TABLE(0b,1b)
: "=d" (rc) : "Q" (*fpc), "0" (-EINVAL));
return rc;
}
static inline void save_fp_regs(freg_t *fprs)
{
asm volatile("std 0,%0" : "=Q" (fprs[0]));
asm volatile("std 2,%0" : "=Q" (fprs[2]));
asm volatile("std 4,%0" : "=Q" (fprs[4]));
asm volatile("std 6,%0" : "=Q" (fprs[6]));
asm volatile("std 1,%0" : "=Q" (fprs[1]));
asm volatile("std 3,%0" : "=Q" (fprs[3]));
asm volatile("std 5,%0" : "=Q" (fprs[5]));
asm volatile("std 7,%0" : "=Q" (fprs[7]));
asm volatile("std 8,%0" : "=Q" (fprs[8]));
asm volatile("std 9,%0" : "=Q" (fprs[9]));
asm volatile("std 10,%0" : "=Q" (fprs[10]));
asm volatile("std 11,%0" : "=Q" (fprs[11]));
asm volatile("std 12,%0" : "=Q" (fprs[12]));
asm volatile("std 13,%0" : "=Q" (fprs[13]));
asm volatile("std 14,%0" : "=Q" (fprs[14]));
asm volatile("std 15,%0" : "=Q" (fprs[15]));
}
static inline void restore_fp_regs(freg_t *fprs)
{
asm volatile("ld 0,%0" : : "Q" (fprs[0]));
asm volatile("ld 2,%0" : : "Q" (fprs[2]));
asm volatile("ld 4,%0" : : "Q" (fprs[4]));
asm volatile("ld 6,%0" : : "Q" (fprs[6]));
asm volatile("ld 1,%0" : : "Q" (fprs[1]));
asm volatile("ld 3,%0" : : "Q" (fprs[3]));
asm volatile("ld 5,%0" : : "Q" (fprs[5]));
asm volatile("ld 7,%0" : : "Q" (fprs[7]));
asm volatile("ld 8,%0" : : "Q" (fprs[8]));
asm volatile("ld 9,%0" : : "Q" (fprs[9]));
asm volatile("ld 10,%0" : : "Q" (fprs[10]));
asm volatile("ld 11,%0" : : "Q" (fprs[11]));
asm volatile("ld 12,%0" : : "Q" (fprs[12]));
asm volatile("ld 13,%0" : : "Q" (fprs[13]));
asm volatile("ld 14,%0" : : "Q" (fprs[14]));
asm volatile("ld 15,%0" : : "Q" (fprs[15]));
}
static inline void save_vx_regs(__vector128 *vxrs)
{
typedef struct { __vector128 _[__NUM_VXRS]; } addrtype;
asm volatile(
" la 1,%0\n"
" .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */
" .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */
: "=Q" (*(addrtype *) vxrs) : : "1");
}
static inline void save_vx_regs_safe(__vector128 *vxrs)
{
unsigned long cr0, flags;
flags = arch_local_irq_save();
__ctl_store(cr0, 0, 0);
__ctl_set_bit(0, 17);
__ctl_set_bit(0, 18);
save_vx_regs(vxrs);
__ctl_load(cr0, 0, 0);
arch_local_irq_restore(flags);
}
static inline void restore_vx_regs(__vector128 *vxrs)
{
typedef struct { __vector128 _[__NUM_VXRS]; } addrtype;
asm volatile(
" la 1,%0\n"
" .word 0xe70f,0x1000,0x0036\n" /* vlm 0,15,0(1) */
" .word 0xe70f,0x1100,0x0c36\n" /* vlm 16,31,256(1) */
: : "Q" (*(addrtype *) vxrs) : "1");
}
static inline void save_fp_vx_regs(struct task_struct *task)
{
if (task->thread.vxrs)
save_vx_regs(task->thread.vxrs);
else
save_fp_regs(task->thread.fp_regs.fprs);
}
static inline void restore_fp_vx_regs(struct task_struct *task)
{
if (task->thread.vxrs)
restore_vx_regs(task->thread.vxrs);
else
restore_fp_regs(task->thread.fp_regs.fprs);
}
static inline void save_access_regs(unsigned int *acrs) static inline void save_access_regs(unsigned int *acrs)
{ {
typedef struct { int _[NUM_ACRS]; } acrstype; typedef struct { int _[NUM_ACRS]; } acrstype;
...@@ -157,15 +30,13 @@ static inline void restore_access_regs(unsigned int *acrs) ...@@ -157,15 +30,13 @@ static inline void restore_access_regs(unsigned int *acrs)
#define switch_to(prev,next,last) do { \ #define switch_to(prev,next,last) do { \
if (prev->mm) { \ if (prev->mm) { \
save_fp_ctl(&prev->thread.fp_regs.fpc); \ save_fpu_regs(); \
save_fp_vx_regs(prev); \
save_access_regs(&prev->thread.acrs[0]); \ save_access_regs(&prev->thread.acrs[0]); \
save_ri_cb(prev->thread.ri_cb); \ save_ri_cb(prev->thread.ri_cb); \
} \ } \
if (next->mm) { \ if (next->mm) { \
update_cr_regs(next); \ update_cr_regs(next); \
restore_fp_ctl(&next->thread.fp_regs.fpc); \ set_cpu_flag(CIF_FPU); \
restore_fp_vx_regs(next); \
restore_access_regs(&next->thread.acrs[0]); \ restore_access_regs(&next->thread.acrs[0]); \
restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \ restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \
} \ } \
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#define _ASM_S390_TOPOLOGY_H #define _ASM_S390_TOPOLOGY_H
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <asm/numa.h>
struct sysinfo_15_1_x; struct sysinfo_15_1_x;
struct cpu; struct cpu;
...@@ -13,6 +14,7 @@ struct cpu_topology_s390 { ...@@ -13,6 +14,7 @@ struct cpu_topology_s390 {
unsigned short core_id; unsigned short core_id;
unsigned short socket_id; unsigned short socket_id;
unsigned short book_id; unsigned short book_id;
unsigned short node_id;
cpumask_t thread_mask; cpumask_t thread_mask;
cpumask_t core_mask; cpumask_t core_mask;
cpumask_t book_mask; cpumask_t book_mask;
...@@ -52,6 +54,43 @@ static inline void topology_expect_change(void) { } ...@@ -52,6 +54,43 @@ static inline void topology_expect_change(void) { }
#define POLARIZATION_VM (2) #define POLARIZATION_VM (2)
#define POLARIZATION_VH (3) #define POLARIZATION_VH (3)
#define SD_BOOK_INIT SD_CPU_INIT
#ifdef CONFIG_NUMA
#define cpu_to_node cpu_to_node
static inline int cpu_to_node(int cpu)
{
return per_cpu(cpu_topology, cpu).node_id;
}
/* Returns a pointer to the cpumask of CPUs on node 'node'. */
#define cpumask_of_node cpumask_of_node
static inline const struct cpumask *cpumask_of_node(int node)
{
return node_to_cpumask_map[node];
}
/*
* Returns the number of the node containing node 'node'. This
* architecture is flat, so it is a pretty simple function!
*/
#define parent_node(node) (node)
#define pcibus_to_node(bus) __pcibus_to_node(bus)
#define node_distance(a, b) __node_distance(a, b)
#else /* !CONFIG_NUMA */
#define numa_node_id numa_node_id
static inline int numa_node_id(void)
{
return 0;
}
#endif /* CONFIG_NUMA */
#include <asm-generic/topology.h> #include <asm-generic/topology.h>
#endif /* _ASM_S390_TOPOLOGY_H */ #endif /* _ASM_S390_TOPOLOGY_H */
...@@ -11,16 +11,24 @@ ...@@ -11,16 +11,24 @@
#define __IGNORE_time #define __IGNORE_time
/* Ignore NUMA system calls. Not wired up on s390. */ /* Ignore system calls that are also reachable via sys_socketcall */
#define __IGNORE_mbind
#define __IGNORE_get_mempolicy
#define __IGNORE_set_mempolicy
#define __IGNORE_migrate_pages
#define __IGNORE_move_pages
/* Ignore system calls that are also reachable via sys_socket */
#define __IGNORE_recvmmsg #define __IGNORE_recvmmsg
#define __IGNORE_sendmmsg #define __IGNORE_sendmmsg
#define __IGNORE_socket
#define __IGNORE_socketpair
#define __IGNORE_bind
#define __IGNORE_connect
#define __IGNORE_listen
#define __IGNORE_accept4
#define __IGNORE_getsockopt
#define __IGNORE_setsockopt
#define __IGNORE_getsockname
#define __IGNORE_getpeername
#define __IGNORE_sendto
#define __IGNORE_sendmsg
#define __IGNORE_recvfrom
#define __IGNORE_recvmsg
#define __IGNORE_shutdown
#define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_SYS_ALARM #define __ARCH_WANT_SYS_ALARM
......
/*
* Support for Vector Instructions
*
* Assembler macros to generate .byte/.word code for particular
* vector instructions that are supported by recent binutils (>= 2.26) only.
*
* Copyright IBM Corp. 2015
* Author(s): Hendrik Brueckner <brueckner@linux.vnet.ibm.com>
*/
#ifndef __ASM_S390_VX_INSN_H
#define __ASM_S390_VX_INSN_H
#ifdef __ASSEMBLY__
/* Macros to generate vector instruction byte code */
#define REG_NUM_INVALID 255
/* GR_NUM - Retrieve general-purpose register number
*
* @opd: Operand to store register number
* @r64: String designation register in the format "%rN"
*/
.macro GR_NUM opd gr
\opd = REG_NUM_INVALID
.ifc \gr,%r0
\opd = 0
.endif
.ifc \gr,%r1
\opd = 1
.endif
.ifc \gr,%r2
\opd = 2
.endif
.ifc \gr,%r3
\opd = 3
.endif
.ifc \gr,%r4
\opd = 4
.endif
.ifc \gr,%r5
\opd = 5
.endif
.ifc \gr,%r6
\opd = 6
.endif
.ifc \gr,%r7
\opd = 7
.endif
.ifc \gr,%r8
\opd = 8
.endif
.ifc \gr,%r9
\opd = 9
.endif
.ifc \gr,%r10
\opd = 10
.endif
.ifc \gr,%r11
\opd = 11
.endif
.ifc \gr,%r12
\opd = 12
.endif
.ifc \gr,%r13
\opd = 13
.endif
.ifc \gr,%r14
\opd = 14
.endif
.ifc \gr,%r15
\opd = 15
.endif
.if \opd == REG_NUM_INVALID
.error "Invalid general-purpose register designation: \gr"
.endif
.endm
/* VX_R() - Macro to encode the VX_NUM into the instruction */
#define VX_R(v) (v & 0x0F)
/* VX_NUM - Retrieve vector register number
*
* @opd: Operand to store register number
* @vxr: String designation register in the format "%vN"
*
* The vector register number is used for as input number to the
* instruction and, as well as, to compute the RXB field of the
* instruction. To encode the particular vector register number,
* use the VX_R(v) macro to extract the instruction opcode.
*/
.macro VX_NUM opd vxr
\opd = REG_NUM_INVALID
.ifc \vxr,%v0
\opd = 0
.endif
.ifc \vxr,%v1
\opd = 1
.endif
.ifc \vxr,%v2
\opd = 2
.endif
.ifc \vxr,%v3
\opd = 3
.endif
.ifc \vxr,%v4
\opd = 4
.endif
.ifc \vxr,%v5
\opd = 5
.endif
.ifc \vxr,%v6
\opd = 6
.endif
.ifc \vxr,%v7
\opd = 7
.endif
.ifc \vxr,%v8
\opd = 8
.endif
.ifc \vxr,%v9
\opd = 9
.endif
.ifc \vxr,%v10
\opd = 10
.endif
.ifc \vxr,%v11
\opd = 11
.endif
.ifc \vxr,%v12
\opd = 12
.endif
.ifc \vxr,%v13
\opd = 13
.endif
.ifc \vxr,%v14
\opd = 14
.endif
.ifc \vxr,%v15
\opd = 15
.endif
.ifc \vxr,%v16
\opd = 16
.endif
.ifc \vxr,%v17
\opd = 17
.endif
.ifc \vxr,%v18
\opd = 18
.endif
.ifc \vxr,%v19
\opd = 19
.endif
.ifc \vxr,%v20
\opd = 20
.endif
.ifc \vxr,%v21
\opd = 21
.endif
.ifc \vxr,%v22
\opd = 22
.endif
.ifc \vxr,%v23
\opd = 23
.endif
.ifc \vxr,%v24
\opd = 24
.endif
.ifc \vxr,%v25
\opd = 25
.endif
.ifc \vxr,%v26
\opd = 26
.endif
.ifc \vxr,%v27
\opd = 27
.endif
.ifc \vxr,%v28
\opd = 28
.endif
.ifc \vxr,%v29
\opd = 29
.endif
.ifc \vxr,%v30
\opd = 30
.endif
.ifc \vxr,%v31
\opd = 31
.endif
.if \opd == REG_NUM_INVALID
.error "Invalid vector register designation: \vxr"
.endif
.endm
/* RXB - Compute most significant bit used vector registers
*
* @rxb: Operand to store computed RXB value
* @v1: First vector register designated operand
* @v2: Second vector register designated operand
* @v3: Third vector register designated operand
* @v4: Fourth vector register designated operand
*/
.macro RXB rxb v1 v2=0 v3=0 v4=0
\rxb = 0
.if \v1 & 0x10
\rxb = \rxb | 0x08
.endif
.if \v2 & 0x10
\rxb = \rxb | 0x04
.endif
.if \v3 & 0x10
\rxb = \rxb | 0x02
.endif
.if \v4 & 0x10
\rxb = \rxb | 0x01
.endif
.endm
/* MRXB - Generate Element Size Control and RXB value
*
* @m: Element size control
* @v1: First vector register designated operand (for RXB)
* @v2: Second vector register designated operand (for RXB)
* @v3: Third vector register designated operand (for RXB)
* @v4: Fourth vector register designated operand (for RXB)
*/
.macro MRXB m v1 v2=0 v3=0 v4=0
rxb = 0
RXB rxb, \v1, \v2, \v3, \v4
.byte (\m << 4) | rxb
.endm
/* MRXBOPC - Generate Element Size Control, RXB, and final Opcode fields
*
* @m: Element size control
* @opc: Opcode
* @v1: First vector register designated operand (for RXB)
* @v2: Second vector register designated operand (for RXB)
* @v3: Third vector register designated operand (for RXB)
* @v4: Fourth vector register designated operand (for RXB)
*/
.macro MRXBOPC m opc v1 v2=0 v3=0 v4=0
MRXB \m, \v1, \v2, \v3, \v4
.byte \opc
.endm
/* Vector support instructions */
/* VECTOR GENERATE BYTE MASK */
.macro VGBM vr imm2
VX_NUM v1, \vr
.word (0xE700 | (VX_R(v1) << 4))
.word \imm2
MRXBOPC 0, 0x44, v1
.endm
.macro VZERO vxr
VGBM \vxr, 0
.endm
.macro VONE vxr
VGBM \vxr, 0xFFFF
.endm
/* VECTOR LOAD VR ELEMENT FROM GR */
.macro VLVG v, gr, disp, m
VX_NUM v1, \v
GR_NUM b2, "%r0"
GR_NUM r3, \gr
.word 0xE700 | (VX_R(v1) << 4) | r3
.word (b2 << 12) | (\disp)
MRXBOPC \m, 0x22, v1
.endm
.macro VLVGB v, gr, index, base
VLVG \v, \gr, \index, \base, 0
.endm
.macro VLVGH v, gr, index
VLVG \v, \gr, \index, 1
.endm
.macro VLVGF v, gr, index
VLVG \v, \gr, \index, 2
.endm
.macro VLVGG v, gr, index
VLVG \v, \gr, \index, 3
.endm
/* VECTOR LOAD */
.macro VL v, disp, index="%r0", base
VX_NUM v1, \v
GR_NUM x2, \index
GR_NUM b2, \base
.word 0xE700 | (VX_R(v1) << 4) | x2
.word (b2 << 12) | (\disp)
MRXBOPC 0, 0x06, v1
.endm
/* VECTOR LOAD ELEMENT */
.macro VLEx vr1, disp, index="%r0", base, m3, opc
VX_NUM v1, \vr1
GR_NUM x2, \index
GR_NUM b2, \base
.word 0xE700 | (VX_R(v1) << 4) | x2
.word (b2 << 12) | (\disp)
MRXBOPC \m3, \opc, v1
.endm
.macro VLEB vr1, disp, index="%r0", base, m3
VLEx \vr1, \disp, \index, \base, \m3, 0x00
.endm
.macro VLEH vr1, disp, index="%r0", base, m3
VLEx \vr1, \disp, \index, \base, \m3, 0x01
.endm
.macro VLEF vr1, disp, index="%r0", base, m3
VLEx \vr1, \disp, \index, \base, \m3, 0x03
.endm
.macro VLEG vr1, disp, index="%r0", base, m3
VLEx \vr1, \disp, \index, \base, \m3, 0x02
.endm
/* VECTOR LOAD ELEMENT IMMEDIATE */
.macro VLEIx vr1, imm2, m3, opc
VX_NUM v1, \vr1
.word 0xE700 | (VX_R(v1) << 4)
.word \imm2
MRXBOPC \m3, \opc, v1
.endm
.macro VLEIB vr1, imm2, index
VLEIx \vr1, \imm2, \index, 0x40
.endm
.macro VLEIH vr1, imm2, index
VLEIx \vr1, \imm2, \index, 0x41
.endm
.macro VLEIF vr1, imm2, index
VLEIx \vr1, \imm2, \index, 0x43
.endm
.macro VLEIG vr1, imm2, index
VLEIx \vr1, \imm2, \index, 0x42
.endm
/* VECTOR LOAD GR FROM VR ELEMENT */
.macro VLGV gr, vr, disp, base="%r0", m
GR_NUM r1, \gr
GR_NUM b2, \base
VX_NUM v3, \vr
.word 0xE700 | (r1 << 4) | VX_R(v3)
.word (b2 << 12) | (\disp)
MRXBOPC \m, 0x21, v3
.endm
.macro VLGVB gr, vr, disp, base="%r0"
VLGV \gr, \vr, \disp, \base, 0
.endm
.macro VLGVH gr, vr, disp, base="%r0"
VLGV \gr, \vr, \disp, \base, 1
.endm
.macro VLGVF gr, vr, disp, base="%r0"
VLGV \gr, \vr, \disp, \base, 2
.endm
.macro VLGVG gr, vr, disp, base="%r0"
VLGV \gr, \vr, \disp, \base, 3
.endm
/* VECTOR LOAD MULTIPLE */
.macro VLM vfrom, vto, disp, base
VX_NUM v1, \vfrom
VX_NUM v3, \vto
GR_NUM b2, \base /* Base register */
.word 0xE700 | (VX_R(v1) << 4) | VX_R(v3)
.word (b2 << 12) | (\disp)
MRXBOPC 0, 0x36, v1, v3
.endm
/* VECTOR STORE MULTIPLE */
.macro VSTM vfrom, vto, disp, base
VX_NUM v1, \vfrom
VX_NUM v3, \vto
GR_NUM b2, \base /* Base register */
.word 0xE700 | (VX_R(v1) << 4) | VX_R(v3)
.word (b2 << 12) | (\disp)
MRXBOPC 0, 0x3E, v1, v3
.endm
/* VECTOR PERMUTE */
.macro VPERM vr1, vr2, vr3, vr4
VX_NUM v1, \vr1
VX_NUM v2, \vr2
VX_NUM v3, \vr3
VX_NUM v4, \vr4
.word 0xE700 | (VX_R(v1) << 4) | VX_R(v2)
.word (VX_R(v3) << 12)
MRXBOPC VX_R(v4), 0x8C, v1, v2, v3, v4
.endm
/* VECTOR UNPACK LOGICAL LOW */
.macro VUPLL vr1, vr2, m3
VX_NUM v1, \vr1
VX_NUM v2, \vr2
.word 0xE700 | (VX_R(v1) << 4) | VX_R(v2)
.word 0x0000
MRXBOPC \m3, 0xD4, v1, v2
.endm
.macro VUPLLB vr1, vr2
VUPLL \vr1, \vr2, 0
.endm
.macro VUPLLH vr1, vr2
VUPLL \vr1, \vr2, 1
.endm
.macro VUPLLF vr1, vr2
VUPLL \vr1, \vr2, 2
.endm
/* Vector integer instructions */
/* VECTOR EXCLUSIVE OR */
.macro VX vr1, vr2, vr3
VX_NUM v1, \vr1
VX_NUM v2, \vr2
VX_NUM v3, \vr3
.word 0xE700 | (VX_R(v1) << 4) | VX_R(v2)
.word (VX_R(v3) << 12)
MRXBOPC 0, 0x6D, v1, v2, v3
.endm
/* VECTOR GALOIS FIELD MULTIPLY SUM */
.macro VGFM vr1, vr2, vr3, m4
VX_NUM v1, \vr1
VX_NUM v2, \vr2
VX_NUM v3, \vr3
.word 0xE700 | (VX_R(v1) << 4) | VX_R(v2)
.word (VX_R(v3) << 12)
MRXBOPC \m4, 0xB4, v1, v2, v3
.endm
.macro VGFMB vr1, vr2, vr3
VGFM \vr1, \vr2, \vr3, 0
.endm
.macro VGFMH vr1, vr2, vr3
VGFM \vr1, \vr2, \vr3, 1
.endm
.macro VGFMF vr1, vr2, vr3
VGFM \vr1, \vr2, \vr3, 2
.endm
.macro VGFMG vr1, vr2, vr3
VGFM \vr1, \vr2, \vr3, 3
.endm
/* VECTOR GALOIS FIELD MULTIPLY SUM AND ACCUMULATE */
.macro VGFMA vr1, vr2, vr3, vr4, m5
VX_NUM v1, \vr1
VX_NUM v2, \vr2
VX_NUM v3, \vr3
VX_NUM v4, \vr4
.word 0xE700 | (VX_R(v1) << 4) | VX_R(v2)
.word (VX_R(v3) << 12) | (\m5 << 8)
MRXBOPC VX_R(v4), 0xBC, v1, v2, v3, v4
.endm
.macro VGFMAB vr1, vr2, vr3, vr4
VGFMA \vr1, \vr2, \vr3, \vr4, 0
.endm
.macro VGFMAH vr1, vr2, vr3, vr4
VGFMA \vr1, \vr2, \vr3, \vr4, 1
.endm
.macro VGFMAF vr1, vr2, vr3, vr4
VGFMA \vr1, \vr2, \vr3, \vr4, 2
.endm
.macro VGFMAG vr1, vr2, vr3, vr4
VGFMA \vr1, \vr2, \vr3, \vr4, 3
.endm
/* VECTOR SHIFT RIGHT LOGICAL BY BYTE */
.macro VSRLB vr1, vr2, vr3
VX_NUM v1, \vr1
VX_NUM v2, \vr2
VX_NUM v3, \vr3
.word 0xE700 | (VX_R(v1) << 4) | VX_R(v2)
.word (VX_R(v3) << 12)
MRXBOPC 0, 0x7D, v1, v2, v3
.endm
#endif /* __ASSEMBLY__ */
#endif /* __ASM_S390_VX_INSN_H */
...@@ -204,9 +204,9 @@ ...@@ -204,9 +204,9 @@
#define __NR_statfs64 265 #define __NR_statfs64 265
#define __NR_fstatfs64 266 #define __NR_fstatfs64 266
#define __NR_remap_file_pages 267 #define __NR_remap_file_pages 267
/* Number 268 is reserved for new sys_mbind */ #define __NR_mbind 268
/* Number 269 is reserved for new sys_get_mempolicy */ #define __NR_get_mempolicy 269
/* Number 270 is reserved for new sys_set_mempolicy */ #define __NR_set_mempolicy 270
#define __NR_mq_open 271 #define __NR_mq_open 271
#define __NR_mq_unlink 272 #define __NR_mq_unlink 272
#define __NR_mq_timedsend 273 #define __NR_mq_timedsend 273
...@@ -223,7 +223,7 @@ ...@@ -223,7 +223,7 @@
#define __NR_inotify_init 284 #define __NR_inotify_init 284
#define __NR_inotify_add_watch 285 #define __NR_inotify_add_watch 285
#define __NR_inotify_rm_watch 286 #define __NR_inotify_rm_watch 286
/* Number 287 is reserved for new sys_migrate_pages */ #define __NR_migrate_pages 287
#define __NR_openat 288 #define __NR_openat 288
#define __NR_mkdirat 289 #define __NR_mkdirat 289
#define __NR_mknodat 290 #define __NR_mknodat 290
...@@ -245,7 +245,7 @@ ...@@ -245,7 +245,7 @@
#define __NR_sync_file_range 307 #define __NR_sync_file_range 307
#define __NR_tee 308 #define __NR_tee 308
#define __NR_vmsplice 309 #define __NR_vmsplice 309
/* Number 310 is reserved for new sys_move_pages */ #define __NR_move_pages 310
#define __NR_getcpu 311 #define __NR_getcpu 311
#define __NR_epoll_pwait 312 #define __NR_epoll_pwait 312
#define __NR_utimes 313 #define __NR_utimes 313
......
...@@ -28,6 +28,17 @@ CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' ...@@ -28,6 +28,17 @@ CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"'
CFLAGS_sysinfo.o += -w CFLAGS_sysinfo.o += -w
#
# Use -march=z900 for sclp.c to be able to print an error message if
# the kernel is started on a machine which is too old
#
CFLAGS_REMOVE_sclp.o = $(CC_FLAGS_FTRACE)
ifneq ($(CC_FLAGS_MARCH),-march=z900)
CFLAGS_REMOVE_sclp.o += $(CC_FLAGS_MARCH)
CFLAGS_sclp.o += -march=z900
endif
GCOV_PROFILE_sclp.o := n
obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o
obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
obj-y += debug.o irq.o ipl.o dis.o diag.o sclp.o vdso.o obj-y += debug.o irq.o ipl.o dis.o diag.o sclp.o vdso.o
......
...@@ -28,6 +28,9 @@ int main(void) ...@@ -28,6 +28,9 @@ int main(void)
DEFINE(__TASK_pid, offsetof(struct task_struct, pid)); DEFINE(__TASK_pid, offsetof(struct task_struct, pid));
BLANK(); BLANK();
DEFINE(__THREAD_ksp, offsetof(struct thread_struct, ksp)); DEFINE(__THREAD_ksp, offsetof(struct thread_struct, ksp));
DEFINE(__THREAD_FPU_fpc, offsetof(struct thread_struct, fpu.fpc));
DEFINE(__THREAD_FPU_flags, offsetof(struct thread_struct, fpu.flags));
DEFINE(__THREAD_FPU_regs, offsetof(struct thread_struct, fpu.regs));
DEFINE(__THREAD_per_cause, offsetof(struct thread_struct, per_event.cause)); DEFINE(__THREAD_per_cause, offsetof(struct thread_struct, per_event.cause));
DEFINE(__THREAD_per_address, offsetof(struct thread_struct, per_event.address)); DEFINE(__THREAD_per_address, offsetof(struct thread_struct, per_event.address));
DEFINE(__THREAD_per_paid, offsetof(struct thread_struct, per_event.paid)); DEFINE(__THREAD_per_paid, offsetof(struct thread_struct, per_event.paid));
......
...@@ -153,33 +153,14 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) ...@@ -153,33 +153,14 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
/* Store registers needed to create the signal frame */ /* Store registers needed to create the signal frame */
static void store_sigregs(void) static void store_sigregs(void)
{ {
int i;
save_access_regs(current->thread.acrs); save_access_regs(current->thread.acrs);
save_fp_ctl(&current->thread.fp_regs.fpc); save_fpu_regs();
if (current->thread.vxrs) {
save_vx_regs(current->thread.vxrs);
for (i = 0; i < __NUM_FPRS; i++)
current->thread.fp_regs.fprs[i] =
*(freg_t *)(current->thread.vxrs + i);
} else
save_fp_regs(current->thread.fp_regs.fprs);
} }
/* Load registers after signal return */ /* Load registers after signal return */
static void load_sigregs(void) static void load_sigregs(void)
{ {
int i;
restore_access_regs(current->thread.acrs); restore_access_regs(current->thread.acrs);
/* restore_fp_ctl is done in restore_sigregs */
if (current->thread.vxrs) {
for (i = 0; i < __NUM_FPRS; i++)
*(freg_t *)(current->thread.vxrs + i) =
current->thread.fp_regs.fprs[i];
restore_vx_regs(current->thread.vxrs);
} else
restore_fp_regs(current->thread.fp_regs.fprs);
} }
static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs)
...@@ -196,8 +177,7 @@ static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) ...@@ -196,8 +177,7 @@ static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs)
user_sregs.regs.gprs[i] = (__u32) regs->gprs[i]; user_sregs.regs.gprs[i] = (__u32) regs->gprs[i];
memcpy(&user_sregs.regs.acrs, current->thread.acrs, memcpy(&user_sregs.regs.acrs, current->thread.acrs,
sizeof(user_sregs.regs.acrs)); sizeof(user_sregs.regs.acrs));
memcpy(&user_sregs.fpregs, &current->thread.fp_regs, fpregs_store((_s390_fp_regs *) &user_sregs.fpregs, &current->thread.fpu);
sizeof(user_sregs.fpregs));
if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32))) if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32)))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -217,8 +197,8 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) ...@@ -217,8 +197,8 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW32_MASK_RI)) if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW32_MASK_RI))
return -EINVAL; return -EINVAL;
/* Loading the floating-point-control word can fail. Do that first. */ /* Test the floating-point-control word. */
if (restore_fp_ctl(&user_sregs.fpregs.fpc)) if (test_fp_ctl(user_sregs.fpregs.fpc))
return -EINVAL; return -EINVAL;
/* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */ /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
...@@ -235,9 +215,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) ...@@ -235,9 +215,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
regs->gprs[i] = (__u64) user_sregs.regs.gprs[i]; regs->gprs[i] = (__u64) user_sregs.regs.gprs[i];
memcpy(&current->thread.acrs, &user_sregs.regs.acrs, memcpy(&current->thread.acrs, &user_sregs.regs.acrs,
sizeof(current->thread.acrs)); sizeof(current->thread.acrs));
fpregs_load((_s390_fp_regs *) &user_sregs.fpregs, &current->thread.fpu);
memcpy(&current->thread.fp_regs, &user_sregs.fpregs,
sizeof(current->thread.fp_regs));
clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
return 0; return 0;
...@@ -258,13 +236,13 @@ static int save_sigregs_ext32(struct pt_regs *regs, ...@@ -258,13 +236,13 @@ static int save_sigregs_ext32(struct pt_regs *regs,
return -EFAULT; return -EFAULT;
/* Save vector registers to signal stack */ /* Save vector registers to signal stack */
if (current->thread.vxrs) { if (is_vx_task(current)) {
for (i = 0; i < __NUM_VXRS_LOW; i++) for (i = 0; i < __NUM_VXRS_LOW; i++)
vxrs[i] = *((__u64 *)(current->thread.vxrs + i) + 1); vxrs[i] = *((__u64 *)(current->thread.fpu.vxrs + i) + 1);
if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, if (__copy_to_user(&sregs_ext->vxrs_low, vxrs,
sizeof(sregs_ext->vxrs_low)) || sizeof(sregs_ext->vxrs_low)) ||
__copy_to_user(&sregs_ext->vxrs_high, __copy_to_user(&sregs_ext->vxrs_high,
current->thread.vxrs + __NUM_VXRS_LOW, current->thread.fpu.vxrs + __NUM_VXRS_LOW,
sizeof(sregs_ext->vxrs_high))) sizeof(sregs_ext->vxrs_high)))
return -EFAULT; return -EFAULT;
} }
...@@ -286,15 +264,15 @@ static int restore_sigregs_ext32(struct pt_regs *regs, ...@@ -286,15 +264,15 @@ static int restore_sigregs_ext32(struct pt_regs *regs,
*(__u32 *)&regs->gprs[i] = gprs_high[i]; *(__u32 *)&regs->gprs[i] = gprs_high[i];
/* Restore vector registers from signal stack */ /* Restore vector registers from signal stack */
if (current->thread.vxrs) { if (is_vx_task(current)) {
if (__copy_from_user(vxrs, &sregs_ext->vxrs_low, if (__copy_from_user(vxrs, &sregs_ext->vxrs_low,
sizeof(sregs_ext->vxrs_low)) || sizeof(sregs_ext->vxrs_low)) ||
__copy_from_user(current->thread.vxrs + __NUM_VXRS_LOW, __copy_from_user(current->thread.fpu.vxrs + __NUM_VXRS_LOW,
&sregs_ext->vxrs_high, &sregs_ext->vxrs_high,
sizeof(sregs_ext->vxrs_high))) sizeof(sregs_ext->vxrs_high)))
return -EFAULT; return -EFAULT;
for (i = 0; i < __NUM_VXRS_LOW; i++) for (i = 0; i < __NUM_VXRS_LOW; i++)
*((__u64 *)(current->thread.vxrs + i) + 1) = vxrs[i]; *((__u64 *)(current->thread.fpu.vxrs + i) + 1) = vxrs[i];
} }
return 0; return 0;
} }
...@@ -308,6 +286,7 @@ COMPAT_SYSCALL_DEFINE0(sigreturn) ...@@ -308,6 +286,7 @@ COMPAT_SYSCALL_DEFINE0(sigreturn)
if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32)) if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE32))
goto badframe; goto badframe;
set_current_blocked(&set); set_current_blocked(&set);
save_fpu_regs();
if (restore_sigregs32(regs, &frame->sregs)) if (restore_sigregs32(regs, &frame->sregs))
goto badframe; goto badframe;
if (restore_sigregs_ext32(regs, &frame->sregs_ext)) if (restore_sigregs_ext32(regs, &frame->sregs_ext))
...@@ -330,6 +309,7 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn) ...@@ -330,6 +309,7 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn)
set_current_blocked(&set); set_current_blocked(&set);
if (compat_restore_altstack(&frame->uc.uc_stack)) if (compat_restore_altstack(&frame->uc.uc_stack))
goto badframe; goto badframe;
save_fpu_regs();
if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) if (restore_sigregs32(regs, &frame->uc.uc_mcontext))
goto badframe; goto badframe;
if (restore_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext)) if (restore_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext))
...@@ -472,7 +452,7 @@ static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, ...@@ -472,7 +452,7 @@ static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set,
*/ */
uc_flags = UC_GPRS_HIGH; uc_flags = UC_GPRS_HIGH;
if (MACHINE_HAS_VX) { if (MACHINE_HAS_VX) {
if (current->thread.vxrs) if (is_vx_task(current))
uc_flags |= UC_VXRS; uc_flags |= UC_VXRS;
} else } else
frame_size -= sizeof(frame->uc.uc_mcontext_ext.vxrs_low) + frame_size -= sizeof(frame->uc.uc_mcontext_ext.vxrs_low) +
......
This diff is collapsed.
...@@ -370,6 +370,7 @@ ENTRY(startup_kdump) ...@@ -370,6 +370,7 @@ ENTRY(startup_kdump)
xc 0x200(256),0x200 # partially clear lowcore xc 0x200(256),0x200 # partially clear lowcore
xc 0x300(256),0x300 xc 0x300(256),0x300
xc 0xe00(256),0xe00 xc 0xe00(256),0xe00
lctlg %c0,%c15,0x200(%r0) # initialize control registers
stck __LC_LAST_UPDATE_CLOCK stck __LC_LAST_UPDATE_CLOCK
spt 6f-.LPG0(%r13) spt 6f-.LPG0(%r13)
mvc __LC_LAST_UPDATE_TIMER(8),6f-.LPG0(%r13) mvc __LC_LAST_UPDATE_TIMER(8),6f-.LPG0(%r13)
...@@ -413,9 +414,9 @@ ENTRY(startup_kdump) ...@@ -413,9 +414,9 @@ ENTRY(startup_kdump)
# followed by the facility words. # followed by the facility words.
#if defined(CONFIG_MARCH_Z13) #if defined(CONFIG_MARCH_Z13)
.long 3, 0xc100eff2, 0xf46ce800, 0x00400000 .long 2, 0xc100eff2, 0xf46cc800
#elif defined(CONFIG_MARCH_ZEC12) #elif defined(CONFIG_MARCH_ZEC12)
.long 3, 0xc100eff2, 0xf46ce800, 0x00400000 .long 2, 0xc100eff2, 0xf46cc800
#elif defined(CONFIG_MARCH_Z196) #elif defined(CONFIG_MARCH_Z196)
.long 2, 0xc100eff2, 0xf46c0000 .long 2, 0xc100eff2, 0xf46c0000
#elif defined(CONFIG_MARCH_Z10) #elif defined(CONFIG_MARCH_Z10)
......
...@@ -44,12 +44,9 @@ static void jump_label_bug(struct jump_entry *entry, struct insn *expected, ...@@ -44,12 +44,9 @@ static void jump_label_bug(struct jump_entry *entry, struct insn *expected,
unsigned char *ipn = (unsigned char *)new; unsigned char *ipn = (unsigned char *)new;
pr_emerg("Jump label code mismatch at %pS [%p]\n", ipc, ipc); pr_emerg("Jump label code mismatch at %pS [%p]\n", ipc, ipc);
pr_emerg("Found: %02x %02x %02x %02x %02x %02x\n", pr_emerg("Found: %6ph\n", ipc);
ipc[0], ipc[1], ipc[2], ipc[3], ipc[4], ipc[5]); pr_emerg("Expected: %6ph\n", ipe);
pr_emerg("Expected: %02x %02x %02x %02x %02x %02x\n", pr_emerg("New: %6ph\n", ipn);
ipe[0], ipe[1], ipe[2], ipe[3], ipe[4], ipe[5]);
pr_emerg("New: %02x %02x %02x %02x %02x %02x\n",
ipn[0], ipn[1], ipn[2], ipn[3], ipn[4], ipn[5]);
panic("Corrupted kernel text"); panic("Corrupted kernel text");
} }
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/crw.h> #include <asm/crw.h>
#include <asm/switch_to.h> #include <asm/switch_to.h>
#include <asm/fpu-internal.h>
#include <asm/ctl_reg.h> #include <asm/ctl_reg.h>
struct mcck_struct { struct mcck_struct {
...@@ -164,8 +165,12 @@ static int notrace s390_revalidate_registers(struct mci *mci) ...@@ -164,8 +165,12 @@ static int notrace s390_revalidate_registers(struct mci *mci)
cr0.val = S390_lowcore.cregs_save_area[0]; cr0.val = S390_lowcore.cregs_save_area[0];
cr0.afp = cr0.vx = 1; cr0.afp = cr0.vx = 1;
__ctl_load(cr0.val, 0, 0); __ctl_load(cr0.val, 0, 0);
restore_vx_regs((__vector128 *) asm volatile(
&S390_lowcore.vector_save_area); " la 1,%0\n"
" .word 0xe70f,0x1000,0x0036\n" /* vlm 0,15,0(1) */
" .word 0xe70f,0x1100,0x0c36\n" /* vlm 16,31,256(1) */
: : "Q" (*(struct vx_array *)
&S390_lowcore.vector_save_area) : "1");
__ctl_load(S390_lowcore.cregs_save_area[0], 0, 0); __ctl_load(S390_lowcore.cregs_save_area[0], 0, 0);
} }
/* Revalidate access registers */ /* Revalidate access registers */
...@@ -358,4 +363,4 @@ static int __init machine_check_init(void) ...@@ -358,4 +363,4 @@ static int __init machine_check_init(void)
ctl_set_bit(14, 24); /* enable warning MCH */ ctl_set_bit(14, 24); /* enable warning MCH */
return 0; return 0;
} }
arch_initcall(machine_check_init); early_initcall(machine_check_init);
...@@ -1019,12 +1019,9 @@ static int perf_push_sample(struct perf_event *event, struct sf_raw_sample *sfr) ...@@ -1019,12 +1019,9 @@ static int perf_push_sample(struct perf_event *event, struct sf_raw_sample *sfr)
break; break;
} }
/* The host-program-parameter (hpp) contains the sie control /* The host-program-parameter (hpp) contains the pid of
* block that is set by sie64a() in entry64.S. Check if hpp * the CPU thread as set by sie64a() in entry.S.
* refers to a valid control block and set sde_regs flags * If non-zero assume a guest sample.
* accordingly. This would allow to use hpp values for other
* purposes too.
* For now, simply use a non-zero value as guest indicator.
*/ */
if (sfr->basic.hpp) if (sfr->basic.hpp)
sde_regs->in_guest = 1; sde_regs->in_guest = 1;
......
...@@ -81,8 +81,38 @@ void release_thread(struct task_struct *dead_task) ...@@ -81,8 +81,38 @@ void release_thread(struct task_struct *dead_task)
void arch_release_task_struct(struct task_struct *tsk) void arch_release_task_struct(struct task_struct *tsk)
{ {
if (tsk->thread.vxrs) /* Free either the floating-point or the vector register save area */
kfree(tsk->thread.vxrs); kfree(tsk->thread.fpu.regs);
}
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
*dst = *src;
/* Set up a new floating-point register save area */
dst->thread.fpu.fpc = 0;
dst->thread.fpu.flags = 0; /* Always start with VX disabled */
dst->thread.fpu.fprs = kzalloc(sizeof(freg_t) * __NUM_FPRS,
GFP_KERNEL|__GFP_REPEAT);
if (!dst->thread.fpu.fprs)
return -ENOMEM;
/*
* Save the floating-point or vector register state of the current
* task. The state is not saved for early kernel threads, for example,
* the init_task, which do not have an allocated save area.
* The CIF_FPU flag is set in any case to lazy clear or restore a saved
* state when switching to a different task or returning to user space.
*/
save_fpu_regs();
dst->thread.fpu.fpc = current->thread.fpu.fpc;
if (is_vx_task(current))
convert_vx_to_fp(dst->thread.fpu.fprs,
current->thread.fpu.vxrs);
else
memcpy(dst->thread.fpu.fprs, current->thread.fpu.fprs,
sizeof(freg_t) * __NUM_FPRS);
return 0;
} }
int copy_thread(unsigned long clone_flags, unsigned long new_stackp, int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
...@@ -142,11 +172,6 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp, ...@@ -142,11 +172,6 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
p->thread.ri_signum = 0; p->thread.ri_signum = 0;
frame->childregs.psw.mask &= ~PSW_MASK_RI; frame->childregs.psw.mask &= ~PSW_MASK_RI;
/* Save the fpu registers to new thread structure. */
save_fp_ctl(&p->thread.fp_regs.fpc);
save_fp_regs(p->thread.fp_regs.fprs);
p->thread.fp_regs.pad = 0;
p->thread.vxrs = NULL;
/* Set a new TLS ? */ /* Set a new TLS ? */
if (clone_flags & CLONE_SETTLS) { if (clone_flags & CLONE_SETTLS) {
unsigned long tls = frame->childregs.gprs[6]; unsigned long tls = frame->childregs.gprs[6];
...@@ -162,7 +187,7 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp, ...@@ -162,7 +187,7 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
asmlinkage void execve_tail(void) asmlinkage void execve_tail(void)
{ {
current->thread.fp_regs.fpc = 0; current->thread.fpu.fpc = 0;
asm volatile("sfpc %0" : : "d" (0)); asm volatile("sfpc %0" : : "d" (0));
} }
...@@ -171,8 +196,15 @@ asmlinkage void execve_tail(void) ...@@ -171,8 +196,15 @@ asmlinkage void execve_tail(void)
*/ */
int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs) int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs)
{ {
save_fp_ctl(&fpregs->fpc); save_fpu_regs();
save_fp_regs(fpregs->fprs); fpregs->fpc = current->thread.fpu.fpc;
fpregs->pad = 0;
if (is_vx_task(current))
convert_vx_to_fp((freg_t *)&fpregs->fprs,
current->thread.fpu.vxrs);
else
memcpy(&fpregs->fprs, current->thread.fpu.fprs,
sizeof(fpregs->fprs));
return 1; return 1;
} }
EXPORT_SYMBOL(dump_fpu); EXPORT_SYMBOL(dump_fpu);
......
...@@ -40,6 +40,15 @@ void cpu_init(void) ...@@ -40,6 +40,15 @@ void cpu_init(void)
enter_lazy_tlb(&init_mm, current); enter_lazy_tlb(&init_mm, current);
} }
/*
* cpu_have_feature - Test CPU features on module initialization
*/
int cpu_have_feature(unsigned int num)
{
return elf_hwcap & (1UL << num);
}
EXPORT_SYMBOL(cpu_have_feature);
/* /*
* show_cpuinfo - Get information on one CPU for use by procfs. * show_cpuinfo - Get information on one CPU for use by procfs.
*/ */
......
...@@ -45,32 +45,21 @@ void update_cr_regs(struct task_struct *task) ...@@ -45,32 +45,21 @@ void update_cr_regs(struct task_struct *task)
struct per_regs old, new; struct per_regs old, new;
/* Take care of the enable/disable of transactional execution. */ /* Take care of the enable/disable of transactional execution. */
if (MACHINE_HAS_TE || MACHINE_HAS_VX) { if (MACHINE_HAS_TE) {
unsigned long cr, cr_new; unsigned long cr, cr_new;
__ctl_store(cr, 0, 0); __ctl_store(cr, 0, 0);
cr_new = cr;
if (MACHINE_HAS_TE) {
/* Set or clear transaction execution TXC bit 8. */ /* Set or clear transaction execution TXC bit 8. */
cr_new |= (1UL << 55); cr_new = cr | (1UL << 55);
if (task->thread.per_flags & PER_FLAG_NO_TE) if (task->thread.per_flags & PER_FLAG_NO_TE)
cr_new &= ~(1UL << 55); cr_new &= ~(1UL << 55);
}
if (MACHINE_HAS_VX) {
/* Enable/disable of vector extension */
cr_new &= ~(1UL << 17);
if (task->thread.vxrs)
cr_new |= (1UL << 17);
}
if (cr_new != cr) if (cr_new != cr)
__ctl_load(cr_new, 0, 0); __ctl_load(cr_new, 0, 0);
if (MACHINE_HAS_TE) { /* Set or clear transaction execution TDC bits 62 and 63. */
/* Set/clear transaction execution TDC bits 62/63. */
__ctl_store(cr, 2, 2); __ctl_store(cr, 2, 2);
cr_new = cr & ~3UL; cr_new = cr & ~3UL;
if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) { if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) {
if (task->thread.per_flags & if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND_TEND)
PER_FLAG_TE_ABORT_RAND_TEND)
cr_new |= 1UL; cr_new |= 1UL;
else else
cr_new |= 2UL; cr_new |= 2UL;
...@@ -78,7 +67,6 @@ void update_cr_regs(struct task_struct *task) ...@@ -78,7 +67,6 @@ void update_cr_regs(struct task_struct *task)
if (cr_new != cr) if (cr_new != cr)
__ctl_load(cr_new, 2, 2); __ctl_load(cr_new, 2, 2);
} }
}
/* Copy user specified PER registers */ /* Copy user specified PER registers */
new.control = thread->per_user.control; new.control = thread->per_user.control;
new.start = thread->per_user.start; new.start = thread->per_user.start;
...@@ -242,21 +230,21 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr) ...@@ -242,21 +230,21 @@ static unsigned long __peek_user(struct task_struct *child, addr_t addr)
/* /*
* floating point control reg. is in the thread structure * floating point control reg. is in the thread structure
*/ */
tmp = child->thread.fp_regs.fpc; tmp = child->thread.fpu.fpc;
tmp <<= BITS_PER_LONG - 32; tmp <<= BITS_PER_LONG - 32;
} else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) { } else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
/* /*
* floating point regs. are either in child->thread.fp_regs * floating point regs. are either in child->thread.fpu
* or the child->thread.vxrs array * or the child->thread.fpu.vxrs array
*/ */
offset = addr - (addr_t) &dummy->regs.fp_regs.fprs; offset = addr - (addr_t) &dummy->regs.fp_regs.fprs;
if (child->thread.vxrs) if (is_vx_task(child))
tmp = *(addr_t *) tmp = *(addr_t *)
((addr_t) child->thread.vxrs + 2*offset); ((addr_t) child->thread.fpu.vxrs + 2*offset);
else else
tmp = *(addr_t *) tmp = *(addr_t *)
((addr_t) &child->thread.fp_regs.fprs + offset); ((addr_t) &child->thread.fpu.fprs + offset);
} else if (addr < (addr_t) (&dummy->regs.per_info + 1)) { } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
/* /*
...@@ -387,20 +375,20 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data) ...@@ -387,20 +375,20 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
if ((unsigned int) data != 0 || if ((unsigned int) data != 0 ||
test_fp_ctl(data >> (BITS_PER_LONG - 32))) test_fp_ctl(data >> (BITS_PER_LONG - 32)))
return -EINVAL; return -EINVAL;
child->thread.fp_regs.fpc = data >> (BITS_PER_LONG - 32); child->thread.fpu.fpc = data >> (BITS_PER_LONG - 32);
} else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) { } else if (addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
/* /*
* floating point regs. are either in child->thread.fp_regs * floating point regs. are either in child->thread.fpu
* or the child->thread.vxrs array * or the child->thread.fpu.vxrs array
*/ */
offset = addr - (addr_t) &dummy->regs.fp_regs.fprs; offset = addr - (addr_t) &dummy->regs.fp_regs.fprs;
if (child->thread.vxrs) if (is_vx_task(child))
*(addr_t *)((addr_t) *(addr_t *)((addr_t)
child->thread.vxrs + 2*offset) = data; child->thread.fpu.vxrs + 2*offset) = data;
else else
*(addr_t *)((addr_t) *(addr_t *)((addr_t)
&child->thread.fp_regs.fprs + offset) = data; &child->thread.fpu.fprs + offset) = data;
} else if (addr < (addr_t) (&dummy->regs.per_info + 1)) { } else if (addr < (addr_t) (&dummy->regs.per_info + 1)) {
/* /*
...@@ -621,20 +609,20 @@ static u32 __peek_user_compat(struct task_struct *child, addr_t addr) ...@@ -621,20 +609,20 @@ static u32 __peek_user_compat(struct task_struct *child, addr_t addr)
/* /*
* floating point control reg. is in the thread structure * floating point control reg. is in the thread structure
*/ */
tmp = child->thread.fp_regs.fpc; tmp = child->thread.fpu.fpc;
} else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) { } else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) {
/* /*
* floating point regs. are either in child->thread.fp_regs * floating point regs. are either in child->thread.fpu
* or the child->thread.vxrs array * or the child->thread.fpu.vxrs array
*/ */
offset = addr - (addr_t) &dummy32->regs.fp_regs.fprs; offset = addr - (addr_t) &dummy32->regs.fp_regs.fprs;
if (child->thread.vxrs) if (is_vx_task(child))
tmp = *(__u32 *) tmp = *(__u32 *)
((addr_t) child->thread.vxrs + 2*offset); ((addr_t) child->thread.fpu.vxrs + 2*offset);
else else
tmp = *(__u32 *) tmp = *(__u32 *)
((addr_t) &child->thread.fp_regs.fprs + offset); ((addr_t) &child->thread.fpu.fprs + offset);
} else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) { } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) {
/* /*
...@@ -746,20 +734,20 @@ static int __poke_user_compat(struct task_struct *child, ...@@ -746,20 +734,20 @@ static int __poke_user_compat(struct task_struct *child,
*/ */
if (test_fp_ctl(tmp)) if (test_fp_ctl(tmp))
return -EINVAL; return -EINVAL;
child->thread.fp_regs.fpc = data; child->thread.fpu.fpc = data;
} else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) { } else if (addr < (addr_t) (&dummy32->regs.fp_regs + 1)) {
/* /*
* floating point regs. are either in child->thread.fp_regs * floating point regs. are either in child->thread.fpu
* or the child->thread.vxrs array * or the child->thread.fpu.vxrs array
*/ */
offset = addr - (addr_t) &dummy32->regs.fp_regs.fprs; offset = addr - (addr_t) &dummy32->regs.fp_regs.fprs;
if (child->thread.vxrs) if (is_vx_task(child))
*(__u32 *)((addr_t) *(__u32 *)((addr_t)
child->thread.vxrs + 2*offset) = tmp; child->thread.fpu.vxrs + 2*offset) = tmp;
else else
*(__u32 *)((addr_t) *(__u32 *)((addr_t)
&child->thread.fp_regs.fprs + offset) = tmp; &child->thread.fpu.fprs + offset) = tmp;
} else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) { } else if (addr < (addr_t) (&dummy32->regs.per_info + 1)) {
/* /*
...@@ -952,18 +940,16 @@ static int s390_fpregs_get(struct task_struct *target, ...@@ -952,18 +940,16 @@ static int s390_fpregs_get(struct task_struct *target,
const struct user_regset *regset, unsigned int pos, const struct user_regset *regset, unsigned int pos,
unsigned int count, void *kbuf, void __user *ubuf) unsigned int count, void *kbuf, void __user *ubuf)
{ {
if (target == current) { _s390_fp_regs fp_regs;
save_fp_ctl(&target->thread.fp_regs.fpc);
save_fp_regs(target->thread.fp_regs.fprs); if (target == current)
} else if (target->thread.vxrs) { save_fpu_regs();
int i;
fp_regs.fpc = target->thread.fpu.fpc;
fpregs_store(&fp_regs, &target->thread.fpu);
for (i = 0; i < __NUM_VXRS_LOW; i++)
target->thread.fp_regs.fprs[i] =
*(freg_t *)(target->thread.vxrs + i);
}
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, return user_regset_copyout(&pos, &count, &kbuf, &ubuf,
&target->thread.fp_regs, 0, -1); &fp_regs, 0, -1);
} }
static int s390_fpregs_set(struct task_struct *target, static int s390_fpregs_set(struct task_struct *target,
...@@ -972,41 +958,33 @@ static int s390_fpregs_set(struct task_struct *target, ...@@ -972,41 +958,33 @@ static int s390_fpregs_set(struct task_struct *target,
const void __user *ubuf) const void __user *ubuf)
{ {
int rc = 0; int rc = 0;
freg_t fprs[__NUM_FPRS];
if (target == current) { if (target == current)
save_fp_ctl(&target->thread.fp_regs.fpc); save_fpu_regs();
save_fp_regs(target->thread.fp_regs.fprs);
}
/* If setting FPC, must validate it first. */ /* If setting FPC, must validate it first. */
if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) { if (count > 0 && pos < offsetof(s390_fp_regs, fprs)) {
u32 ufpc[2] = { target->thread.fp_regs.fpc, 0 }; u32 ufpc[2] = { target->thread.fpu.fpc, 0 };
rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ufpc, rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ufpc,
0, offsetof(s390_fp_regs, fprs)); 0, offsetof(s390_fp_regs, fprs));
if (rc) if (rc)
return rc; return rc;
if (ufpc[1] != 0 || test_fp_ctl(ufpc[0])) if (ufpc[1] != 0 || test_fp_ctl(ufpc[0]))
return -EINVAL; return -EINVAL;
target->thread.fp_regs.fpc = ufpc[0]; target->thread.fpu.fpc = ufpc[0];
} }
if (rc == 0 && count > 0) if (rc == 0 && count > 0)
rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
target->thread.fp_regs.fprs, fprs, offsetof(s390_fp_regs, fprs), -1);
offsetof(s390_fp_regs, fprs), -1); if (rc)
return rc;
if (rc == 0) {
if (target == current) {
restore_fp_ctl(&target->thread.fp_regs.fpc);
restore_fp_regs(target->thread.fp_regs.fprs);
} else if (target->thread.vxrs) {
int i;
for (i = 0; i < __NUM_VXRS_LOW; i++) if (is_vx_task(target))
*(freg_t *)(target->thread.vxrs + i) = convert_fp_to_vx(target->thread.fpu.vxrs, fprs);
target->thread.fp_regs.fprs[i]; else
} memcpy(target->thread.fpu.fprs, &fprs, sizeof(fprs));
}
return rc; return rc;
} }
...@@ -1069,11 +1047,11 @@ static int s390_vxrs_low_get(struct task_struct *target, ...@@ -1069,11 +1047,11 @@ static int s390_vxrs_low_get(struct task_struct *target,
if (!MACHINE_HAS_VX) if (!MACHINE_HAS_VX)
return -ENODEV; return -ENODEV;
if (target->thread.vxrs) { if (is_vx_task(target)) {
if (target == current) if (target == current)
save_vx_regs(target->thread.vxrs); save_fpu_regs();
for (i = 0; i < __NUM_VXRS_LOW; i++) for (i = 0; i < __NUM_VXRS_LOW; i++)
vxrs[i] = *((__u64 *)(target->thread.vxrs + i) + 1); vxrs[i] = *((__u64 *)(target->thread.fpu.vxrs + i) + 1);
} else } else
memset(vxrs, 0, sizeof(vxrs)); memset(vxrs, 0, sizeof(vxrs));
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1);
...@@ -1089,20 +1067,17 @@ static int s390_vxrs_low_set(struct task_struct *target, ...@@ -1089,20 +1067,17 @@ static int s390_vxrs_low_set(struct task_struct *target,
if (!MACHINE_HAS_VX) if (!MACHINE_HAS_VX)
return -ENODEV; return -ENODEV;
if (!target->thread.vxrs) { if (!is_vx_task(target)) {
rc = alloc_vector_registers(target); rc = alloc_vector_registers(target);
if (rc) if (rc)
return rc; return rc;
} else if (target == current) } else if (target == current)
save_vx_regs(target->thread.vxrs); save_fpu_regs();
rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1);
if (rc == 0) { if (rc == 0)
for (i = 0; i < __NUM_VXRS_LOW; i++) for (i = 0; i < __NUM_VXRS_LOW; i++)
*((__u64 *)(target->thread.vxrs + i) + 1) = vxrs[i]; *((__u64 *)(target->thread.fpu.vxrs + i) + 1) = vxrs[i];
if (target == current)
restore_vx_regs(target->thread.vxrs);
}
return rc; return rc;
} }
...@@ -1116,10 +1091,10 @@ static int s390_vxrs_high_get(struct task_struct *target, ...@@ -1116,10 +1091,10 @@ static int s390_vxrs_high_get(struct task_struct *target,
if (!MACHINE_HAS_VX) if (!MACHINE_HAS_VX)
return -ENODEV; return -ENODEV;
if (target->thread.vxrs) { if (is_vx_task(target)) {
if (target == current) if (target == current)
save_vx_regs(target->thread.vxrs); save_fpu_regs();
memcpy(vxrs, target->thread.vxrs + __NUM_VXRS_LOW, memcpy(vxrs, target->thread.fpu.vxrs + __NUM_VXRS_LOW,
sizeof(vxrs)); sizeof(vxrs));
} else } else
memset(vxrs, 0, sizeof(vxrs)); memset(vxrs, 0, sizeof(vxrs));
...@@ -1135,18 +1110,15 @@ static int s390_vxrs_high_set(struct task_struct *target, ...@@ -1135,18 +1110,15 @@ static int s390_vxrs_high_set(struct task_struct *target,
if (!MACHINE_HAS_VX) if (!MACHINE_HAS_VX)
return -ENODEV; return -ENODEV;
if (!target->thread.vxrs) { if (!is_vx_task(target)) {
rc = alloc_vector_registers(target); rc = alloc_vector_registers(target);
if (rc) if (rc)
return rc; return rc;
} else if (target == current) } else if (target == current)
save_vx_regs(target->thread.vxrs); save_fpu_regs();
rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
target->thread.vxrs + __NUM_VXRS_LOW, 0, -1); target->thread.fpu.vxrs + __NUM_VXRS_LOW, 0, -1);
if (rc == 0 && target == current)
restore_vx_regs(target->thread.vxrs);
return rc; return rc;
} }
......
#include <linux/module.h> #include <linux/module.h>
#include <linux/kvm_host.h> #include <linux/kvm_host.h>
#include <asm/fpu-internal.h>
#include <asm/ftrace.h> #include <asm/ftrace.h>
#ifdef CONFIG_FUNCTION_TRACER #ifdef CONFIG_FUNCTION_TRACER
...@@ -8,6 +9,8 @@ EXPORT_SYMBOL(_mcount); ...@@ -8,6 +9,8 @@ EXPORT_SYMBOL(_mcount);
#if IS_ENABLED(CONFIG_KVM) #if IS_ENABLED(CONFIG_KVM)
EXPORT_SYMBOL(sie64a); EXPORT_SYMBOL(sie64a);
EXPORT_SYMBOL(sie_exit); EXPORT_SYMBOL(sie_exit);
EXPORT_SYMBOL(save_fpu_regs);
EXPORT_SYMBOL(__ctl_set_vx);
#endif #endif
EXPORT_SYMBOL(memcpy); EXPORT_SYMBOL(memcpy);
EXPORT_SYMBOL(memset); EXPORT_SYMBOL(memset);
/*
* Mini SCLP driver.
*
* Copyright IBM Corp. 2004, 2009
*
* Author(s): Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>,
* Heiko Carstens <heiko.carstens@de.ibm.com>,
*
*/
#include <linux/linkage.h>
#include <asm/irq.h>
LC_EXT_NEW_PSW = 0x58 # addr of ext int handler
LC_EXT_NEW_PSW_64 = 0x1b0 # addr of ext int handler 64 bit
LC_EXT_INT_PARAM = 0x80 # addr of ext int parameter
LC_EXT_INT_CODE = 0x86 # addr of ext int code
LC_AR_MODE_ID = 0xa3
#
# Subroutine which waits synchronously until either an external interruption
# or a timeout occurs.
#
# Parameters:
# R2 = 0 for no timeout, non-zero for timeout in (approximated) seconds
#
# Returns:
# R2 = 0 on interrupt, 2 on timeout
# R3 = external interruption parameter if R2=0
#
_sclp_wait_int:
stm %r6,%r15,24(%r15) # save registers
basr %r13,0 # get base register
.LbaseS1:
ahi %r15,-96 # create stack frame
la %r8,LC_EXT_NEW_PSW # register int handler
la %r9,.LextpswS1-.LbaseS1(%r13)
tm LC_AR_MODE_ID,1
jno .Lesa1
la %r8,LC_EXT_NEW_PSW_64 # register int handler 64 bit
la %r9,.LextpswS1_64-.LbaseS1(%r13)
.Lesa1:
mvc .LoldpswS1-.LbaseS1(16,%r13),0(%r8)
mvc 0(16,%r8),0(%r9)
epsw %r6,%r7 # set current addressing mode
nill %r6,0x1 # in new psw (31 or 64 bit mode)
nilh %r7,0x8000
stm %r6,%r7,0(%r8)
lhi %r6,0x0200 # cr mask for ext int (cr0.54)
ltr %r2,%r2
jz .LsetctS1
ahi %r6,0x0800 # cr mask for clock int (cr0.52)
stck .LtimeS1-.LbaseS1(%r13) # initiate timeout
al %r2,.LtimeS1-.LbaseS1(%r13)
st %r2,.LtimeS1-.LbaseS1(%r13)
sckc .LtimeS1-.LbaseS1(%r13)
.LsetctS1:
stctl %c0,%c0,.LctlS1-.LbaseS1(%r13) # enable required interrupts
l %r0,.LctlS1-.LbaseS1(%r13)
lhi %r1,~(0x200 | 0x800) # clear old values
nr %r1,%r0
or %r1,%r6 # set new value
st %r1,.LctlS1-.LbaseS1(%r13)
lctl %c0,%c0,.LctlS1-.LbaseS1(%r13)
st %r0,.LctlS1-.LbaseS1(%r13)
lhi %r2,2 # return code for timeout
.LloopS1:
lpsw .LwaitpswS1-.LbaseS1(%r13) # wait until interrupt
.LwaitS1:
lh %r7,LC_EXT_INT_CODE
chi %r7,EXT_IRQ_CLK_COMP # timeout?
je .LtimeoutS1
chi %r7,EXT_IRQ_SERVICE_SIG # service int?
jne .LloopS1
sr %r2,%r2
l %r3,LC_EXT_INT_PARAM
.LtimeoutS1:
lctl %c0,%c0,.LctlS1-.LbaseS1(%r13) # restore interrupt setting
# restore old handler
mvc 0(16,%r8),.LoldpswS1-.LbaseS1(%r13)
lm %r6,%r15,120(%r15) # restore registers
br %r14 # return to caller
.align 8
.LoldpswS1:
.long 0, 0, 0, 0 # old ext int PSW
.LextpswS1:
.long 0x00080000, 0x80000000+.LwaitS1 # PSW to handle ext int
.LextpswS1_64:
.quad 0, .LwaitS1 # PSW to handle ext int, 64 bit
.LwaitpswS1:
.long 0x010a0000, 0x00000000+.LloopS1 # PSW to wait for ext int
.LtimeS1:
.quad 0 # current time
.LctlS1:
.long 0 # CT0 contents
#
# Subroutine to synchronously issue a service call.
#
# Parameters:
# R2 = command word
# R3 = sccb address
#
# Returns:
# R2 = 0 on success, 1 on failure
# R3 = sccb response code if R2 = 0
#
_sclp_servc:
stm %r6,%r15,24(%r15) # save registers
ahi %r15,-96 # create stack frame
lr %r6,%r2 # save command word
lr %r7,%r3 # save sccb address
.LretryS2:
lhi %r2,1 # error return code
.insn rre,0xb2200000,%r6,%r7 # servc
brc 1,.LendS2 # exit if not operational
brc 8,.LnotbusyS2 # go on if not busy
sr %r2,%r2 # wait until no longer busy
bras %r14,_sclp_wait_int
j .LretryS2 # retry
.LnotbusyS2:
sr %r2,%r2 # wait until result
bras %r14,_sclp_wait_int
sr %r2,%r2
lh %r3,6(%r7)
.LendS2:
lm %r6,%r15,120(%r15) # restore registers
br %r14
#
# Subroutine to set up the SCLP interface.
#
# Parameters:
# R2 = 0 to activate, non-zero to deactivate
#
# Returns:
# R2 = 0 on success, non-zero on failure
#
_sclp_setup:
stm %r6,%r15,24(%r15) # save registers
ahi %r15,-96 # create stack frame
basr %r13,0 # get base register
.LbaseS3:
l %r6,.LsccbS0-.LbaseS3(%r13) # prepare init mask sccb
mvc 0(.LinitendS3-.LinitsccbS3,%r6),.LinitsccbS3-.LbaseS3(%r13)
ltr %r2,%r2 # initialization?
jz .LdoinitS3 # go ahead
# clear masks
xc .LinitmaskS3-.LinitsccbS3(8,%r6),.LinitmaskS3-.LinitsccbS3(%r6)
.LdoinitS3:
l %r2,.LwritemaskS3-.LbaseS3(%r13)# get command word
lr %r3,%r6 # get sccb address
bras %r14,_sclp_servc # issue service call
ltr %r2,%r2 # servc successful?
jnz .LerrorS3
chi %r3,0x20 # write mask successful?
jne .LerrorS3
# check masks
la %r2,.LinitmaskS3-.LinitsccbS3(%r6)
l %r1,0(%r2) # receive mask ok?
n %r1,12(%r2)
cl %r1,0(%r2)
jne .LerrorS3
l %r1,4(%r2) # send mask ok?
n %r1,8(%r2)
cl %r1,4(%r2)
sr %r2,%r2
je .LendS3
.LerrorS3:
lhi %r2,1 # error return code
.LendS3:
lm %r6,%r15,120(%r15) # restore registers
br %r14
.LwritemaskS3:
.long 0x00780005 # SCLP command for write mask
.LinitsccbS3:
.word .LinitendS3-.LinitsccbS3
.byte 0,0,0,0
.word 0
.word 0
.word 4
.LinitmaskS3:
.long 0x80000000
.long 0x40000000
.long 0
.long 0
.LinitendS3:
#
# Subroutine which prints a given text to the SCLP console.
#
# Parameters:
# R2 = address of nil-terminated ASCII text
#
# Returns:
# R2 = 0 on success, 1 on failure
#
_sclp_print:
stm %r6,%r15,24(%r15) # save registers
ahi %r15,-96 # create stack frame
basr %r13,0 # get base register
.LbaseS4:
l %r8,.LsccbS0-.LbaseS4(%r13) # prepare write data sccb
mvc 0(.LmtoS4-.LwritesccbS4,%r8),.LwritesccbS4-.LbaseS4(%r13)
la %r7,.LmtoS4-.LwritesccbS4(%r8) # current mto addr
sr %r0,%r0
l %r10,.Lascebc-.LbaseS4(%r13) # address of translation table
.LinitmtoS4:
# initialize mto
mvc 0(.LmtoendS4-.LmtoS4,%r7),.LmtoS4-.LbaseS4(%r13)
lhi %r6,.LmtoendS4-.LmtoS4 # current mto length
.LloopS4:
ic %r0,0(%r2) # get character
ahi %r2,1
ltr %r0,%r0 # end of string?
jz .LfinalizemtoS4
chi %r0,0x0a # end of line (NL)?
jz .LfinalizemtoS4
stc %r0,0(%r6,%r7) # copy to mto
la %r11,0(%r6,%r7)
tr 0(1,%r11),0(%r10) # translate to EBCDIC
ahi %r6,1
j .LloopS4
.LfinalizemtoS4:
sth %r6,0(%r7) # update mto length
lh %r9,.LmdbS4-.LwritesccbS4(%r8) # update mdb length
ar %r9,%r6
sth %r9,.LmdbS4-.LwritesccbS4(%r8)
lh %r9,.LevbufS4-.LwritesccbS4(%r8)# update evbuf length
ar %r9,%r6
sth %r9,.LevbufS4-.LwritesccbS4(%r8)
lh %r9,0(%r8) # update sccb length
ar %r9,%r6
sth %r9,0(%r8)
ar %r7,%r6 # update current mto address
ltr %r0,%r0 # more characters?
jnz .LinitmtoS4
l %r2,.LwritedataS4-.LbaseS4(%r13)# write data
lr %r3,%r8
bras %r14,_sclp_servc
ltr %r2,%r2 # servc successful?
jnz .LendS4
chi %r3,0x20 # write data successful?
je .LendS4
lhi %r2,1 # error return code
.LendS4:
lm %r6,%r15,120(%r15) # restore registers
br %r14
#
# Function which prints a given text to the SCLP console.
#
# Parameters:
# R2 = address of nil-terminated ASCII text
#
# Returns:
# R2 = 0 on success, 1 on failure
#
ENTRY(_sclp_print_early)
stm %r6,%r15,24(%r15) # save registers
ahi %r15,-96 # create stack frame
tm LC_AR_MODE_ID,1
jno .Lesa2
ahi %r15,-80
stmh %r6,%r15,96(%r15) # store upper register halves
basr %r13,0
lmh %r0,%r15,.Lzeroes-.(%r13) # clear upper register halves
.Lesa2:
lr %r10,%r2 # save string pointer
lhi %r2,0
bras %r14,_sclp_setup # enable console
ltr %r2,%r2
jnz .LendS5
lr %r2,%r10
bras %r14,_sclp_print # print string
ltr %r2,%r2
jnz .LendS5
lhi %r2,1
bras %r14,_sclp_setup # disable console
.LendS5:
tm LC_AR_MODE_ID,1
jno .Lesa3
lgfr %r2,%r2 # sign extend return value
lmh %r6,%r15,96(%r15) # restore upper register halves
ahi %r15,80
.Lesa3:
lm %r6,%r15,120(%r15) # restore registers
br %r14
.Lzeroes:
.fill 64,4,0
.LwritedataS4:
.long 0x00760005 # SCLP command for write data
.LwritesccbS4:
# sccb
.word .LmtoS4-.LwritesccbS4
.byte 0
.byte 0,0,0
.word 0
# evbuf
.LevbufS4:
.word .LmtoS4-.LevbufS4
.byte 0x02
.byte 0
.word 0
.LmdbS4:
# mdb
.word .LmtoS4-.LmdbS4
.word 1
.long 0xd4c4c240
.long 1
# go
.LgoS4:
.word .LmtoS4-.LgoS4
.word 1
.long 0
.byte 0,0,0,0,0,0,0,0
.byte 0,0,0
.byte 0
.byte 0,0,0,0,0,0,0
.byte 0
.word 0
.byte 0,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,0,0,0
.LmtoS4:
.word .LmtoendS4-.LmtoS4
.word 4
.word 0x1000
.byte 0
.byte 0,0,0
.LmtoendS4:
# Global constants
.LsccbS0:
.long _sclp_work_area
.Lascebc:
.long _ascebc
.section .data,"aw",@progbits
.balign 4096
_sclp_work_area:
.fill 4096
.previous
/*
* Copyright IBM Corp. 2015
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#include <linux/kernel.h>
#include <asm/ebcdic.h>
#include <asm/irq.h>
#include <asm/lowcore.h>
#include <asm/processor.h>
#include <asm/sclp.h>
static char _sclp_work_area[4096] __aligned(PAGE_SIZE);
static void _sclp_wait_int(void)
{
unsigned long cr0, cr0_new, psw_mask, addr;
psw_t psw_ext_save, psw_wait;
__ctl_store(cr0, 0, 0);
cr0_new = cr0 | 0x200;
__ctl_load(cr0_new, 0, 0);
psw_ext_save = S390_lowcore.external_new_psw;
psw_mask = __extract_psw() & (PSW_MASK_EA | PSW_MASK_BA);
S390_lowcore.external_new_psw.mask = psw_mask;
psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT;
S390_lowcore.ext_int_code = 0;
do {
asm volatile(
" larl %[addr],0f\n"
" stg %[addr],%[psw_wait_addr]\n"
" stg %[addr],%[psw_ext_addr]\n"
" lpswe %[psw_wait]\n"
"0:\n"
: [addr] "=&d" (addr),
[psw_wait_addr] "=Q" (psw_wait.addr),
[psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr)
: [psw_wait] "Q" (psw_wait)
: "cc", "memory");
} while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG);
__ctl_load(cr0, 0, 0);
S390_lowcore.external_new_psw = psw_ext_save;
}
static int _sclp_servc(unsigned int cmd, char *sccb)
{
unsigned int cc;
do {
asm volatile(
" .insn rre,0xb2200000,%1,%2\n"
" ipm %0\n"
: "=d" (cc) : "d" (cmd), "a" (sccb)
: "cc", "memory");
cc >>= 28;
if (cc == 3)
return -EINVAL;
_sclp_wait_int();
} while (cc != 0);
return (*(unsigned short *)(sccb + 6) == 0x20) ? 0 : -EIO;
}
static int _sclp_setup(int disable)
{
static unsigned char init_sccb[] = {
0x00, 0x1c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x04,
0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned int *masks;
int rc;
memcpy(_sclp_work_area, init_sccb, 28);
masks = (unsigned int *)(_sclp_work_area + 12);
if (disable)
memset(masks, 0, 16);
/* SCLP write mask */
rc = _sclp_servc(0x00780005, _sclp_work_area);
if (rc)
return rc;
if ((masks[0] & masks[3]) != masks[0] ||
(masks[1] & masks[2]) != masks[1])
return -EIO;
return 0;
}
static int _sclp_print(const char *str)
{
static unsigned char write_head[] = {
/* sccb header */
0x00, 0x52, /* 0 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 2 */
/* evbuf */
0x00, 0x4a, /* 8 */
0x02, 0x00, 0x00, 0x00, /* 10 */
/* mdb */
0x00, 0x44, /* 14 */
0x00, 0x01, /* 16 */
0xd4, 0xc4, 0xc2, 0x40, /* 18 */
0x00, 0x00, 0x00, 0x01, /* 22 */
/* go */
0x00, 0x38, /* 26 */
0x00, 0x01, /* 28 */
0x00, 0x00, 0x00, 0x00, /* 30 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 34 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 */
0x00, 0x00, 0x00, 0x00, /* 50 */
0x00, 0x00, /* 54 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 64 */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 72 */
0x00, 0x00, /* 80 */
};
static unsigned char write_mto[] = {
/* mto */
0x00, 0x0a, /* 0 */
0x00, 0x04, /* 2 */
0x10, 0x00, /* 4 */
0x00, 0x00, 0x00, 0x00 /* 6 */
};
unsigned char *ptr, ch;
unsigned int count;
memcpy(_sclp_work_area, write_head, sizeof(write_head));
ptr = _sclp_work_area + sizeof(write_head);
do {
memcpy(ptr, write_mto, sizeof(write_mto));
for (count = sizeof(write_mto); (ch = *str++) != 0; count++) {
if (ch == 0x0a)
break;
ptr[count] = _ascebc[ch];
}
/* Update length fields in mto, mdb, evbuf and sccb */
*(unsigned short *) ptr = count;
*(unsigned short *)(_sclp_work_area + 14) += count;
*(unsigned short *)(_sclp_work_area + 8) += count;
*(unsigned short *)(_sclp_work_area + 0) += count;
ptr += count;
} while (ch != 0);
/* SCLP write data */
return _sclp_servc(0x00760005, _sclp_work_area);
}
int _sclp_print_early(const char *str)
{
int rc;
rc = _sclp_setup(0);
if (rc)
return rc;
rc = _sclp_print(str);
if (rc)
return rc;
return _sclp_setup(1);
}
...@@ -62,6 +62,7 @@ ...@@ -62,6 +62,7 @@
#include <asm/os_info.h> #include <asm/os_info.h>
#include <asm/sclp.h> #include <asm/sclp.h>
#include <asm/sysinfo.h> #include <asm/sysinfo.h>
#include <asm/numa.h>
#include "entry.h" #include "entry.h"
/* /*
...@@ -76,7 +77,7 @@ EXPORT_SYMBOL(console_devno); ...@@ -76,7 +77,7 @@ EXPORT_SYMBOL(console_devno);
unsigned int console_irq = -1; unsigned int console_irq = -1;
EXPORT_SYMBOL(console_irq); EXPORT_SYMBOL(console_irq);
unsigned long elf_hwcap = 0; unsigned long elf_hwcap __read_mostly = 0;
char elf_platform[ELF_PLATFORM_SIZE]; char elf_platform[ELF_PLATFORM_SIZE];
int __initdata memory_end_set; int __initdata memory_end_set;
...@@ -688,7 +689,7 @@ static void __init setup_memory(void) ...@@ -688,7 +689,7 @@ static void __init setup_memory(void)
/* /*
* Setup hardware capabilities. * Setup hardware capabilities.
*/ */
static void __init setup_hwcaps(void) static int __init setup_hwcaps(void)
{ {
static const int stfl_bits[6] = { 0, 2, 7, 17, 19, 21 }; static const int stfl_bits[6] = { 0, 2, 7, 17, 19, 21 };
struct cpuid cpu_id; struct cpuid cpu_id;
...@@ -754,9 +755,11 @@ static void __init setup_hwcaps(void) ...@@ -754,9 +755,11 @@ static void __init setup_hwcaps(void)
elf_hwcap |= HWCAP_S390_TE; elf_hwcap |= HWCAP_S390_TE;
/* /*
* Vector extension HWCAP_S390_VXRS is bit 11. * Vector extension HWCAP_S390_VXRS is bit 11. The Vector extension
* can be disabled with the "novx" parameter. Use MACHINE_HAS_VX
* instead of facility bit 129.
*/ */
if (test_facility(129)) if (MACHINE_HAS_VX)
elf_hwcap |= HWCAP_S390_VXRS; elf_hwcap |= HWCAP_S390_VXRS;
get_cpu_id(&cpu_id); get_cpu_id(&cpu_id);
add_device_randomness(&cpu_id, sizeof(cpu_id)); add_device_randomness(&cpu_id, sizeof(cpu_id));
...@@ -793,7 +796,9 @@ static void __init setup_hwcaps(void) ...@@ -793,7 +796,9 @@ static void __init setup_hwcaps(void)
strcpy(elf_platform, "z13"); strcpy(elf_platform, "z13");
break; break;
} }
return 0;
} }
arch_initcall(setup_hwcaps);
/* /*
* Add system information as device randomness * Add system information as device randomness
...@@ -879,11 +884,7 @@ void __init setup_arch(char **cmdline_p) ...@@ -879,11 +884,7 @@ void __init setup_arch(char **cmdline_p)
setup_lowcore(); setup_lowcore();
smp_fill_possible_mask(); smp_fill_possible_mask();
cpu_init(); cpu_init();
numa_setup();
/*
* Setup capabilities (ELF_HWCAP & ELF_PLATFORM).
*/
setup_hwcaps();
/* /*
* Create kernel page tables and switch to virtual addressing. * Create kernel page tables and switch to virtual addressing.
......
...@@ -105,32 +105,13 @@ struct rt_sigframe ...@@ -105,32 +105,13 @@ struct rt_sigframe
static void store_sigregs(void) static void store_sigregs(void)
{ {
save_access_regs(current->thread.acrs); save_access_regs(current->thread.acrs);
save_fp_ctl(&current->thread.fp_regs.fpc); save_fpu_regs();
if (current->thread.vxrs) {
int i;
save_vx_regs(current->thread.vxrs);
for (i = 0; i < __NUM_FPRS; i++)
current->thread.fp_regs.fprs[i] =
*(freg_t *)(current->thread.vxrs + i);
} else
save_fp_regs(current->thread.fp_regs.fprs);
} }
/* Load registers after signal return */ /* Load registers after signal return */
static void load_sigregs(void) static void load_sigregs(void)
{ {
restore_access_regs(current->thread.acrs); restore_access_regs(current->thread.acrs);
/* restore_fp_ctl is done in restore_sigregs */
if (current->thread.vxrs) {
int i;
for (i = 0; i < __NUM_FPRS; i++)
*(freg_t *)(current->thread.vxrs + i) =
current->thread.fp_regs.fprs[i];
restore_vx_regs(current->thread.vxrs);
} else
restore_fp_regs(current->thread.fp_regs.fprs);
} }
/* Returns non-zero on fault. */ /* Returns non-zero on fault. */
...@@ -146,8 +127,7 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) ...@@ -146,8 +127,7 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs)); memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs));
memcpy(&user_sregs.regs.acrs, current->thread.acrs, memcpy(&user_sregs.regs.acrs, current->thread.acrs,
sizeof(user_sregs.regs.acrs)); sizeof(user_sregs.regs.acrs));
memcpy(&user_sregs.fpregs, &current->thread.fp_regs, fpregs_store(&user_sregs.fpregs, &current->thread.fpu);
sizeof(user_sregs.fpregs));
if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs))) if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs)))
return -EFAULT; return -EFAULT;
return 0; return 0;
...@@ -166,8 +146,8 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) ...@@ -166,8 +146,8 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW_MASK_RI)) if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW_MASK_RI))
return -EINVAL; return -EINVAL;
/* Loading the floating-point-control word can fail. Do that first. */ /* Test the floating-point-control word. */
if (restore_fp_ctl(&user_sregs.fpregs.fpc)) if (test_fp_ctl(user_sregs.fpregs.fpc))
return -EINVAL; return -EINVAL;
/* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */ /* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
...@@ -185,8 +165,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) ...@@ -185,8 +165,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
memcpy(&current->thread.acrs, &user_sregs.regs.acrs, memcpy(&current->thread.acrs, &user_sregs.regs.acrs,
sizeof(current->thread.acrs)); sizeof(current->thread.acrs));
memcpy(&current->thread.fp_regs, &user_sregs.fpregs, fpregs_load(&user_sregs.fpregs, &current->thread.fpu);
sizeof(current->thread.fp_regs));
clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
return 0; return 0;
...@@ -200,13 +179,13 @@ static int save_sigregs_ext(struct pt_regs *regs, ...@@ -200,13 +179,13 @@ static int save_sigregs_ext(struct pt_regs *regs,
int i; int i;
/* Save vector registers to signal stack */ /* Save vector registers to signal stack */
if (current->thread.vxrs) { if (is_vx_task(current)) {
for (i = 0; i < __NUM_VXRS_LOW; i++) for (i = 0; i < __NUM_VXRS_LOW; i++)
vxrs[i] = *((__u64 *)(current->thread.vxrs + i) + 1); vxrs[i] = *((__u64 *)(current->thread.fpu.vxrs + i) + 1);
if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, if (__copy_to_user(&sregs_ext->vxrs_low, vxrs,
sizeof(sregs_ext->vxrs_low)) || sizeof(sregs_ext->vxrs_low)) ||
__copy_to_user(&sregs_ext->vxrs_high, __copy_to_user(&sregs_ext->vxrs_high,
current->thread.vxrs + __NUM_VXRS_LOW, current->thread.fpu.vxrs + __NUM_VXRS_LOW,
sizeof(sregs_ext->vxrs_high))) sizeof(sregs_ext->vxrs_high)))
return -EFAULT; return -EFAULT;
} }
...@@ -220,15 +199,15 @@ static int restore_sigregs_ext(struct pt_regs *regs, ...@@ -220,15 +199,15 @@ static int restore_sigregs_ext(struct pt_regs *regs,
int i; int i;
/* Restore vector registers from signal stack */ /* Restore vector registers from signal stack */
if (current->thread.vxrs) { if (is_vx_task(current)) {
if (__copy_from_user(vxrs, &sregs_ext->vxrs_low, if (__copy_from_user(vxrs, &sregs_ext->vxrs_low,
sizeof(sregs_ext->vxrs_low)) || sizeof(sregs_ext->vxrs_low)) ||
__copy_from_user(current->thread.vxrs + __NUM_VXRS_LOW, __copy_from_user(current->thread.fpu.vxrs + __NUM_VXRS_LOW,
&sregs_ext->vxrs_high, &sregs_ext->vxrs_high,
sizeof(sregs_ext->vxrs_high))) sizeof(sregs_ext->vxrs_high)))
return -EFAULT; return -EFAULT;
for (i = 0; i < __NUM_VXRS_LOW; i++) for (i = 0; i < __NUM_VXRS_LOW; i++)
*((__u64 *)(current->thread.vxrs + i) + 1) = vxrs[i]; *((__u64 *)(current->thread.fpu.vxrs + i) + 1) = vxrs[i];
} }
return 0; return 0;
} }
...@@ -243,6 +222,7 @@ SYSCALL_DEFINE0(sigreturn) ...@@ -243,6 +222,7 @@ SYSCALL_DEFINE0(sigreturn)
if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE))
goto badframe; goto badframe;
set_current_blocked(&set); set_current_blocked(&set);
save_fpu_regs();
if (restore_sigregs(regs, &frame->sregs)) if (restore_sigregs(regs, &frame->sregs))
goto badframe; goto badframe;
if (restore_sigregs_ext(regs, &frame->sregs_ext)) if (restore_sigregs_ext(regs, &frame->sregs_ext))
...@@ -266,6 +246,7 @@ SYSCALL_DEFINE0(rt_sigreturn) ...@@ -266,6 +246,7 @@ SYSCALL_DEFINE0(rt_sigreturn)
set_current_blocked(&set); set_current_blocked(&set);
if (restore_altstack(&frame->uc.uc_stack)) if (restore_altstack(&frame->uc.uc_stack))
goto badframe; goto badframe;
save_fpu_regs();
if (restore_sigregs(regs, &frame->uc.uc_mcontext)) if (restore_sigregs(regs, &frame->uc.uc_mcontext))
goto badframe; goto badframe;
if (restore_sigregs_ext(regs, &frame->uc.uc_mcontext_ext)) if (restore_sigregs_ext(regs, &frame->uc.uc_mcontext_ext))
...@@ -400,7 +381,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, ...@@ -400,7 +381,7 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
uc_flags = 0; uc_flags = 0;
if (MACHINE_HAS_VX) { if (MACHINE_HAS_VX) {
frame_size += sizeof(_sigregs_ext); frame_size += sizeof(_sigregs_ext);
if (current->thread.vxrs) if (is_vx_task(current))
uc_flags |= UC_VXRS; uc_flags |= UC_VXRS;
} }
frame = get_sigframe(&ksig->ka, regs, frame_size); frame = get_sigframe(&ksig->ka, regs, frame_size);
......
...@@ -532,8 +532,8 @@ EXPORT_SYMBOL(smp_ctl_clear_bit); ...@@ -532,8 +532,8 @@ EXPORT_SYMBOL(smp_ctl_clear_bit);
#ifdef CONFIG_CRASH_DUMP #ifdef CONFIG_CRASH_DUMP
static void __smp_store_cpu_state(struct save_area_ext *sa_ext, u16 address, static void __init __smp_store_cpu_state(struct save_area_ext *sa_ext,
int is_boot_cpu) u16 address, int is_boot_cpu)
{ {
void *lc = (void *)(unsigned long) store_prefix(); void *lc = (void *)(unsigned long) store_prefix();
unsigned long vx_sa; unsigned long vx_sa;
......
...@@ -276,9 +276,9 @@ SYSCALL(sys_ni_syscall,compat_sys_s390_fadvise64_64) ...@@ -276,9 +276,9 @@ SYSCALL(sys_ni_syscall,compat_sys_s390_fadvise64_64)
SYSCALL(sys_statfs64,compat_sys_statfs64) SYSCALL(sys_statfs64,compat_sys_statfs64)
SYSCALL(sys_fstatfs64,compat_sys_fstatfs64) SYSCALL(sys_fstatfs64,compat_sys_fstatfs64)
SYSCALL(sys_remap_file_pages,compat_sys_remap_file_pages) SYSCALL(sys_remap_file_pages,compat_sys_remap_file_pages)
NI_SYSCALL /* 268 sys_mbind */ SYSCALL(sys_mbind,compat_sys_mbind)
NI_SYSCALL /* 269 sys_get_mempolicy */ SYSCALL(sys_get_mempolicy,compat_sys_get_mempolicy)
NI_SYSCALL /* 270 sys_set_mempolicy */ SYSCALL(sys_set_mempolicy,compat_sys_set_mempolicy)
SYSCALL(sys_mq_open,compat_sys_mq_open) SYSCALL(sys_mq_open,compat_sys_mq_open)
SYSCALL(sys_mq_unlink,compat_sys_mq_unlink) SYSCALL(sys_mq_unlink,compat_sys_mq_unlink)
SYSCALL(sys_mq_timedsend,compat_sys_mq_timedsend) SYSCALL(sys_mq_timedsend,compat_sys_mq_timedsend)
...@@ -295,7 +295,7 @@ SYSCALL(sys_ioprio_get,compat_sys_ioprio_get) ...@@ -295,7 +295,7 @@ SYSCALL(sys_ioprio_get,compat_sys_ioprio_get)
SYSCALL(sys_inotify_init,sys_inotify_init) SYSCALL(sys_inotify_init,sys_inotify_init)
SYSCALL(sys_inotify_add_watch,compat_sys_inotify_add_watch) /* 285 */ SYSCALL(sys_inotify_add_watch,compat_sys_inotify_add_watch) /* 285 */
SYSCALL(sys_inotify_rm_watch,compat_sys_inotify_rm_watch) SYSCALL(sys_inotify_rm_watch,compat_sys_inotify_rm_watch)
NI_SYSCALL /* 287 sys_migrate_pages */ SYSCALL(sys_migrate_pages,compat_sys_migrate_pages)
SYSCALL(sys_openat,compat_sys_openat) SYSCALL(sys_openat,compat_sys_openat)
SYSCALL(sys_mkdirat,compat_sys_mkdirat) SYSCALL(sys_mkdirat,compat_sys_mkdirat)
SYSCALL(sys_mknodat,compat_sys_mknodat) /* 290 */ SYSCALL(sys_mknodat,compat_sys_mknodat) /* 290 */
...@@ -318,7 +318,7 @@ SYSCALL(sys_splice,compat_sys_splice) ...@@ -318,7 +318,7 @@ SYSCALL(sys_splice,compat_sys_splice)
SYSCALL(sys_sync_file_range,compat_sys_s390_sync_file_range) SYSCALL(sys_sync_file_range,compat_sys_s390_sync_file_range)
SYSCALL(sys_tee,compat_sys_tee) SYSCALL(sys_tee,compat_sys_tee)
SYSCALL(sys_vmsplice,compat_sys_vmsplice) SYSCALL(sys_vmsplice,compat_sys_vmsplice)
NI_SYSCALL /* 310 sys_move_pages */ SYSCALL(sys_move_pages,compat_sys_move_pages)
SYSCALL(sys_getcpu,compat_sys_getcpu) SYSCALL(sys_getcpu,compat_sys_getcpu)
SYSCALL(sys_epoll_pwait,compat_sys_epoll_pwait) SYSCALL(sys_epoll_pwait,compat_sys_epoll_pwait)
SYSCALL(sys_utimes,compat_sys_utimes) SYSCALL(sys_utimes,compat_sys_utimes)
......
...@@ -18,7 +18,10 @@ ...@@ -18,7 +18,10 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/nodemask.h>
#include <linux/node.h>
#include <asm/sysinfo.h> #include <asm/sysinfo.h>
#include <asm/numa.h>
#define PTF_HORIZONTAL (0UL) #define PTF_HORIZONTAL (0UL)
#define PTF_VERTICAL (1UL) #define PTF_VERTICAL (1UL)
...@@ -37,8 +40,10 @@ static struct sysinfo_15_1_x *tl_info; ...@@ -37,8 +40,10 @@ static struct sysinfo_15_1_x *tl_info;
static int topology_enabled = 1; static int topology_enabled = 1;
static DECLARE_WORK(topology_work, topology_work_fn); static DECLARE_WORK(topology_work, topology_work_fn);
/* topology_lock protects the socket and book linked lists */ /*
static DEFINE_SPINLOCK(topology_lock); * Socket/Book linked lists and per_cpu(cpu_topology) updates are
* protected by "sched_domains_mutex".
*/
static struct mask_info socket_info; static struct mask_info socket_info;
static struct mask_info book_info; static struct mask_info book_info;
...@@ -188,7 +193,6 @@ static void tl_to_masks(struct sysinfo_15_1_x *info) ...@@ -188,7 +193,6 @@ static void tl_to_masks(struct sysinfo_15_1_x *info)
{ {
struct cpuid cpu_id; struct cpuid cpu_id;
spin_lock_irq(&topology_lock);
get_cpu_id(&cpu_id); get_cpu_id(&cpu_id);
clear_masks(); clear_masks();
switch (cpu_id.machine) { switch (cpu_id.machine) {
...@@ -199,7 +203,6 @@ static void tl_to_masks(struct sysinfo_15_1_x *info) ...@@ -199,7 +203,6 @@ static void tl_to_masks(struct sysinfo_15_1_x *info)
default: default:
__tl_to_masks_generic(info); __tl_to_masks_generic(info);
} }
spin_unlock_irq(&topology_lock);
} }
static void topology_update_polarization_simple(void) static void topology_update_polarization_simple(void)
...@@ -244,10 +247,8 @@ int topology_set_cpu_management(int fc) ...@@ -244,10 +247,8 @@ int topology_set_cpu_management(int fc)
static void update_cpu_masks(void) static void update_cpu_masks(void)
{ {
unsigned long flags;
int cpu; int cpu;
spin_lock_irqsave(&topology_lock, flags);
for_each_possible_cpu(cpu) { for_each_possible_cpu(cpu) {
per_cpu(cpu_topology, cpu).thread_mask = cpu_thread_map(cpu); per_cpu(cpu_topology, cpu).thread_mask = cpu_thread_map(cpu);
per_cpu(cpu_topology, cpu).core_mask = cpu_group_map(&socket_info, cpu); per_cpu(cpu_topology, cpu).core_mask = cpu_group_map(&socket_info, cpu);
...@@ -259,7 +260,7 @@ static void update_cpu_masks(void) ...@@ -259,7 +260,7 @@ static void update_cpu_masks(void)
per_cpu(cpu_topology, cpu).book_id = cpu; per_cpu(cpu_topology, cpu).book_id = cpu;
} }
} }
spin_unlock_irqrestore(&topology_lock, flags); numa_update_cpu_topology();
} }
void store_topology(struct sysinfo_15_1_x *info) void store_topology(struct sysinfo_15_1_x *info)
...@@ -274,21 +275,21 @@ int arch_update_cpu_topology(void) ...@@ -274,21 +275,21 @@ int arch_update_cpu_topology(void)
{ {
struct sysinfo_15_1_x *info = tl_info; struct sysinfo_15_1_x *info = tl_info;
struct device *dev; struct device *dev;
int cpu; int cpu, rc = 0;
if (!MACHINE_HAS_TOPOLOGY) { if (MACHINE_HAS_TOPOLOGY) {
update_cpu_masks(); rc = 1;
topology_update_polarization_simple();
return 0;
}
store_topology(info); store_topology(info);
tl_to_masks(info); tl_to_masks(info);
}
update_cpu_masks(); update_cpu_masks();
if (!MACHINE_HAS_TOPOLOGY)
topology_update_polarization_simple();
for_each_online_cpu(cpu) { for_each_online_cpu(cpu) {
dev = get_cpu_device(cpu); dev = get_cpu_device(cpu);
kobject_uevent(&dev->kobj, KOBJ_CHANGE); kobject_uevent(&dev->kobj, KOBJ_CHANGE);
} }
return 1; return rc;
} }
static void topology_work_fn(struct work_struct *work) static void topology_work_fn(struct work_struct *work)
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/switch_to.h> #include <asm/fpu-internal.h>
#include "entry.h" #include "entry.h"
int show_unhandled_signals = 1; int show_unhandled_signals = 1;
...@@ -151,7 +151,7 @@ DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN, ...@@ -151,7 +151,7 @@ DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN, DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN,
"transaction constraint exception") "transaction constraint exception")
static inline void do_fp_trap(struct pt_regs *regs, int fpc) static inline void do_fp_trap(struct pt_regs *regs, __u32 fpc)
{ {
int si_code = 0; int si_code = 0;
/* FPC[2] is Data Exception Code */ /* FPC[2] is Data Exception Code */
...@@ -227,7 +227,7 @@ DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, ...@@ -227,7 +227,7 @@ DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN,
int alloc_vector_registers(struct task_struct *tsk) int alloc_vector_registers(struct task_struct *tsk)
{ {
__vector128 *vxrs; __vector128 *vxrs;
int i; freg_t *fprs;
/* Allocate vector register save area. */ /* Allocate vector register save area. */
vxrs = kzalloc(sizeof(__vector128) * __NUM_VXRS, vxrs = kzalloc(sizeof(__vector128) * __NUM_VXRS,
...@@ -236,15 +236,13 @@ int alloc_vector_registers(struct task_struct *tsk) ...@@ -236,15 +236,13 @@ int alloc_vector_registers(struct task_struct *tsk)
return -ENOMEM; return -ENOMEM;
preempt_disable(); preempt_disable();
if (tsk == current) if (tsk == current)
save_fp_regs(tsk->thread.fp_regs.fprs); save_fpu_regs();
/* Copy the 16 floating point registers */ /* Copy the 16 floating point registers */
for (i = 0; i < 16; i++) convert_fp_to_vx(vxrs, tsk->thread.fpu.fprs);
*(freg_t *) &vxrs[i] = tsk->thread.fp_regs.fprs[i]; fprs = tsk->thread.fpu.fprs;
tsk->thread.vxrs = vxrs; tsk->thread.fpu.vxrs = vxrs;
if (tsk == current) { tsk->thread.fpu.flags |= FPU_USE_VX;
__ctl_set_bit(0, 17); kfree(fprs);
restore_vx_regs(vxrs);
}
preempt_enable(); preempt_enable();
return 0; return 0;
} }
...@@ -259,8 +257,8 @@ void vector_exception(struct pt_regs *regs) ...@@ -259,8 +257,8 @@ void vector_exception(struct pt_regs *regs)
} }
/* get vector interrupt code from fpc */ /* get vector interrupt code from fpc */
asm volatile("stfpc %0" : "=Q" (current->thread.fp_regs.fpc)); save_fpu_regs();
vic = (current->thread.fp_regs.fpc & 0xf00) >> 8; vic = (current->thread.fpu.fpc & 0xf00) >> 8;
switch (vic) { switch (vic) {
case 1: /* invalid vector operation */ case 1: /* invalid vector operation */
si_code = FPE_FLTINV; si_code = FPE_FLTINV;
...@@ -297,22 +295,22 @@ void data_exception(struct pt_regs *regs) ...@@ -297,22 +295,22 @@ void data_exception(struct pt_regs *regs)
location = get_trap_ip(regs); location = get_trap_ip(regs);
asm volatile("stfpc %0" : "=Q" (current->thread.fp_regs.fpc)); save_fpu_regs();
/* Check for vector register enablement */ /* Check for vector register enablement */
if (MACHINE_HAS_VX && !current->thread.vxrs && if (MACHINE_HAS_VX && !is_vx_task(current) &&
(current->thread.fp_regs.fpc & FPC_DXC_MASK) == 0xfe00) { (current->thread.fpu.fpc & FPC_DXC_MASK) == 0xfe00) {
alloc_vector_registers(current); alloc_vector_registers(current);
/* Vector data exception is suppressing, rewind psw. */ /* Vector data exception is suppressing, rewind psw. */
regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16); regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16);
clear_pt_regs_flag(regs, PIF_PER_TRAP); clear_pt_regs_flag(regs, PIF_PER_TRAP);
return; return;
} }
if (current->thread.fp_regs.fpc & FPC_DXC_MASK) if (current->thread.fpu.fpc & FPC_DXC_MASK)
signal = SIGFPE; signal = SIGFPE;
else else
signal = SIGILL; signal = SIGILL;
if (signal == SIGFPE) if (signal == SIGFPE)
do_fp_trap(regs, current->thread.fp_regs.fpc); do_fp_trap(regs, current->thread.fpu.fpc);
else if (signal) else if (signal)
do_trap(regs, signal, ILL_ILLOPN, "data exception"); do_trap(regs, signal, ILL_ILLOPN, "data exception");
} }
......
...@@ -13,7 +13,7 @@ KBUILD_AFLAGS_31 += -m31 -s ...@@ -13,7 +13,7 @@ KBUILD_AFLAGS_31 += -m31 -s
KBUILD_CFLAGS_31 := $(filter-out -m64,$(KBUILD_CFLAGS)) KBUILD_CFLAGS_31 := $(filter-out -m64,$(KBUILD_CFLAGS))
KBUILD_CFLAGS_31 += -m31 -fPIC -shared -fno-common -fno-builtin KBUILD_CFLAGS_31 += -m31 -fPIC -shared -fno-common -fno-builtin
KBUILD_CFLAGS_31 += -nostdlib -Wl,-soname=linux-vdso32.so.1 \ KBUILD_CFLAGS_31 += -nostdlib -Wl,-soname=linux-vdso32.so.1 \
$(call cc-ldoption, -Wl$(comma)--hash-style=sysv) $(call cc-ldoption, -Wl$(comma)--hash-style=both)
$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_31) $(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_31)
$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_31) $(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_31)
......
...@@ -13,7 +13,7 @@ KBUILD_AFLAGS_64 += -m64 -s ...@@ -13,7 +13,7 @@ KBUILD_AFLAGS_64 += -m64 -s
KBUILD_CFLAGS_64 := $(filter-out -m64,$(KBUILD_CFLAGS)) KBUILD_CFLAGS_64 := $(filter-out -m64,$(KBUILD_CFLAGS))
KBUILD_CFLAGS_64 += -m64 -fPIC -shared -fno-common -fno-builtin KBUILD_CFLAGS_64 += -m64 -fPIC -shared -fno-common -fno-builtin
KBUILD_CFLAGS_64 += -nostdlib -Wl,-soname=linux-vdso64.so.1 \ KBUILD_CFLAGS_64 += -nostdlib -Wl,-soname=linux-vdso64.so.1 \
$(call cc-ldoption, -Wl$(comma)--hash-style=sysv) $(call cc-ldoption, -Wl$(comma)--hash-style=both)
$(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_64) $(targets:%=$(obj)/%.dbg): KBUILD_CFLAGS = $(KBUILD_CFLAGS_64)
$(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_64) $(targets:%=$(obj)/%.dbg): KBUILD_AFLAGS = $(KBUILD_AFLAGS_64)
......
...@@ -28,6 +28,7 @@ static atomic64_t virt_timer_elapsed; ...@@ -28,6 +28,7 @@ static atomic64_t virt_timer_elapsed;
static DEFINE_PER_CPU(u64, mt_cycles[32]); static DEFINE_PER_CPU(u64, mt_cycles[32]);
static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 }; static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 };
static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 }; static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 };
static DEFINE_PER_CPU(u64, mt_scaling_jiffies);
static inline u64 get_vtimer(void) static inline u64 get_vtimer(void)
{ {
...@@ -85,7 +86,8 @@ static int do_account_vtime(struct task_struct *tsk, int hardirq_offset) ...@@ -85,7 +86,8 @@ static int do_account_vtime(struct task_struct *tsk, int hardirq_offset)
S390_lowcore.steal_timer += S390_lowcore.last_update_clock - clock; S390_lowcore.steal_timer += S390_lowcore.last_update_clock - clock;
/* Do MT utilization calculation */ /* Do MT utilization calculation */
if (smp_cpu_mtid) { if (smp_cpu_mtid &&
time_after64(jiffies_64, __this_cpu_read(mt_scaling_jiffies))) {
u64 cycles_new[32], *cycles_old; u64 cycles_new[32], *cycles_old;
u64 delta, mult, div; u64 delta, mult, div;
...@@ -105,6 +107,7 @@ static int do_account_vtime(struct task_struct *tsk, int hardirq_offset) ...@@ -105,6 +107,7 @@ static int do_account_vtime(struct task_struct *tsk, int hardirq_offset)
sizeof(u64) * (smp_cpu_mtid + 1)); sizeof(u64) * (smp_cpu_mtid + 1));
} }
} }
__this_cpu_write(mt_scaling_jiffies, jiffies_64);
} }
user = S390_lowcore.user_timer - ti->user_timer; user = S390_lowcore.user_timer - ti->user_timer;
...@@ -376,4 +379,11 @@ void vtime_init(void) ...@@ -376,4 +379,11 @@ void vtime_init(void)
{ {
/* set initial cpu timer */ /* set initial cpu timer */
set_vtimer(VTIMER_MAX_SLICE); set_vtimer(VTIMER_MAX_SLICE);
/* Setup initial MT scaling values */
if (smp_cpu_mtid) {
__this_cpu_write(mt_scaling_jiffies, jiffies);
__this_cpu_write(mt_scaling_mult, 1);
__this_cpu_write(mt_scaling_div, 1);
stcctm5(smp_cpu_mtid + 1, this_cpu_ptr(mt_cycles));
}
} }
...@@ -1283,21 +1283,54 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu) ...@@ -1283,21 +1283,54 @@ int kvm_arch_vcpu_init(struct kvm_vcpu *vcpu)
return 0; return 0;
} }
/*
* Backs up the current FP/VX register save area on a particular
* destination. Used to switch between different register save
* areas.
*/
static inline void save_fpu_to(struct fpu *dst)
{
dst->fpc = current->thread.fpu.fpc;
dst->flags = current->thread.fpu.flags;
dst->regs = current->thread.fpu.regs;
}
/*
* Switches the FP/VX register save area from which to lazy
* restore register contents.
*/
static inline void load_fpu_from(struct fpu *from)
{
current->thread.fpu.fpc = from->fpc;
current->thread.fpu.flags = from->flags;
current->thread.fpu.regs = from->regs;
}
void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
{ {
save_fp_ctl(&vcpu->arch.host_fpregs.fpc); /* Save host register state */
if (test_kvm_facility(vcpu->kvm, 129)) save_fpu_regs();
save_vx_regs((__vector128 *)&vcpu->arch.host_vregs->vrs); save_fpu_to(&vcpu->arch.host_fpregs);
else
save_fp_regs(vcpu->arch.host_fpregs.fprs);
save_access_regs(vcpu->arch.host_acrs);
if (test_kvm_facility(vcpu->kvm, 129)) { if (test_kvm_facility(vcpu->kvm, 129)) {
restore_fp_ctl(&vcpu->run->s.regs.fpc); current->thread.fpu.fpc = vcpu->run->s.regs.fpc;
restore_vx_regs((__vector128 *)&vcpu->run->s.regs.vrs); current->thread.fpu.flags = FPU_USE_VX;
} else { /*
restore_fp_ctl(&vcpu->arch.guest_fpregs.fpc); * Use the register save area in the SIE-control block
restore_fp_regs(vcpu->arch.guest_fpregs.fprs); * for register restore and save in kvm_arch_vcpu_put()
} */
current->thread.fpu.vxrs =
(__vector128 *)&vcpu->run->s.regs.vrs;
/* Always enable the vector extension for KVM */
__ctl_set_vx();
} else
load_fpu_from(&vcpu->arch.guest_fpregs);
if (test_fp_ctl(current->thread.fpu.fpc))
/* User space provided an invalid FPC, let's clear it */
current->thread.fpu.fpc = 0;
save_access_regs(vcpu->arch.host_acrs);
restore_access_regs(vcpu->run->s.regs.acrs); restore_access_regs(vcpu->run->s.regs.acrs);
gmap_enable(vcpu->arch.gmap); gmap_enable(vcpu->arch.gmap);
atomic_set_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags); atomic_set_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
...@@ -1307,19 +1340,22 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu) ...@@ -1307,19 +1340,22 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
{ {
atomic_clear_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags); atomic_clear_mask(CPUSTAT_RUNNING, &vcpu->arch.sie_block->cpuflags);
gmap_disable(vcpu->arch.gmap); gmap_disable(vcpu->arch.gmap);
if (test_kvm_facility(vcpu->kvm, 129)) {
save_fp_ctl(&vcpu->run->s.regs.fpc); save_fpu_regs();
save_vx_regs((__vector128 *)&vcpu->run->s.regs.vrs);
} else {
save_fp_ctl(&vcpu->arch.guest_fpregs.fpc);
save_fp_regs(vcpu->arch.guest_fpregs.fprs);
}
save_access_regs(vcpu->run->s.regs.acrs);
restore_fp_ctl(&vcpu->arch.host_fpregs.fpc);
if (test_kvm_facility(vcpu->kvm, 129)) if (test_kvm_facility(vcpu->kvm, 129))
restore_vx_regs((__vector128 *)&vcpu->arch.host_vregs->vrs); /*
* kvm_arch_vcpu_load() set up the register save area to
* the &vcpu->run->s.regs.vrs and, thus, the vector registers
* are already saved. Only the floating-point control must be
* copied.
*/
vcpu->run->s.regs.fpc = current->thread.fpu.fpc;
else else
restore_fp_regs(vcpu->arch.host_fpregs.fprs); save_fpu_to(&vcpu->arch.guest_fpregs);
load_fpu_from(&vcpu->arch.host_fpregs);
save_access_regs(vcpu->run->s.regs.acrs);
restore_access_regs(vcpu->arch.host_acrs); restore_access_regs(vcpu->arch.host_acrs);
} }
...@@ -1464,7 +1500,6 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, ...@@ -1464,7 +1500,6 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
vcpu->arch.sie_block = &sie_page->sie_block; vcpu->arch.sie_block = &sie_page->sie_block;
vcpu->arch.sie_block->itdba = (unsigned long) &sie_page->itdb; vcpu->arch.sie_block->itdba = (unsigned long) &sie_page->itdb;
vcpu->arch.host_vregs = &sie_page->vregs;
vcpu->arch.sie_block->icpua = id; vcpu->arch.sie_block->icpua = id;
if (!kvm_is_ucontrol(kvm)) { if (!kvm_is_ucontrol(kvm)) {
...@@ -1486,6 +1521,19 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm, ...@@ -1486,6 +1521,19 @@ struct kvm_vcpu *kvm_arch_vcpu_create(struct kvm *kvm,
vcpu->arch.local_int.wq = &vcpu->wq; vcpu->arch.local_int.wq = &vcpu->wq;
vcpu->arch.local_int.cpuflags = &vcpu->arch.sie_block->cpuflags; vcpu->arch.local_int.cpuflags = &vcpu->arch.sie_block->cpuflags;
/*
* Allocate a save area for floating-point registers. If the vector
* extension is available, register contents are saved in the SIE
* control block. The allocated save area is still required in
* particular places, for example, in kvm_s390_vcpu_store_status().
*/
vcpu->arch.guest_fpregs.fprs = kzalloc(sizeof(freg_t) * __NUM_FPRS,
GFP_KERNEL);
if (!vcpu->arch.guest_fpregs.fprs) {
rc = -ENOMEM;
goto out_free_sie_block;
}
rc = kvm_vcpu_init(vcpu, kvm, id); rc = kvm_vcpu_init(vcpu, kvm, id);
if (rc) if (rc)
goto out_free_sie_block; goto out_free_sie_block;
...@@ -1708,16 +1756,16 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) ...@@ -1708,16 +1756,16 @@ int kvm_arch_vcpu_ioctl_set_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
{ {
if (test_fp_ctl(fpu->fpc)) if (test_fp_ctl(fpu->fpc))
return -EINVAL; return -EINVAL;
memcpy(&vcpu->arch.guest_fpregs.fprs, &fpu->fprs, sizeof(fpu->fprs)); memcpy(vcpu->arch.guest_fpregs.fprs, &fpu->fprs, sizeof(fpu->fprs));
vcpu->arch.guest_fpregs.fpc = fpu->fpc; vcpu->arch.guest_fpregs.fpc = fpu->fpc;
restore_fp_ctl(&vcpu->arch.guest_fpregs.fpc); save_fpu_regs();
restore_fp_regs(vcpu->arch.guest_fpregs.fprs); load_fpu_from(&vcpu->arch.guest_fpregs);
return 0; return 0;
} }
int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) int kvm_arch_vcpu_ioctl_get_fpu(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu)
{ {
memcpy(&fpu->fprs, &vcpu->arch.guest_fpregs.fprs, sizeof(fpu->fprs)); memcpy(&fpu->fprs, vcpu->arch.guest_fpregs.fprs, sizeof(fpu->fprs));
fpu->fpc = vcpu->arch.guest_fpregs.fpc; fpu->fpc = vcpu->arch.guest_fpregs.fpc;
return 0; return 0;
} }
...@@ -2268,8 +2316,21 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr) ...@@ -2268,8 +2316,21 @@ int kvm_s390_vcpu_store_status(struct kvm_vcpu *vcpu, unsigned long addr)
* copying in vcpu load/put. Lets update our copies before we save * copying in vcpu load/put. Lets update our copies before we save
* it into the save area * it into the save area
*/ */
save_fp_ctl(&vcpu->arch.guest_fpregs.fpc); save_fpu_regs();
save_fp_regs(vcpu->arch.guest_fpregs.fprs); if (test_kvm_facility(vcpu->kvm, 129)) {
/*
* If the vector extension is available, the vector registers
* which overlaps with floating-point registers are saved in
* the SIE-control block. Hence, extract the floating-point
* registers and the FPC value and store them in the
* guest_fpregs structure.
*/
WARN_ON(!is_vx_task(current)); /* XXX remove later */
vcpu->arch.guest_fpregs.fpc = current->thread.fpu.fpc;
convert_vx_to_fp(vcpu->arch.guest_fpregs.fprs,
current->thread.fpu.vxrs);
} else
save_fpu_to(&vcpu->arch.guest_fpregs);
save_access_regs(vcpu->run->s.regs.acrs); save_access_regs(vcpu->run->s.regs.acrs);
return kvm_s390_store_status_unloaded(vcpu, addr); return kvm_s390_store_status_unloaded(vcpu, addr);
...@@ -2296,10 +2357,13 @@ int kvm_s390_vcpu_store_adtl_status(struct kvm_vcpu *vcpu, unsigned long addr) ...@@ -2296,10 +2357,13 @@ int kvm_s390_vcpu_store_adtl_status(struct kvm_vcpu *vcpu, unsigned long addr)
/* /*
* The guest VXRS are in the host VXRs due to the lazy * The guest VXRS are in the host VXRs due to the lazy
* copying in vcpu load/put. Let's update our copies before we save * copying in vcpu load/put. We can simply call save_fpu_regs()
* it into the save area. * to save the current register state because we are in the
* middle of a load/put cycle.
*
* Let's update our copies before we save it into the save area.
*/ */
save_vx_regs((__vector128 *)&vcpu->run->s.regs.vrs); save_fpu_regs();
return kvm_s390_store_adtl_status_unloaded(vcpu, addr); return kvm_s390_store_adtl_status_unloaded(vcpu, addr);
} }
......
...@@ -26,6 +26,7 @@ void __delay(unsigned long loops) ...@@ -26,6 +26,7 @@ void __delay(unsigned long loops)
*/ */
asm volatile("0: brct %0,0b" : : "d" ((loops/2) + 1)); asm volatile("0: brct %0,0b" : : "d" ((loops/2) + 1));
} }
EXPORT_SYMBOL(__delay);
static void __udelay_disabled(unsigned long long usecs) static void __udelay_disabled(unsigned long long usecs)
{ {
......
...@@ -370,22 +370,9 @@ long __strncpy_from_user(char *dst, const char __user *src, long size) ...@@ -370,22 +370,9 @@ long __strncpy_from_user(char *dst, const char __user *src, long size)
} }
EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(__strncpy_from_user);
/*
* The "old" uaccess variant without mvcos can be enforced with the
* uaccess_primary kernel parameter. This is mainly for debugging purposes.
*/
static int uaccess_primary __initdata;
static int __init parse_uaccess_pt(char *__unused)
{
uaccess_primary = 1;
return 0;
}
early_param("uaccess_primary", parse_uaccess_pt);
static int __init uaccess_init(void) static int __init uaccess_init(void)
{ {
if (!uaccess_primary && test_facility(27)) if (test_facility(27))
static_key_slow_inc(&have_mvcos); static_key_slow_inc(&have_mvcos);
return 0; return 0;
} }
......
...@@ -646,7 +646,7 @@ static void pfault_interrupt(struct ext_code ext_code, ...@@ -646,7 +646,7 @@ static void pfault_interrupt(struct ext_code ext_code,
return; return;
inc_irq_stat(IRQEXT_PFL); inc_irq_stat(IRQEXT_PFL);
/* Get the token (= pid of the affected task). */ /* Get the token (= pid of the affected task). */
pid = sizeof(void *) == 4 ? param32 : param64; pid = param64;
rcu_read_lock(); rcu_read_lock();
tsk = find_task_by_pid_ns(pid, &init_pid_ns); tsk = find_task_by_pid_ns(pid, &init_pid_ns);
if (tsk) if (tsk)
......
...@@ -30,6 +30,9 @@ static inline int gup_pte_range(pmd_t *pmdp, pmd_t pmd, unsigned long addr, ...@@ -30,6 +30,9 @@ static inline int gup_pte_range(pmd_t *pmdp, pmd_t pmd, unsigned long addr,
do { do {
pte = *ptep; pte = *ptep;
barrier(); barrier();
/* Similar to the PMD case, NUMA hinting must take slow path */
if (pte_protnone(pte))
return 0;
if ((pte_val(pte) & mask) != 0) if ((pte_val(pte) & mask) != 0)
return 0; return 0;
VM_BUG_ON(!pfn_valid(pte_pfn(pte))); VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
...@@ -125,6 +128,13 @@ static inline int gup_pmd_range(pud_t *pudp, pud_t pud, unsigned long addr, ...@@ -125,6 +128,13 @@ static inline int gup_pmd_range(pud_t *pudp, pud_t pud, unsigned long addr,
if (pmd_none(pmd) || pmd_trans_splitting(pmd)) if (pmd_none(pmd) || pmd_trans_splitting(pmd))
return 0; return 0;
if (unlikely(pmd_large(pmd))) { if (unlikely(pmd_large(pmd))) {
/*
* NUMA hinting faults need to be handled in the GUP
* slowpath for accounting purposes and so that they
* can be serialised against THP migration.
*/
if (pmd_protnone(pmd))
return 0;
if (!gup_huge_pmd(pmdp, pmd, addr, next, if (!gup_huge_pmd(pmdp, pmd, addr, next,
write, pages, nr)) write, pages, nr))
return 0; return 0;
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/initrd.h> #include <linux/initrd.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/memblock.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/pgtable.h> #include <asm/pgtable.h>
...@@ -138,7 +139,7 @@ void __init mem_init(void) ...@@ -138,7 +139,7 @@ void __init mem_init(void)
cpumask_set_cpu(0, mm_cpumask(&init_mm)); cpumask_set_cpu(0, mm_cpumask(&init_mm));
atomic_set(&init_mm.context.attach_count, 1); atomic_set(&init_mm.context.attach_count, 1);
max_mapnr = max_low_pfn; set_max_mapnr(max_low_pfn);
high_memory = (void *) __va(max_low_pfn * PAGE_SIZE); high_memory = (void *) __va(max_low_pfn * PAGE_SIZE);
/* Setup guest page hinting */ /* Setup guest page hinting */
...@@ -170,37 +171,36 @@ void __init free_initrd_mem(unsigned long start, unsigned long end) ...@@ -170,37 +171,36 @@ void __init free_initrd_mem(unsigned long start, unsigned long end)
#ifdef CONFIG_MEMORY_HOTPLUG #ifdef CONFIG_MEMORY_HOTPLUG
int arch_add_memory(int nid, u64 start, u64 size) int arch_add_memory(int nid, u64 start, u64 size)
{ {
unsigned long zone_start_pfn, zone_end_pfn, nr_pages; unsigned long normal_end_pfn = PFN_DOWN(memblock_end_of_DRAM());
unsigned long dma_end_pfn = PFN_DOWN(MAX_DMA_ADDRESS);
unsigned long start_pfn = PFN_DOWN(start); unsigned long start_pfn = PFN_DOWN(start);
unsigned long size_pages = PFN_DOWN(size); unsigned long size_pages = PFN_DOWN(size);
struct zone *zone; unsigned long nr_pages;
int rc; int rc, zone_enum;
rc = vmem_add_mapping(start, size); rc = vmem_add_mapping(start, size);
if (rc) if (rc)
return rc; return rc;
for_each_zone(zone) {
if (zone_idx(zone) != ZONE_MOVABLE) { while (size_pages > 0) {
/* Add range within existing zone limits */ if (start_pfn < dma_end_pfn) {
zone_start_pfn = zone->zone_start_pfn; nr_pages = (start_pfn + size_pages > dma_end_pfn) ?
zone_end_pfn = zone->zone_start_pfn + dma_end_pfn - start_pfn : size_pages;
zone->spanned_pages; zone_enum = ZONE_DMA;
} else if (start_pfn < normal_end_pfn) {
nr_pages = (start_pfn + size_pages > normal_end_pfn) ?
normal_end_pfn - start_pfn : size_pages;
zone_enum = ZONE_NORMAL;
} else { } else {
/* Add remaining range to ZONE_MOVABLE */ nr_pages = size_pages;
zone_start_pfn = start_pfn; zone_enum = ZONE_MOVABLE;
zone_end_pfn = start_pfn + size_pages;
} }
if (start_pfn < zone_start_pfn || start_pfn >= zone_end_pfn) rc = __add_pages(nid, NODE_DATA(nid)->node_zones + zone_enum,
continue; start_pfn, size_pages);
nr_pages = (start_pfn + size_pages > zone_end_pfn) ?
zone_end_pfn - start_pfn : size_pages;
rc = __add_pages(nid, zone, start_pfn, nr_pages);
if (rc) if (rc)
break; break;
start_pfn += nr_pages; start_pfn += nr_pages;
size_pages -= nr_pages; size_pages -= nr_pages;
if (!size_pages)
break;
} }
if (rc) if (rc)
vmem_remove_mapping(start, size); vmem_remove_mapping(start, size);
......
...@@ -10,11 +10,7 @@ ...@@ -10,11 +10,7 @@
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/module.h>
#include <linux/quicklist.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/swapops.h> #include <linux/swapops.h>
...@@ -28,12 +24,9 @@ ...@@ -28,12 +24,9 @@
#include <asm/tlbflush.h> #include <asm/tlbflush.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#define ALLOC_ORDER 2
#define FRAG_MASK 0x03
unsigned long *crst_table_alloc(struct mm_struct *mm) unsigned long *crst_table_alloc(struct mm_struct *mm)
{ {
struct page *page = alloc_pages(GFP_KERNEL, ALLOC_ORDER); struct page *page = alloc_pages(GFP_KERNEL, 2);
if (!page) if (!page)
return NULL; return NULL;
...@@ -42,7 +35,7 @@ unsigned long *crst_table_alloc(struct mm_struct *mm) ...@@ -42,7 +35,7 @@ unsigned long *crst_table_alloc(struct mm_struct *mm)
void crst_table_free(struct mm_struct *mm, unsigned long *table) void crst_table_free(struct mm_struct *mm, unsigned long *table)
{ {
free_pages((unsigned long) table, ALLOC_ORDER); free_pages((unsigned long) table, 2);
} }
static void __crst_table_upgrade(void *arg) static void __crst_table_upgrade(void *arg)
...@@ -176,7 +169,7 @@ struct gmap *gmap_alloc(struct mm_struct *mm, unsigned long limit) ...@@ -176,7 +169,7 @@ struct gmap *gmap_alloc(struct mm_struct *mm, unsigned long limit)
INIT_RADIX_TREE(&gmap->host_to_guest, GFP_ATOMIC); INIT_RADIX_TREE(&gmap->host_to_guest, GFP_ATOMIC);
spin_lock_init(&gmap->guest_table_lock); spin_lock_init(&gmap->guest_table_lock);
gmap->mm = mm; gmap->mm = mm;
page = alloc_pages(GFP_KERNEL, ALLOC_ORDER); page = alloc_pages(GFP_KERNEL, 2);
if (!page) if (!page)
goto out_free; goto out_free;
page->index = 0; page->index = 0;
...@@ -247,7 +240,7 @@ void gmap_free(struct gmap *gmap) ...@@ -247,7 +240,7 @@ void gmap_free(struct gmap *gmap)
/* Free all segment & region tables. */ /* Free all segment & region tables. */
list_for_each_entry_safe(page, next, &gmap->crst_list, lru) list_for_each_entry_safe(page, next, &gmap->crst_list, lru)
__free_pages(page, ALLOC_ORDER); __free_pages(page, 2);
gmap_radix_tree_free(&gmap->guest_to_host); gmap_radix_tree_free(&gmap->guest_to_host);
gmap_radix_tree_free(&gmap->host_to_guest); gmap_radix_tree_free(&gmap->host_to_guest);
down_write(&gmap->mm->mmap_sem); down_write(&gmap->mm->mmap_sem);
...@@ -287,7 +280,7 @@ static int gmap_alloc_table(struct gmap *gmap, unsigned long *table, ...@@ -287,7 +280,7 @@ static int gmap_alloc_table(struct gmap *gmap, unsigned long *table,
unsigned long *new; unsigned long *new;
/* since we dont free the gmap table until gmap_free we can unlock */ /* since we dont free the gmap table until gmap_free we can unlock */
page = alloc_pages(GFP_KERNEL, ALLOC_ORDER); page = alloc_pages(GFP_KERNEL, 2);
if (!page) if (!page)
return -ENOMEM; return -ENOMEM;
new = (unsigned long *) page_to_phys(page); new = (unsigned long *) page_to_phys(page);
...@@ -302,7 +295,7 @@ static int gmap_alloc_table(struct gmap *gmap, unsigned long *table, ...@@ -302,7 +295,7 @@ static int gmap_alloc_table(struct gmap *gmap, unsigned long *table,
} }
spin_unlock(&gmap->mm->page_table_lock); spin_unlock(&gmap->mm->page_table_lock);
if (page) if (page)
__free_pages(page, ALLOC_ORDER); __free_pages(page, 2);
return 0; return 0;
} }
...@@ -795,40 +788,6 @@ void gmap_do_ipte_notify(struct mm_struct *mm, unsigned long vmaddr, pte_t *pte) ...@@ -795,40 +788,6 @@ void gmap_do_ipte_notify(struct mm_struct *mm, unsigned long vmaddr, pte_t *pte)
} }
EXPORT_SYMBOL_GPL(gmap_do_ipte_notify); EXPORT_SYMBOL_GPL(gmap_do_ipte_notify);
static inline int page_table_with_pgste(struct page *page)
{
return atomic_read(&page->_mapcount) == 0;
}
static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm)
{
struct page *page;
unsigned long *table;
page = alloc_page(GFP_KERNEL|__GFP_REPEAT);
if (!page)
return NULL;
if (!pgtable_page_ctor(page)) {
__free_page(page);
return NULL;
}
atomic_set(&page->_mapcount, 0);
table = (unsigned long *) page_to_phys(page);
clear_table(table, _PAGE_INVALID, PAGE_SIZE/2);
clear_table(table + PTRS_PER_PTE, 0, PAGE_SIZE/2);
return table;
}
static inline void page_table_free_pgste(unsigned long *table)
{
struct page *page;
page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
pgtable_page_dtor(page);
atomic_set(&page->_mapcount, -1);
__free_page(page);
}
int set_guest_storage_key(struct mm_struct *mm, unsigned long addr, int set_guest_storage_key(struct mm_struct *mm, unsigned long addr,
unsigned long key, bool nq) unsigned long key, bool nq)
{ {
...@@ -957,20 +916,6 @@ __initcall(page_table_register_sysctl); ...@@ -957,20 +916,6 @@ __initcall(page_table_register_sysctl);
#else /* CONFIG_PGSTE */ #else /* CONFIG_PGSTE */
static inline int page_table_with_pgste(struct page *page)
{
return 0;
}
static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm)
{
return NULL;
}
static inline void page_table_free_pgste(unsigned long *table)
{
}
static inline void gmap_unlink(struct mm_struct *mm, unsigned long *table, static inline void gmap_unlink(struct mm_struct *mm, unsigned long *table,
unsigned long vmaddr) unsigned long vmaddr)
{ {
...@@ -994,24 +939,33 @@ static inline unsigned int atomic_xor_bits(atomic_t *v, unsigned int bits) ...@@ -994,24 +939,33 @@ static inline unsigned int atomic_xor_bits(atomic_t *v, unsigned int bits)
*/ */
unsigned long *page_table_alloc(struct mm_struct *mm) unsigned long *page_table_alloc(struct mm_struct *mm)
{ {
unsigned long *uninitialized_var(table); unsigned long *table;
struct page *uninitialized_var(page); struct page *page;
unsigned int mask, bit; unsigned int mask, bit;
if (mm_alloc_pgste(mm)) /* Try to get a fragment of a 4K page as a 2K page table */
return page_table_alloc_pgste(mm); if (!mm_alloc_pgste(mm)) {
/* Allocate fragments of a 4K page as 1K/2K page table */ table = NULL;
spin_lock_bh(&mm->context.list_lock); spin_lock_bh(&mm->context.list_lock);
mask = FRAG_MASK;
if (!list_empty(&mm->context.pgtable_list)) { if (!list_empty(&mm->context.pgtable_list)) {
page = list_first_entry(&mm->context.pgtable_list, page = list_first_entry(&mm->context.pgtable_list,
struct page, lru); struct page, lru);
table = (unsigned long *) page_to_phys(page);
mask = atomic_read(&page->_mapcount); mask = atomic_read(&page->_mapcount);
mask = mask | (mask >> 4); mask = (mask | (mask >> 4)) & 3;
if (mask != 3) {
table = (unsigned long *) page_to_phys(page);
bit = mask & 1; /* =1 -> second 2K */
if (bit)
table += PTRS_PER_PTE;
atomic_xor_bits(&page->_mapcount, 1U << bit);
list_del(&page->lru);
}
} }
if ((mask & FRAG_MASK) == FRAG_MASK) {
spin_unlock_bh(&mm->context.list_lock); spin_unlock_bh(&mm->context.list_lock);
if (table)
return table;
}
/* Allocate a fresh page */
page = alloc_page(GFP_KERNEL|__GFP_REPEAT); page = alloc_page(GFP_KERNEL|__GFP_REPEAT);
if (!page) if (!page)
return NULL; return NULL;
...@@ -1019,19 +973,21 @@ unsigned long *page_table_alloc(struct mm_struct *mm) ...@@ -1019,19 +973,21 @@ unsigned long *page_table_alloc(struct mm_struct *mm)
__free_page(page); __free_page(page);
return NULL; return NULL;
} }
atomic_set(&page->_mapcount, 1); /* Initialize page table */
table = (unsigned long *) page_to_phys(page); table = (unsigned long *) page_to_phys(page);
if (mm_alloc_pgste(mm)) {
/* Return 4K page table with PGSTEs */
atomic_set(&page->_mapcount, 3);
clear_table(table, _PAGE_INVALID, PAGE_SIZE/2);
clear_table(table + PTRS_PER_PTE, 0, PAGE_SIZE/2);
} else {
/* Return the first 2K fragment of the page */
atomic_set(&page->_mapcount, 1);
clear_table(table, _PAGE_INVALID, PAGE_SIZE); clear_table(table, _PAGE_INVALID, PAGE_SIZE);
spin_lock_bh(&mm->context.list_lock); spin_lock_bh(&mm->context.list_lock);
list_add(&page->lru, &mm->context.pgtable_list); list_add(&page->lru, &mm->context.pgtable_list);
} else {
for (bit = 1; mask & bit; bit <<= 1)
table += PTRS_PER_PTE;
mask = atomic_xor_bits(&page->_mapcount, bit);
if ((mask & FRAG_MASK) == FRAG_MASK)
list_del(&page->lru);
}
spin_unlock_bh(&mm->context.list_lock); spin_unlock_bh(&mm->context.list_lock);
}
return table; return table;
} }
...@@ -1041,37 +997,23 @@ void page_table_free(struct mm_struct *mm, unsigned long *table) ...@@ -1041,37 +997,23 @@ void page_table_free(struct mm_struct *mm, unsigned long *table)
unsigned int bit, mask; unsigned int bit, mask;
page = pfn_to_page(__pa(table) >> PAGE_SHIFT); page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
if (page_table_with_pgste(page)) if (!mm_alloc_pgste(mm)) {
return page_table_free_pgste(table); /* Free 2K page table fragment of a 4K page */
/* Free 1K/2K page table fragment of a 4K page */ bit = (__pa(table) & ~PAGE_MASK)/(PTRS_PER_PTE*sizeof(pte_t));
bit = 1 << ((__pa(table) & ~PAGE_MASK)/(PTRS_PER_PTE*sizeof(pte_t)));
spin_lock_bh(&mm->context.list_lock); spin_lock_bh(&mm->context.list_lock);
if ((atomic_read(&page->_mapcount) & FRAG_MASK) != FRAG_MASK) mask = atomic_xor_bits(&page->_mapcount, 1U << bit);
list_del(&page->lru); if (mask & 3)
mask = atomic_xor_bits(&page->_mapcount, bit);
if (mask & FRAG_MASK)
list_add(&page->lru, &mm->context.pgtable_list); list_add(&page->lru, &mm->context.pgtable_list);
else
list_del(&page->lru);
spin_unlock_bh(&mm->context.list_lock); spin_unlock_bh(&mm->context.list_lock);
if (mask == 0) { if (mask != 0)
pgtable_page_dtor(page); return;
atomic_set(&page->_mapcount, -1);
__free_page(page);
} }
}
static void __page_table_free_rcu(void *table, unsigned bit)
{
struct page *page;
if (bit == FRAG_MASK)
return page_table_free_pgste(table);
/* Free 1K/2K page table fragment of a 4K page */
page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
if (atomic_xor_bits(&page->_mapcount, bit) == 0) {
pgtable_page_dtor(page); pgtable_page_dtor(page);
atomic_set(&page->_mapcount, -1); atomic_set(&page->_mapcount, -1);
__free_page(page); __free_page(page);
}
} }
void page_table_free_rcu(struct mmu_gather *tlb, unsigned long *table, void page_table_free_rcu(struct mmu_gather *tlb, unsigned long *table,
...@@ -1083,34 +1025,45 @@ void page_table_free_rcu(struct mmu_gather *tlb, unsigned long *table, ...@@ -1083,34 +1025,45 @@ void page_table_free_rcu(struct mmu_gather *tlb, unsigned long *table,
mm = tlb->mm; mm = tlb->mm;
page = pfn_to_page(__pa(table) >> PAGE_SHIFT); page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
if (page_table_with_pgste(page)) { if (mm_alloc_pgste(mm)) {
gmap_unlink(mm, table, vmaddr); gmap_unlink(mm, table, vmaddr);
table = (unsigned long *) (__pa(table) | FRAG_MASK); table = (unsigned long *) (__pa(table) | 3);
tlb_remove_table(tlb, table); tlb_remove_table(tlb, table);
return; return;
} }
bit = 1 << ((__pa(table) & ~PAGE_MASK) / (PTRS_PER_PTE*sizeof(pte_t))); bit = (__pa(table) & ~PAGE_MASK) / (PTRS_PER_PTE*sizeof(pte_t));
spin_lock_bh(&mm->context.list_lock); spin_lock_bh(&mm->context.list_lock);
if ((atomic_read(&page->_mapcount) & FRAG_MASK) != FRAG_MASK) mask = atomic_xor_bits(&page->_mapcount, 0x11U << bit);
list_del(&page->lru); if (mask & 3)
mask = atomic_xor_bits(&page->_mapcount, bit | (bit << 4));
if (mask & FRAG_MASK)
list_add_tail(&page->lru, &mm->context.pgtable_list); list_add_tail(&page->lru, &mm->context.pgtable_list);
else
list_del(&page->lru);
spin_unlock_bh(&mm->context.list_lock); spin_unlock_bh(&mm->context.list_lock);
table = (unsigned long *) (__pa(table) | (bit << 4)); table = (unsigned long *) (__pa(table) | (1U << bit));
tlb_remove_table(tlb, table); tlb_remove_table(tlb, table);
} }
static void __tlb_remove_table(void *_table) static void __tlb_remove_table(void *_table)
{ {
const unsigned long mask = (FRAG_MASK << 4) | FRAG_MASK; unsigned int mask = (unsigned long) _table & 3;
void *table = (void *)((unsigned long) _table & ~mask); void *table = (void *)((unsigned long) _table ^ mask);
unsigned type = (unsigned long) _table & mask; struct page *page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
if (type) switch (mask) {
__page_table_free_rcu(table, type); case 0: /* pmd or pud */
else free_pages((unsigned long) table, 2);
free_pages((unsigned long) table, ALLOC_ORDER); break;
case 1: /* lower 2K of a 4K page table */
case 2: /* higher 2K of a 4K page table */
if (atomic_xor_bits(&page->_mapcount, mask << 4) != 0)
break;
/* fallthrough */
case 3: /* 4K page table with pgstes */
pgtable_page_dtor(page);
atomic_set(&page->_mapcount, -1);
__free_page(page);
break;
}
} }
static void tlb_remove_table_smp_sync(void *arg) static void tlb_remove_table_smp_sync(void *arg)
......
obj-y += numa.o
obj-y += toptree.o
obj-$(CONFIG_NUMA_EMU) += mode_emu.o
This diff is collapsed.
This diff is collapsed.
/*
* NUMA support for s390
*
* Define declarations used for communication between NUMA mode
* implementations and NUMA core functionality.
*
* Copyright IBM Corp. 2015
*/
#ifndef __S390_NUMA_MODE_H
#define __S390_NUMA_MODE_H
struct numa_mode {
char *name; /* Name of mode */
void (*setup)(void); /* Initizalize mode */
void (*update_cpu_topology)(void); /* Called by topology code */
int (*__pfn_to_nid)(unsigned long pfn); /* PFN to node ID */
unsigned long (*align)(void); /* Minimum node alignment */
int (*distance)(int a, int b); /* Distance between two nodes */
};
extern const struct numa_mode numa_mode_plain;
extern const struct numa_mode numa_mode_emu;
#endif /* __S390_NUMA_MODE_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -534,6 +534,7 @@ struct dasd_attention_data { ...@@ -534,6 +534,7 @@ struct dasd_attention_data {
#define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/ #define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/
#define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */ #define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */
#define DASD_FLAG_ABORTALL 12 /* Abort all noretry requests */ #define DASD_FLAG_ABORTALL 12 /* Abort all noretry requests */
#define DASD_FLAG_PATH_VERIFY 13 /* Path verification worker running */
#define DASD_SLEEPON_START_TAG ((void *) 1) #define DASD_SLEEPON_START_TAG ((void *) 1)
#define DASD_SLEEPON_END_TAG ((void *) 2) #define DASD_SLEEPON_END_TAG ((void *) 2)
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment