diff options
109 files changed, 4738 insertions, 972 deletions
diff --git a/Documentation/kprobes.txt b/Documentation/kprobes.txt index 4bbeca8483ed..4227ec2e3ab2 100644 --- a/Documentation/kprobes.txt +++ b/Documentation/kprobes.txt | |||
| @@ -300,6 +300,7 @@ architectures: | |||
| 300 | - arm | 300 | - arm |
| 301 | - ppc | 301 | - ppc |
| 302 | - mips | 302 | - mips |
| 303 | - s390 | ||
| 303 | 304 | ||
| 304 | 3. Configuring Kprobes | 305 | 3. Configuring Kprobes |
| 305 | 306 | ||
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 296391395b95..f2cf1f90295b 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig | |||
| @@ -58,6 +58,9 @@ config NO_IOPORT_MAP | |||
| 58 | config PCI_QUIRKS | 58 | config PCI_QUIRKS |
| 59 | def_bool n | 59 | def_bool n |
| 60 | 60 | ||
| 61 | config ARCH_SUPPORTS_UPROBES | ||
| 62 | def_bool 64BIT | ||
| 63 | |||
| 61 | config S390 | 64 | config S390 |
| 62 | def_bool y | 65 | def_bool y |
| 63 | select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE | 66 | select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE |
| @@ -97,6 +100,7 @@ config S390 | |||
| 97 | select ARCH_WANT_IPC_PARSE_VERSION | 100 | select ARCH_WANT_IPC_PARSE_VERSION |
| 98 | select BUILDTIME_EXTABLE_SORT | 101 | select BUILDTIME_EXTABLE_SORT |
| 99 | select CLONE_BACKWARDS2 | 102 | select CLONE_BACKWARDS2 |
| 103 | select DYNAMIC_FTRACE if FUNCTION_TRACER | ||
| 100 | select GENERIC_CLOCKEVENTS | 104 | select GENERIC_CLOCKEVENTS |
| 101 | select GENERIC_CPU_DEVICES if !SMP | 105 | select GENERIC_CPU_DEVICES if !SMP |
| 102 | select GENERIC_FIND_FIRST_BIT | 106 | select GENERIC_FIND_FIRST_BIT |
| @@ -113,10 +117,11 @@ config S390 | |||
| 113 | select HAVE_CMPXCHG_LOCAL | 117 | select HAVE_CMPXCHG_LOCAL |
| 114 | select HAVE_C_RECORDMCOUNT | 118 | select HAVE_C_RECORDMCOUNT |
| 115 | select HAVE_DEBUG_KMEMLEAK | 119 | select HAVE_DEBUG_KMEMLEAK |
| 116 | select HAVE_DYNAMIC_FTRACE | 120 | select HAVE_DYNAMIC_FTRACE if 64BIT |
| 121 | select HAVE_DYNAMIC_FTRACE_WITH_REGS if 64BIT | ||
| 117 | select HAVE_FTRACE_MCOUNT_RECORD | 122 | select HAVE_FTRACE_MCOUNT_RECORD |
| 118 | select HAVE_FUNCTION_GRAPH_TRACER | 123 | select HAVE_FUNCTION_GRAPH_TRACER if 64BIT |
| 119 | select HAVE_FUNCTION_TRACER | 124 | select HAVE_FUNCTION_TRACER if 64BIT |
| 120 | select HAVE_FUTEX_CMPXCHG if FUTEX | 125 | select HAVE_FUTEX_CMPXCHG if FUTEX |
| 121 | select HAVE_KERNEL_BZIP2 | 126 | select HAVE_KERNEL_BZIP2 |
| 122 | select HAVE_KERNEL_GZIP | 127 | select HAVE_KERNEL_GZIP |
diff --git a/arch/s390/Makefile b/arch/s390/Makefile index 874e6d6e9c5f..878e67973151 100644 --- a/arch/s390/Makefile +++ b/arch/s390/Makefile | |||
| @@ -35,13 +35,16 @@ endif | |||
| 35 | 35 | ||
| 36 | export LD_BFD | 36 | export LD_BFD |
| 37 | 37 | ||
| 38 | cflags-$(CONFIG_MARCH_G5) += -march=g5 | 38 | mflags-$(CONFIG_MARCH_G5) := -march=g5 |
| 39 | cflags-$(CONFIG_MARCH_Z900) += -march=z900 | 39 | mflags-$(CONFIG_MARCH_Z900) := -march=z900 |
| 40 | cflags-$(CONFIG_MARCH_Z990) += -march=z990 | 40 | mflags-$(CONFIG_MARCH_Z990) := -march=z990 |
| 41 | cflags-$(CONFIG_MARCH_Z9_109) += -march=z9-109 | 41 | mflags-$(CONFIG_MARCH_Z9_109) := -march=z9-109 |
| 42 | cflags-$(CONFIG_MARCH_Z10) += -march=z10 | 42 | mflags-$(CONFIG_MARCH_Z10) := -march=z10 |
| 43 | cflags-$(CONFIG_MARCH_Z196) += -march=z196 | 43 | mflags-$(CONFIG_MARCH_Z196) := -march=z196 |
| 44 | cflags-$(CONFIG_MARCH_ZEC12) += -march=zEC12 | 44 | mflags-$(CONFIG_MARCH_ZEC12) := -march=zEC12 |
| 45 | |||
| 46 | aflags-y += $(mflags-y) | ||
| 47 | cflags-y += $(mflags-y) | ||
| 45 | 48 | ||
| 46 | cflags-$(CONFIG_MARCH_G5_TUNE) += -mtune=g5 | 49 | cflags-$(CONFIG_MARCH_G5_TUNE) += -mtune=g5 |
| 47 | cflags-$(CONFIG_MARCH_Z900_TUNE) += -mtune=z900 | 50 | cflags-$(CONFIG_MARCH_Z900_TUNE) += -mtune=z900 |
diff --git a/arch/s390/include/asm/barrier.h b/arch/s390/include/asm/barrier.h index 19ff956b752b..b5dce6544d76 100644 --- a/arch/s390/include/asm/barrier.h +++ b/arch/s390/include/asm/barrier.h | |||
| @@ -15,11 +15,13 @@ | |||
| 15 | 15 | ||
| 16 | #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES | 16 | #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES |
| 17 | /* Fast-BCR without checkpoint synchronization */ | 17 | /* Fast-BCR without checkpoint synchronization */ |
| 18 | #define mb() do { asm volatile("bcr 14,0" : : : "memory"); } while (0) | 18 | #define __ASM_BARRIER "bcr 14,0\n" |
| 19 | #else | 19 | #else |
| 20 | #define mb() do { asm volatile("bcr 15,0" : : : "memory"); } while (0) | 20 | #define __ASM_BARRIER "bcr 15,0\n" |
| 21 | #endif | 21 | #endif |
| 22 | 22 | ||
| 23 | #define mb() do { asm volatile(__ASM_BARRIER : : : "memory"); } while (0) | ||
| 24 | |||
| 23 | #define rmb() mb() | 25 | #define rmb() mb() |
| 24 | #define wmb() mb() | 26 | #define wmb() mb() |
| 25 | #define read_barrier_depends() do { } while(0) | 27 | #define read_barrier_depends() do { } while(0) |
diff --git a/arch/s390/include/asm/cputime.h b/arch/s390/include/asm/cputime.h index 3001887f94b7..f8c196984853 100644 --- a/arch/s390/include/asm/cputime.h +++ b/arch/s390/include/asm/cputime.h | |||
| @@ -8,8 +8,6 @@ | |||
| 8 | #define _S390_CPUTIME_H | 8 | #define _S390_CPUTIME_H |
| 9 | 9 | ||
| 10 | #include <linux/types.h> | 10 | #include <linux/types.h> |
| 11 | #include <linux/percpu.h> | ||
| 12 | #include <linux/spinlock.h> | ||
| 13 | #include <asm/div64.h> | 11 | #include <asm/div64.h> |
| 14 | 12 | ||
| 15 | 13 | ||
| @@ -167,28 +165,8 @@ static inline clock_t cputime64_to_clock_t(cputime64_t cputime) | |||
| 167 | return clock; | 165 | return clock; |
| 168 | } | 166 | } |
| 169 | 167 | ||
| 170 | struct s390_idle_data { | 168 | cputime64_t arch_cpu_idle_time(int cpu); |
| 171 | int nohz_delay; | ||
| 172 | unsigned int sequence; | ||
| 173 | unsigned long long idle_count; | ||
| 174 | unsigned long long idle_time; | ||
| 175 | unsigned long long clock_idle_enter; | ||
| 176 | unsigned long long clock_idle_exit; | ||
| 177 | unsigned long long timer_idle_enter; | ||
| 178 | unsigned long long timer_idle_exit; | ||
| 179 | }; | ||
| 180 | 169 | ||
| 181 | DECLARE_PER_CPU(struct s390_idle_data, s390_idle); | 170 | #define arch_idle_time(cpu) arch_cpu_idle_time(cpu) |
| 182 | |||
| 183 | cputime64_t s390_get_idle_time(int cpu); | ||
| 184 | |||
| 185 | #define arch_idle_time(cpu) s390_get_idle_time(cpu) | ||
| 186 | |||
| 187 | static inline int s390_nohz_delay(int cpu) | ||
| 188 | { | ||
| 189 | return __get_cpu_var(s390_idle).nohz_delay != 0; | ||
| 190 | } | ||
| 191 | |||
| 192 | #define arch_needs_cpu(cpu) s390_nohz_delay(cpu) | ||
| 193 | 171 | ||
| 194 | #endif /* _S390_CPUTIME_H */ | 172 | #endif /* _S390_CPUTIME_H */ |
diff --git a/arch/s390/include/asm/dis.h b/arch/s390/include/asm/dis.h index 04a83f5773cd..60323c21938b 100644 --- a/arch/s390/include/asm/dis.h +++ b/arch/s390/include/asm/dis.h | |||
| @@ -13,12 +13,13 @@ | |||
| 13 | #define OPERAND_FPR 0x2 /* Operand printed as %fx */ | 13 | #define OPERAND_FPR 0x2 /* Operand printed as %fx */ |
| 14 | #define OPERAND_AR 0x4 /* Operand printed as %ax */ | 14 | #define OPERAND_AR 0x4 /* Operand printed as %ax */ |
| 15 | #define OPERAND_CR 0x8 /* Operand printed as %cx */ | 15 | #define OPERAND_CR 0x8 /* Operand printed as %cx */ |
| 16 | #define OPERAND_DISP 0x10 /* Operand printed as displacement */ | 16 | #define OPERAND_VR 0x10 /* Operand printed as %vx */ |
| 17 | #define OPERAND_BASE 0x20 /* Operand printed as base register */ | 17 | #define OPERAND_DISP 0x20 /* Operand printed as displacement */ |
| 18 | #define OPERAND_INDEX 0x40 /* Operand printed as index register */ | 18 | #define OPERAND_BASE 0x40 /* Operand printed as base register */ |
| 19 | #define OPERAND_PCREL 0x80 /* Operand printed as pc-relative symbol */ | 19 | #define OPERAND_INDEX 0x80 /* Operand printed as index register */ |
| 20 | #define OPERAND_SIGNED 0x100 /* Operand printed as signed value */ | 20 | #define OPERAND_PCREL 0x100 /* Operand printed as pc-relative symbol */ |
| 21 | #define OPERAND_LENGTH 0x200 /* Operand printed as length (+1) */ | 21 | #define OPERAND_SIGNED 0x200 /* Operand printed as signed value */ |
| 22 | #define OPERAND_LENGTH 0x400 /* Operand printed as length (+1) */ | ||
| 22 | 23 | ||
| 23 | 24 | ||
| 24 | struct s390_operand { | 25 | struct s390_operand { |
diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h index 78f4f8711d58..f6e43d39e3d8 100644 --- a/arch/s390/include/asm/elf.h +++ b/arch/s390/include/asm/elf.h | |||
| @@ -102,6 +102,7 @@ | |||
| 102 | #define HWCAP_S390_ETF3EH 256 | 102 | #define HWCAP_S390_ETF3EH 256 |
| 103 | #define HWCAP_S390_HIGH_GPRS 512 | 103 | #define HWCAP_S390_HIGH_GPRS 512 |
| 104 | #define HWCAP_S390_TE 1024 | 104 | #define HWCAP_S390_TE 1024 |
| 105 | #define HWCAP_S390_VXRS 2048 | ||
| 105 | 106 | ||
| 106 | /* | 107 | /* |
| 107 | * These are used to set parameters in the core dumps. | 108 | * These are used to set parameters in the core dumps. |
| @@ -225,6 +226,6 @@ int arch_setup_additional_pages(struct linux_binprm *, int); | |||
| 225 | extern unsigned long arch_randomize_brk(struct mm_struct *mm); | 226 | extern unsigned long arch_randomize_brk(struct mm_struct *mm); |
| 226 | #define arch_randomize_brk arch_randomize_brk | 227 | #define arch_randomize_brk arch_randomize_brk |
| 227 | 228 | ||
| 228 | void *fill_cpu_elf_notes(void *ptr, struct save_area *sa); | 229 | void *fill_cpu_elf_notes(void *ptr, struct save_area *sa, __vector128 *vxrs); |
| 229 | 230 | ||
| 230 | #endif | 231 | #endif |
diff --git a/arch/s390/include/asm/ftrace.h b/arch/s390/include/asm/ftrace.h index bf246dae1367..3aef8afec336 100644 --- a/arch/s390/include/asm/ftrace.h +++ b/arch/s390/include/asm/ftrace.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | #ifndef __ASSEMBLY__ | 4 | #ifndef __ASSEMBLY__ |
| 5 | 5 | ||
| 6 | extern void _mcount(void); | 6 | extern void _mcount(void); |
| 7 | extern char ftrace_graph_caller_end; | ||
| 7 | 8 | ||
| 8 | struct dyn_arch_ftrace { }; | 9 | struct dyn_arch_ftrace { }; |
| 9 | 10 | ||
| @@ -17,10 +18,8 @@ static inline unsigned long ftrace_call_adjust(unsigned long addr) | |||
| 17 | 18 | ||
| 18 | #endif /* __ASSEMBLY__ */ | 19 | #endif /* __ASSEMBLY__ */ |
| 19 | 20 | ||
| 20 | #ifdef CONFIG_64BIT | 21 | #define MCOUNT_INSN_SIZE 18 |
| 21 | #define MCOUNT_INSN_SIZE 12 | 22 | |
| 22 | #else | 23 | #define ARCH_SUPPORTS_FTRACE_OPS 1 |
| 23 | #define MCOUNT_INSN_SIZE 22 | ||
| 24 | #endif | ||
| 25 | 24 | ||
| 26 | #endif /* _ASM_S390_FTRACE_H */ | 25 | #endif /* _ASM_S390_FTRACE_H */ |
diff --git a/arch/s390/include/asm/idle.h b/arch/s390/include/asm/idle.h new file mode 100644 index 000000000000..6af037f574b8 --- /dev/null +++ b/arch/s390/include/asm/idle.h | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | /* | ||
| 2 | * Copyright IBM Corp. 2014 | ||
| 3 | * | ||
| 4 | * Author: Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
| 5 | */ | ||
| 6 | |||
| 7 | #ifndef _S390_IDLE_H | ||
| 8 | #define _S390_IDLE_H | ||
| 9 | |||
| 10 | #include <linux/types.h> | ||
| 11 | #include <linux/device.h> | ||
| 12 | |||
| 13 | struct s390_idle_data { | ||
| 14 | unsigned int sequence; | ||
| 15 | unsigned long long idle_count; | ||
| 16 | unsigned long long idle_time; | ||
| 17 | unsigned long long clock_idle_enter; | ||
| 18 | unsigned long long clock_idle_exit; | ||
| 19 | unsigned long long timer_idle_enter; | ||
| 20 | unsigned long long timer_idle_exit; | ||
| 21 | }; | ||
| 22 | |||
| 23 | extern struct device_attribute dev_attr_idle_count; | ||
| 24 | extern struct device_attribute dev_attr_idle_time_us; | ||
| 25 | |||
| 26 | #endif /* _S390_IDLE_H */ | ||
diff --git a/arch/s390/include/asm/ipl.h b/arch/s390/include/asm/ipl.h index c81661e756a0..ece606c2ee86 100644 --- a/arch/s390/include/asm/ipl.h +++ b/arch/s390/include/asm/ipl.h | |||
| @@ -89,12 +89,12 @@ extern u32 ipl_flags; | |||
| 89 | extern u32 dump_prefix_page; | 89 | extern u32 dump_prefix_page; |
| 90 | 90 | ||
| 91 | struct dump_save_areas { | 91 | struct dump_save_areas { |
| 92 | struct save_area **areas; | 92 | struct save_area_ext **areas; |
| 93 | int count; | 93 | int count; |
| 94 | }; | 94 | }; |
| 95 | 95 | ||
| 96 | extern struct dump_save_areas dump_save_areas; | 96 | extern struct dump_save_areas dump_save_areas; |
| 97 | struct save_area *dump_save_area_create(int cpu); | 97 | struct save_area_ext *dump_save_area_create(int cpu); |
| 98 | 98 | ||
| 99 | extern void do_reipl(void); | 99 | extern void do_reipl(void); |
| 100 | extern void do_halt(void); | 100 | extern void do_halt(void); |
diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index c4dd400a2791..e787cc1bff8f 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h | |||
| @@ -51,6 +51,7 @@ enum interruption_class { | |||
| 51 | IRQEXT_CMS, | 51 | IRQEXT_CMS, |
| 52 | IRQEXT_CMC, | 52 | IRQEXT_CMC, |
| 53 | IRQEXT_CMR, | 53 | IRQEXT_CMR, |
| 54 | IRQEXT_FTP, | ||
| 54 | IRQIO_CIO, | 55 | IRQIO_CIO, |
| 55 | IRQIO_QAI, | 56 | IRQIO_QAI, |
| 56 | IRQIO_DAS, | 57 | IRQIO_DAS, |
diff --git a/arch/s390/include/asm/kprobes.h b/arch/s390/include/asm/kprobes.h index 4176dfe0fba1..98629173ce3b 100644 --- a/arch/s390/include/asm/kprobes.h +++ b/arch/s390/include/asm/kprobes.h | |||
| @@ -84,6 +84,10 @@ int kprobe_fault_handler(struct pt_regs *regs, int trapnr); | |||
| 84 | int kprobe_exceptions_notify(struct notifier_block *self, | 84 | int kprobe_exceptions_notify(struct notifier_block *self, |
| 85 | unsigned long val, void *data); | 85 | unsigned long val, void *data); |
| 86 | 86 | ||
| 87 | int probe_is_prohibited_opcode(u16 *insn); | ||
| 88 | int probe_get_fixup_type(u16 *insn); | ||
| 89 | int probe_is_insn_relative_long(u16 *insn); | ||
| 90 | |||
| 87 | #define flush_insn_slot(p) do { } while (0) | 91 | #define flush_insn_slot(p) do { } while (0) |
| 88 | 92 | ||
| 89 | #endif /* _ASM_S390_KPROBES_H */ | 93 | #endif /* _ASM_S390_KPROBES_H */ |
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h index 4349197ab9df..6cc51fe84410 100644 --- a/arch/s390/include/asm/lowcore.h +++ b/arch/s390/include/asm/lowcore.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <linux/types.h> | 11 | #include <linux/types.h> |
| 12 | #include <asm/ptrace.h> | 12 | #include <asm/ptrace.h> |
| 13 | #include <asm/cpu.h> | 13 | #include <asm/cpu.h> |
| 14 | #include <asm/types.h> | ||
| 14 | 15 | ||
| 15 | #ifdef CONFIG_32BIT | 16 | #ifdef CONFIG_32BIT |
| 16 | 17 | ||
| @@ -31,6 +32,11 @@ struct save_area { | |||
| 31 | u32 ctrl_regs[16]; | 32 | u32 ctrl_regs[16]; |
| 32 | } __packed; | 33 | } __packed; |
| 33 | 34 | ||
| 35 | struct save_area_ext { | ||
| 36 | struct save_area sa; | ||
| 37 | __vector128 vx_regs[32]; | ||
| 38 | }; | ||
| 39 | |||
| 34 | struct _lowcore { | 40 | struct _lowcore { |
| 35 | psw_t restart_psw; /* 0x0000 */ | 41 | psw_t restart_psw; /* 0x0000 */ |
| 36 | psw_t restart_old_psw; /* 0x0008 */ | 42 | psw_t restart_old_psw; /* 0x0008 */ |
| @@ -183,6 +189,11 @@ struct save_area { | |||
| 183 | u64 ctrl_regs[16]; | 189 | u64 ctrl_regs[16]; |
| 184 | } __packed; | 190 | } __packed; |
| 185 | 191 | ||
| 192 | struct save_area_ext { | ||
| 193 | struct save_area sa; | ||
| 194 | __vector128 vx_regs[32]; | ||
| 195 | }; | ||
| 196 | |||
| 186 | struct _lowcore { | 197 | struct _lowcore { |
| 187 | __u8 pad_0x0000[0x0014-0x0000]; /* 0x0000 */ | 198 | __u8 pad_0x0000[0x0014-0x0000]; /* 0x0000 */ |
| 188 | __u32 ipl_parmblock_ptr; /* 0x0014 */ | 199 | __u32 ipl_parmblock_ptr; /* 0x0014 */ |
| @@ -310,7 +321,10 @@ struct _lowcore { | |||
| 310 | 321 | ||
| 311 | /* Extended facility list */ | 322 | /* Extended facility list */ |
| 312 | __u64 stfle_fac_list[32]; /* 0x0f00 */ | 323 | __u64 stfle_fac_list[32]; /* 0x0f00 */ |
| 313 | __u8 pad_0x1000[0x11b8-0x1000]; /* 0x1000 */ | 324 | __u8 pad_0x1000[0x11b0-0x1000]; /* 0x1000 */ |
| 325 | |||
| 326 | /* Pointer to vector register save area */ | ||
| 327 | __u64 vector_save_area_addr; /* 0x11b0 */ | ||
| 314 | 328 | ||
| 315 | /* 64 bit extparam used for pfault/diag 250: defined by architecture */ | 329 | /* 64 bit extparam used for pfault/diag 250: defined by architecture */ |
| 316 | __u64 ext_params2; /* 0x11B8 */ | 330 | __u64 ext_params2; /* 0x11B8 */ |
| @@ -334,9 +348,10 @@ struct _lowcore { | |||
| 334 | 348 | ||
| 335 | /* Transaction abort diagnostic block */ | 349 | /* Transaction abort diagnostic block */ |
| 336 | __u8 pgm_tdb[256]; /* 0x1800 */ | 350 | __u8 pgm_tdb[256]; /* 0x1800 */ |
| 351 | __u8 pad_0x1900[0x1c00-0x1900]; /* 0x1900 */ | ||
| 337 | 352 | ||
| 338 | /* align to the top of the prefix area */ | 353 | /* Software defined save area for vector registers */ |
| 339 | __u8 pad_0x1900[0x2000-0x1900]; /* 0x1900 */ | 354 | __u8 vector_save_area[1024]; /* 0x1c00 */ |
| 340 | } __packed; | 355 | } __packed; |
| 341 | 356 | ||
| 342 | #endif /* CONFIG_32BIT */ | 357 | #endif /* CONFIG_32BIT */ |
diff --git a/arch/s390/include/asm/nmi.h b/arch/s390/include/asm/nmi.h index 35f8ec185616..3027a5a72b74 100644 --- a/arch/s390/include/asm/nmi.h +++ b/arch/s390/include/asm/nmi.h | |||
| @@ -38,7 +38,7 @@ struct mci { | |||
| 38 | __u32 pm : 1; /* 22 psw program mask and cc validity */ | 38 | __u32 pm : 1; /* 22 psw program mask and cc validity */ |
| 39 | __u32 ia : 1; /* 23 psw instruction address validity */ | 39 | __u32 ia : 1; /* 23 psw instruction address validity */ |
| 40 | __u32 fa : 1; /* 24 failing storage address validity */ | 40 | __u32 fa : 1; /* 24 failing storage address validity */ |
| 41 | __u32 : 1; /* 25 */ | 41 | __u32 vr : 1; /* 25 vector register validity */ |
| 42 | __u32 ec : 1; /* 26 external damage code validity */ | 42 | __u32 ec : 1; /* 26 external damage code validity */ |
| 43 | __u32 fp : 1; /* 27 floating point register validity */ | 43 | __u32 fp : 1; /* 27 floating point register validity */ |
| 44 | __u32 gr : 1; /* 28 general register validity */ | 44 | __u32 gr : 1; /* 28 general register validity */ |
diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index b7054356cc98..57c882761dea 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h | |||
| @@ -217,7 +217,6 @@ extern unsigned long MODULES_END; | |||
| 217 | */ | 217 | */ |
| 218 | 218 | ||
| 219 | /* Hardware bits in the page table entry */ | 219 | /* Hardware bits in the page table entry */ |
| 220 | #define _PAGE_CO 0x100 /* HW Change-bit override */ | ||
| 221 | #define _PAGE_PROTECT 0x200 /* HW read-only bit */ | 220 | #define _PAGE_PROTECT 0x200 /* HW read-only bit */ |
| 222 | #define _PAGE_INVALID 0x400 /* HW invalid bit */ | 221 | #define _PAGE_INVALID 0x400 /* HW invalid bit */ |
| 223 | #define _PAGE_LARGE 0x800 /* Bit to mark a large pte */ | 222 | #define _PAGE_LARGE 0x800 /* Bit to mark a large pte */ |
| @@ -234,8 +233,8 @@ extern unsigned long MODULES_END; | |||
| 234 | #define __HAVE_ARCH_PTE_SPECIAL | 233 | #define __HAVE_ARCH_PTE_SPECIAL |
| 235 | 234 | ||
| 236 | /* Set of bits not changed in pte_modify */ | 235 | /* Set of bits not changed in pte_modify */ |
| 237 | #define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_CO | \ | 236 | #define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_DIRTY | \ |
| 238 | _PAGE_DIRTY | _PAGE_YOUNG) | 237 | _PAGE_YOUNG) |
| 239 | 238 | ||
| 240 | /* | 239 | /* |
| 241 | * handle_pte_fault uses pte_present, pte_none and pte_file to find out the | 240 | * handle_pte_fault uses pte_present, pte_none and pte_file to find out the |
| @@ -354,7 +353,6 @@ extern unsigned long MODULES_END; | |||
| 354 | 353 | ||
| 355 | #define _REGION3_ENTRY_LARGE 0x400 /* RTTE-format control, large page */ | 354 | #define _REGION3_ENTRY_LARGE 0x400 /* RTTE-format control, large page */ |
| 356 | #define _REGION3_ENTRY_RO 0x200 /* page protection bit */ | 355 | #define _REGION3_ENTRY_RO 0x200 /* page protection bit */ |
| 357 | #define _REGION3_ENTRY_CO 0x100 /* change-recording override */ | ||
| 358 | 356 | ||
| 359 | /* Bits in the segment table entry */ | 357 | /* Bits in the segment table entry */ |
| 360 | #define _SEGMENT_ENTRY_BITS 0xfffffffffffffe33UL | 358 | #define _SEGMENT_ENTRY_BITS 0xfffffffffffffe33UL |
| @@ -371,7 +369,6 @@ extern unsigned long MODULES_END; | |||
| 371 | #define _SEGMENT_ENTRY_YOUNG 0x1000 /* SW segment young bit */ | 369 | #define _SEGMENT_ENTRY_YOUNG 0x1000 /* SW segment young bit */ |
| 372 | #define _SEGMENT_ENTRY_SPLIT 0x0800 /* THP splitting bit */ | 370 | #define _SEGMENT_ENTRY_SPLIT 0x0800 /* THP splitting bit */ |
| 373 | #define _SEGMENT_ENTRY_LARGE 0x0400 /* STE-format control, large page */ | 371 | #define _SEGMENT_ENTRY_LARGE 0x0400 /* STE-format control, large page */ |
| 374 | #define _SEGMENT_ENTRY_CO 0x0100 /* change-recording override */ | ||
| 375 | #define _SEGMENT_ENTRY_READ 0x0002 /* SW segment read bit */ | 372 | #define _SEGMENT_ENTRY_READ 0x0002 /* SW segment read bit */ |
| 376 | #define _SEGMENT_ENTRY_WRITE 0x0001 /* SW segment write bit */ | 373 | #define _SEGMENT_ENTRY_WRITE 0x0001 /* SW segment write bit */ |
| 377 | 374 | ||
| @@ -873,8 +870,6 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr, | |||
| 873 | pgste = pgste_set_pte(ptep, pgste, entry); | 870 | pgste = pgste_set_pte(ptep, pgste, entry); |
| 874 | pgste_set_unlock(ptep, pgste); | 871 | pgste_set_unlock(ptep, pgste); |
| 875 | } else { | 872 | } else { |
| 876 | if (!(pte_val(entry) & _PAGE_INVALID) && MACHINE_HAS_EDAT1) | ||
| 877 | pte_val(entry) |= _PAGE_CO; | ||
| 878 | *ptep = entry; | 873 | *ptep = entry; |
| 879 | } | 874 | } |
| 880 | } | 875 | } |
| @@ -1044,6 +1039,22 @@ static inline void __ptep_ipte_local(unsigned long address, pte_t *ptep) | |||
| 1044 | : "=m" (*ptep) : "m" (*ptep), "a" (pto), "a" (address)); | 1039 | : "=m" (*ptep) : "m" (*ptep), "a" (pto), "a" (address)); |
| 1045 | } | 1040 | } |
| 1046 | 1041 | ||
| 1042 | static inline void __ptep_ipte_range(unsigned long address, int nr, pte_t *ptep) | ||
| 1043 | { | ||
| 1044 | unsigned long pto = (unsigned long) ptep; | ||
| 1045 | |||
| 1046 | #ifndef CONFIG_64BIT | ||
| 1047 | /* pto in ESA mode must point to the start of the segment table */ | ||
| 1048 | pto &= 0x7ffffc00; | ||
| 1049 | #endif | ||
| 1050 | /* Invalidate a range of ptes + global TLB flush of the ptes */ | ||
| 1051 | do { | ||
| 1052 | asm volatile( | ||
| 1053 | " .insn rrf,0xb2210000,%2,%0,%1,0" | ||
| 1054 | : "+a" (address), "+a" (nr) : "a" (pto) : "memory"); | ||
| 1055 | } while (nr != 255); | ||
| 1056 | } | ||
| 1057 | |||
| 1047 | static inline void ptep_flush_direct(struct mm_struct *mm, | 1058 | static inline void ptep_flush_direct(struct mm_struct *mm, |
| 1048 | unsigned long address, pte_t *ptep) | 1059 | unsigned long address, pte_t *ptep) |
| 1049 | { | 1060 | { |
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h index e568fc8a7250..d559bdb03d18 100644 --- a/arch/s390/include/asm/processor.h +++ b/arch/s390/include/asm/processor.h | |||
| @@ -13,9 +13,11 @@ | |||
| 13 | 13 | ||
| 14 | #define CIF_MCCK_PENDING 0 /* machine check handling is pending */ | 14 | #define CIF_MCCK_PENDING 0 /* machine check handling is pending */ |
| 15 | #define CIF_ASCE 1 /* user asce needs fixup / uaccess */ | 15 | #define CIF_ASCE 1 /* user asce needs fixup / uaccess */ |
| 16 | #define CIF_NOHZ_DELAY 2 /* delay HZ disable for a tick */ | ||
| 16 | 17 | ||
| 17 | #define _CIF_MCCK_PENDING (1<<CIF_MCCK_PENDING) | 18 | #define _CIF_MCCK_PENDING (1<<CIF_MCCK_PENDING) |
| 18 | #define _CIF_ASCE (1<<CIF_ASCE) | 19 | #define _CIF_ASCE (1<<CIF_ASCE) |
| 20 | #define _CIF_NOHZ_DELAY (1<<CIF_NOHZ_DELAY) | ||
| 19 | 21 | ||
| 20 | 22 | ||
| 21 | #ifndef __ASSEMBLY__ | 23 | #ifndef __ASSEMBLY__ |
| @@ -43,6 +45,8 @@ static inline int test_cpu_flag(int flag) | |||
| 43 | return !!(S390_lowcore.cpu_flags & (1U << flag)); | 45 | return !!(S390_lowcore.cpu_flags & (1U << flag)); |
| 44 | } | 46 | } |
| 45 | 47 | ||
| 48 | #define arch_needs_cpu() test_cpu_flag(CIF_NOHZ_DELAY) | ||
| 49 | |||
| 46 | /* | 50 | /* |
| 47 | * Default implementation of macro that returns current | 51 | * Default implementation of macro that returns current |
| 48 | * instruction pointer ("program counter"). | 52 | * instruction pointer ("program counter"). |
| @@ -113,6 +117,7 @@ struct thread_struct { | |||
| 113 | int ri_signum; | 117 | int ri_signum; |
| 114 | #ifdef CONFIG_64BIT | 118 | #ifdef CONFIG_64BIT |
| 115 | unsigned char trap_tdb[256]; /* Transaction abort diagnose block */ | 119 | unsigned char trap_tdb[256]; /* Transaction abort diagnose block */ |
| 120 | __vector128 *vxrs; /* Vector register save area */ | ||
| 116 | #endif | 121 | #endif |
| 117 | }; | 122 | }; |
| 118 | 123 | ||
| @@ -285,7 +290,12 @@ static inline unsigned long __rewind_psw(psw_t psw, unsigned long ilc) | |||
| 285 | return (psw.addr - ilc) & mask; | 290 | return (psw.addr - ilc) & mask; |
| 286 | #endif | 291 | #endif |
| 287 | } | 292 | } |
| 288 | 293 | ||
| 294 | /* | ||
| 295 | * Function to stop a processor until the next interrupt occurs | ||
| 296 | */ | ||
| 297 | void enabled_wait(void); | ||
| 298 | |||
| 289 | /* | 299 | /* |
| 290 | * Function to drop a processor into disabled wait state | 300 | * Function to drop a processor into disabled wait state |
| 291 | */ | 301 | */ |
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h index 55d69dd7473c..be317feff7ac 100644 --- a/arch/s390/include/asm/ptrace.h +++ b/arch/s390/include/asm/ptrace.h | |||
| @@ -161,6 +161,12 @@ static inline long regs_return_value(struct pt_regs *regs) | |||
| 161 | return regs->gprs[2]; | 161 | return regs->gprs[2]; |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | static inline void instruction_pointer_set(struct pt_regs *regs, | ||
| 165 | unsigned long val) | ||
| 166 | { | ||
| 167 | regs->psw.addr = val | PSW_ADDR_AMODE; | ||
| 168 | } | ||
| 169 | |||
| 164 | int regs_query_register_offset(const char *name); | 170 | int regs_query_register_offset(const char *name); |
| 165 | const char *regs_query_register_name(unsigned int offset); | 171 | const char *regs_query_register_name(unsigned int offset); |
| 166 | unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset); | 172 | unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset); |
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h index 089a49814c50..7736fdd72595 100644 --- a/arch/s390/include/asm/setup.h +++ b/arch/s390/include/asm/setup.h | |||
| @@ -55,8 +55,8 @@ extern void detect_memory_memblock(void); | |||
| 55 | #define MACHINE_FLAG_LPP (1UL << 13) | 55 | #define MACHINE_FLAG_LPP (1UL << 13) |
| 56 | #define MACHINE_FLAG_TOPOLOGY (1UL << 14) | 56 | #define MACHINE_FLAG_TOPOLOGY (1UL << 14) |
| 57 | #define MACHINE_FLAG_TE (1UL << 15) | 57 | #define MACHINE_FLAG_TE (1UL << 15) |
| 58 | #define MACHINE_FLAG_RRBM (1UL << 16) | ||
| 59 | #define MACHINE_FLAG_TLB_LC (1UL << 17) | 58 | #define MACHINE_FLAG_TLB_LC (1UL << 17) |
| 59 | #define MACHINE_FLAG_VX (1UL << 18) | ||
| 60 | 60 | ||
| 61 | #define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) | 61 | #define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) |
| 62 | #define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) | 62 | #define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) |
| @@ -78,8 +78,8 @@ extern void detect_memory_memblock(void); | |||
| 78 | #define MACHINE_HAS_LPP (0) | 78 | #define MACHINE_HAS_LPP (0) |
| 79 | #define MACHINE_HAS_TOPOLOGY (0) | 79 | #define MACHINE_HAS_TOPOLOGY (0) |
| 80 | #define MACHINE_HAS_TE (0) | 80 | #define MACHINE_HAS_TE (0) |
| 81 | #define MACHINE_HAS_RRBM (0) | ||
| 82 | #define MACHINE_HAS_TLB_LC (0) | 81 | #define MACHINE_HAS_TLB_LC (0) |
| 82 | #define MACHINE_HAS_VX (0) | ||
| 83 | #else /* CONFIG_64BIT */ | 83 | #else /* CONFIG_64BIT */ |
| 84 | #define MACHINE_HAS_IEEE (1) | 84 | #define MACHINE_HAS_IEEE (1) |
| 85 | #define MACHINE_HAS_CSP (1) | 85 | #define MACHINE_HAS_CSP (1) |
| @@ -91,8 +91,8 @@ extern void detect_memory_memblock(void); | |||
| 91 | #define MACHINE_HAS_LPP (S390_lowcore.machine_flags & MACHINE_FLAG_LPP) | 91 | #define MACHINE_HAS_LPP (S390_lowcore.machine_flags & MACHINE_FLAG_LPP) |
| 92 | #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) | 92 | #define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) |
| 93 | #define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) | 93 | #define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) |
| 94 | #define MACHINE_HAS_RRBM (S390_lowcore.machine_flags & MACHINE_FLAG_RRBM) | ||
| 95 | #define MACHINE_HAS_TLB_LC (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_LC) | 94 | #define MACHINE_HAS_TLB_LC (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_LC) |
| 95 | #define MACHINE_HAS_VX (S390_lowcore.machine_flags & MACHINE_FLAG_VX) | ||
| 96 | #endif /* CONFIG_64BIT */ | 96 | #endif /* CONFIG_64BIT */ |
| 97 | 97 | ||
| 98 | /* | 98 | /* |
diff --git a/arch/s390/include/asm/sigp.h b/arch/s390/include/asm/sigp.h index bf9c823d4020..49576115dbb7 100644 --- a/arch/s390/include/asm/sigp.h +++ b/arch/s390/include/asm/sigp.h | |||
| @@ -15,6 +15,7 @@ | |||
| 15 | #define SIGP_SET_ARCHITECTURE 18 | 15 | #define SIGP_SET_ARCHITECTURE 18 |
| 16 | #define SIGP_COND_EMERGENCY_SIGNAL 19 | 16 | #define SIGP_COND_EMERGENCY_SIGNAL 19 |
| 17 | #define SIGP_SENSE_RUNNING 21 | 17 | #define SIGP_SENSE_RUNNING 21 |
| 18 | #define SIGP_STORE_ADDITIONAL_STATUS 23 | ||
| 18 | 19 | ||
| 19 | /* SIGP condition codes */ | 20 | /* SIGP condition codes */ |
| 20 | #define SIGP_CC_ORDER_CODE_ACCEPTED 0 | 21 | #define SIGP_CC_ORDER_CODE_ACCEPTED 0 |
| @@ -33,9 +34,10 @@ | |||
| 33 | 34 | ||
| 34 | #ifndef __ASSEMBLY__ | 35 | #ifndef __ASSEMBLY__ |
| 35 | 36 | ||
| 36 | static inline int __pcpu_sigp(u16 addr, u8 order, u32 parm, u32 *status) | 37 | static inline int __pcpu_sigp(u16 addr, u8 order, unsigned long parm, |
| 38 | u32 *status) | ||
| 37 | { | 39 | { |
| 38 | register unsigned int reg1 asm ("1") = parm; | 40 | register unsigned long reg1 asm ("1") = parm; |
| 39 | int cc; | 41 | int cc; |
| 40 | 42 | ||
| 41 | asm volatile( | 43 | asm volatile( |
diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h index 4f1307962a95..762d4f88af5a 100644 --- a/arch/s390/include/asm/smp.h +++ b/arch/s390/include/asm/smp.h | |||
| @@ -29,7 +29,6 @@ extern int smp_find_processor_id(u16 address); | |||
| 29 | extern int smp_store_status(int cpu); | 29 | extern int smp_store_status(int cpu); |
| 30 | extern int smp_vcpu_scheduled(int cpu); | 30 | extern int smp_vcpu_scheduled(int cpu); |
| 31 | extern void smp_yield_cpu(int cpu); | 31 | extern void smp_yield_cpu(int cpu); |
| 32 | extern void smp_yield(void); | ||
| 33 | extern void smp_cpu_set_polarization(int cpu, int val); | 32 | extern void smp_cpu_set_polarization(int cpu, int val); |
| 34 | extern int smp_cpu_get_polarization(int cpu); | 33 | extern int smp_cpu_get_polarization(int cpu); |
| 35 | extern void smp_fill_possible_mask(void); | 34 | extern void smp_fill_possible_mask(void); |
| @@ -50,7 +49,6 @@ static inline int smp_find_processor_id(u16 address) { return 0; } | |||
| 50 | static inline int smp_store_status(int cpu) { return 0; } | 49 | static inline int smp_store_status(int cpu) { return 0; } |
| 51 | static inline int smp_vcpu_scheduled(int cpu) { return 1; } | 50 | static inline int smp_vcpu_scheduled(int cpu) { return 1; } |
| 52 | static inline void smp_yield_cpu(int cpu) { } | 51 | static inline void smp_yield_cpu(int cpu) { } |
| 53 | static inline void smp_yield(void) { } | ||
| 54 | static inline void smp_fill_possible_mask(void) { } | 52 | static inline void smp_fill_possible_mask(void) { } |
| 55 | 53 | ||
| 56 | #endif /* CONFIG_SMP */ | 54 | #endif /* CONFIG_SMP */ |
diff --git a/arch/s390/include/asm/spinlock.h b/arch/s390/include/asm/spinlock.h index 96879f7ad6da..d6bdf906caa5 100644 --- a/arch/s390/include/asm/spinlock.h +++ b/arch/s390/include/asm/spinlock.h | |||
| @@ -37,11 +37,17 @@ _raw_compare_and_swap(unsigned int *lock, unsigned int old, unsigned int new) | |||
| 37 | * (the type definitions are in asm/spinlock_types.h) | 37 | * (the type definitions are in asm/spinlock_types.h) |
| 38 | */ | 38 | */ |
| 39 | 39 | ||
| 40 | void arch_lock_relax(unsigned int cpu); | ||
| 41 | |||
| 40 | void arch_spin_lock_wait(arch_spinlock_t *); | 42 | void arch_spin_lock_wait(arch_spinlock_t *); |
| 41 | int arch_spin_trylock_retry(arch_spinlock_t *); | 43 | int arch_spin_trylock_retry(arch_spinlock_t *); |
| 42 | void arch_spin_relax(arch_spinlock_t *); | ||
| 43 | void arch_spin_lock_wait_flags(arch_spinlock_t *, unsigned long flags); | 44 | void arch_spin_lock_wait_flags(arch_spinlock_t *, unsigned long flags); |
| 44 | 45 | ||
| 46 | static inline void arch_spin_relax(arch_spinlock_t *lock) | ||
| 47 | { | ||
| 48 | arch_lock_relax(lock->lock); | ||
| 49 | } | ||
| 50 | |||
| 45 | static inline u32 arch_spin_lockval(int cpu) | 51 | static inline u32 arch_spin_lockval(int cpu) |
| 46 | { | 52 | { |
| 47 | return ~cpu; | 53 | return ~cpu; |
| @@ -64,11 +70,6 @@ static inline int arch_spin_trylock_once(arch_spinlock_t *lp) | |||
| 64 | _raw_compare_and_swap(&lp->lock, 0, SPINLOCK_LOCKVAL)); | 70 | _raw_compare_and_swap(&lp->lock, 0, SPINLOCK_LOCKVAL)); |
| 65 | } | 71 | } |
| 66 | 72 | ||
| 67 | static inline int arch_spin_tryrelease_once(arch_spinlock_t *lp) | ||
| 68 | { | ||
| 69 | return _raw_compare_and_swap(&lp->lock, SPINLOCK_LOCKVAL, 0); | ||
| 70 | } | ||
| 71 | |||
| 72 | static inline void arch_spin_lock(arch_spinlock_t *lp) | 73 | static inline void arch_spin_lock(arch_spinlock_t *lp) |
| 73 | { | 74 | { |
| 74 | if (!arch_spin_trylock_once(lp)) | 75 | if (!arch_spin_trylock_once(lp)) |
| @@ -91,7 +92,13 @@ static inline int arch_spin_trylock(arch_spinlock_t *lp) | |||
| 91 | 92 | ||
| 92 | static inline void arch_spin_unlock(arch_spinlock_t *lp) | 93 | static inline void arch_spin_unlock(arch_spinlock_t *lp) |
| 93 | { | 94 | { |
| 94 | arch_spin_tryrelease_once(lp); | 95 | typecheck(unsigned int, lp->lock); |
| 96 | asm volatile( | ||
| 97 | __ASM_BARRIER | ||
| 98 | "st %1,%0\n" | ||
| 99 | : "+Q" (lp->lock) | ||
| 100 | : "d" (0) | ||
| 101 | : "cc", "memory"); | ||
| 95 | } | 102 | } |
| 96 | 103 | ||
| 97 | static inline void arch_spin_unlock_wait(arch_spinlock_t *lock) | 104 | static inline void arch_spin_unlock_wait(arch_spinlock_t *lock) |
| @@ -123,13 +130,12 @@ static inline void arch_spin_unlock_wait(arch_spinlock_t *lock) | |||
| 123 | */ | 130 | */ |
| 124 | #define arch_write_can_lock(x) ((x)->lock == 0) | 131 | #define arch_write_can_lock(x) ((x)->lock == 0) |
| 125 | 132 | ||
| 126 | extern void _raw_read_lock_wait(arch_rwlock_t *lp); | ||
| 127 | extern void _raw_read_lock_wait_flags(arch_rwlock_t *lp, unsigned long flags); | ||
| 128 | extern int _raw_read_trylock_retry(arch_rwlock_t *lp); | 133 | extern int _raw_read_trylock_retry(arch_rwlock_t *lp); |
| 129 | extern void _raw_write_lock_wait(arch_rwlock_t *lp); | ||
| 130 | extern void _raw_write_lock_wait_flags(arch_rwlock_t *lp, unsigned long flags); | ||
| 131 | extern int _raw_write_trylock_retry(arch_rwlock_t *lp); | 134 | extern int _raw_write_trylock_retry(arch_rwlock_t *lp); |
| 132 | 135 | ||
| 136 | #define arch_read_lock_flags(lock, flags) arch_read_lock(lock) | ||
| 137 | #define arch_write_lock_flags(lock, flags) arch_write_lock(lock) | ||
| 138 | |||
| 133 | static inline int arch_read_trylock_once(arch_rwlock_t *rw) | 139 | static inline int arch_read_trylock_once(arch_rwlock_t *rw) |
| 134 | { | 140 | { |
| 135 | unsigned int old = ACCESS_ONCE(rw->lock); | 141 | unsigned int old = ACCESS_ONCE(rw->lock); |
| @@ -144,16 +150,82 @@ static inline int arch_write_trylock_once(arch_rwlock_t *rw) | |||
| 144 | _raw_compare_and_swap(&rw->lock, 0, 0x80000000)); | 150 | _raw_compare_and_swap(&rw->lock, 0, 0x80000000)); |
| 145 | } | 151 | } |
| 146 | 152 | ||
| 153 | #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES | ||
| 154 | |||
| 155 | #define __RAW_OP_OR "lao" | ||
| 156 | #define __RAW_OP_AND "lan" | ||
| 157 | #define __RAW_OP_ADD "laa" | ||
| 158 | |||
| 159 | #define __RAW_LOCK(ptr, op_val, op_string) \ | ||
| 160 | ({ \ | ||
| 161 | unsigned int old_val; \ | ||
| 162 | \ | ||
| 163 | typecheck(unsigned int *, ptr); \ | ||
| 164 | asm volatile( \ | ||
| 165 | op_string " %0,%2,%1\n" \ | ||
| 166 | "bcr 14,0\n" \ | ||
| 167 | : "=d" (old_val), "+Q" (*ptr) \ | ||
| 168 | : "d" (op_val) \ | ||
| 169 | : "cc", "memory"); \ | ||
| 170 | old_val; \ | ||
| 171 | }) | ||
| 172 | |||
| 173 | #define __RAW_UNLOCK(ptr, op_val, op_string) \ | ||
| 174 | ({ \ | ||
| 175 | unsigned int old_val; \ | ||
| 176 | \ | ||
| 177 | typecheck(unsigned int *, ptr); \ | ||
| 178 | asm volatile( \ | ||
| 179 | "bcr 14,0\n" \ | ||
| 180 | op_string " %0,%2,%1\n" \ | ||
| 181 | : "=d" (old_val), "+Q" (*ptr) \ | ||
| 182 | : "d" (op_val) \ | ||
| 183 | : "cc", "memory"); \ | ||
| 184 | old_val; \ | ||
| 185 | }) | ||
| 186 | |||
| 187 | extern void _raw_read_lock_wait(arch_rwlock_t *lp); | ||
| 188 | extern void _raw_write_lock_wait(arch_rwlock_t *lp, unsigned int prev); | ||
| 189 | |||
| 147 | static inline void arch_read_lock(arch_rwlock_t *rw) | 190 | static inline void arch_read_lock(arch_rwlock_t *rw) |
| 148 | { | 191 | { |
| 149 | if (!arch_read_trylock_once(rw)) | 192 | unsigned int old; |
| 193 | |||
| 194 | old = __RAW_LOCK(&rw->lock, 1, __RAW_OP_ADD); | ||
| 195 | if ((int) old < 0) | ||
| 150 | _raw_read_lock_wait(rw); | 196 | _raw_read_lock_wait(rw); |
| 151 | } | 197 | } |
| 152 | 198 | ||
| 153 | static inline void arch_read_lock_flags(arch_rwlock_t *rw, unsigned long flags) | 199 | static inline void arch_read_unlock(arch_rwlock_t *rw) |
| 200 | { | ||
| 201 | __RAW_UNLOCK(&rw->lock, -1, __RAW_OP_ADD); | ||
| 202 | } | ||
| 203 | |||
| 204 | static inline void arch_write_lock(arch_rwlock_t *rw) | ||
| 205 | { | ||
| 206 | unsigned int old; | ||
| 207 | |||
| 208 | old = __RAW_LOCK(&rw->lock, 0x80000000, __RAW_OP_OR); | ||
| 209 | if (old != 0) | ||
| 210 | _raw_write_lock_wait(rw, old); | ||
| 211 | rw->owner = SPINLOCK_LOCKVAL; | ||
| 212 | } | ||
| 213 | |||
| 214 | static inline void arch_write_unlock(arch_rwlock_t *rw) | ||
| 215 | { | ||
| 216 | rw->owner = 0; | ||
| 217 | __RAW_UNLOCK(&rw->lock, 0x7fffffff, __RAW_OP_AND); | ||
| 218 | } | ||
| 219 | |||
| 220 | #else /* CONFIG_HAVE_MARCH_Z196_FEATURES */ | ||
| 221 | |||
| 222 | extern void _raw_read_lock_wait(arch_rwlock_t *lp); | ||
| 223 | extern void _raw_write_lock_wait(arch_rwlock_t *lp); | ||
| 224 | |||
| 225 | static inline void arch_read_lock(arch_rwlock_t *rw) | ||
| 154 | { | 226 | { |
| 155 | if (!arch_read_trylock_once(rw)) | 227 | if (!arch_read_trylock_once(rw)) |
| 156 | _raw_read_lock_wait_flags(rw, flags); | 228 | _raw_read_lock_wait(rw); |
| 157 | } | 229 | } |
| 158 | 230 | ||
| 159 | static inline void arch_read_unlock(arch_rwlock_t *rw) | 231 | static inline void arch_read_unlock(arch_rwlock_t *rw) |
| @@ -169,19 +241,24 @@ static inline void arch_write_lock(arch_rwlock_t *rw) | |||
| 169 | { | 241 | { |
| 170 | if (!arch_write_trylock_once(rw)) | 242 | if (!arch_write_trylock_once(rw)) |
| 171 | _raw_write_lock_wait(rw); | 243 | _raw_write_lock_wait(rw); |
| 172 | } | 244 | rw->owner = SPINLOCK_LOCKVAL; |
| 173 | |||
| 174 | static inline void arch_write_lock_flags(arch_rwlock_t *rw, unsigned long flags) | ||
| 175 | { | ||
| 176 | if (!arch_write_trylock_once(rw)) | ||
| 177 | _raw_write_lock_wait_flags(rw, flags); | ||
| 178 | } | 245 | } |
| 179 | 246 | ||
| 180 | static inline void arch_write_unlock(arch_rwlock_t *rw) | 247 | static inline void arch_write_unlock(arch_rwlock_t *rw) |
| 181 | { | 248 | { |
| 182 | _raw_compare_and_swap(&rw->lock, 0x80000000, 0); | 249 | typecheck(unsigned int, rw->lock); |
| 250 | |||
| 251 | rw->owner = 0; | ||
| 252 | asm volatile( | ||
| 253 | __ASM_BARRIER | ||
| 254 | "st %1,%0\n" | ||
| 255 | : "+Q" (rw->lock) | ||
| 256 | : "d" (0) | ||
| 257 | : "cc", "memory"); | ||
| 183 | } | 258 | } |
| 184 | 259 | ||
| 260 | #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */ | ||
| 261 | |||
| 185 | static inline int arch_read_trylock(arch_rwlock_t *rw) | 262 | static inline int arch_read_trylock(arch_rwlock_t *rw) |
| 186 | { | 263 | { |
| 187 | if (!arch_read_trylock_once(rw)) | 264 | if (!arch_read_trylock_once(rw)) |
| @@ -191,12 +268,20 @@ static inline int arch_read_trylock(arch_rwlock_t *rw) | |||
| 191 | 268 | ||
| 192 | static inline int arch_write_trylock(arch_rwlock_t *rw) | 269 | static inline int arch_write_trylock(arch_rwlock_t *rw) |
| 193 | { | 270 | { |
| 194 | if (!arch_write_trylock_once(rw)) | 271 | if (!arch_write_trylock_once(rw) && !_raw_write_trylock_retry(rw)) |
| 195 | return _raw_write_trylock_retry(rw); | 272 | return 0; |
| 273 | rw->owner = SPINLOCK_LOCKVAL; | ||
| 196 | return 1; | 274 | return 1; |
| 197 | } | 275 | } |
| 198 | 276 | ||
| 199 | #define arch_read_relax(lock) cpu_relax() | 277 | static inline void arch_read_relax(arch_rwlock_t *rw) |
| 200 | #define arch_write_relax(lock) cpu_relax() | 278 | { |
| 279 | arch_lock_relax(rw->owner); | ||
| 280 | } | ||
| 281 | |||
| 282 | static inline void arch_write_relax(arch_rwlock_t *rw) | ||
| 283 | { | ||
| 284 | arch_lock_relax(rw->owner); | ||
| 285 | } | ||
| 201 | 286 | ||
| 202 | #endif /* __ASM_SPINLOCK_H */ | 287 | #endif /* __ASM_SPINLOCK_H */ |
diff --git a/arch/s390/include/asm/spinlock_types.h b/arch/s390/include/asm/spinlock_types.h index b2cd6ff7c2c5..d84b6939237c 100644 --- a/arch/s390/include/asm/spinlock_types.h +++ b/arch/s390/include/asm/spinlock_types.h | |||
| @@ -13,6 +13,7 @@ typedef struct { | |||
| 13 | 13 | ||
| 14 | typedef struct { | 14 | typedef struct { |
| 15 | unsigned int lock; | 15 | unsigned int lock; |
| 16 | unsigned int owner; | ||
| 16 | } arch_rwlock_t; | 17 | } arch_rwlock_t; |
| 17 | 18 | ||
| 18 | #define __ARCH_RW_LOCK_UNLOCKED { 0 } | 19 | #define __ARCH_RW_LOCK_UNLOCKED { 0 } |
diff --git a/arch/s390/include/asm/switch_to.h b/arch/s390/include/asm/switch_to.h index 18ea9e3f8142..2542a7e4c8b4 100644 --- a/arch/s390/include/asm/switch_to.h +++ b/arch/s390/include/asm/switch_to.h | |||
| @@ -103,6 +103,61 @@ static inline void restore_fp_regs(freg_t *fprs) | |||
| 103 | asm volatile("ld 15,%0" : : "Q" (fprs[15])); | 103 | asm volatile("ld 15,%0" : : "Q" (fprs[15])); |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | static inline void save_vx_regs(__vector128 *vxrs) | ||
| 107 | { | ||
| 108 | typedef struct { __vector128 _[__NUM_VXRS]; } addrtype; | ||
| 109 | |||
| 110 | asm volatile( | ||
| 111 | " la 1,%0\n" | ||
| 112 | " .word 0xe70f,0x1000,0x003e\n" /* vstm 0,15,0(1) */ | ||
| 113 | " .word 0xe70f,0x1100,0x0c3e\n" /* vstm 16,31,256(1) */ | ||
| 114 | : "=Q" (*(addrtype *) vxrs) : : "1"); | ||
| 115 | } | ||
| 116 | |||
| 117 | static inline void save_vx_regs_safe(__vector128 *vxrs) | ||
| 118 | { | ||
| 119 | unsigned long cr0, flags; | ||
| 120 | |||
| 121 | flags = arch_local_irq_save(); | ||
| 122 | __ctl_store(cr0, 0, 0); | ||
| 123 | __ctl_set_bit(0, 17); | ||
| 124 | __ctl_set_bit(0, 18); | ||
| 125 | save_vx_regs(vxrs); | ||
| 126 | __ctl_load(cr0, 0, 0); | ||
| 127 | arch_local_irq_restore(flags); | ||
| 128 | } | ||
| 129 | |||
| 130 | static inline void restore_vx_regs(__vector128 *vxrs) | ||
| 131 | { | ||
| 132 | typedef struct { __vector128 _[__NUM_VXRS]; } addrtype; | ||
| 133 | |||
| 134 | asm volatile( | ||
| 135 | " la 1,%0\n" | ||
| 136 | " .word 0xe70f,0x1000,0x0036\n" /* vlm 0,15,0(1) */ | ||
| 137 | " .word 0xe70f,0x1100,0x0c36\n" /* vlm 16,31,256(1) */ | ||
| 138 | : : "Q" (*(addrtype *) vxrs) : "1"); | ||
| 139 | } | ||
| 140 | |||
| 141 | static inline void save_fp_vx_regs(struct task_struct *task) | ||
| 142 | { | ||
| 143 | #ifdef CONFIG_64BIT | ||
| 144 | if (task->thread.vxrs) | ||
| 145 | save_vx_regs(task->thread.vxrs); | ||
| 146 | else | ||
| 147 | #endif | ||
| 148 | save_fp_regs(task->thread.fp_regs.fprs); | ||
| 149 | } | ||
| 150 | |||
| 151 | static inline void restore_fp_vx_regs(struct task_struct *task) | ||
| 152 | { | ||
| 153 | #ifdef CONFIG_64BIT | ||
| 154 | if (task->thread.vxrs) | ||
| 155 | restore_vx_regs(task->thread.vxrs); | ||
| 156 | else | ||
| 157 | #endif | ||
| 158 | restore_fp_regs(task->thread.fp_regs.fprs); | ||
| 159 | } | ||
| 160 | |||
| 106 | static inline void save_access_regs(unsigned int *acrs) | 161 | static inline void save_access_regs(unsigned int *acrs) |
| 107 | { | 162 | { |
| 108 | typedef struct { int _[NUM_ACRS]; } acrstype; | 163 | typedef struct { int _[NUM_ACRS]; } acrstype; |
| @@ -120,16 +175,16 @@ static inline void restore_access_regs(unsigned int *acrs) | |||
| 120 | #define switch_to(prev,next,last) do { \ | 175 | #define switch_to(prev,next,last) do { \ |
| 121 | if (prev->mm) { \ | 176 | if (prev->mm) { \ |
| 122 | save_fp_ctl(&prev->thread.fp_regs.fpc); \ | 177 | save_fp_ctl(&prev->thread.fp_regs.fpc); \ |
| 123 | save_fp_regs(prev->thread.fp_regs.fprs); \ | 178 | save_fp_vx_regs(prev); \ |
| 124 | save_access_regs(&prev->thread.acrs[0]); \ | 179 | save_access_regs(&prev->thread.acrs[0]); \ |
| 125 | save_ri_cb(prev->thread.ri_cb); \ | 180 | save_ri_cb(prev->thread.ri_cb); \ |
| 126 | } \ | 181 | } \ |
| 127 | if (next->mm) { \ | 182 | if (next->mm) { \ |
| 183 | update_cr_regs(next); \ | ||
| 128 | restore_fp_ctl(&next->thread.fp_regs.fpc); \ | 184 | restore_fp_ctl(&next->thread.fp_regs.fpc); \ |
| 129 | restore_fp_regs(next->thread.fp_regs.fprs); \ | 185 | restore_fp_vx_regs(next); \ |
| 130 | restore_access_regs(&next->thread.acrs[0]); \ | 186 | restore_access_regs(&next->thread.acrs[0]); \ |
| 131 | restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \ | 187 | restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \ |
| 132 | update_cr_regs(next); \ | ||
| 133 | } \ | 188 | } \ |
| 134 | prev = __switch_to(prev,next); \ | 189 | prev = __switch_to(prev,next); \ |
| 135 | } while (0) | 190 | } while (0) |
diff --git a/arch/s390/include/asm/thread_info.h b/arch/s390/include/asm/thread_info.h index b833e9c0bfbf..4d62fd5b56e5 100644 --- a/arch/s390/include/asm/thread_info.h +++ b/arch/s390/include/asm/thread_info.h | |||
| @@ -84,11 +84,13 @@ static inline struct thread_info *current_thread_info(void) | |||
| 84 | #define TIF_SYSCALL_AUDIT 4 /* syscall auditing active */ | 84 | #define TIF_SYSCALL_AUDIT 4 /* syscall auditing active */ |
| 85 | #define TIF_SECCOMP 5 /* secure computing */ | 85 | #define TIF_SECCOMP 5 /* secure computing */ |
| 86 | #define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */ | 86 | #define TIF_SYSCALL_TRACEPOINT 6 /* syscall tracepoint instrumentation */ |
| 87 | #define TIF_UPROBE 7 /* breakpointed or single-stepping */ | ||
| 87 | #define TIF_31BIT 16 /* 32bit process */ | 88 | #define TIF_31BIT 16 /* 32bit process */ |
| 88 | #define TIF_MEMDIE 17 /* is terminating due to OOM killer */ | 89 | #define TIF_MEMDIE 17 /* is terminating due to OOM killer */ |
| 89 | #define TIF_RESTORE_SIGMASK 18 /* restore signal mask in do_signal() */ | 90 | #define TIF_RESTORE_SIGMASK 18 /* restore signal mask in do_signal() */ |
| 90 | #define TIF_SINGLE_STEP 19 /* This task is single stepped */ | 91 | #define TIF_SINGLE_STEP 19 /* This task is single stepped */ |
| 91 | #define TIF_BLOCK_STEP 20 /* This task is block stepped */ | 92 | #define TIF_BLOCK_STEP 20 /* This task is block stepped */ |
| 93 | #define TIF_UPROBE_SINGLESTEP 21 /* This task is uprobe single stepped */ | ||
| 92 | 94 | ||
| 93 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) | 95 | #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) |
| 94 | #define _TIF_SIGPENDING (1<<TIF_SIGPENDING) | 96 | #define _TIF_SIGPENDING (1<<TIF_SIGPENDING) |
| @@ -97,6 +99,7 @@ static inline struct thread_info *current_thread_info(void) | |||
| 97 | #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) | 99 | #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) |
| 98 | #define _TIF_SECCOMP (1<<TIF_SECCOMP) | 100 | #define _TIF_SECCOMP (1<<TIF_SECCOMP) |
| 99 | #define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT) | 101 | #define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT) |
| 102 | #define _TIF_UPROBE (1<<TIF_UPROBE) | ||
| 100 | #define _TIF_31BIT (1<<TIF_31BIT) | 103 | #define _TIF_31BIT (1<<TIF_31BIT) |
| 101 | #define _TIF_SINGLE_STEP (1<<TIF_SINGLE_STEP) | 104 | #define _TIF_SINGLE_STEP (1<<TIF_SINGLE_STEP) |
| 102 | 105 | ||
diff --git a/arch/s390/include/asm/uprobes.h b/arch/s390/include/asm/uprobes.h new file mode 100644 index 000000000000..1411dff7fea7 --- /dev/null +++ b/arch/s390/include/asm/uprobes.h | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | /* | ||
| 2 | * User-space Probes (UProbes) for s390 | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2014 | ||
| 5 | * Author(s): Jan Willeke, | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef _ASM_UPROBES_H | ||
| 9 | #define _ASM_UPROBES_H | ||
| 10 | |||
| 11 | #include <linux/notifier.h> | ||
| 12 | |||
| 13 | typedef u16 uprobe_opcode_t; | ||
| 14 | |||
| 15 | #define UPROBE_XOL_SLOT_BYTES 256 /* cache aligned */ | ||
| 16 | |||
| 17 | #define UPROBE_SWBP_INSN 0x0002 | ||
| 18 | #define UPROBE_SWBP_INSN_SIZE 2 | ||
| 19 | |||
| 20 | struct arch_uprobe { | ||
| 21 | union{ | ||
| 22 | uprobe_opcode_t insn[3]; | ||
| 23 | uprobe_opcode_t ixol[3]; | ||
| 24 | }; | ||
| 25 | unsigned int saved_per : 1; | ||
| 26 | unsigned int saved_int_code; | ||
| 27 | }; | ||
| 28 | |||
| 29 | struct arch_uprobe_task { | ||
| 30 | }; | ||
| 31 | |||
| 32 | int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, | ||
| 33 | unsigned long addr); | ||
| 34 | int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); | ||
| 35 | int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); | ||
| 36 | bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); | ||
| 37 | int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, | ||
| 38 | void *data); | ||
| 39 | void arch_uprobe_abort_xol(struct arch_uprobe *ap, struct pt_regs *regs); | ||
| 40 | unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline, | ||
| 41 | struct pt_regs *regs); | ||
| 42 | #endif /* _ASM_UPROBES_H */ | ||
diff --git a/arch/s390/include/asm/vdso.h b/arch/s390/include/asm/vdso.h index bc9746a7d47c..a62526d09201 100644 --- a/arch/s390/include/asm/vdso.h +++ b/arch/s390/include/asm/vdso.h | |||
| @@ -22,13 +22,17 @@ struct vdso_data { | |||
| 22 | __u64 xtime_tod_stamp; /* TOD clock for xtime 0x08 */ | 22 | __u64 xtime_tod_stamp; /* TOD clock for xtime 0x08 */ |
| 23 | __u64 xtime_clock_sec; /* Kernel time 0x10 */ | 23 | __u64 xtime_clock_sec; /* Kernel time 0x10 */ |
| 24 | __u64 xtime_clock_nsec; /* 0x18 */ | 24 | __u64 xtime_clock_nsec; /* 0x18 */ |
| 25 | __u64 wtom_clock_sec; /* Wall to monotonic clock 0x20 */ | 25 | __u64 xtime_coarse_sec; /* Coarse kernel time 0x20 */ |
| 26 | __u64 wtom_clock_nsec; /* 0x28 */ | 26 | __u64 xtime_coarse_nsec; /* 0x28 */ |
| 27 | __u32 tz_minuteswest; /* Minutes west of Greenwich 0x30 */ | 27 | __u64 wtom_clock_sec; /* Wall to monotonic clock 0x30 */ |
| 28 | __u32 tz_dsttime; /* Type of dst correction 0x34 */ | 28 | __u64 wtom_clock_nsec; /* 0x38 */ |
| 29 | __u32 ectg_available; /* ECTG instruction present 0x38 */ | 29 | __u64 wtom_coarse_sec; /* Coarse wall to monotonic 0x40 */ |
| 30 | __u32 tk_mult; /* Mult. used for xtime_nsec 0x3c */ | 30 | __u64 wtom_coarse_nsec; /* 0x48 */ |
| 31 | __u32 tk_shift; /* Shift used for xtime_nsec 0x40 */ | 31 | __u32 tz_minuteswest; /* Minutes west of Greenwich 0x50 */ |
| 32 | __u32 tz_dsttime; /* Type of dst correction 0x54 */ | ||
| 33 | __u32 ectg_available; /* ECTG instruction present 0x58 */ | ||
| 34 | __u32 tk_mult; /* Mult. used for xtime_nsec 0x5c */ | ||
| 35 | __u32 tk_shift; /* Shift used for xtime_nsec 0x60 */ | ||
| 32 | }; | 36 | }; |
| 33 | 37 | ||
| 34 | struct vdso_per_cpu_data { | 38 | struct vdso_per_cpu_data { |
diff --git a/arch/s390/include/asm/vtimer.h b/arch/s390/include/asm/vtimer.h index bfe25d513ad2..10a179af62d8 100644 --- a/arch/s390/include/asm/vtimer.h +++ b/arch/s390/include/asm/vtimer.h | |||
| @@ -28,6 +28,4 @@ extern int del_virt_timer(struct vtimer_list *timer); | |||
| 28 | extern void init_cpu_vtimer(void); | 28 | extern void init_cpu_vtimer(void); |
| 29 | extern void vtime_init(void); | 29 | extern void vtime_init(void); |
| 30 | 30 | ||
| 31 | extern void vtime_stop_cpu(void); | ||
| 32 | |||
| 33 | #endif /* _ASM_S390_TIMER_H */ | 31 | #endif /* _ASM_S390_TIMER_H */ |
diff --git a/arch/s390/include/uapi/asm/sigcontext.h b/arch/s390/include/uapi/asm/sigcontext.h index b30de9c01bbe..5f0b8d7ddb0b 100644 --- a/arch/s390/include/uapi/asm/sigcontext.h +++ b/arch/s390/include/uapi/asm/sigcontext.h | |||
| @@ -7,10 +7,14 @@ | |||
| 7 | #define _ASM_S390_SIGCONTEXT_H | 7 | #define _ASM_S390_SIGCONTEXT_H |
| 8 | 8 | ||
| 9 | #include <linux/compiler.h> | 9 | #include <linux/compiler.h> |
| 10 | #include <linux/types.h> | ||
| 10 | 11 | ||
| 11 | #define __NUM_GPRS 16 | 12 | #define __NUM_GPRS 16 |
| 12 | #define __NUM_FPRS 16 | 13 | #define __NUM_FPRS 16 |
| 13 | #define __NUM_ACRS 16 | 14 | #define __NUM_ACRS 16 |
| 15 | #define __NUM_VXRS 32 | ||
| 16 | #define __NUM_VXRS_LOW 16 | ||
| 17 | #define __NUM_VXRS_HIGH 16 | ||
| 14 | 18 | ||
| 15 | #ifndef __s390x__ | 19 | #ifndef __s390x__ |
| 16 | 20 | ||
| @@ -59,6 +63,16 @@ typedef struct | |||
| 59 | _s390_fp_regs fpregs; | 63 | _s390_fp_regs fpregs; |
| 60 | } _sigregs; | 64 | } _sigregs; |
| 61 | 65 | ||
| 66 | typedef struct | ||
| 67 | { | ||
| 68 | #ifndef __s390x__ | ||
| 69 | unsigned long gprs_high[__NUM_GPRS]; | ||
| 70 | #endif | ||
| 71 | unsigned long long vxrs_low[__NUM_VXRS_LOW]; | ||
| 72 | __vector128 vxrs_high[__NUM_VXRS_HIGH]; | ||
| 73 | unsigned char __reserved[128]; | ||
| 74 | } _sigregs_ext; | ||
| 75 | |||
| 62 | struct sigcontext | 76 | struct sigcontext |
| 63 | { | 77 | { |
| 64 | unsigned long oldmask[_SIGCONTEXT_NSIG_WORDS]; | 78 | unsigned long oldmask[_SIGCONTEXT_NSIG_WORDS]; |
diff --git a/arch/s390/include/uapi/asm/types.h b/arch/s390/include/uapi/asm/types.h index 038f2b9178a4..3c3951e3415b 100644 --- a/arch/s390/include/uapi/asm/types.h +++ b/arch/s390/include/uapi/asm/types.h | |||
| @@ -17,6 +17,10 @@ | |||
| 17 | typedef unsigned long addr_t; | 17 | typedef unsigned long addr_t; |
| 18 | typedef __signed__ long saddr_t; | 18 | typedef __signed__ long saddr_t; |
| 19 | 19 | ||
| 20 | typedef struct { | ||
| 21 | __u32 u[4]; | ||
| 22 | } __vector128; | ||
| 23 | |||
| 20 | #endif /* __ASSEMBLY__ */ | 24 | #endif /* __ASSEMBLY__ */ |
| 21 | 25 | ||
| 22 | #endif /* _UAPI_S390_TYPES_H */ | 26 | #endif /* _UAPI_S390_TYPES_H */ |
diff --git a/arch/s390/include/uapi/asm/ucontext.h b/arch/s390/include/uapi/asm/ucontext.h index 3e077b2a4705..64a69aa5dde0 100644 --- a/arch/s390/include/uapi/asm/ucontext.h +++ b/arch/s390/include/uapi/asm/ucontext.h | |||
| @@ -7,10 +7,15 @@ | |||
| 7 | #ifndef _ASM_S390_UCONTEXT_H | 7 | #ifndef _ASM_S390_UCONTEXT_H |
| 8 | #define _ASM_S390_UCONTEXT_H | 8 | #define _ASM_S390_UCONTEXT_H |
| 9 | 9 | ||
| 10 | #define UC_EXTENDED 0x00000001 | 10 | #define UC_GPRS_HIGH 1 /* uc_mcontext_ext has valid high gprs */ |
| 11 | 11 | #define UC_VXRS 2 /* uc_mcontext_ext has valid vector regs */ | |
| 12 | #ifndef __s390x__ | ||
| 13 | 12 | ||
| 13 | /* | ||
| 14 | * The struct ucontext_extended describes how the registers are stored | ||
| 15 | * on a rt signal frame. Please note that the structure is not fixed, | ||
| 16 | * if new CPU registers are added to the user state the size of the | ||
| 17 | * struct ucontext_extended will increase. | ||
| 18 | */ | ||
| 14 | struct ucontext_extended { | 19 | struct ucontext_extended { |
| 15 | unsigned long uc_flags; | 20 | unsigned long uc_flags; |
| 16 | struct ucontext *uc_link; | 21 | struct ucontext *uc_link; |
| @@ -19,11 +24,9 @@ struct ucontext_extended { | |||
| 19 | sigset_t uc_sigmask; | 24 | sigset_t uc_sigmask; |
| 20 | /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ | 25 | /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ |
| 21 | unsigned char __unused[128 - sizeof(sigset_t)]; | 26 | unsigned char __unused[128 - sizeof(sigset_t)]; |
| 22 | unsigned long uc_gprs_high[16]; | 27 | _sigregs_ext uc_mcontext_ext; |
| 23 | }; | 28 | }; |
| 24 | 29 | ||
| 25 | #endif | ||
| 26 | |||
| 27 | struct ucontext { | 30 | struct ucontext { |
| 28 | unsigned long uc_flags; | 31 | unsigned long uc_flags; |
| 29 | struct ucontext *uc_link; | 32 | struct ucontext *uc_link; |
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile index a95c4ca99617..204c43a4c245 100644 --- a/arch/s390/kernel/Makefile +++ b/arch/s390/kernel/Makefile | |||
| @@ -28,7 +28,7 @@ CFLAGS_ptrace.o += -DUTS_MACHINE='"$(UTS_MACHINE)"' | |||
| 28 | 28 | ||
| 29 | CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w | 29 | CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w |
| 30 | 30 | ||
| 31 | obj-y := traps.o time.o process.o base.o early.o setup.o vtime.o | 31 | obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o |
| 32 | obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o | 32 | obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o |
| 33 | obj-y += debug.o irq.o ipl.o dis.o diag.o sclp.o vdso.o | 33 | obj-y += debug.o irq.o ipl.o dis.o diag.o sclp.o vdso.o |
| 34 | obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o | 34 | obj-y += sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o pgm_check.o |
| @@ -52,11 +52,9 @@ obj-$(CONFIG_COMPAT) += compat_wrapper.o $(compat-obj-y) | |||
| 52 | 52 | ||
| 53 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | 53 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
| 54 | obj-$(CONFIG_KPROBES) += kprobes.o | 54 | obj-$(CONFIG_KPROBES) += kprobes.o |
| 55 | obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o) | 55 | obj-$(CONFIG_FUNCTION_TRACER) += mcount.o ftrace.o |
| 56 | obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o | ||
| 57 | obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o | ||
| 58 | obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o | ||
| 59 | obj-$(CONFIG_CRASH_DUMP) += crash_dump.o | 56 | obj-$(CONFIG_CRASH_DUMP) += crash_dump.o |
| 57 | obj-$(CONFIG_UPROBES) += uprobes.o | ||
| 60 | 58 | ||
| 61 | ifdef CONFIG_64BIT | 59 | ifdef CONFIG_64BIT |
| 62 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o perf_cpum_sf.o \ | 60 | obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o perf_cpum_sf.o \ |
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c index afe1715a4eb7..ef279a136801 100644 --- a/arch/s390/kernel/asm-offsets.c +++ b/arch/s390/kernel/asm-offsets.c | |||
| @@ -9,7 +9,7 @@ | |||
| 9 | #include <linux/kbuild.h> | 9 | #include <linux/kbuild.h> |
| 10 | #include <linux/kvm_host.h> | 10 | #include <linux/kvm_host.h> |
| 11 | #include <linux/sched.h> | 11 | #include <linux/sched.h> |
| 12 | #include <asm/cputime.h> | 12 | #include <asm/idle.h> |
| 13 | #include <asm/vdso.h> | 13 | #include <asm/vdso.h> |
| 14 | #include <asm/pgtable.h> | 14 | #include <asm/pgtable.h> |
| 15 | 15 | ||
| @@ -62,8 +62,12 @@ int main(void) | |||
| 62 | DEFINE(__VDSO_XTIME_STAMP, offsetof(struct vdso_data, xtime_tod_stamp)); | 62 | DEFINE(__VDSO_XTIME_STAMP, offsetof(struct vdso_data, xtime_tod_stamp)); |
| 63 | DEFINE(__VDSO_XTIME_SEC, offsetof(struct vdso_data, xtime_clock_sec)); | 63 | DEFINE(__VDSO_XTIME_SEC, offsetof(struct vdso_data, xtime_clock_sec)); |
| 64 | DEFINE(__VDSO_XTIME_NSEC, offsetof(struct vdso_data, xtime_clock_nsec)); | 64 | DEFINE(__VDSO_XTIME_NSEC, offsetof(struct vdso_data, xtime_clock_nsec)); |
| 65 | DEFINE(__VDSO_XTIME_CRS_SEC, offsetof(struct vdso_data, xtime_coarse_sec)); | ||
| 66 | DEFINE(__VDSO_XTIME_CRS_NSEC, offsetof(struct vdso_data, xtime_coarse_nsec)); | ||
| 65 | DEFINE(__VDSO_WTOM_SEC, offsetof(struct vdso_data, wtom_clock_sec)); | 67 | DEFINE(__VDSO_WTOM_SEC, offsetof(struct vdso_data, wtom_clock_sec)); |
| 66 | DEFINE(__VDSO_WTOM_NSEC, offsetof(struct vdso_data, wtom_clock_nsec)); | 68 | DEFINE(__VDSO_WTOM_NSEC, offsetof(struct vdso_data, wtom_clock_nsec)); |
| 69 | DEFINE(__VDSO_WTOM_CRS_SEC, offsetof(struct vdso_data, wtom_coarse_sec)); | ||
| 70 | DEFINE(__VDSO_WTOM_CRS_NSEC, offsetof(struct vdso_data, wtom_coarse_nsec)); | ||
| 67 | DEFINE(__VDSO_TIMEZONE, offsetof(struct vdso_data, tz_minuteswest)); | 71 | DEFINE(__VDSO_TIMEZONE, offsetof(struct vdso_data, tz_minuteswest)); |
| 68 | DEFINE(__VDSO_ECTG_OK, offsetof(struct vdso_data, ectg_available)); | 72 | DEFINE(__VDSO_ECTG_OK, offsetof(struct vdso_data, ectg_available)); |
| 69 | DEFINE(__VDSO_TK_MULT, offsetof(struct vdso_data, tk_mult)); | 73 | DEFINE(__VDSO_TK_MULT, offsetof(struct vdso_data, tk_mult)); |
| @@ -73,8 +77,11 @@ int main(void) | |||
| 73 | /* constants used by the vdso */ | 77 | /* constants used by the vdso */ |
| 74 | DEFINE(__CLOCK_REALTIME, CLOCK_REALTIME); | 78 | DEFINE(__CLOCK_REALTIME, CLOCK_REALTIME); |
| 75 | DEFINE(__CLOCK_MONOTONIC, CLOCK_MONOTONIC); | 79 | DEFINE(__CLOCK_MONOTONIC, CLOCK_MONOTONIC); |
| 80 | DEFINE(__CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE); | ||
| 81 | DEFINE(__CLOCK_MONOTONIC_COARSE, CLOCK_MONOTONIC_COARSE); | ||
| 76 | DEFINE(__CLOCK_THREAD_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID); | 82 | DEFINE(__CLOCK_THREAD_CPUTIME_ID, CLOCK_THREAD_CPUTIME_ID); |
| 77 | DEFINE(__CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); | 83 | DEFINE(__CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC); |
| 84 | DEFINE(__CLOCK_COARSE_RES, LOW_RES_NSEC); | ||
| 78 | BLANK(); | 85 | BLANK(); |
| 79 | /* idle data offsets */ | 86 | /* idle data offsets */ |
| 80 | DEFINE(__CLOCK_IDLE_ENTER, offsetof(struct s390_idle_data, clock_idle_enter)); | 87 | DEFINE(__CLOCK_IDLE_ENTER, offsetof(struct s390_idle_data, clock_idle_enter)); |
diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h index 70d4b7c4beaa..a0a886c04977 100644 --- a/arch/s390/kernel/compat_linux.h +++ b/arch/s390/kernel/compat_linux.h | |||
| @@ -50,6 +50,14 @@ typedef struct | |||
| 50 | _s390_fp_regs32 fpregs; | 50 | _s390_fp_regs32 fpregs; |
| 51 | } _sigregs32; | 51 | } _sigregs32; |
| 52 | 52 | ||
| 53 | typedef struct | ||
| 54 | { | ||
| 55 | __u32 gprs_high[__NUM_GPRS]; | ||
| 56 | __u64 vxrs_low[__NUM_VXRS_LOW]; | ||
| 57 | __vector128 vxrs_high[__NUM_VXRS_HIGH]; | ||
| 58 | __u8 __reserved[128]; | ||
| 59 | } _sigregs_ext32; | ||
| 60 | |||
| 53 | #define _SIGCONTEXT_NSIG32 64 | 61 | #define _SIGCONTEXT_NSIG32 64 |
| 54 | #define _SIGCONTEXT_NSIG_BPW32 32 | 62 | #define _SIGCONTEXT_NSIG_BPW32 32 |
| 55 | #define __SIGNAL_FRAMESIZE32 96 | 63 | #define __SIGNAL_FRAMESIZE32 96 |
| @@ -72,6 +80,7 @@ struct ucontext32 { | |||
| 72 | compat_sigset_t uc_sigmask; | 80 | compat_sigset_t uc_sigmask; |
| 73 | /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ | 81 | /* Allow for uc_sigmask growth. Glibc uses a 1024-bit sigset_t. */ |
| 74 | unsigned char __unused[128 - sizeof(compat_sigset_t)]; | 82 | unsigned char __unused[128 - sizeof(compat_sigset_t)]; |
| 83 | _sigregs_ext32 uc_mcontext_ext; | ||
| 75 | }; | 84 | }; |
| 76 | 85 | ||
| 77 | struct stat64_emu31; | 86 | struct stat64_emu31; |
diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c index 598b0b42668b..009f5eb11125 100644 --- a/arch/s390/kernel/compat_signal.c +++ b/arch/s390/kernel/compat_signal.c | |||
| @@ -36,17 +36,16 @@ typedef struct | |||
| 36 | struct sigcontext32 sc; | 36 | struct sigcontext32 sc; |
| 37 | _sigregs32 sregs; | 37 | _sigregs32 sregs; |
| 38 | int signo; | 38 | int signo; |
| 39 | __u32 gprs_high[NUM_GPRS]; | 39 | _sigregs_ext32 sregs_ext; |
| 40 | __u8 retcode[S390_SYSCALL_SIZE]; | 40 | __u16 svc_insn; /* Offset of svc_insn is NOT fixed! */ |
| 41 | } sigframe32; | 41 | } sigframe32; |
| 42 | 42 | ||
| 43 | typedef struct | 43 | typedef struct |
| 44 | { | 44 | { |
| 45 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; | 45 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE32]; |
| 46 | __u8 retcode[S390_SYSCALL_SIZE]; | 46 | __u16 svc_insn; |
| 47 | compat_siginfo_t info; | 47 | compat_siginfo_t info; |
| 48 | struct ucontext32 uc; | 48 | struct ucontext32 uc; |
| 49 | __u32 gprs_high[NUM_GPRS]; | ||
| 50 | } rt_sigframe32; | 49 | } rt_sigframe32; |
| 51 | 50 | ||
| 52 | int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) | 51 | int copy_siginfo_to_user32(compat_siginfo_t __user *to, const siginfo_t *from) |
| @@ -151,6 +150,38 @@ int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from) | |||
| 151 | return err ? -EFAULT : 0; | 150 | return err ? -EFAULT : 0; |
| 152 | } | 151 | } |
| 153 | 152 | ||
| 153 | /* Store registers needed to create the signal frame */ | ||
| 154 | static void store_sigregs(void) | ||
| 155 | { | ||
| 156 | int i; | ||
| 157 | |||
| 158 | save_access_regs(current->thread.acrs); | ||
| 159 | save_fp_ctl(¤t->thread.fp_regs.fpc); | ||
| 160 | if (current->thread.vxrs) { | ||
| 161 | save_vx_regs(current->thread.vxrs); | ||
| 162 | for (i = 0; i < __NUM_FPRS; i++) | ||
| 163 | current->thread.fp_regs.fprs[i] = | ||
| 164 | *(freg_t *)(current->thread.vxrs + i); | ||
| 165 | } else | ||
| 166 | save_fp_regs(current->thread.fp_regs.fprs); | ||
| 167 | } | ||
| 168 | |||
| 169 | /* Load registers after signal return */ | ||
| 170 | static void load_sigregs(void) | ||
| 171 | { | ||
| 172 | int i; | ||
| 173 | |||
| 174 | restore_access_regs(current->thread.acrs); | ||
| 175 | /* restore_fp_ctl is done in restore_sigregs */ | ||
| 176 | if (current->thread.vxrs) { | ||
| 177 | for (i = 0; i < __NUM_FPRS; i++) | ||
| 178 | *(freg_t *)(current->thread.vxrs + i) = | ||
| 179 | current->thread.fp_regs.fprs[i]; | ||
| 180 | restore_vx_regs(current->thread.vxrs); | ||
| 181 | } else | ||
| 182 | restore_fp_regs(current->thread.fp_regs.fprs); | ||
| 183 | } | ||
| 184 | |||
| 154 | static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) | 185 | static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) |
| 155 | { | 186 | { |
| 156 | _sigregs32 user_sregs; | 187 | _sigregs32 user_sregs; |
| @@ -163,11 +194,8 @@ static int save_sigregs32(struct pt_regs *regs, _sigregs32 __user *sregs) | |||
| 163 | (__u32)(regs->psw.mask & PSW_MASK_BA); | 194 | (__u32)(regs->psw.mask & PSW_MASK_BA); |
| 164 | for (i = 0; i < NUM_GPRS; i++) | 195 | for (i = 0; i < NUM_GPRS; i++) |
| 165 | user_sregs.regs.gprs[i] = (__u32) regs->gprs[i]; | 196 | user_sregs.regs.gprs[i] = (__u32) regs->gprs[i]; |
| 166 | save_access_regs(current->thread.acrs); | ||
| 167 | memcpy(&user_sregs.regs.acrs, current->thread.acrs, | 197 | memcpy(&user_sregs.regs.acrs, current->thread.acrs, |
| 168 | sizeof(user_sregs.regs.acrs)); | 198 | sizeof(user_sregs.regs.acrs)); |
| 169 | save_fp_ctl(¤t->thread.fp_regs.fpc); | ||
| 170 | save_fp_regs(current->thread.fp_regs.fprs); | ||
| 171 | memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, | 199 | memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, |
| 172 | sizeof(user_sregs.fpregs)); | 200 | sizeof(user_sregs.fpregs)); |
| 173 | if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32))) | 201 | if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs32))) |
| @@ -207,37 +235,67 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs) | |||
| 207 | regs->gprs[i] = (__u64) user_sregs.regs.gprs[i]; | 235 | regs->gprs[i] = (__u64) user_sregs.regs.gprs[i]; |
| 208 | memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, | 236 | memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, |
| 209 | sizeof(current->thread.acrs)); | 237 | sizeof(current->thread.acrs)); |
| 210 | restore_access_regs(current->thread.acrs); | ||
| 211 | 238 | ||
| 212 | memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, | 239 | memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, |
| 213 | sizeof(current->thread.fp_regs)); | 240 | sizeof(current->thread.fp_regs)); |
| 214 | 241 | ||
| 215 | restore_fp_regs(current->thread.fp_regs.fprs); | ||
| 216 | clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ | 242 | clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ |
| 217 | return 0; | 243 | return 0; |
| 218 | } | 244 | } |
| 219 | 245 | ||
| 220 | static int save_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs) | 246 | static int save_sigregs_ext32(struct pt_regs *regs, |
| 247 | _sigregs_ext32 __user *sregs_ext) | ||
| 221 | { | 248 | { |
| 222 | __u32 gprs_high[NUM_GPRS]; | 249 | __u32 gprs_high[NUM_GPRS]; |
| 250 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
| 223 | int i; | 251 | int i; |
| 224 | 252 | ||
| 253 | /* Save high gprs to signal stack */ | ||
| 225 | for (i = 0; i < NUM_GPRS; i++) | 254 | for (i = 0; i < NUM_GPRS; i++) |
| 226 | gprs_high[i] = regs->gprs[i] >> 32; | 255 | gprs_high[i] = regs->gprs[i] >> 32; |
| 227 | if (__copy_to_user(uregs, &gprs_high, sizeof(gprs_high))) | 256 | if (__copy_to_user(&sregs_ext->gprs_high, &gprs_high, |
| 257 | sizeof(sregs_ext->gprs_high))) | ||
| 228 | return -EFAULT; | 258 | return -EFAULT; |
| 259 | |||
| 260 | /* Save vector registers to signal stack */ | ||
| 261 | if (current->thread.vxrs) { | ||
| 262 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
| 263 | vxrs[i] = *((__u64 *)(current->thread.vxrs + i) + 1); | ||
| 264 | if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, | ||
| 265 | sizeof(sregs_ext->vxrs_low)) || | ||
| 266 | __copy_to_user(&sregs_ext->vxrs_high, | ||
| 267 | current->thread.vxrs + __NUM_VXRS_LOW, | ||
| 268 | sizeof(sregs_ext->vxrs_high))) | ||
| 269 | return -EFAULT; | ||
| 270 | } | ||
| 229 | return 0; | 271 | return 0; |
| 230 | } | 272 | } |
| 231 | 273 | ||
| 232 | static int restore_sigregs_gprs_high(struct pt_regs *regs, __u32 __user *uregs) | 274 | static int restore_sigregs_ext32(struct pt_regs *regs, |
| 275 | _sigregs_ext32 __user *sregs_ext) | ||
| 233 | { | 276 | { |
| 234 | __u32 gprs_high[NUM_GPRS]; | 277 | __u32 gprs_high[NUM_GPRS]; |
| 278 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
| 235 | int i; | 279 | int i; |
| 236 | 280 | ||
| 237 | if (__copy_from_user(&gprs_high, uregs, sizeof(gprs_high))) | 281 | /* Restore high gprs from signal stack */ |
| 282 | if (__copy_from_user(&gprs_high, &sregs_ext->gprs_high, | ||
| 283 | sizeof(&sregs_ext->gprs_high))) | ||
| 238 | return -EFAULT; | 284 | return -EFAULT; |
| 239 | for (i = 0; i < NUM_GPRS; i++) | 285 | for (i = 0; i < NUM_GPRS; i++) |
| 240 | *(__u32 *)®s->gprs[i] = gprs_high[i]; | 286 | *(__u32 *)®s->gprs[i] = gprs_high[i]; |
| 287 | |||
| 288 | /* Restore vector registers from signal stack */ | ||
| 289 | if (current->thread.vxrs) { | ||
| 290 | if (__copy_from_user(vxrs, &sregs_ext->vxrs_low, | ||
| 291 | sizeof(sregs_ext->vxrs_low)) || | ||
| 292 | __copy_from_user(current->thread.vxrs + __NUM_VXRS_LOW, | ||
| 293 | &sregs_ext->vxrs_high, | ||
| 294 | sizeof(sregs_ext->vxrs_high))) | ||
| 295 | return -EFAULT; | ||
| 296 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
| 297 | *((__u64 *)(current->thread.vxrs + i) + 1) = vxrs[i]; | ||
| 298 | } | ||
| 241 | return 0; | 299 | return 0; |
| 242 | } | 300 | } |
| 243 | 301 | ||
| @@ -252,8 +310,9 @@ COMPAT_SYSCALL_DEFINE0(sigreturn) | |||
| 252 | set_current_blocked(&set); | 310 | set_current_blocked(&set); |
| 253 | if (restore_sigregs32(regs, &frame->sregs)) | 311 | if (restore_sigregs32(regs, &frame->sregs)) |
| 254 | goto badframe; | 312 | goto badframe; |
| 255 | if (restore_sigregs_gprs_high(regs, frame->gprs_high)) | 313 | if (restore_sigregs_ext32(regs, &frame->sregs_ext)) |
| 256 | goto badframe; | 314 | goto badframe; |
| 315 | load_sigregs(); | ||
| 257 | return regs->gprs[2]; | 316 | return regs->gprs[2]; |
| 258 | badframe: | 317 | badframe: |
| 259 | force_sig(SIGSEGV, current); | 318 | force_sig(SIGSEGV, current); |
| @@ -269,12 +328,13 @@ COMPAT_SYSCALL_DEFINE0(rt_sigreturn) | |||
| 269 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | 328 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) |
| 270 | goto badframe; | 329 | goto badframe; |
| 271 | set_current_blocked(&set); | 330 | set_current_blocked(&set); |
| 331 | if (compat_restore_altstack(&frame->uc.uc_stack)) | ||
| 332 | goto badframe; | ||
| 272 | if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) | 333 | if (restore_sigregs32(regs, &frame->uc.uc_mcontext)) |
| 273 | goto badframe; | 334 | goto badframe; |
| 274 | if (restore_sigregs_gprs_high(regs, frame->gprs_high)) | 335 | if (restore_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext)) |
| 275 | goto badframe; | 336 | goto badframe; |
| 276 | if (compat_restore_altstack(&frame->uc.uc_stack)) | 337 | load_sigregs(); |
| 277 | goto badframe; | ||
| 278 | return regs->gprs[2]; | 338 | return regs->gprs[2]; |
| 279 | badframe: | 339 | badframe: |
| 280 | force_sig(SIGSEGV, current); | 340 | force_sig(SIGSEGV, current); |
| @@ -324,37 +384,64 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, | |||
| 324 | struct pt_regs *regs) | 384 | struct pt_regs *regs) |
| 325 | { | 385 | { |
| 326 | int sig = ksig->sig; | 386 | int sig = ksig->sig; |
| 327 | sigframe32 __user *frame = get_sigframe(&ksig->ka, regs, sizeof(sigframe32)); | 387 | sigframe32 __user *frame; |
| 328 | 388 | struct sigcontext32 sc; | |
| 389 | unsigned long restorer; | ||
| 390 | size_t frame_size; | ||
| 391 | |||
| 392 | /* | ||
| 393 | * gprs_high are always present for 31-bit compat tasks. | ||
| 394 | * The space for vector registers is only allocated if | ||
| 395 | * the machine supports it | ||
| 396 | */ | ||
| 397 | frame_size = sizeof(*frame) - sizeof(frame->sregs_ext.__reserved); | ||
| 398 | if (!MACHINE_HAS_VX) | ||
| 399 | frame_size -= sizeof(frame->sregs_ext.vxrs_low) + | ||
| 400 | sizeof(frame->sregs_ext.vxrs_high); | ||
| 401 | frame = get_sigframe(&ksig->ka, regs, frame_size); | ||
| 329 | if (frame == (void __user *) -1UL) | 402 | if (frame == (void __user *) -1UL) |
| 330 | return -EFAULT; | 403 | return -EFAULT; |
| 331 | 404 | ||
| 332 | if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32)) | 405 | /* Set up backchain. */ |
| 406 | if (__put_user(regs->gprs[15], (unsigned int __user *) frame)) | ||
| 407 | return -EFAULT; | ||
| 408 | |||
| 409 | /* Create struct sigcontext32 on the signal stack */ | ||
| 410 | memcpy(&sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE32); | ||
| 411 | sc.sregs = (__u32)(unsigned long __force) &frame->sregs; | ||
| 412 | if (__copy_to_user(&frame->sc, &sc, sizeof(frame->sc))) | ||
| 333 | return -EFAULT; | 413 | return -EFAULT; |
| 334 | 414 | ||
| 415 | /* Store registers needed to create the signal frame */ | ||
| 416 | store_sigregs(); | ||
| 417 | |||
| 418 | /* Create _sigregs32 on the signal stack */ | ||
| 335 | if (save_sigregs32(regs, &frame->sregs)) | 419 | if (save_sigregs32(regs, &frame->sregs)) |
| 336 | return -EFAULT; | 420 | return -EFAULT; |
| 337 | if (save_sigregs_gprs_high(regs, frame->gprs_high)) | 421 | |
| 422 | /* Place signal number on stack to allow backtrace from handler. */ | ||
| 423 | if (__put_user(regs->gprs[2], (int __force __user *) &frame->signo)) | ||
| 338 | return -EFAULT; | 424 | return -EFAULT; |
| 339 | if (__put_user((unsigned long) &frame->sregs, &frame->sc.sregs)) | 425 | |
| 426 | /* Create _sigregs_ext32 on the signal stack */ | ||
| 427 | if (save_sigregs_ext32(regs, &frame->sregs_ext)) | ||
| 340 | return -EFAULT; | 428 | return -EFAULT; |
| 341 | 429 | ||
| 342 | /* Set up to return from userspace. If provided, use a stub | 430 | /* Set up to return from userspace. If provided, use a stub |
| 343 | already in userspace. */ | 431 | already in userspace. */ |
| 344 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { | 432 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { |
| 345 | regs->gprs[14] = (__u64 __force) ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; | 433 | restorer = (unsigned long __force) |
| 434 | ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; | ||
| 346 | } else { | 435 | } else { |
| 347 | regs->gprs[14] = (__u64 __force) frame->retcode | PSW32_ADDR_AMODE; | 436 | /* Signal frames without vectors registers are short ! */ |
| 348 | if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, | 437 | __u16 __user *svc = (void *) frame + frame_size - 2; |
| 349 | (u16 __force __user *)(frame->retcode))) | 438 | if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, svc)) |
| 350 | return -EFAULT; | 439 | return -EFAULT; |
| 440 | restorer = (unsigned long __force) svc | PSW32_ADDR_AMODE; | ||
| 351 | } | 441 | } |
| 352 | 442 | ||
| 353 | /* Set up backchain. */ | ||
| 354 | if (__put_user(regs->gprs[15], (unsigned int __user *) frame)) | ||
| 355 | return -EFAULT; | ||
| 356 | |||
| 357 | /* Set up registers for signal handler */ | 443 | /* Set up registers for signal handler */ |
| 444 | regs->gprs[14] = restorer; | ||
| 358 | regs->gprs[15] = (__force __u64) frame; | 445 | regs->gprs[15] = (__force __u64) frame; |
| 359 | /* Force 31 bit amode and default user address space control. */ | 446 | /* Force 31 bit amode and default user address space control. */ |
| 360 | regs->psw.mask = PSW_MASK_BA | | 447 | regs->psw.mask = PSW_MASK_BA | |
| @@ -375,50 +462,69 @@ static int setup_frame32(struct ksignal *ksig, sigset_t *set, | |||
| 375 | regs->gprs[6] = task_thread_info(current)->last_break; | 462 | regs->gprs[6] = task_thread_info(current)->last_break; |
| 376 | } | 463 | } |
| 377 | 464 | ||
| 378 | /* Place signal number on stack to allow backtrace from handler. */ | ||
| 379 | if (__put_user(regs->gprs[2], (int __force __user *) &frame->signo)) | ||
| 380 | return -EFAULT; | ||
| 381 | return 0; | 465 | return 0; |
| 382 | } | 466 | } |
| 383 | 467 | ||
| 384 | static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, | 468 | static int setup_rt_frame32(struct ksignal *ksig, sigset_t *set, |
| 385 | struct pt_regs *regs) | 469 | struct pt_regs *regs) |
| 386 | { | 470 | { |
| 387 | int err = 0; | 471 | rt_sigframe32 __user *frame; |
| 388 | rt_sigframe32 __user *frame = get_sigframe(&ksig->ka, regs, sizeof(rt_sigframe32)); | 472 | unsigned long restorer; |
| 389 | 473 | size_t frame_size; | |
| 474 | u32 uc_flags; | ||
| 475 | |||
| 476 | frame_size = sizeof(*frame) - | ||
| 477 | sizeof(frame->uc.uc_mcontext_ext.__reserved); | ||
| 478 | /* | ||
| 479 | * gprs_high are always present for 31-bit compat tasks. | ||
| 480 | * The space for vector registers is only allocated if | ||
| 481 | * the machine supports it | ||
| 482 | */ | ||
| 483 | uc_flags = UC_GPRS_HIGH; | ||
| 484 | if (MACHINE_HAS_VX) { | ||
| 485 | if (current->thread.vxrs) | ||
| 486 | uc_flags |= UC_VXRS; | ||
| 487 | } else | ||
| 488 | frame_size -= sizeof(frame->uc.uc_mcontext_ext.vxrs_low) + | ||
| 489 | sizeof(frame->uc.uc_mcontext_ext.vxrs_high); | ||
| 490 | frame = get_sigframe(&ksig->ka, regs, frame_size); | ||
| 390 | if (frame == (void __user *) -1UL) | 491 | if (frame == (void __user *) -1UL) |
| 391 | return -EFAULT; | 492 | return -EFAULT; |
| 392 | 493 | ||
| 393 | if (copy_siginfo_to_user32(&frame->info, &ksig->info)) | 494 | /* Set up backchain. */ |
| 394 | return -EFAULT; | 495 | if (__put_user(regs->gprs[15], (unsigned int __force __user *) frame)) |
| 395 | |||
| 396 | /* Create the ucontext. */ | ||
| 397 | err |= __put_user(UC_EXTENDED, &frame->uc.uc_flags); | ||
| 398 | err |= __put_user(0, &frame->uc.uc_link); | ||
| 399 | err |= __compat_save_altstack(&frame->uc.uc_stack, regs->gprs[15]); | ||
| 400 | err |= save_sigregs32(regs, &frame->uc.uc_mcontext); | ||
| 401 | err |= save_sigregs_gprs_high(regs, frame->gprs_high); | ||
| 402 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
| 403 | if (err) | ||
| 404 | return -EFAULT; | 496 | return -EFAULT; |
| 405 | 497 | ||
| 406 | /* Set up to return from userspace. If provided, use a stub | 498 | /* Set up to return from userspace. If provided, use a stub |
| 407 | already in userspace. */ | 499 | already in userspace. */ |
| 408 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { | 500 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { |
| 409 | regs->gprs[14] = (__u64 __force) ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; | 501 | restorer = (unsigned long __force) |
| 502 | ksig->ka.sa.sa_restorer | PSW32_ADDR_AMODE; | ||
| 410 | } else { | 503 | } else { |
| 411 | regs->gprs[14] = (__u64 __force) frame->retcode | PSW32_ADDR_AMODE; | 504 | __u16 __user *svc = &frame->svc_insn; |
| 412 | if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, | 505 | if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc)) |
| 413 | (u16 __force __user *)(frame->retcode))) | ||
| 414 | return -EFAULT; | 506 | return -EFAULT; |
| 507 | restorer = (unsigned long __force) svc | PSW32_ADDR_AMODE; | ||
| 415 | } | 508 | } |
| 416 | 509 | ||
| 417 | /* Set up backchain. */ | 510 | /* Create siginfo on the signal stack */ |
| 418 | if (__put_user(regs->gprs[15], (unsigned int __force __user *) frame)) | 511 | if (copy_siginfo_to_user32(&frame->info, &ksig->info)) |
| 512 | return -EFAULT; | ||
| 513 | |||
| 514 | /* Store registers needed to create the signal frame */ | ||
| 515 | store_sigregs(); | ||
| 516 | |||
| 517 | /* Create ucontext on the signal stack. */ | ||
| 518 | if (__put_user(uc_flags, &frame->uc.uc_flags) || | ||
| 519 | __put_user(0, &frame->uc.uc_link) || | ||
| 520 | __compat_save_altstack(&frame->uc.uc_stack, regs->gprs[15]) || | ||
| 521 | save_sigregs32(regs, &frame->uc.uc_mcontext) || | ||
| 522 | __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)) || | ||
| 523 | save_sigregs_ext32(regs, &frame->uc.uc_mcontext_ext)) | ||
| 419 | return -EFAULT; | 524 | return -EFAULT; |
| 420 | 525 | ||
| 421 | /* Set up registers for signal handler */ | 526 | /* Set up registers for signal handler */ |
| 527 | regs->gprs[14] = restorer; | ||
| 422 | regs->gprs[15] = (__force __u64) frame; | 528 | regs->gprs[15] = (__force __u64) frame; |
| 423 | /* Force 31 bit amode and default user address space control. */ | 529 | /* Force 31 bit amode and default user address space control. */ |
| 424 | regs->psw.mask = PSW_MASK_BA | | 530 | regs->psw.mask = PSW_MASK_BA | |
diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c index a3b9150e6802..9f73c8059022 100644 --- a/arch/s390/kernel/crash_dump.c +++ b/arch/s390/kernel/crash_dump.c | |||
| @@ -46,9 +46,9 @@ struct dump_save_areas dump_save_areas; | |||
| 46 | /* | 46 | /* |
| 47 | * Allocate and add a save area for a CPU | 47 | * Allocate and add a save area for a CPU |
| 48 | */ | 48 | */ |
| 49 | struct save_area *dump_save_area_create(int cpu) | 49 | struct save_area_ext *dump_save_area_create(int cpu) |
| 50 | { | 50 | { |
| 51 | struct save_area **save_areas, *save_area; | 51 | struct save_area_ext **save_areas, *save_area; |
| 52 | 52 | ||
| 53 | save_area = kmalloc(sizeof(*save_area), GFP_KERNEL); | 53 | save_area = kmalloc(sizeof(*save_area), GFP_KERNEL); |
| 54 | if (!save_area) | 54 | if (!save_area) |
| @@ -386,9 +386,45 @@ static void *nt_s390_prefix(void *ptr, struct save_area *sa) | |||
| 386 | } | 386 | } |
| 387 | 387 | ||
| 388 | /* | 388 | /* |
| 389 | * Initialize vxrs high note (full 128 bit VX registers 16-31) | ||
| 390 | */ | ||
| 391 | static void *nt_s390_vx_high(void *ptr, __vector128 *vx_regs) | ||
| 392 | { | ||
| 393 | return nt_init(ptr, NT_S390_VXRS_HIGH, &vx_regs[16], | ||
| 394 | 16 * sizeof(__vector128), KEXEC_CORE_NOTE_NAME); | ||
| 395 | } | ||
| 396 | |||
| 397 | /* | ||
| 398 | * Initialize vxrs low note (lower halves of VX registers 0-15) | ||
| 399 | */ | ||
| 400 | static void *nt_s390_vx_low(void *ptr, __vector128 *vx_regs) | ||
| 401 | { | ||
| 402 | Elf64_Nhdr *note; | ||
| 403 | u64 len; | ||
| 404 | int i; | ||
| 405 | |||
| 406 | note = (Elf64_Nhdr *)ptr; | ||
| 407 | note->n_namesz = strlen(KEXEC_CORE_NOTE_NAME) + 1; | ||
| 408 | note->n_descsz = 16 * 8; | ||
| 409 | note->n_type = NT_S390_VXRS_LOW; | ||
| 410 | len = sizeof(Elf64_Nhdr); | ||
| 411 | |||
| 412 | memcpy(ptr + len, KEXEC_CORE_NOTE_NAME, note->n_namesz); | ||
| 413 | len = roundup(len + note->n_namesz, 4); | ||
| 414 | |||
| 415 | ptr += len; | ||
| 416 | /* Copy lower halves of SIMD registers 0-15 */ | ||
| 417 | for (i = 0; i < 16; i++) { | ||
| 418 | memcpy(ptr, &vx_regs[i], 8); | ||
| 419 | ptr += 8; | ||
| 420 | } | ||
| 421 | return ptr; | ||
| 422 | } | ||
| 423 | |||
| 424 | /* | ||
| 389 | * Fill ELF notes for one CPU with save area registers | 425 | * Fill ELF notes for one CPU with save area registers |
| 390 | */ | 426 | */ |
| 391 | void *fill_cpu_elf_notes(void *ptr, struct save_area *sa) | 427 | void *fill_cpu_elf_notes(void *ptr, struct save_area *sa, __vector128 *vx_regs) |
| 392 | { | 428 | { |
| 393 | ptr = nt_prstatus(ptr, sa); | 429 | ptr = nt_prstatus(ptr, sa); |
| 394 | ptr = nt_fpregset(ptr, sa); | 430 | ptr = nt_fpregset(ptr, sa); |
| @@ -397,6 +433,10 @@ void *fill_cpu_elf_notes(void *ptr, struct save_area *sa) | |||
| 397 | ptr = nt_s390_tod_preg(ptr, sa); | 433 | ptr = nt_s390_tod_preg(ptr, sa); |
| 398 | ptr = nt_s390_ctrs(ptr, sa); | 434 | ptr = nt_s390_ctrs(ptr, sa); |
| 399 | ptr = nt_s390_prefix(ptr, sa); | 435 | ptr = nt_s390_prefix(ptr, sa); |
| 436 | if (MACHINE_HAS_VX && vx_regs) { | ||
| 437 | ptr = nt_s390_vx_low(ptr, vx_regs); | ||
| 438 | ptr = nt_s390_vx_high(ptr, vx_regs); | ||
| 439 | } | ||
| 400 | return ptr; | 440 | return ptr; |
| 401 | } | 441 | } |
| 402 | 442 | ||
| @@ -484,7 +524,7 @@ static int get_cpu_cnt(void) | |||
| 484 | int i, cpus = 0; | 524 | int i, cpus = 0; |
| 485 | 525 | ||
| 486 | for (i = 0; i < dump_save_areas.count; i++) { | 526 | for (i = 0; i < dump_save_areas.count; i++) { |
| 487 | if (dump_save_areas.areas[i]->pref_reg == 0) | 527 | if (dump_save_areas.areas[i]->sa.pref_reg == 0) |
| 488 | continue; | 528 | continue; |
| 489 | cpus++; | 529 | cpus++; |
| 490 | } | 530 | } |
| @@ -530,17 +570,17 @@ static void loads_init(Elf64_Phdr *phdr, u64 loads_offset) | |||
| 530 | */ | 570 | */ |
| 531 | static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset) | 571 | static void *notes_init(Elf64_Phdr *phdr, void *ptr, u64 notes_offset) |
| 532 | { | 572 | { |
| 533 | struct save_area *sa; | 573 | struct save_area_ext *sa_ext; |
| 534 | void *ptr_start = ptr; | 574 | void *ptr_start = ptr; |
| 535 | int i; | 575 | int i; |
| 536 | 576 | ||
| 537 | ptr = nt_prpsinfo(ptr); | 577 | ptr = nt_prpsinfo(ptr); |
| 538 | 578 | ||
| 539 | for (i = 0; i < dump_save_areas.count; i++) { | 579 | for (i = 0; i < dump_save_areas.count; i++) { |
| 540 | sa = dump_save_areas.areas[i]; | 580 | sa_ext = dump_save_areas.areas[i]; |
| 541 | if (sa->pref_reg == 0) | 581 | if (sa_ext->sa.pref_reg == 0) |
| 542 | continue; | 582 | continue; |
| 543 | ptr = fill_cpu_elf_notes(ptr, sa); | 583 | ptr = fill_cpu_elf_notes(ptr, &sa_ext->sa, sa_ext->vx_regs); |
| 544 | } | 584 | } |
| 545 | ptr = nt_vmcoreinfo(ptr); | 585 | ptr = nt_vmcoreinfo(ptr); |
| 546 | memset(phdr, 0, sizeof(*phdr)); | 586 | memset(phdr, 0, sizeof(*phdr)); |
| @@ -581,7 +621,7 @@ int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size) | |||
| 581 | 621 | ||
| 582 | mem_chunk_cnt = get_mem_chunk_cnt(); | 622 | mem_chunk_cnt = get_mem_chunk_cnt(); |
| 583 | 623 | ||
| 584 | alloc_size = 0x1000 + get_cpu_cnt() * 0x300 + | 624 | alloc_size = 0x1000 + get_cpu_cnt() * 0x4a0 + |
| 585 | mem_chunk_cnt * sizeof(Elf64_Phdr); | 625 | mem_chunk_cnt * sizeof(Elf64_Phdr); |
| 586 | hdr = kzalloc_panic(alloc_size); | 626 | hdr = kzalloc_panic(alloc_size); |
| 587 | /* Init elf header */ | 627 | /* Init elf header */ |
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c index 993efe6a887c..f3762937dd82 100644 --- a/arch/s390/kernel/dis.c +++ b/arch/s390/kernel/dis.c | |||
| @@ -60,6 +60,11 @@ enum { | |||
| 60 | A_28, /* Access reg. starting at position 28 */ | 60 | A_28, /* Access reg. starting at position 28 */ |
| 61 | C_8, /* Control reg. starting at position 8 */ | 61 | C_8, /* Control reg. starting at position 8 */ |
| 62 | C_12, /* Control reg. starting at position 12 */ | 62 | C_12, /* Control reg. starting at position 12 */ |
| 63 | V_8, /* Vector reg. starting at position 8, extension bit at 36 */ | ||
| 64 | V_12, /* Vector reg. starting at position 12, extension bit at 37 */ | ||
| 65 | V_16, /* Vector reg. starting at position 16, extension bit at 38 */ | ||
| 66 | V_32, /* Vector reg. starting at position 32, extension bit at 39 */ | ||
| 67 | W_12, /* Vector reg. at bit 12, extension at bit 37, used as index */ | ||
| 63 | B_16, /* Base register starting at position 16 */ | 68 | B_16, /* Base register starting at position 16 */ |
| 64 | B_32, /* Base register starting at position 32 */ | 69 | B_32, /* Base register starting at position 32 */ |
| 65 | X_12, /* Index register starting at position 12 */ | 70 | X_12, /* Index register starting at position 12 */ |
| @@ -82,6 +87,8 @@ enum { | |||
| 82 | U8_24, /* 8 bit unsigned value starting at 24 */ | 87 | U8_24, /* 8 bit unsigned value starting at 24 */ |
| 83 | U8_32, /* 8 bit unsigned value starting at 32 */ | 88 | U8_32, /* 8 bit unsigned value starting at 32 */ |
| 84 | I8_8, /* 8 bit signed value starting at 8 */ | 89 | I8_8, /* 8 bit signed value starting at 8 */ |
| 90 | I8_16, /* 8 bit signed value starting at 16 */ | ||
| 91 | I8_24, /* 8 bit signed value starting at 24 */ | ||
| 85 | I8_32, /* 8 bit signed value starting at 32 */ | 92 | I8_32, /* 8 bit signed value starting at 32 */ |
| 86 | J12_12, /* PC relative offset at 12 */ | 93 | J12_12, /* PC relative offset at 12 */ |
| 87 | I16_16, /* 16 bit signed value starting at 16 */ | 94 | I16_16, /* 16 bit signed value starting at 16 */ |
| @@ -96,6 +103,9 @@ enum { | |||
| 96 | U32_16, /* 32 bit unsigned value starting at 16 */ | 103 | U32_16, /* 32 bit unsigned value starting at 16 */ |
| 97 | M_16, /* 4 bit optional mask starting at 16 */ | 104 | M_16, /* 4 bit optional mask starting at 16 */ |
| 98 | M_20, /* 4 bit optional mask starting at 20 */ | 105 | M_20, /* 4 bit optional mask starting at 20 */ |
| 106 | M_24, /* 4 bit optional mask starting at 24 */ | ||
| 107 | M_28, /* 4 bit optional mask starting at 28 */ | ||
| 108 | M_32, /* 4 bit optional mask starting at 32 */ | ||
| 99 | RO_28, /* optional GPR starting at position 28 */ | 109 | RO_28, /* optional GPR starting at position 28 */ |
| 100 | }; | 110 | }; |
| 101 | 111 | ||
| @@ -130,7 +140,7 @@ enum { | |||
| 130 | INSTR_RSY_RDRM, | 140 | INSTR_RSY_RDRM, |
| 131 | INSTR_RS_AARD, INSTR_RS_CCRD, INSTR_RS_R0RD, INSTR_RS_RRRD, | 141 | INSTR_RS_AARD, INSTR_RS_CCRD, INSTR_RS_R0RD, INSTR_RS_RRRD, |
| 132 | INSTR_RS_RURD, | 142 | INSTR_RS_RURD, |
| 133 | INSTR_RXE_FRRD, INSTR_RXE_RRRD, | 143 | INSTR_RXE_FRRD, INSTR_RXE_RRRD, INSTR_RXE_RRRDM, |
| 134 | INSTR_RXF_FRRDF, | 144 | INSTR_RXF_FRRDF, |
| 135 | INSTR_RXY_FRRD, INSTR_RXY_RRRD, INSTR_RXY_URRD, | 145 | INSTR_RXY_FRRD, INSTR_RXY_RRRD, INSTR_RXY_URRD, |
| 136 | INSTR_RX_FRRD, INSTR_RX_RRRD, INSTR_RX_URRD, | 146 | INSTR_RX_FRRD, INSTR_RX_RRRD, INSTR_RX_URRD, |
| @@ -143,6 +153,17 @@ enum { | |||
| 143 | INSTR_SS_L0RDRD, INSTR_SS_LIRDRD, INSTR_SS_LLRDRD, INSTR_SS_RRRDRD, | 153 | INSTR_SS_L0RDRD, INSTR_SS_LIRDRD, INSTR_SS_LLRDRD, INSTR_SS_RRRDRD, |
| 144 | INSTR_SS_RRRDRD2, INSTR_SS_RRRDRD3, | 154 | INSTR_SS_RRRDRD2, INSTR_SS_RRRDRD3, |
| 145 | INSTR_S_00, INSTR_S_RD, | 155 | INSTR_S_00, INSTR_S_RD, |
| 156 | INSTR_VRI_V0IM, INSTR_VRI_V0I0, INSTR_VRI_V0IIM, INSTR_VRI_VVIM, | ||
| 157 | INSTR_VRI_VVV0IM, INSTR_VRI_VVV0I0, INSTR_VRI_VVIMM, | ||
| 158 | INSTR_VRR_VV00MMM, INSTR_VRR_VV000MM, INSTR_VRR_VV0000M, | ||
| 159 | INSTR_VRR_VV00000, INSTR_VRR_VVV0M0M, INSTR_VRR_VV00M0M, | ||
| 160 | INSTR_VRR_VVV000M, INSTR_VRR_VVV000V, INSTR_VRR_VVV0000, | ||
| 161 | INSTR_VRR_VVV0MMM, INSTR_VRR_VVV00MM, INSTR_VRR_VVVMM0V, | ||
| 162 | INSTR_VRR_VVVM0MV, INSTR_VRR_VVVM00V, INSTR_VRR_VRR0000, | ||
| 163 | INSTR_VRS_VVRDM, INSTR_VRS_VVRD0, INSTR_VRS_VRRDM, INSTR_VRS_VRRD0, | ||
| 164 | INSTR_VRS_RVRDM, | ||
| 165 | INSTR_VRV_VVRDM, INSTR_VRV_VWRDM, | ||
| 166 | INSTR_VRX_VRRDM, INSTR_VRX_VRRD0, | ||
| 146 | }; | 167 | }; |
| 147 | 168 | ||
| 148 | static const struct s390_operand operands[] = | 169 | static const struct s390_operand operands[] = |
| @@ -168,6 +189,11 @@ static const struct s390_operand operands[] = | |||
| 168 | [A_28] = { 4, 28, OPERAND_AR }, | 189 | [A_28] = { 4, 28, OPERAND_AR }, |
| 169 | [C_8] = { 4, 8, OPERAND_CR }, | 190 | [C_8] = { 4, 8, OPERAND_CR }, |
| 170 | [C_12] = { 4, 12, OPERAND_CR }, | 191 | [C_12] = { 4, 12, OPERAND_CR }, |
| 192 | [V_8] = { 4, 8, OPERAND_VR }, | ||
| 193 | [V_12] = { 4, 12, OPERAND_VR }, | ||
| 194 | [V_16] = { 4, 16, OPERAND_VR }, | ||
| 195 | [V_32] = { 4, 32, OPERAND_VR }, | ||
| 196 | [W_12] = { 4, 12, OPERAND_INDEX | OPERAND_VR }, | ||
| 171 | [B_16] = { 4, 16, OPERAND_BASE | OPERAND_GPR }, | 197 | [B_16] = { 4, 16, OPERAND_BASE | OPERAND_GPR }, |
| 172 | [B_32] = { 4, 32, OPERAND_BASE | OPERAND_GPR }, | 198 | [B_32] = { 4, 32, OPERAND_BASE | OPERAND_GPR }, |
| 173 | [X_12] = { 4, 12, OPERAND_INDEX | OPERAND_GPR }, | 199 | [X_12] = { 4, 12, OPERAND_INDEX | OPERAND_GPR }, |
| @@ -190,6 +216,11 @@ static const struct s390_operand operands[] = | |||
| 190 | [U8_24] = { 8, 24, 0 }, | 216 | [U8_24] = { 8, 24, 0 }, |
| 191 | [U8_32] = { 8, 32, 0 }, | 217 | [U8_32] = { 8, 32, 0 }, |
| 192 | [J12_12] = { 12, 12, OPERAND_PCREL }, | 218 | [J12_12] = { 12, 12, OPERAND_PCREL }, |
| 219 | [I8_8] = { 8, 8, OPERAND_SIGNED }, | ||
| 220 | [I8_16] = { 8, 16, OPERAND_SIGNED }, | ||
| 221 | [I8_24] = { 8, 24, OPERAND_SIGNED }, | ||
| 222 | [I8_32] = { 8, 32, OPERAND_SIGNED }, | ||
| 223 | [I16_32] = { 16, 32, OPERAND_SIGNED }, | ||
| 193 | [I16_16] = { 16, 16, OPERAND_SIGNED }, | 224 | [I16_16] = { 16, 16, OPERAND_SIGNED }, |
| 194 | [U16_16] = { 16, 16, 0 }, | 225 | [U16_16] = { 16, 16, 0 }, |
| 195 | [U16_32] = { 16, 32, 0 }, | 226 | [U16_32] = { 16, 32, 0 }, |
| @@ -202,6 +233,9 @@ static const struct s390_operand operands[] = | |||
| 202 | [U32_16] = { 32, 16, 0 }, | 233 | [U32_16] = { 32, 16, 0 }, |
| 203 | [M_16] = { 4, 16, 0 }, | 234 | [M_16] = { 4, 16, 0 }, |
| 204 | [M_20] = { 4, 20, 0 }, | 235 | [M_20] = { 4, 20, 0 }, |
| 236 | [M_24] = { 4, 24, 0 }, | ||
| 237 | [M_28] = { 4, 28, 0 }, | ||
| 238 | [M_32] = { 4, 32, 0 }, | ||
| 205 | [RO_28] = { 4, 28, OPERAND_GPR } | 239 | [RO_28] = { 4, 28, OPERAND_GPR } |
| 206 | }; | 240 | }; |
| 207 | 241 | ||
| @@ -283,6 +317,7 @@ static const unsigned char formats[][7] = { | |||
| 283 | [INSTR_RS_RURD] = { 0xff, R_8,U4_12,D_20,B_16,0,0 }, | 317 | [INSTR_RS_RURD] = { 0xff, R_8,U4_12,D_20,B_16,0,0 }, |
| 284 | [INSTR_RXE_FRRD] = { 0xff, F_8,D_20,X_12,B_16,0,0 }, | 318 | [INSTR_RXE_FRRD] = { 0xff, F_8,D_20,X_12,B_16,0,0 }, |
| 285 | [INSTR_RXE_RRRD] = { 0xff, R_8,D_20,X_12,B_16,0,0 }, | 319 | [INSTR_RXE_RRRD] = { 0xff, R_8,D_20,X_12,B_16,0,0 }, |
| 320 | [INSTR_RXE_RRRDM] = { 0xff, R_8,D_20,X_12,B_16,M_32,0 }, | ||
| 286 | [INSTR_RXF_FRRDF] = { 0xff, F_32,F_8,D_20,X_12,B_16,0 }, | 321 | [INSTR_RXF_FRRDF] = { 0xff, F_32,F_8,D_20,X_12,B_16,0 }, |
| 287 | [INSTR_RXY_FRRD] = { 0xff, F_8,D20_20,X_12,B_16,0,0 }, | 322 | [INSTR_RXY_FRRD] = { 0xff, F_8,D20_20,X_12,B_16,0,0 }, |
| 288 | [INSTR_RXY_RRRD] = { 0xff, R_8,D20_20,X_12,B_16,0,0 }, | 323 | [INSTR_RXY_RRRD] = { 0xff, R_8,D20_20,X_12,B_16,0,0 }, |
| @@ -307,6 +342,37 @@ static const unsigned char formats[][7] = { | |||
| 307 | [INSTR_SS_RRRDRD] = { 0xff, D_20,R_8,B_16,D_36,B_32,R_12 }, | 342 | [INSTR_SS_RRRDRD] = { 0xff, D_20,R_8,B_16,D_36,B_32,R_12 }, |
| 308 | [INSTR_S_00] = { 0xff, 0,0,0,0,0,0 }, | 343 | [INSTR_S_00] = { 0xff, 0,0,0,0,0,0 }, |
| 309 | [INSTR_S_RD] = { 0xff, D_20,B_16,0,0,0,0 }, | 344 | [INSTR_S_RD] = { 0xff, D_20,B_16,0,0,0,0 }, |
| 345 | [INSTR_VRI_V0IM] = { 0xff, V_8,I16_16,M_32,0,0,0 }, | ||
| 346 | [INSTR_VRI_V0I0] = { 0xff, V_8,I16_16,0,0,0,0 }, | ||
| 347 | [INSTR_VRI_V0IIM] = { 0xff, V_8,I8_16,I8_24,M_32,0,0 }, | ||
| 348 | [INSTR_VRI_VVIM] = { 0xff, V_8,I16_16,V_12,M_32,0,0 }, | ||
| 349 | [INSTR_VRI_VVV0IM]= { 0xff, V_8,V_12,V_16,I8_24,M_32,0 }, | ||
| 350 | [INSTR_VRI_VVV0I0]= { 0xff, V_8,V_12,V_16,I8_24,0,0 }, | ||
| 351 | [INSTR_VRI_VVIMM] = { 0xff, V_8,V_12,I16_16,M_32,M_28,0 }, | ||
| 352 | [INSTR_VRR_VV00MMM]={ 0xff, V_8,V_12,M_32,M_28,M_24,0 }, | ||
| 353 | [INSTR_VRR_VV000MM]={ 0xff, V_8,V_12,M_32,M_28,0,0 }, | ||
| 354 | [INSTR_VRR_VV0000M]={ 0xff, V_8,V_12,M_32,0,0,0 }, | ||
| 355 | [INSTR_VRR_VV00000]={ 0xff, V_8,V_12,0,0,0,0 }, | ||
| 356 | [INSTR_VRR_VVV0M0M]={ 0xff, V_8,V_12,V_16,M_32,M_24,0 }, | ||
| 357 | [INSTR_VRR_VV00M0M]={ 0xff, V_8,V_12,M_32,M_24,0,0 }, | ||
| 358 | [INSTR_VRR_VVV000M]={ 0xff, V_8,V_12,V_16,M_32,0,0 }, | ||
| 359 | [INSTR_VRR_VVV000V]={ 0xff, V_8,V_12,V_16,V_32,0,0 }, | ||
| 360 | [INSTR_VRR_VVV0000]={ 0xff, V_8,V_12,V_16,0,0,0 }, | ||
| 361 | [INSTR_VRR_VVV0MMM]={ 0xff, V_8,V_12,V_16,M_32,M_28,M_24 }, | ||
| 362 | [INSTR_VRR_VVV00MM]={ 0xff, V_8,V_12,V_16,M_32,M_28,0 }, | ||
| 363 | [INSTR_VRR_VVVMM0V]={ 0xff, V_8,V_12,V_16,V_32,M_20,M_24 }, | ||
| 364 | [INSTR_VRR_VVVM0MV]={ 0xff, V_8,V_12,V_16,V_32,M_28,M_20 }, | ||
| 365 | [INSTR_VRR_VVVM00V]={ 0xff, V_8,V_12,V_16,V_32,M_20,0 }, | ||
| 366 | [INSTR_VRR_VRR0000]={ 0xff, V_8,R_12,R_16,0,0,0 }, | ||
| 367 | [INSTR_VRS_VVRDM] = { 0xff, V_8,V_12,D_20,B_16,M_32,0 }, | ||
| 368 | [INSTR_VRS_VVRD0] = { 0xff, V_8,V_12,D_20,B_16,0,0 }, | ||
| 369 | [INSTR_VRS_VRRDM] = { 0xff, V_8,R_12,D_20,B_16,M_32,0 }, | ||
| 370 | [INSTR_VRS_VRRD0] = { 0xff, V_8,R_12,D_20,B_16,0,0 }, | ||
| 371 | [INSTR_VRS_RVRDM] = { 0xff, R_8,V_12,D_20,B_16,M_32,0 }, | ||
| 372 | [INSTR_VRV_VVRDM] = { 0xff, V_8,V_12,D_20,B_16,M_32,0 }, | ||
| 373 | [INSTR_VRV_VWRDM] = { 0xff, V_8,D_20,W_12,B_16,M_32,0 }, | ||
| 374 | [INSTR_VRX_VRRDM] = { 0xff, V_8,D_20,X_12,B_16,M_32,0 }, | ||
| 375 | [INSTR_VRX_VRRD0] = { 0xff, V_8,D_20,X_12,B_16,0,0 }, | ||
| 310 | }; | 376 | }; |
| 311 | 377 | ||
| 312 | enum { | 378 | enum { |
| @@ -381,6 +447,11 @@ enum { | |||
| 381 | LONG_INSN_MPCIFC, | 447 | LONG_INSN_MPCIFC, |
| 382 | LONG_INSN_STPCIFC, | 448 | LONG_INSN_STPCIFC, |
| 383 | LONG_INSN_PCISTB, | 449 | LONG_INSN_PCISTB, |
| 450 | LONG_INSN_VPOPCT, | ||
| 451 | LONG_INSN_VERLLV, | ||
| 452 | LONG_INSN_VESRAV, | ||
| 453 | LONG_INSN_VESRLV, | ||
| 454 | LONG_INSN_VSBCBI | ||
| 384 | }; | 455 | }; |
| 385 | 456 | ||
| 386 | static char *long_insn_name[] = { | 457 | static char *long_insn_name[] = { |
| @@ -455,6 +526,11 @@ static char *long_insn_name[] = { | |||
| 455 | [LONG_INSN_MPCIFC] = "mpcifc", | 526 | [LONG_INSN_MPCIFC] = "mpcifc", |
| 456 | [LONG_INSN_STPCIFC] = "stpcifc", | 527 | [LONG_INSN_STPCIFC] = "stpcifc", |
| 457 | [LONG_INSN_PCISTB] = "pcistb", | 528 | [LONG_INSN_PCISTB] = "pcistb", |
| 529 | [LONG_INSN_VPOPCT] = "vpopct", | ||
| 530 | [LONG_INSN_VERLLV] = "verllv", | ||
| 531 | [LONG_INSN_VESRAV] = "vesrav", | ||
| 532 | [LONG_INSN_VESRLV] = "vesrlv", | ||
| 533 | [LONG_INSN_VSBCBI] = "vsbcbi", | ||
| 458 | }; | 534 | }; |
| 459 | 535 | ||
| 460 | static struct s390_insn opcode[] = { | 536 | static struct s390_insn opcode[] = { |
| @@ -1369,6 +1445,150 @@ static struct s390_insn opcode_e5[] = { | |||
| 1369 | { "", 0, INSTR_INVALID } | 1445 | { "", 0, INSTR_INVALID } |
| 1370 | }; | 1446 | }; |
| 1371 | 1447 | ||
| 1448 | static struct s390_insn opcode_e7[] = { | ||
| 1449 | #ifdef CONFIG_64BIT | ||
| 1450 | { "lcbb", 0x27, INSTR_RXE_RRRDM }, | ||
| 1451 | { "vgef", 0x13, INSTR_VRV_VVRDM }, | ||
| 1452 | { "vgeg", 0x12, INSTR_VRV_VVRDM }, | ||
| 1453 | { "vgbm", 0x44, INSTR_VRI_V0I0 }, | ||
| 1454 | { "vgm", 0x46, INSTR_VRI_V0IIM }, | ||
| 1455 | { "vl", 0x06, INSTR_VRX_VRRD0 }, | ||
| 1456 | { "vlr", 0x56, INSTR_VRR_VV00000 }, | ||
| 1457 | { "vlrp", 0x05, INSTR_VRX_VRRDM }, | ||
| 1458 | { "vleb", 0x00, INSTR_VRX_VRRDM }, | ||
| 1459 | { "vleh", 0x01, INSTR_VRX_VRRDM }, | ||
| 1460 | { "vlef", 0x03, INSTR_VRX_VRRDM }, | ||
| 1461 | { "vleg", 0x02, INSTR_VRX_VRRDM }, | ||
| 1462 | { "vleib", 0x40, INSTR_VRI_V0IM }, | ||
| 1463 | { "vleih", 0x41, INSTR_VRI_V0IM }, | ||
| 1464 | { "vleif", 0x43, INSTR_VRI_V0IM }, | ||
| 1465 | { "vleig", 0x42, INSTR_VRI_V0IM }, | ||
| 1466 | { "vlgv", 0x21, INSTR_VRS_RVRDM }, | ||
| 1467 | { "vllez", 0x04, INSTR_VRX_VRRDM }, | ||
| 1468 | { "vlm", 0x36, INSTR_VRS_VVRD0 }, | ||
| 1469 | { "vlbb", 0x07, INSTR_VRX_VRRDM }, | ||
| 1470 | { "vlvg", 0x22, INSTR_VRS_VRRDM }, | ||
| 1471 | { "vlvgp", 0x62, INSTR_VRR_VRR0000 }, | ||
| 1472 | { "vll", 0x37, INSTR_VRS_VRRD0 }, | ||
| 1473 | { "vmrh", 0x61, INSTR_VRR_VVV000M }, | ||
| 1474 | { "vmrl", 0x60, INSTR_VRR_VVV000M }, | ||
| 1475 | { "vpk", 0x94, INSTR_VRR_VVV000M }, | ||
| 1476 | { "vpks", 0x97, INSTR_VRR_VVV0M0M }, | ||
| 1477 | { "vpkls", 0x95, INSTR_VRR_VVV0M0M }, | ||
| 1478 | { "vperm", 0x8c, INSTR_VRR_VVV000V }, | ||
| 1479 | { "vpdi", 0x84, INSTR_VRR_VVV000M }, | ||
| 1480 | { "vrep", 0x4d, INSTR_VRI_VVIM }, | ||
| 1481 | { "vrepi", 0x45, INSTR_VRI_V0IM }, | ||
| 1482 | { "vscef", 0x1b, INSTR_VRV_VWRDM }, | ||
| 1483 | { "vsceg", 0x1a, INSTR_VRV_VWRDM }, | ||
| 1484 | { "vsel", 0x8d, INSTR_VRR_VVV000V }, | ||
| 1485 | { "vseg", 0x5f, INSTR_VRR_VV0000M }, | ||
| 1486 | { "vst", 0x0e, INSTR_VRX_VRRD0 }, | ||
| 1487 | { "vsteb", 0x08, INSTR_VRX_VRRDM }, | ||
| 1488 | { "vsteh", 0x09, INSTR_VRX_VRRDM }, | ||
| 1489 | { "vstef", 0x0b, INSTR_VRX_VRRDM }, | ||
| 1490 | { "vsteg", 0x0a, INSTR_VRX_VRRDM }, | ||
| 1491 | { "vstm", 0x3e, INSTR_VRS_VVRD0 }, | ||
| 1492 | { "vstl", 0x3f, INSTR_VRS_VRRD0 }, | ||
| 1493 | { "vuph", 0xd7, INSTR_VRR_VV0000M }, | ||
| 1494 | { "vuplh", 0xd5, INSTR_VRR_VV0000M }, | ||
| 1495 | { "vupl", 0xd6, INSTR_VRR_VV0000M }, | ||
| 1496 | { "vupll", 0xd4, INSTR_VRR_VV0000M }, | ||
| 1497 | { "va", 0xf3, INSTR_VRR_VVV000M }, | ||
| 1498 | { "vacc", 0xf1, INSTR_VRR_VVV000M }, | ||
| 1499 | { "vac", 0xbb, INSTR_VRR_VVVM00V }, | ||
| 1500 | { "vaccc", 0xb9, INSTR_VRR_VVVM00V }, | ||
| 1501 | { "vn", 0x68, INSTR_VRR_VVV0000 }, | ||
| 1502 | { "vnc", 0x69, INSTR_VRR_VVV0000 }, | ||
| 1503 | { "vavg", 0xf2, INSTR_VRR_VVV000M }, | ||
| 1504 | { "vavgl", 0xf0, INSTR_VRR_VVV000M }, | ||
| 1505 | { "vcksm", 0x66, INSTR_VRR_VVV0000 }, | ||
| 1506 | { "vec", 0xdb, INSTR_VRR_VV0000M }, | ||
| 1507 | { "vecl", 0xd9, INSTR_VRR_VV0000M }, | ||
| 1508 | { "vceq", 0xf8, INSTR_VRR_VVV0M0M }, | ||
| 1509 | { "vch", 0xfb, INSTR_VRR_VVV0M0M }, | ||
| 1510 | { "vchl", 0xf9, INSTR_VRR_VVV0M0M }, | ||
| 1511 | { "vclz", 0x53, INSTR_VRR_VV0000M }, | ||
| 1512 | { "vctz", 0x52, INSTR_VRR_VV0000M }, | ||
| 1513 | { "vx", 0x6d, INSTR_VRR_VVV0000 }, | ||
| 1514 | { "vgfm", 0xb4, INSTR_VRR_VVV000M }, | ||
| 1515 | { "vgfma", 0xbc, INSTR_VRR_VVVM00V }, | ||
| 1516 | { "vlc", 0xde, INSTR_VRR_VV0000M }, | ||
| 1517 | { "vlp", 0xdf, INSTR_VRR_VV0000M }, | ||
| 1518 | { "vmx", 0xff, INSTR_VRR_VVV000M }, | ||
| 1519 | { "vmxl", 0xfd, INSTR_VRR_VVV000M }, | ||
| 1520 | { "vmn", 0xfe, INSTR_VRR_VVV000M }, | ||
| 1521 | { "vmnl", 0xfc, INSTR_VRR_VVV000M }, | ||
| 1522 | { "vmal", 0xaa, INSTR_VRR_VVVM00V }, | ||
| 1523 | { "vmae", 0xae, INSTR_VRR_VVVM00V }, | ||
| 1524 | { "vmale", 0xac, INSTR_VRR_VVVM00V }, | ||
| 1525 | { "vmah", 0xab, INSTR_VRR_VVVM00V }, | ||
| 1526 | { "vmalh", 0xa9, INSTR_VRR_VVVM00V }, | ||
| 1527 | { "vmao", 0xaf, INSTR_VRR_VVVM00V }, | ||
| 1528 | { "vmalo", 0xad, INSTR_VRR_VVVM00V }, | ||
| 1529 | { "vmh", 0xa3, INSTR_VRR_VVV000M }, | ||
| 1530 | { "vmlh", 0xa1, INSTR_VRR_VVV000M }, | ||
| 1531 | { "vml", 0xa2, INSTR_VRR_VVV000M }, | ||
| 1532 | { "vme", 0xa6, INSTR_VRR_VVV000M }, | ||
| 1533 | { "vmle", 0xa4, INSTR_VRR_VVV000M }, | ||
| 1534 | { "vmo", 0xa7, INSTR_VRR_VVV000M }, | ||
| 1535 | { "vmlo", 0xa5, INSTR_VRR_VVV000M }, | ||
| 1536 | { "vno", 0x6b, INSTR_VRR_VVV0000 }, | ||
| 1537 | { "vo", 0x6a, INSTR_VRR_VVV0000 }, | ||
| 1538 | { { 0, LONG_INSN_VPOPCT }, 0x50, INSTR_VRR_VV0000M }, | ||
| 1539 | { { 0, LONG_INSN_VERLLV }, 0x73, INSTR_VRR_VVV000M }, | ||
| 1540 | { "verll", 0x33, INSTR_VRS_VVRDM }, | ||
| 1541 | { "verim", 0x72, INSTR_VRI_VVV0IM }, | ||
| 1542 | { "veslv", 0x70, INSTR_VRR_VVV000M }, | ||
| 1543 | { "vesl", 0x30, INSTR_VRS_VVRDM }, | ||
| 1544 | { { 0, LONG_INSN_VESRAV }, 0x7a, INSTR_VRR_VVV000M }, | ||
| 1545 | { "vesra", 0x3a, INSTR_VRS_VVRDM }, | ||
| 1546 | { { 0, LONG_INSN_VESRLV }, 0x78, INSTR_VRR_VVV000M }, | ||
| 1547 | { "vesrl", 0x38, INSTR_VRS_VVRDM }, | ||
| 1548 | { "vsl", 0x74, INSTR_VRR_VVV0000 }, | ||
| 1549 | { "vslb", 0x75, INSTR_VRR_VVV0000 }, | ||
| 1550 | { "vsldb", 0x77, INSTR_VRI_VVV0I0 }, | ||
| 1551 | { "vsra", 0x7e, INSTR_VRR_VVV0000 }, | ||
| 1552 | { "vsrab", 0x7f, INSTR_VRR_VVV0000 }, | ||
| 1553 | { "vsrl", 0x7c, INSTR_VRR_VVV0000 }, | ||
| 1554 | { "vsrlb", 0x7d, INSTR_VRR_VVV0000 }, | ||
| 1555 | { "vs", 0xf7, INSTR_VRR_VVV000M }, | ||
| 1556 | { "vscb", 0xf5, INSTR_VRR_VVV000M }, | ||
| 1557 | { "vsb", 0xbf, INSTR_VRR_VVVM00V }, | ||
| 1558 | { { 0, LONG_INSN_VSBCBI }, 0xbd, INSTR_VRR_VVVM00V }, | ||
| 1559 | { "vsumg", 0x65, INSTR_VRR_VVV000M }, | ||
| 1560 | { "vsumq", 0x67, INSTR_VRR_VVV000M }, | ||
| 1561 | { "vsum", 0x64, INSTR_VRR_VVV000M }, | ||
| 1562 | { "vtm", 0xd8, INSTR_VRR_VV00000 }, | ||
| 1563 | { "vfae", 0x82, INSTR_VRR_VVV0M0M }, | ||
| 1564 | { "vfee", 0x80, INSTR_VRR_VVV0M0M }, | ||
| 1565 | { "vfene", 0x81, INSTR_VRR_VVV0M0M }, | ||
| 1566 | { "vistr", 0x5c, INSTR_VRR_VV00M0M }, | ||
| 1567 | { "vstrc", 0x8a, INSTR_VRR_VVVMM0V }, | ||
| 1568 | { "vfa", 0xe3, INSTR_VRR_VVV00MM }, | ||
| 1569 | { "wfc", 0xcb, INSTR_VRR_VV000MM }, | ||
| 1570 | { "wfk", 0xca, INSTR_VRR_VV000MM }, | ||
| 1571 | { "vfce", 0xe8, INSTR_VRR_VVV0MMM }, | ||
| 1572 | { "vfch", 0xeb, INSTR_VRR_VVV0MMM }, | ||
| 1573 | { "vfche", 0xea, INSTR_VRR_VVV0MMM }, | ||
| 1574 | { "vcdg", 0xc3, INSTR_VRR_VV00MMM }, | ||
| 1575 | { "vcdlg", 0xc1, INSTR_VRR_VV00MMM }, | ||
| 1576 | { "vcgd", 0xc2, INSTR_VRR_VV00MMM }, | ||
| 1577 | { "vclgd", 0xc0, INSTR_VRR_VV00MMM }, | ||
| 1578 | { "vfd", 0xe5, INSTR_VRR_VVV00MM }, | ||
| 1579 | { "vfi", 0xc7, INSTR_VRR_VV00MMM }, | ||
| 1580 | { "vlde", 0xc4, INSTR_VRR_VV000MM }, | ||
| 1581 | { "vled", 0xc5, INSTR_VRR_VV00MMM }, | ||
| 1582 | { "vfm", 0xe7, INSTR_VRR_VVV00MM }, | ||
| 1583 | { "vfma", 0x8f, INSTR_VRR_VVVM0MV }, | ||
| 1584 | { "vfms", 0x8e, INSTR_VRR_VVVM0MV }, | ||
| 1585 | { "vfpso", 0xcc, INSTR_VRR_VV00MMM }, | ||
| 1586 | { "vfsq", 0xce, INSTR_VRR_VV000MM }, | ||
| 1587 | { "vfs", 0xe2, INSTR_VRR_VVV00MM }, | ||
| 1588 | { "vftci", 0x4a, INSTR_VRI_VVIMM }, | ||
| 1589 | #endif | ||
| 1590 | }; | ||
| 1591 | |||
| 1372 | static struct s390_insn opcode_eb[] = { | 1592 | static struct s390_insn opcode_eb[] = { |
| 1373 | #ifdef CONFIG_64BIT | 1593 | #ifdef CONFIG_64BIT |
| 1374 | { "lmg", 0x04, INSTR_RSY_RRRD }, | 1594 | { "lmg", 0x04, INSTR_RSY_RRRD }, |
| @@ -1552,16 +1772,17 @@ static struct s390_insn opcode_ed[] = { | |||
| 1552 | static unsigned int extract_operand(unsigned char *code, | 1772 | static unsigned int extract_operand(unsigned char *code, |
| 1553 | const struct s390_operand *operand) | 1773 | const struct s390_operand *operand) |
| 1554 | { | 1774 | { |
| 1775 | unsigned char *cp; | ||
| 1555 | unsigned int val; | 1776 | unsigned int val; |
| 1556 | int bits; | 1777 | int bits; |
| 1557 | 1778 | ||
| 1558 | /* Extract fragments of the operand byte for byte. */ | 1779 | /* Extract fragments of the operand byte for byte. */ |
| 1559 | code += operand->shift / 8; | 1780 | cp = code + operand->shift / 8; |
| 1560 | bits = (operand->shift & 7) + operand->bits; | 1781 | bits = (operand->shift & 7) + operand->bits; |
| 1561 | val = 0; | 1782 | val = 0; |
| 1562 | do { | 1783 | do { |
| 1563 | val <<= 8; | 1784 | val <<= 8; |
| 1564 | val |= (unsigned int) *code++; | 1785 | val |= (unsigned int) *cp++; |
| 1565 | bits -= 8; | 1786 | bits -= 8; |
| 1566 | } while (bits > 0); | 1787 | } while (bits > 0); |
| 1567 | val >>= -bits; | 1788 | val >>= -bits; |
| @@ -1571,6 +1792,18 @@ static unsigned int extract_operand(unsigned char *code, | |||
| 1571 | if (operand->bits == 20 && operand->shift == 20) | 1792 | if (operand->bits == 20 && operand->shift == 20) |
| 1572 | val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; | 1793 | val = (val & 0xff) << 12 | (val & 0xfff00) >> 8; |
| 1573 | 1794 | ||
| 1795 | /* Check for register extensions bits for vector registers. */ | ||
| 1796 | if (operand->flags & OPERAND_VR) { | ||
| 1797 | if (operand->shift == 8) | ||
| 1798 | val |= (code[4] & 8) << 1; | ||
| 1799 | else if (operand->shift == 12) | ||
| 1800 | val |= (code[4] & 4) << 2; | ||
| 1801 | else if (operand->shift == 16) | ||
| 1802 | val |= (code[4] & 2) << 3; | ||
| 1803 | else if (operand->shift == 32) | ||
| 1804 | val |= (code[4] & 1) << 4; | ||
| 1805 | } | ||
| 1806 | |||
| 1574 | /* Sign extend value if the operand is signed or pc relative. */ | 1807 | /* Sign extend value if the operand is signed or pc relative. */ |
| 1575 | if ((operand->flags & (OPERAND_SIGNED | OPERAND_PCREL)) && | 1808 | if ((operand->flags & (OPERAND_SIGNED | OPERAND_PCREL)) && |
| 1576 | (val & (1U << (operand->bits - 1)))) | 1809 | (val & (1U << (operand->bits - 1)))) |
| @@ -1639,6 +1872,10 @@ struct s390_insn *find_insn(unsigned char *code) | |||
| 1639 | case 0xe5: | 1872 | case 0xe5: |
| 1640 | table = opcode_e5; | 1873 | table = opcode_e5; |
| 1641 | break; | 1874 | break; |
| 1875 | case 0xe7: | ||
| 1876 | table = opcode_e7; | ||
| 1877 | opfrag = code[5]; | ||
| 1878 | break; | ||
| 1642 | case 0xeb: | 1879 | case 0xeb: |
| 1643 | table = opcode_eb; | 1880 | table = opcode_eb; |
| 1644 | opfrag = code[5]; | 1881 | opfrag = code[5]; |
| @@ -1734,6 +1971,8 @@ static int print_insn(char *buffer, unsigned char *code, unsigned long addr) | |||
| 1734 | ptr += sprintf(ptr, "%%a%i", value); | 1971 | ptr += sprintf(ptr, "%%a%i", value); |
| 1735 | else if (operand->flags & OPERAND_CR) | 1972 | else if (operand->flags & OPERAND_CR) |
| 1736 | ptr += sprintf(ptr, "%%c%i", value); | 1973 | ptr += sprintf(ptr, "%%c%i", value); |
| 1974 | else if (operand->flags & OPERAND_VR) | ||
| 1975 | ptr += sprintf(ptr, "%%v%i", value); | ||
| 1737 | else if (operand->flags & OPERAND_PCREL) | 1976 | else if (operand->flags & OPERAND_PCREL) |
| 1738 | ptr += sprintf(ptr, "%lx", (signed int) value | 1977 | ptr += sprintf(ptr, "%lx", (signed int) value |
| 1739 | + addr); | 1978 | + addr); |
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c index 0dff972a169c..cef2879edff3 100644 --- a/arch/s390/kernel/early.c +++ b/arch/s390/kernel/early.c | |||
| @@ -390,10 +390,10 @@ static __init void detect_machine_facilities(void) | |||
| 390 | S390_lowcore.machine_flags |= MACHINE_FLAG_LPP; | 390 | S390_lowcore.machine_flags |= MACHINE_FLAG_LPP; |
| 391 | if (test_facility(50) && test_facility(73)) | 391 | if (test_facility(50) && test_facility(73)) |
| 392 | S390_lowcore.machine_flags |= MACHINE_FLAG_TE; | 392 | S390_lowcore.machine_flags |= MACHINE_FLAG_TE; |
| 393 | if (test_facility(66)) | ||
| 394 | S390_lowcore.machine_flags |= MACHINE_FLAG_RRBM; | ||
| 395 | if (test_facility(51)) | 393 | if (test_facility(51)) |
| 396 | S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC; | 394 | S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC; |
| 395 | if (test_facility(129)) | ||
| 396 | S390_lowcore.machine_flags |= MACHINE_FLAG_VX; | ||
| 397 | #endif | 397 | #endif |
| 398 | } | 398 | } |
| 399 | 399 | ||
diff --git a/arch/s390/kernel/entry.h b/arch/s390/kernel/entry.h index 1aad48398d06..0554b9771c9f 100644 --- a/arch/s390/kernel/entry.h +++ b/arch/s390/kernel/entry.h | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
| 5 | #include <linux/signal.h> | 5 | #include <linux/signal.h> |
| 6 | #include <asm/ptrace.h> | 6 | #include <asm/ptrace.h> |
| 7 | #include <asm/cputime.h> | 7 | #include <asm/idle.h> |
| 8 | 8 | ||
| 9 | extern void *restart_stack; | 9 | extern void *restart_stack; |
| 10 | extern unsigned long suspend_zero_pages; | 10 | extern unsigned long suspend_zero_pages; |
| @@ -21,6 +21,8 @@ void psw_idle(struct s390_idle_data *, unsigned long); | |||
| 21 | asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); | 21 | asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); |
| 22 | asmlinkage void do_syscall_trace_exit(struct pt_regs *regs); | 22 | asmlinkage void do_syscall_trace_exit(struct pt_regs *regs); |
| 23 | 23 | ||
| 24 | int alloc_vector_registers(struct task_struct *tsk); | ||
| 25 | |||
| 24 | void do_protection_exception(struct pt_regs *regs); | 26 | void do_protection_exception(struct pt_regs *regs); |
| 25 | void do_dat_exception(struct pt_regs *regs); | 27 | void do_dat_exception(struct pt_regs *regs); |
| 26 | 28 | ||
| @@ -43,8 +45,10 @@ void special_op_exception(struct pt_regs *regs); | |||
| 43 | void specification_exception(struct pt_regs *regs); | 45 | void specification_exception(struct pt_regs *regs); |
| 44 | void transaction_exception(struct pt_regs *regs); | 46 | void transaction_exception(struct pt_regs *regs); |
| 45 | void translation_exception(struct pt_regs *regs); | 47 | void translation_exception(struct pt_regs *regs); |
| 48 | void vector_exception(struct pt_regs *regs); | ||
| 46 | 49 | ||
| 47 | void do_per_trap(struct pt_regs *regs); | 50 | void do_per_trap(struct pt_regs *regs); |
| 51 | void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str); | ||
| 48 | void syscall_trace(struct pt_regs *regs, int entryexit); | 52 | void syscall_trace(struct pt_regs *regs, int entryexit); |
| 49 | void kernel_stack_overflow(struct pt_regs * regs); | 53 | void kernel_stack_overflow(struct pt_regs * regs); |
| 50 | void do_signal(struct pt_regs *regs); | 54 | void do_signal(struct pt_regs *regs); |
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index f2e674c702e1..7b2e03afd017 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S | |||
| @@ -42,7 +42,8 @@ STACK_SHIFT = PAGE_SHIFT + THREAD_ORDER | |||
| 42 | STACK_SIZE = 1 << STACK_SHIFT | 42 | STACK_SIZE = 1 << STACK_SHIFT |
| 43 | STACK_INIT = STACK_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE | 43 | STACK_INIT = STACK_SIZE - STACK_FRAME_OVERHEAD - __PT_SIZE |
| 44 | 44 | ||
| 45 | _TIF_WORK = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED) | 45 | _TIF_WORK = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \ |
| 46 | _TIF_UPROBE) | ||
| 46 | _TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \ | 47 | _TIF_TRACE = (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SECCOMP | \ |
| 47 | _TIF_SYSCALL_TRACEPOINT) | 48 | _TIF_SYSCALL_TRACEPOINT) |
| 48 | _CIF_WORK = (_CIF_MCCK_PENDING | _CIF_ASCE) | 49 | _CIF_WORK = (_CIF_MCCK_PENDING | _CIF_ASCE) |
| @@ -265,6 +266,10 @@ sysc_work: | |||
| 265 | jo sysc_mcck_pending | 266 | jo sysc_mcck_pending |
| 266 | tm __TI_flags+7(%r12),_TIF_NEED_RESCHED | 267 | tm __TI_flags+7(%r12),_TIF_NEED_RESCHED |
| 267 | jo sysc_reschedule | 268 | jo sysc_reschedule |
| 269 | #ifdef CONFIG_UPROBES | ||
| 270 | tm __TI_flags+7(%r12),_TIF_UPROBE | ||
| 271 | jo sysc_uprobe_notify | ||
| 272 | #endif | ||
| 268 | tm __PT_FLAGS+7(%r11),_PIF_PER_TRAP | 273 | tm __PT_FLAGS+7(%r11),_PIF_PER_TRAP |
| 269 | jo sysc_singlestep | 274 | jo sysc_singlestep |
| 270 | tm __TI_flags+7(%r12),_TIF_SIGPENDING | 275 | tm __TI_flags+7(%r12),_TIF_SIGPENDING |
| @@ -323,6 +328,16 @@ sysc_notify_resume: | |||
| 323 | jg do_notify_resume | 328 | jg do_notify_resume |
| 324 | 329 | ||
| 325 | # | 330 | # |
| 331 | # _TIF_UPROBE is set, call uprobe_notify_resume | ||
| 332 | # | ||
| 333 | #ifdef CONFIG_UPROBES | ||
| 334 | sysc_uprobe_notify: | ||
| 335 | lgr %r2,%r11 # pass pointer to pt_regs | ||
| 336 | larl %r14,sysc_return | ||
| 337 | jg uprobe_notify_resume | ||
| 338 | #endif | ||
| 339 | |||
| 340 | # | ||
| 326 | # _PIF_PER_TRAP is set, call do_per_trap | 341 | # _PIF_PER_TRAP is set, call do_per_trap |
| 327 | # | 342 | # |
| 328 | sysc_singlestep: | 343 | sysc_singlestep: |
diff --git a/arch/s390/kernel/ftrace.c b/arch/s390/kernel/ftrace.c index 54d6493c4a56..51d14fe5eb9a 100644 --- a/arch/s390/kernel/ftrace.c +++ b/arch/s390/kernel/ftrace.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * Dynamic function tracer architecture backend. | 2 | * Dynamic function tracer architecture backend. |
| 3 | * | 3 | * |
| 4 | * Copyright IBM Corp. 2009 | 4 | * Copyright IBM Corp. 2009,2014 |
| 5 | * | 5 | * |
| 6 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, | 6 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, |
| 7 | * Martin Schwidefsky <schwidefsky@de.ibm.com> | 7 | * Martin Schwidefsky <schwidefsky@de.ibm.com> |
| @@ -17,100 +17,76 @@ | |||
| 17 | #include <asm/asm-offsets.h> | 17 | #include <asm/asm-offsets.h> |
| 18 | #include "entry.h" | 18 | #include "entry.h" |
| 19 | 19 | ||
| 20 | #ifdef CONFIG_DYNAMIC_FTRACE | 20 | void mcount_replace_code(void); |
| 21 | |||
| 22 | void ftrace_disable_code(void); | 21 | void ftrace_disable_code(void); |
| 23 | void ftrace_enable_insn(void); | 22 | void ftrace_enable_insn(void); |
| 24 | 23 | ||
| 25 | #ifdef CONFIG_64BIT | ||
| 26 | /* | 24 | /* |
| 27 | * The 64-bit mcount code looks like this: | 25 | * The mcount code looks like this: |
| 28 | * stg %r14,8(%r15) # offset 0 | 26 | * stg %r14,8(%r15) # offset 0 |
| 29 | * > larl %r1,<&counter> # offset 6 | 27 | * larl %r1,<&counter> # offset 6 |
| 30 | * > brasl %r14,_mcount # offset 12 | 28 | * brasl %r14,_mcount # offset 12 |
| 31 | * lg %r14,8(%r15) # offset 18 | 29 | * lg %r14,8(%r15) # offset 18 |
| 32 | * Total length is 24 bytes. The middle two instructions of the mcount | 30 | * Total length is 24 bytes. The complete mcount block initially gets replaced |
| 33 | * block get overwritten by ftrace_make_nop / ftrace_make_call. | 31 | * by ftrace_make_nop. Subsequent calls to ftrace_make_call / ftrace_make_nop |
| 34 | * The 64-bit enabled ftrace code block looks like this: | 32 | * only patch the jg/lg instruction within the block. |
| 35 | * stg %r14,8(%r15) # offset 0 | 33 | * Note: we do not patch the first instruction to an unconditional branch, |
| 34 | * since that would break kprobes/jprobes. It is easier to leave the larl | ||
| 35 | * instruction in and only modify the second instruction. | ||
| 36 | * The enabled ftrace code block looks like this: | ||
| 37 | * larl %r0,.+24 # offset 0 | ||
| 36 | * > lg %r1,__LC_FTRACE_FUNC # offset 6 | 38 | * > lg %r1,__LC_FTRACE_FUNC # offset 6 |
| 37 | * > lgr %r0,%r0 # offset 12 | 39 | * br %r1 # offset 12 |
| 38 | * > basr %r14,%r1 # offset 16 | 40 | * brcl 0,0 # offset 14 |
| 39 | * lg %r14,8(%15) # offset 18 | 41 | * brc 0,0 # offset 20 |
| 40 | * The return points of the mcount/ftrace function have the same offset 18. | 42 | * The ftrace function gets called with a non-standard C function call ABI |
| 41 | * The 64-bit disable ftrace code block looks like this: | 43 | * where r0 contains the return address. It is also expected that the called |
| 42 | * stg %r14,8(%r15) # offset 0 | 44 | * function only clobbers r0 and r1, but restores r2-r15. |
| 45 | * The return point of the ftrace function has offset 24, so execution | ||
| 46 | * continues behind the mcount block. | ||
| 47 | * larl %r0,.+24 # offset 0 | ||
| 43 | * > jg .+18 # offset 6 | 48 | * > jg .+18 # offset 6 |
| 44 | * > lgr %r0,%r0 # offset 12 | 49 | * br %r1 # offset 12 |
| 45 | * > basr %r14,%r1 # offset 16 | 50 | * brcl 0,0 # offset 14 |
| 46 | * lg %r14,8(%15) # offset 18 | 51 | * brc 0,0 # offset 20 |
| 47 | * The jg instruction branches to offset 24 to skip as many instructions | 52 | * The jg instruction branches to offset 24 to skip as many instructions |
| 48 | * as possible. | 53 | * as possible. |
| 49 | */ | 54 | */ |
| 50 | asm( | 55 | asm( |
| 51 | " .align 4\n" | 56 | " .align 4\n" |
| 57 | "mcount_replace_code:\n" | ||
| 58 | " larl %r0,0f\n" | ||
| 52 | "ftrace_disable_code:\n" | 59 | "ftrace_disable_code:\n" |
| 53 | " jg 0f\n" | 60 | " jg 0f\n" |
| 54 | " lgr %r0,%r0\n" | 61 | " br %r1\n" |
| 55 | " basr %r14,%r1\n" | 62 | " brcl 0,0\n" |
| 63 | " brc 0,0\n" | ||
| 56 | "0:\n" | 64 | "0:\n" |
| 57 | " .align 4\n" | 65 | " .align 4\n" |
| 58 | "ftrace_enable_insn:\n" | 66 | "ftrace_enable_insn:\n" |
| 59 | " lg %r1,"__stringify(__LC_FTRACE_FUNC)"\n"); | 67 | " lg %r1,"__stringify(__LC_FTRACE_FUNC)"\n"); |
| 60 | 68 | ||
| 69 | #define MCOUNT_BLOCK_SIZE 24 | ||
| 70 | #define MCOUNT_INSN_OFFSET 6 | ||
| 61 | #define FTRACE_INSN_SIZE 6 | 71 | #define FTRACE_INSN_SIZE 6 |
| 62 | 72 | ||
| 63 | #else /* CONFIG_64BIT */ | 73 | int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, |
| 64 | /* | 74 | unsigned long addr) |
| 65 | * The 31-bit mcount code looks like this: | 75 | { |
| 66 | * st %r14,4(%r15) # offset 0 | 76 | return 0; |
| 67 | * > bras %r1,0f # offset 4 | 77 | } |
| 68 | * > .long _mcount # offset 8 | ||
| 69 | * > .long <&counter> # offset 12 | ||
| 70 | * > 0: l %r14,0(%r1) # offset 16 | ||
| 71 | * > l %r1,4(%r1) # offset 20 | ||
| 72 | * basr %r14,%r14 # offset 24 | ||
| 73 | * l %r14,4(%r15) # offset 26 | ||
| 74 | * Total length is 30 bytes. The twenty bytes starting from offset 4 | ||
| 75 | * to offset 24 get overwritten by ftrace_make_nop / ftrace_make_call. | ||
| 76 | * The 31-bit enabled ftrace code block looks like this: | ||
| 77 | * st %r14,4(%r15) # offset 0 | ||
| 78 | * > l %r14,__LC_FTRACE_FUNC # offset 4 | ||
| 79 | * > j 0f # offset 8 | ||
| 80 | * > .fill 12,1,0x07 # offset 12 | ||
| 81 | * 0: basr %r14,%r14 # offset 24 | ||
| 82 | * l %r14,4(%r14) # offset 26 | ||
| 83 | * The return points of the mcount/ftrace function have the same offset 26. | ||
| 84 | * The 31-bit disabled ftrace code block looks like this: | ||
| 85 | * st %r14,4(%r15) # offset 0 | ||
| 86 | * > j .+26 # offset 4 | ||
| 87 | * > j 0f # offset 8 | ||
| 88 | * > .fill 12,1,0x07 # offset 12 | ||
| 89 | * 0: basr %r14,%r14 # offset 24 | ||
| 90 | * l %r14,4(%r14) # offset 26 | ||
| 91 | * The j instruction branches to offset 30 to skip as many instructions | ||
| 92 | * as possible. | ||
| 93 | */ | ||
| 94 | asm( | ||
| 95 | " .align 4\n" | ||
| 96 | "ftrace_disable_code:\n" | ||
| 97 | " j 1f\n" | ||
| 98 | " j 0f\n" | ||
| 99 | " .fill 12,1,0x07\n" | ||
| 100 | "0: basr %r14,%r14\n" | ||
| 101 | "1:\n" | ||
| 102 | " .align 4\n" | ||
| 103 | "ftrace_enable_insn:\n" | ||
| 104 | " l %r14,"__stringify(__LC_FTRACE_FUNC)"\n"); | ||
| 105 | |||
| 106 | #define FTRACE_INSN_SIZE 4 | ||
| 107 | |||
| 108 | #endif /* CONFIG_64BIT */ | ||
| 109 | |||
| 110 | 78 | ||
| 111 | int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, | 79 | int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, |
| 112 | unsigned long addr) | 80 | unsigned long addr) |
| 113 | { | 81 | { |
| 82 | /* Initial replacement of the whole mcount block */ | ||
| 83 | if (addr == MCOUNT_ADDR) { | ||
| 84 | if (probe_kernel_write((void *) rec->ip - MCOUNT_INSN_OFFSET, | ||
| 85 | mcount_replace_code, | ||
| 86 | MCOUNT_BLOCK_SIZE)) | ||
| 87 | return -EPERM; | ||
| 88 | return 0; | ||
| 89 | } | ||
| 114 | if (probe_kernel_write((void *) rec->ip, ftrace_disable_code, | 90 | if (probe_kernel_write((void *) rec->ip, ftrace_disable_code, |
| 115 | MCOUNT_INSN_SIZE)) | 91 | MCOUNT_INSN_SIZE)) |
| 116 | return -EPERM; | 92 | return -EPERM; |
| @@ -135,8 +111,6 @@ int __init ftrace_dyn_arch_init(void) | |||
| 135 | return 0; | 111 | return 0; |
| 136 | } | 112 | } |
| 137 | 113 | ||
| 138 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
| 139 | |||
| 140 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 114 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 141 | /* | 115 | /* |
| 142 | * Hook the return address and push it in the stack of return addresses | 116 | * Hook the return address and push it in the stack of return addresses |
| @@ -162,31 +136,26 @@ out: | |||
| 162 | return parent; | 136 | return parent; |
| 163 | } | 137 | } |
| 164 | 138 | ||
| 165 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 166 | /* | 139 | /* |
| 167 | * Patch the kernel code at ftrace_graph_caller location. The instruction | 140 | * Patch the kernel code at ftrace_graph_caller location. The instruction |
| 168 | * there is branch relative and save to prepare_ftrace_return. To disable | 141 | * there is branch relative on condition. To enable the ftrace graph code |
| 169 | * the call to prepare_ftrace_return we patch the bras offset to point | 142 | * block, we simply patch the mask field of the instruction to zero and |
| 170 | * directly after the instructions. To enable the call we calculate | 143 | * turn the instruction into a nop. |
| 171 | * the original offset to prepare_ftrace_return and put it back. | 144 | * To disable the ftrace graph code the mask field will be patched to |
| 145 | * all ones, which turns the instruction into an unconditional branch. | ||
| 172 | */ | 146 | */ |
| 173 | int ftrace_enable_ftrace_graph_caller(void) | 147 | int ftrace_enable_ftrace_graph_caller(void) |
| 174 | { | 148 | { |
| 175 | unsigned short offset; | 149 | u8 op = 0x04; /* set mask field to zero */ |
| 176 | 150 | ||
| 177 | offset = ((void *) prepare_ftrace_return - | 151 | return probe_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op)); |
| 178 | (void *) ftrace_graph_caller) / 2; | ||
| 179 | return probe_kernel_write((void *) ftrace_graph_caller + 2, | ||
| 180 | &offset, sizeof(offset)); | ||
| 181 | } | 152 | } |
| 182 | 153 | ||
| 183 | int ftrace_disable_ftrace_graph_caller(void) | 154 | int ftrace_disable_ftrace_graph_caller(void) |
| 184 | { | 155 | { |
| 185 | static unsigned short offset = 0x0002; | 156 | u8 op = 0xf4; /* set mask field to all ones */ |
| 186 | 157 | ||
| 187 | return probe_kernel_write((void *) ftrace_graph_caller + 2, | 158 | return probe_kernel_write(__va(ftrace_graph_caller)+1, &op, sizeof(op)); |
| 188 | &offset, sizeof(offset)); | ||
| 189 | } | 159 | } |
| 190 | 160 | ||
| 191 | #endif /* CONFIG_DYNAMIC_FTRACE */ | ||
| 192 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ | 161 | #endif /* CONFIG_FUNCTION_GRAPH_TRACER */ |
diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S index e88d35d74950..d62eee11f0b5 100644 --- a/arch/s390/kernel/head.S +++ b/arch/s390/kernel/head.S | |||
| @@ -398,7 +398,7 @@ ENTRY(startup_kdump) | |||
| 398 | xc __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST | 398 | xc __LC_STFL_FAC_LIST(8),__LC_STFL_FAC_LIST |
| 399 | #ifndef CONFIG_MARCH_G5 | 399 | #ifndef CONFIG_MARCH_G5 |
| 400 | # check capabilities against MARCH_{G5,Z900,Z990,Z9_109,Z10} | 400 | # check capabilities against MARCH_{G5,Z900,Z990,Z9_109,Z10} |
| 401 | .insn s,0xb2b10000,__LC_STFL_FAC_LIST # store facility list | 401 | .insn s,0xb2b10000,0 # store facilities @ __LC_STFL_FAC_LIST |
| 402 | tm __LC_STFL_FAC_LIST,0x01 # stfle available ? | 402 | tm __LC_STFL_FAC_LIST,0x01 # stfle available ? |
| 403 | jz 0f | 403 | jz 0f |
| 404 | la %r0,1 | 404 | la %r0,1 |
diff --git a/arch/s390/kernel/idle.c b/arch/s390/kernel/idle.c new file mode 100644 index 000000000000..c846aee7372f --- /dev/null +++ b/arch/s390/kernel/idle.c | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | /* | ||
| 2 | * Idle functions for s390. | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2014 | ||
| 5 | * | ||
| 6 | * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/kernel.h> | ||
| 10 | #include <linux/kernel_stat.h> | ||
| 11 | #include <linux/kprobes.h> | ||
| 12 | #include <linux/notifier.h> | ||
| 13 | #include <linux/init.h> | ||
| 14 | #include <linux/cpu.h> | ||
| 15 | #include <asm/cputime.h> | ||
| 16 | #include <asm/nmi.h> | ||
| 17 | #include <asm/smp.h> | ||
| 18 | #include "entry.h" | ||
| 19 | |||
| 20 | static DEFINE_PER_CPU(struct s390_idle_data, s390_idle); | ||
| 21 | |||
| 22 | void __kprobes enabled_wait(void) | ||
| 23 | { | ||
| 24 | struct s390_idle_data *idle = &__get_cpu_var(s390_idle); | ||
| 25 | unsigned long long idle_time; | ||
| 26 | unsigned long psw_mask; | ||
| 27 | |||
| 28 | trace_hardirqs_on(); | ||
| 29 | |||
| 30 | /* Wait for external, I/O or machine check interrupt. */ | ||
| 31 | psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT | | ||
| 32 | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; | ||
| 33 | clear_cpu_flag(CIF_NOHZ_DELAY); | ||
| 34 | |||
| 35 | /* Call the assembler magic in entry.S */ | ||
| 36 | psw_idle(idle, psw_mask); | ||
| 37 | |||
| 38 | /* Account time spent with enabled wait psw loaded as idle time. */ | ||
| 39 | idle->sequence++; | ||
| 40 | smp_wmb(); | ||
| 41 | idle_time = idle->clock_idle_exit - idle->clock_idle_enter; | ||
| 42 | idle->clock_idle_enter = idle->clock_idle_exit = 0ULL; | ||
| 43 | idle->idle_time += idle_time; | ||
| 44 | idle->idle_count++; | ||
| 45 | account_idle_time(idle_time); | ||
| 46 | smp_wmb(); | ||
| 47 | idle->sequence++; | ||
| 48 | } | ||
| 49 | |||
| 50 | static ssize_t show_idle_count(struct device *dev, | ||
| 51 | struct device_attribute *attr, char *buf) | ||
| 52 | { | ||
| 53 | struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); | ||
| 54 | unsigned long long idle_count; | ||
| 55 | unsigned int sequence; | ||
| 56 | |||
| 57 | do { | ||
| 58 | sequence = ACCESS_ONCE(idle->sequence); | ||
| 59 | idle_count = ACCESS_ONCE(idle->idle_count); | ||
| 60 | if (ACCESS_ONCE(idle->clock_idle_enter)) | ||
| 61 | idle_count++; | ||
| 62 | } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); | ||
| 63 | return sprintf(buf, "%llu\n", idle_count); | ||
| 64 | } | ||
| 65 | DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); | ||
| 66 | |||
| 67 | static ssize_t show_idle_time(struct device *dev, | ||
| 68 | struct device_attribute *attr, char *buf) | ||
| 69 | { | ||
| 70 | struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); | ||
| 71 | unsigned long long now, idle_time, idle_enter, idle_exit; | ||
| 72 | unsigned int sequence; | ||
| 73 | |||
| 74 | do { | ||
| 75 | now = get_tod_clock(); | ||
| 76 | sequence = ACCESS_ONCE(idle->sequence); | ||
| 77 | idle_time = ACCESS_ONCE(idle->idle_time); | ||
| 78 | idle_enter = ACCESS_ONCE(idle->clock_idle_enter); | ||
| 79 | idle_exit = ACCESS_ONCE(idle->clock_idle_exit); | ||
| 80 | } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); | ||
| 81 | idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0; | ||
| 82 | return sprintf(buf, "%llu\n", idle_time >> 12); | ||
| 83 | } | ||
| 84 | DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); | ||
| 85 | |||
| 86 | cputime64_t arch_cpu_idle_time(int cpu) | ||
| 87 | { | ||
| 88 | struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); | ||
| 89 | unsigned long long now, idle_enter, idle_exit; | ||
| 90 | unsigned int sequence; | ||
| 91 | |||
| 92 | do { | ||
| 93 | now = get_tod_clock(); | ||
| 94 | sequence = ACCESS_ONCE(idle->sequence); | ||
| 95 | idle_enter = ACCESS_ONCE(idle->clock_idle_enter); | ||
| 96 | idle_exit = ACCESS_ONCE(idle->clock_idle_exit); | ||
| 97 | } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); | ||
| 98 | return idle_enter ? ((idle_exit ?: now) - idle_enter) : 0; | ||
| 99 | } | ||
| 100 | |||
| 101 | void arch_cpu_idle_enter(void) | ||
| 102 | { | ||
| 103 | local_mcck_disable(); | ||
| 104 | } | ||
| 105 | |||
| 106 | void arch_cpu_idle(void) | ||
| 107 | { | ||
| 108 | if (!test_cpu_flag(CIF_MCCK_PENDING)) | ||
| 109 | /* Halt the cpu and keep track of cpu time accounting. */ | ||
| 110 | enabled_wait(); | ||
| 111 | local_irq_enable(); | ||
| 112 | } | ||
| 113 | |||
| 114 | void arch_cpu_idle_exit(void) | ||
| 115 | { | ||
| 116 | local_mcck_enable(); | ||
| 117 | if (test_cpu_flag(CIF_MCCK_PENDING)) | ||
| 118 | s390_handle_mcck(); | ||
| 119 | } | ||
| 120 | |||
| 121 | void arch_cpu_idle_dead(void) | ||
| 122 | { | ||
| 123 | cpu_die(); | ||
| 124 | } | ||
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 8eb82443cfbd..1b8a38ab7861 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c | |||
| @@ -70,6 +70,7 @@ static const struct irq_class irqclass_sub_desc[NR_ARCH_IRQS] = { | |||
| 70 | {.irq = IRQEXT_CMS, .name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"}, | 70 | {.irq = IRQEXT_CMS, .name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"}, |
| 71 | {.irq = IRQEXT_CMC, .name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"}, | 71 | {.irq = IRQEXT_CMC, .name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"}, |
| 72 | {.irq = IRQEXT_CMR, .name = "CMR", .desc = "[EXT] CPU-Measurement: RI"}, | 72 | {.irq = IRQEXT_CMR, .name = "CMR", .desc = "[EXT] CPU-Measurement: RI"}, |
| 73 | {.irq = IRQEXT_FTP, .name = "FTP", .desc = "[EXT] HMC FTP Service"}, | ||
| 73 | {.irq = IRQIO_CIO, .name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"}, | 74 | {.irq = IRQIO_CIO, .name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"}, |
| 74 | {.irq = IRQIO_QAI, .name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"}, | 75 | {.irq = IRQIO_QAI, .name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"}, |
| 75 | {.irq = IRQIO_DAS, .name = "DAS", .desc = "[I/O] DASD"}, | 76 | {.irq = IRQIO_DAS, .name = "DAS", .desc = "[I/O] DASD"}, |
| @@ -258,7 +259,7 @@ static irqreturn_t do_ext_interrupt(int irq, void *dummy) | |||
| 258 | 259 | ||
| 259 | ext_code = *(struct ext_code *) ®s->int_code; | 260 | ext_code = *(struct ext_code *) ®s->int_code; |
| 260 | if (ext_code.code != EXT_IRQ_CLK_COMP) | 261 | if (ext_code.code != EXT_IRQ_CLK_COMP) |
| 261 | __get_cpu_var(s390_idle).nohz_delay = 1; | 262 | set_cpu_flag(CIF_NOHZ_DELAY); |
| 262 | 263 | ||
| 263 | index = ext_hash(ext_code.code); | 264 | index = ext_hash(ext_code.code); |
| 264 | rcu_read_lock(); | 265 | rcu_read_lock(); |
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c index bc71a7b95af5..27ae5433fe4d 100644 --- a/arch/s390/kernel/kprobes.c +++ b/arch/s390/kernel/kprobes.c | |||
| @@ -58,161 +58,13 @@ struct kprobe_insn_cache kprobe_dmainsn_slots = { | |||
| 58 | .insn_size = MAX_INSN_SIZE, | 58 | .insn_size = MAX_INSN_SIZE, |
| 59 | }; | 59 | }; |
| 60 | 60 | ||
| 61 | static int __kprobes is_prohibited_opcode(kprobe_opcode_t *insn) | ||
| 62 | { | ||
| 63 | if (!is_known_insn((unsigned char *)insn)) | ||
| 64 | return -EINVAL; | ||
| 65 | switch (insn[0] >> 8) { | ||
| 66 | case 0x0c: /* bassm */ | ||
| 67 | case 0x0b: /* bsm */ | ||
| 68 | case 0x83: /* diag */ | ||
| 69 | case 0x44: /* ex */ | ||
| 70 | case 0xac: /* stnsm */ | ||
| 71 | case 0xad: /* stosm */ | ||
| 72 | return -EINVAL; | ||
| 73 | case 0xc6: | ||
| 74 | switch (insn[0] & 0x0f) { | ||
| 75 | case 0x00: /* exrl */ | ||
| 76 | return -EINVAL; | ||
| 77 | } | ||
| 78 | } | ||
| 79 | switch (insn[0]) { | ||
| 80 | case 0x0101: /* pr */ | ||
| 81 | case 0xb25a: /* bsa */ | ||
| 82 | case 0xb240: /* bakr */ | ||
| 83 | case 0xb258: /* bsg */ | ||
| 84 | case 0xb218: /* pc */ | ||
| 85 | case 0xb228: /* pt */ | ||
| 86 | case 0xb98d: /* epsw */ | ||
| 87 | return -EINVAL; | ||
| 88 | } | ||
| 89 | return 0; | ||
| 90 | } | ||
| 91 | |||
| 92 | static int __kprobes get_fixup_type(kprobe_opcode_t *insn) | ||
| 93 | { | ||
| 94 | /* default fixup method */ | ||
| 95 | int fixup = FIXUP_PSW_NORMAL; | ||
| 96 | |||
| 97 | switch (insn[0] >> 8) { | ||
| 98 | case 0x05: /* balr */ | ||
| 99 | case 0x0d: /* basr */ | ||
| 100 | fixup = FIXUP_RETURN_REGISTER; | ||
| 101 | /* if r2 = 0, no branch will be taken */ | ||
| 102 | if ((insn[0] & 0x0f) == 0) | ||
| 103 | fixup |= FIXUP_BRANCH_NOT_TAKEN; | ||
| 104 | break; | ||
| 105 | case 0x06: /* bctr */ | ||
| 106 | case 0x07: /* bcr */ | ||
| 107 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 108 | break; | ||
| 109 | case 0x45: /* bal */ | ||
| 110 | case 0x4d: /* bas */ | ||
| 111 | fixup = FIXUP_RETURN_REGISTER; | ||
| 112 | break; | ||
| 113 | case 0x47: /* bc */ | ||
| 114 | case 0x46: /* bct */ | ||
| 115 | case 0x86: /* bxh */ | ||
| 116 | case 0x87: /* bxle */ | ||
| 117 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 118 | break; | ||
| 119 | case 0x82: /* lpsw */ | ||
| 120 | fixup = FIXUP_NOT_REQUIRED; | ||
| 121 | break; | ||
| 122 | case 0xb2: /* lpswe */ | ||
| 123 | if ((insn[0] & 0xff) == 0xb2) | ||
| 124 | fixup = FIXUP_NOT_REQUIRED; | ||
| 125 | break; | ||
| 126 | case 0xa7: /* bras */ | ||
| 127 | if ((insn[0] & 0x0f) == 0x05) | ||
| 128 | fixup |= FIXUP_RETURN_REGISTER; | ||
| 129 | break; | ||
| 130 | case 0xc0: | ||
| 131 | if ((insn[0] & 0x0f) == 0x05) /* brasl */ | ||
| 132 | fixup |= FIXUP_RETURN_REGISTER; | ||
| 133 | break; | ||
| 134 | case 0xeb: | ||
| 135 | switch (insn[2] & 0xff) { | ||
| 136 | case 0x44: /* bxhg */ | ||
| 137 | case 0x45: /* bxleg */ | ||
| 138 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 139 | break; | ||
| 140 | } | ||
| 141 | break; | ||
| 142 | case 0xe3: /* bctg */ | ||
| 143 | if ((insn[2] & 0xff) == 0x46) | ||
| 144 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 145 | break; | ||
| 146 | case 0xec: | ||
| 147 | switch (insn[2] & 0xff) { | ||
| 148 | case 0xe5: /* clgrb */ | ||
| 149 | case 0xe6: /* cgrb */ | ||
| 150 | case 0xf6: /* crb */ | ||
| 151 | case 0xf7: /* clrb */ | ||
| 152 | case 0xfc: /* cgib */ | ||
| 153 | case 0xfd: /* cglib */ | ||
| 154 | case 0xfe: /* cib */ | ||
| 155 | case 0xff: /* clib */ | ||
| 156 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 157 | break; | ||
| 158 | } | ||
| 159 | break; | ||
| 160 | } | ||
| 161 | return fixup; | ||
| 162 | } | ||
| 163 | |||
| 164 | static int __kprobes is_insn_relative_long(kprobe_opcode_t *insn) | ||
| 165 | { | ||
| 166 | /* Check if we have a RIL-b or RIL-c format instruction which | ||
| 167 | * we need to modify in order to avoid instruction emulation. */ | ||
| 168 | switch (insn[0] >> 8) { | ||
| 169 | case 0xc0: | ||
| 170 | if ((insn[0] & 0x0f) == 0x00) /* larl */ | ||
| 171 | return true; | ||
| 172 | break; | ||
| 173 | case 0xc4: | ||
| 174 | switch (insn[0] & 0x0f) { | ||
| 175 | case 0x02: /* llhrl */ | ||
| 176 | case 0x04: /* lghrl */ | ||
| 177 | case 0x05: /* lhrl */ | ||
| 178 | case 0x06: /* llghrl */ | ||
| 179 | case 0x07: /* sthrl */ | ||
| 180 | case 0x08: /* lgrl */ | ||
| 181 | case 0x0b: /* stgrl */ | ||
| 182 | case 0x0c: /* lgfrl */ | ||
| 183 | case 0x0d: /* lrl */ | ||
| 184 | case 0x0e: /* llgfrl */ | ||
| 185 | case 0x0f: /* strl */ | ||
| 186 | return true; | ||
| 187 | } | ||
| 188 | break; | ||
| 189 | case 0xc6: | ||
| 190 | switch (insn[0] & 0x0f) { | ||
| 191 | case 0x02: /* pfdrl */ | ||
| 192 | case 0x04: /* cghrl */ | ||
| 193 | case 0x05: /* chrl */ | ||
| 194 | case 0x06: /* clghrl */ | ||
| 195 | case 0x07: /* clhrl */ | ||
| 196 | case 0x08: /* cgrl */ | ||
| 197 | case 0x0a: /* clgrl */ | ||
| 198 | case 0x0c: /* cgfrl */ | ||
| 199 | case 0x0d: /* crl */ | ||
| 200 | case 0x0e: /* clgfrl */ | ||
| 201 | case 0x0f: /* clrl */ | ||
| 202 | return true; | ||
| 203 | } | ||
| 204 | break; | ||
| 205 | } | ||
| 206 | return false; | ||
| 207 | } | ||
| 208 | |||
| 209 | static void __kprobes copy_instruction(struct kprobe *p) | 61 | static void __kprobes copy_instruction(struct kprobe *p) |
| 210 | { | 62 | { |
| 211 | s64 disp, new_disp; | 63 | s64 disp, new_disp; |
| 212 | u64 addr, new_addr; | 64 | u64 addr, new_addr; |
| 213 | 65 | ||
| 214 | memcpy(p->ainsn.insn, p->addr, insn_length(p->opcode >> 8)); | 66 | memcpy(p->ainsn.insn, p->addr, insn_length(p->opcode >> 8)); |
| 215 | if (!is_insn_relative_long(p->ainsn.insn)) | 67 | if (!probe_is_insn_relative_long(p->ainsn.insn)) |
| 216 | return; | 68 | return; |
| 217 | /* | 69 | /* |
| 218 | * For pc-relative instructions in RIL-b or RIL-c format patch the | 70 | * For pc-relative instructions in RIL-b or RIL-c format patch the |
| @@ -276,7 +128,7 @@ int __kprobes arch_prepare_kprobe(struct kprobe *p) | |||
| 276 | if ((unsigned long) p->addr & 0x01) | 128 | if ((unsigned long) p->addr & 0x01) |
| 277 | return -EINVAL; | 129 | return -EINVAL; |
| 278 | /* Make sure the probe isn't going on a difficult instruction */ | 130 | /* Make sure the probe isn't going on a difficult instruction */ |
| 279 | if (is_prohibited_opcode(p->addr)) | 131 | if (probe_is_prohibited_opcode(p->addr)) |
| 280 | return -EINVAL; | 132 | return -EINVAL; |
| 281 | if (s390_get_insn_slot(p)) | 133 | if (s390_get_insn_slot(p)) |
| 282 | return -ENOMEM; | 134 | return -ENOMEM; |
| @@ -605,7 +457,7 @@ static void __kprobes resume_execution(struct kprobe *p, struct pt_regs *regs) | |||
| 605 | { | 457 | { |
| 606 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 458 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
| 607 | unsigned long ip = regs->psw.addr & PSW_ADDR_INSN; | 459 | unsigned long ip = regs->psw.addr & PSW_ADDR_INSN; |
| 608 | int fixup = get_fixup_type(p->ainsn.insn); | 460 | int fixup = probe_get_fixup_type(p->ainsn.insn); |
| 609 | 461 | ||
| 610 | if (fixup & FIXUP_PSW_NORMAL) | 462 | if (fixup & FIXUP_PSW_NORMAL) |
| 611 | ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn; | 463 | ip += (unsigned long) p->addr - (unsigned long) p->ainsn.insn; |
| @@ -789,11 +641,6 @@ void __kprobes jprobe_return(void) | |||
| 789 | asm volatile(".word 0x0002"); | 641 | asm volatile(".word 0x0002"); |
| 790 | } | 642 | } |
| 791 | 643 | ||
| 792 | static void __used __kprobes jprobe_return_end(void) | ||
| 793 | { | ||
| 794 | asm volatile("bcr 0,0"); | ||
| 795 | } | ||
| 796 | |||
| 797 | int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) | 644 | int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs) |
| 798 | { | 645 | { |
| 799 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); | 646 | struct kprobe_ctlblk *kcb = get_kprobe_ctlblk(); |
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c index 719e27b2cf22..4685337fa7c6 100644 --- a/arch/s390/kernel/machine_kexec.c +++ b/arch/s390/kernel/machine_kexec.c | |||
| @@ -25,6 +25,7 @@ | |||
| 25 | #include <asm/elf.h> | 25 | #include <asm/elf.h> |
| 26 | #include <asm/asm-offsets.h> | 26 | #include <asm/asm-offsets.h> |
| 27 | #include <asm/os_info.h> | 27 | #include <asm/os_info.h> |
| 28 | #include <asm/switch_to.h> | ||
| 28 | 29 | ||
| 29 | typedef void (*relocate_kernel_t)(kimage_entry_t *, unsigned long); | 30 | typedef void (*relocate_kernel_t)(kimage_entry_t *, unsigned long); |
| 30 | 31 | ||
| @@ -43,7 +44,7 @@ static void add_elf_notes(int cpu) | |||
| 43 | 44 | ||
| 44 | memcpy((void *) (4608UL + sa->pref_reg), sa, sizeof(*sa)); | 45 | memcpy((void *) (4608UL + sa->pref_reg), sa, sizeof(*sa)); |
| 45 | ptr = (u64 *) per_cpu_ptr(crash_notes, cpu); | 46 | ptr = (u64 *) per_cpu_ptr(crash_notes, cpu); |
| 46 | ptr = fill_cpu_elf_notes(ptr, sa); | 47 | ptr = fill_cpu_elf_notes(ptr, sa, NULL); |
| 47 | memset(ptr, 0, sizeof(struct elf_note)); | 48 | memset(ptr, 0, sizeof(struct elf_note)); |
| 48 | } | 49 | } |
| 49 | 50 | ||
| @@ -53,8 +54,11 @@ static void add_elf_notes(int cpu) | |||
| 53 | static void setup_regs(void) | 54 | static void setup_regs(void) |
| 54 | { | 55 | { |
| 55 | unsigned long sa = S390_lowcore.prefixreg_save_area + SAVE_AREA_BASE; | 56 | unsigned long sa = S390_lowcore.prefixreg_save_area + SAVE_AREA_BASE; |
| 57 | struct _lowcore *lc; | ||
| 56 | int cpu, this_cpu; | 58 | int cpu, this_cpu; |
| 57 | 59 | ||
| 60 | /* Get lowcore pointer from store status of this CPU (absolute zero) */ | ||
| 61 | lc = (struct _lowcore *)(unsigned long)S390_lowcore.prefixreg_save_area; | ||
| 58 | this_cpu = smp_find_processor_id(stap()); | 62 | this_cpu = smp_find_processor_id(stap()); |
| 59 | add_elf_notes(this_cpu); | 63 | add_elf_notes(this_cpu); |
| 60 | for_each_online_cpu(cpu) { | 64 | for_each_online_cpu(cpu) { |
| @@ -64,6 +68,8 @@ static void setup_regs(void) | |||
| 64 | continue; | 68 | continue; |
| 65 | add_elf_notes(cpu); | 69 | add_elf_notes(cpu); |
| 66 | } | 70 | } |
| 71 | if (MACHINE_HAS_VX) | ||
| 72 | save_vx_regs_safe((void *) lc->vector_save_area_addr); | ||
| 67 | /* Copy dump CPU store status info to absolute zero */ | 73 | /* Copy dump CPU store status info to absolute zero */ |
| 68 | memcpy((void *) SAVE_AREA_BASE, (void *) sa, sizeof(struct save_area)); | 74 | memcpy((void *) SAVE_AREA_BASE, (void *) sa, sizeof(struct save_area)); |
| 69 | } | 75 | } |
diff --git a/arch/s390/kernel/mcount.S b/arch/s390/kernel/mcount.S index 433c6dbfa442..4300ea374826 100644 --- a/arch/s390/kernel/mcount.S +++ b/arch/s390/kernel/mcount.S | |||
| @@ -8,62 +8,72 @@ | |||
| 8 | #include <linux/linkage.h> | 8 | #include <linux/linkage.h> |
| 9 | #include <asm/asm-offsets.h> | 9 | #include <asm/asm-offsets.h> |
| 10 | #include <asm/ftrace.h> | 10 | #include <asm/ftrace.h> |
| 11 | #include <asm/ptrace.h> | ||
| 11 | 12 | ||
| 12 | .section .kprobes.text, "ax" | 13 | .section .kprobes.text, "ax" |
| 13 | 14 | ||
| 14 | ENTRY(ftrace_stub) | 15 | ENTRY(ftrace_stub) |
| 15 | br %r14 | 16 | br %r14 |
| 16 | 17 | ||
| 18 | #define STACK_FRAME_SIZE (STACK_FRAME_OVERHEAD + __PT_SIZE) | ||
| 19 | #define STACK_PTREGS (STACK_FRAME_OVERHEAD) | ||
| 20 | #define STACK_PTREGS_GPRS (STACK_PTREGS + __PT_GPRS) | ||
| 21 | #define STACK_PTREGS_PSW (STACK_PTREGS + __PT_PSW) | ||
| 22 | |||
| 17 | ENTRY(_mcount) | 23 | ENTRY(_mcount) |
| 18 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 19 | br %r14 | 24 | br %r14 |
| 20 | 25 | ||
| 21 | ENTRY(ftrace_caller) | 26 | ENTRY(ftrace_caller) |
| 27 | .globl ftrace_regs_caller | ||
| 28 | .set ftrace_regs_caller,ftrace_caller | ||
| 29 | lgr %r1,%r15 | ||
| 30 | aghi %r15,-STACK_FRAME_SIZE | ||
| 31 | stg %r1,__SF_BACKCHAIN(%r15) | ||
| 32 | stg %r1,(STACK_PTREGS_GPRS+15*8)(%r15) | ||
| 33 | stg %r0,(STACK_PTREGS_PSW+8)(%r15) | ||
| 34 | stmg %r2,%r14,(STACK_PTREGS_GPRS+2*8)(%r15) | ||
| 35 | #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES | ||
| 36 | aghik %r2,%r0,-MCOUNT_INSN_SIZE | ||
| 37 | lgrl %r4,function_trace_op | ||
| 38 | lgrl %r1,ftrace_trace_function | ||
| 39 | #else | ||
| 40 | lgr %r2,%r0 | ||
| 41 | aghi %r2,-MCOUNT_INSN_SIZE | ||
| 42 | larl %r4,function_trace_op | ||
| 43 | lg %r4,0(%r4) | ||
| 44 | larl %r1,ftrace_trace_function | ||
| 45 | lg %r1,0(%r1) | ||
| 22 | #endif | 46 | #endif |
| 23 | stm %r2,%r5,16(%r15) | 47 | lgr %r3,%r14 |
| 24 | bras %r1,1f | 48 | la %r5,STACK_PTREGS(%r15) |
| 25 | 0: .long ftrace_trace_function | 49 | basr %r14,%r1 |
| 26 | 1: st %r14,56(%r15) | ||
| 27 | lr %r0,%r15 | ||
| 28 | ahi %r15,-96 | ||
| 29 | l %r3,100(%r15) | ||
| 30 | la %r2,0(%r14) | ||
| 31 | st %r0,__SF_BACKCHAIN(%r15) | ||
| 32 | la %r3,0(%r3) | ||
| 33 | ahi %r2,-MCOUNT_INSN_SIZE | ||
| 34 | l %r14,0b-0b(%r1) | ||
| 35 | l %r14,0(%r14) | ||
| 36 | basr %r14,%r14 | ||
| 37 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 50 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 38 | l %r2,100(%r15) | 51 | # The j instruction gets runtime patched to a nop instruction. |
| 39 | l %r3,152(%r15) | 52 | # See ftrace_enable_ftrace_graph_caller. |
| 40 | ENTRY(ftrace_graph_caller) | 53 | ENTRY(ftrace_graph_caller) |
| 41 | # The bras instruction gets runtime patched to call prepare_ftrace_return. | 54 | j ftrace_graph_caller_end |
| 42 | # See ftrace_enable_ftrace_graph_caller. The patched instruction is: | 55 | lg %r2,(STACK_PTREGS_GPRS+14*8)(%r15) |
| 43 | # bras %r14,prepare_ftrace_return | 56 | lg %r3,(STACK_PTREGS_PSW+8)(%r15) |
| 44 | bras %r14,0f | 57 | brasl %r14,prepare_ftrace_return |
| 45 | 0: st %r2,100(%r15) | 58 | stg %r2,(STACK_PTREGS_GPRS+14*8)(%r15) |
| 59 | ftrace_graph_caller_end: | ||
| 60 | .globl ftrace_graph_caller_end | ||
| 46 | #endif | 61 | #endif |
| 47 | ahi %r15,96 | 62 | lg %r1,(STACK_PTREGS_PSW+8)(%r15) |
| 48 | l %r14,56(%r15) | 63 | lmg %r2,%r15,(STACK_PTREGS_GPRS+2*8)(%r15) |
| 49 | lm %r2,%r5,16(%r15) | 64 | br %r1 |
| 50 | br %r14 | ||
| 51 | 65 | ||
| 52 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | 66 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER |
| 53 | 67 | ||
| 54 | ENTRY(return_to_handler) | 68 | ENTRY(return_to_handler) |
| 55 | stm %r2,%r5,16(%r15) | 69 | stmg %r2,%r5,32(%r15) |
| 56 | st %r14,56(%r15) | 70 | lgr %r1,%r15 |
| 57 | lr %r0,%r15 | 71 | aghi %r15,-STACK_FRAME_OVERHEAD |
| 58 | ahi %r15,-96 | 72 | stg %r1,__SF_BACKCHAIN(%r15) |
| 59 | st %r0,__SF_BACKCHAIN(%r15) | 73 | brasl %r14,ftrace_return_to_handler |
| 60 | bras %r1,0f | 74 | aghi %r15,STACK_FRAME_OVERHEAD |
| 61 | .long ftrace_return_to_handler | 75 | lgr %r14,%r2 |
| 62 | 0: l %r2,0b-0b(%r1) | 76 | lmg %r2,%r5,32(%r15) |
| 63 | basr %r14,%r2 | ||
| 64 | lr %r14,%r2 | ||
| 65 | ahi %r15,96 | ||
| 66 | lm %r2,%r5,16(%r15) | ||
| 67 | br %r14 | 77 | br %r14 |
| 68 | 78 | ||
| 69 | #endif | 79 | #endif |
diff --git a/arch/s390/kernel/mcount64.S b/arch/s390/kernel/mcount64.S deleted file mode 100644 index c67a8bf0fd9a..000000000000 --- a/arch/s390/kernel/mcount64.S +++ /dev/null | |||
| @@ -1,62 +0,0 @@ | |||
| 1 | /* | ||
| 2 | * Copyright IBM Corp. 2008, 2009 | ||
| 3 | * | ||
| 4 | * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>, | ||
| 5 | * | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/linkage.h> | ||
| 9 | #include <asm/asm-offsets.h> | ||
| 10 | #include <asm/ftrace.h> | ||
| 11 | |||
| 12 | .section .kprobes.text, "ax" | ||
| 13 | |||
| 14 | ENTRY(ftrace_stub) | ||
| 15 | br %r14 | ||
| 16 | |||
| 17 | ENTRY(_mcount) | ||
| 18 | #ifdef CONFIG_DYNAMIC_FTRACE | ||
| 19 | br %r14 | ||
| 20 | |||
| 21 | ENTRY(ftrace_caller) | ||
| 22 | #endif | ||
| 23 | stmg %r2,%r5,32(%r15) | ||
| 24 | stg %r14,112(%r15) | ||
| 25 | lgr %r1,%r15 | ||
| 26 | aghi %r15,-160 | ||
| 27 | stg %r1,__SF_BACKCHAIN(%r15) | ||
| 28 | lgr %r2,%r14 | ||
| 29 | lg %r3,168(%r15) | ||
| 30 | aghi %r2,-MCOUNT_INSN_SIZE | ||
| 31 | larl %r14,ftrace_trace_function | ||
| 32 | lg %r14,0(%r14) | ||
| 33 | basr %r14,%r14 | ||
| 34 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
| 35 | lg %r2,168(%r15) | ||
| 36 | lg %r3,272(%r15) | ||
| 37 | ENTRY(ftrace_graph_caller) | ||
| 38 | # The bras instruction gets runtime patched to call prepare_ftrace_return. | ||
| 39 | # See ftrace_enable_ftrace_graph_caller. The patched instruction is: | ||
| 40 | # bras %r14,prepare_ftrace_return | ||
| 41 | bras %r14,0f | ||
| 42 | 0: stg %r2,168(%r15) | ||
| 43 | #endif | ||
| 44 | aghi %r15,160 | ||
| 45 | lmg %r2,%r5,32(%r15) | ||
| 46 | lg %r14,112(%r15) | ||
| 47 | br %r14 | ||
| 48 | |||
| 49 | #ifdef CONFIG_FUNCTION_GRAPH_TRACER | ||
| 50 | |||
| 51 | ENTRY(return_to_handler) | ||
| 52 | stmg %r2,%r5,32(%r15) | ||
| 53 | lgr %r1,%r15 | ||
| 54 | aghi %r15,-160 | ||
| 55 | stg %r1,__SF_BACKCHAIN(%r15) | ||
| 56 | brasl %r14,ftrace_return_to_handler | ||
| 57 | aghi %r15,160 | ||
| 58 | lgr %r14,%r2 | ||
| 59 | lmg %r2,%r5,32(%r15) | ||
| 60 | br %r14 | ||
| 61 | |||
| 62 | #endif | ||
diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 210e1285f75a..db96b418160a 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include <asm/cputime.h> | 20 | #include <asm/cputime.h> |
| 21 | #include <asm/nmi.h> | 21 | #include <asm/nmi.h> |
| 22 | #include <asm/crw.h> | 22 | #include <asm/crw.h> |
| 23 | #include <asm/switch_to.h> | ||
| 23 | 24 | ||
| 24 | struct mcck_struct { | 25 | struct mcck_struct { |
| 25 | int kill_task; | 26 | int kill_task; |
| @@ -163,6 +164,21 @@ static int notrace s390_revalidate_registers(struct mci *mci) | |||
| 163 | " ld 15,120(%0)\n" | 164 | " ld 15,120(%0)\n" |
| 164 | : : "a" (fpt_save_area)); | 165 | : : "a" (fpt_save_area)); |
| 165 | } | 166 | } |
| 167 | |||
| 168 | #ifdef CONFIG_64BIT | ||
| 169 | /* Revalidate vector registers */ | ||
| 170 | if (MACHINE_HAS_VX && current->thread.vxrs) { | ||
| 171 | if (!mci->vr) { | ||
| 172 | /* | ||
| 173 | * Vector registers can't be restored and therefore | ||
| 174 | * the process needs to be terminated. | ||
| 175 | */ | ||
| 176 | kill_task = 1; | ||
| 177 | } | ||
| 178 | restore_vx_regs((__vector128 *) | ||
| 179 | S390_lowcore.vector_save_area_addr); | ||
| 180 | } | ||
| 181 | #endif | ||
| 166 | /* Revalidate access registers */ | 182 | /* Revalidate access registers */ |
| 167 | asm volatile( | 183 | asm volatile( |
| 168 | " lam 0,15,0(%0)" | 184 | " lam 0,15,0(%0)" |
diff --git a/arch/s390/kernel/pgm_check.S b/arch/s390/kernel/pgm_check.S index 813ec7260878..f6f8886399f6 100644 --- a/arch/s390/kernel/pgm_check.S +++ b/arch/s390/kernel/pgm_check.S | |||
| @@ -49,7 +49,7 @@ PGM_CHECK_DEFAULT /* 17 */ | |||
| 49 | PGM_CHECK_64BIT(transaction_exception) /* 18 */ | 49 | PGM_CHECK_64BIT(transaction_exception) /* 18 */ |
| 50 | PGM_CHECK_DEFAULT /* 19 */ | 50 | PGM_CHECK_DEFAULT /* 19 */ |
| 51 | PGM_CHECK_DEFAULT /* 1a */ | 51 | PGM_CHECK_DEFAULT /* 1a */ |
| 52 | PGM_CHECK_DEFAULT /* 1b */ | 52 | PGM_CHECK_64BIT(vector_exception) /* 1b */ |
| 53 | PGM_CHECK(space_switch_exception) /* 1c */ | 53 | PGM_CHECK(space_switch_exception) /* 1c */ |
| 54 | PGM_CHECK(hfp_sqrt_exception) /* 1d */ | 54 | PGM_CHECK(hfp_sqrt_exception) /* 1d */ |
| 55 | PGM_CHECK_DEFAULT /* 1e */ | 55 | PGM_CHECK_DEFAULT /* 1e */ |
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 93b9ca42e5c0..ed84cc224899 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c | |||
| @@ -61,30 +61,6 @@ unsigned long thread_saved_pc(struct task_struct *tsk) | |||
| 61 | return sf->gprs[8]; | 61 | return sf->gprs[8]; |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | void arch_cpu_idle(void) | ||
| 65 | { | ||
| 66 | local_mcck_disable(); | ||
| 67 | if (test_cpu_flag(CIF_MCCK_PENDING)) { | ||
| 68 | local_mcck_enable(); | ||
| 69 | local_irq_enable(); | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | /* Halt the cpu and keep track of cpu time accounting. */ | ||
| 73 | vtime_stop_cpu(); | ||
| 74 | local_irq_enable(); | ||
| 75 | } | ||
| 76 | |||
| 77 | void arch_cpu_idle_exit(void) | ||
| 78 | { | ||
| 79 | if (test_cpu_flag(CIF_MCCK_PENDING)) | ||
| 80 | s390_handle_mcck(); | ||
| 81 | } | ||
| 82 | |||
| 83 | void arch_cpu_idle_dead(void) | ||
| 84 | { | ||
| 85 | cpu_die(); | ||
| 86 | } | ||
| 87 | |||
| 88 | extern void __kprobes kernel_thread_starter(void); | 64 | extern void __kprobes kernel_thread_starter(void); |
| 89 | 65 | ||
| 90 | /* | 66 | /* |
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c index 24612029f450..edefead3b43a 100644 --- a/arch/s390/kernel/processor.c +++ b/arch/s390/kernel/processor.c | |||
| @@ -23,7 +23,6 @@ static DEFINE_PER_CPU(struct cpuid, cpu_id); | |||
| 23 | */ | 23 | */ |
| 24 | void cpu_init(void) | 24 | void cpu_init(void) |
| 25 | { | 25 | { |
| 26 | struct s390_idle_data *idle = &__get_cpu_var(s390_idle); | ||
| 27 | struct cpuid *id = &__get_cpu_var(cpu_id); | 26 | struct cpuid *id = &__get_cpu_var(cpu_id); |
| 28 | 27 | ||
| 29 | get_cpu_id(id); | 28 | get_cpu_id(id); |
| @@ -31,7 +30,6 @@ void cpu_init(void) | |||
| 31 | current->active_mm = &init_mm; | 30 | current->active_mm = &init_mm; |
| 32 | BUG_ON(current->mm); | 31 | BUG_ON(current->mm); |
| 33 | enter_lazy_tlb(&init_mm, current); | 32 | enter_lazy_tlb(&init_mm, current); |
| 34 | memset(idle, 0, sizeof(*idle)); | ||
| 35 | } | 33 | } |
| 36 | 34 | ||
| 37 | /* | 35 | /* |
| @@ -41,7 +39,7 @@ static int show_cpuinfo(struct seq_file *m, void *v) | |||
| 41 | { | 39 | { |
| 42 | static const char *hwcap_str[] = { | 40 | static const char *hwcap_str[] = { |
| 43 | "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", | 41 | "esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp", |
| 44 | "edat", "etf3eh", "highgprs", "te" | 42 | "edat", "etf3eh", "highgprs", "te", "vx" |
| 45 | }; | 43 | }; |
| 46 | unsigned long n = (unsigned long) v - 1; | 44 | unsigned long n = (unsigned long) v - 1; |
| 47 | int i; | 45 | int i; |
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c index bebacad48305..f537e937a988 100644 --- a/arch/s390/kernel/ptrace.c +++ b/arch/s390/kernel/ptrace.c | |||
| @@ -38,15 +38,6 @@ | |||
| 38 | #define CREATE_TRACE_POINTS | 38 | #define CREATE_TRACE_POINTS |
| 39 | #include <trace/events/syscalls.h> | 39 | #include <trace/events/syscalls.h> |
| 40 | 40 | ||
| 41 | enum s390_regset { | ||
| 42 | REGSET_GENERAL, | ||
| 43 | REGSET_FP, | ||
| 44 | REGSET_LAST_BREAK, | ||
| 45 | REGSET_TDB, | ||
| 46 | REGSET_SYSTEM_CALL, | ||
| 47 | REGSET_GENERAL_EXTENDED, | ||
| 48 | }; | ||
| 49 | |||
| 50 | void update_cr_regs(struct task_struct *task) | 41 | void update_cr_regs(struct task_struct *task) |
| 51 | { | 42 | { |
| 52 | struct pt_regs *regs = task_pt_regs(task); | 43 | struct pt_regs *regs = task_pt_regs(task); |
| @@ -55,27 +46,39 @@ void update_cr_regs(struct task_struct *task) | |||
| 55 | 46 | ||
| 56 | #ifdef CONFIG_64BIT | 47 | #ifdef CONFIG_64BIT |
| 57 | /* Take care of the enable/disable of transactional execution. */ | 48 | /* Take care of the enable/disable of transactional execution. */ |
| 58 | if (MACHINE_HAS_TE) { | 49 | if (MACHINE_HAS_TE || MACHINE_HAS_VX) { |
| 59 | unsigned long cr, cr_new; | 50 | unsigned long cr, cr_new; |
| 60 | 51 | ||
| 61 | __ctl_store(cr, 0, 0); | 52 | __ctl_store(cr, 0, 0); |
| 62 | /* Set or clear transaction execution TXC bit 8. */ | 53 | cr_new = cr; |
| 63 | cr_new = cr | (1UL << 55); | 54 | if (MACHINE_HAS_TE) { |
| 64 | if (task->thread.per_flags & PER_FLAG_NO_TE) | 55 | /* Set or clear transaction execution TXC bit 8. */ |
| 65 | cr_new &= ~(1UL << 55); | 56 | cr_new |= (1UL << 55); |
| 57 | if (task->thread.per_flags & PER_FLAG_NO_TE) | ||
| 58 | cr_new &= ~(1UL << 55); | ||
| 59 | } | ||
| 60 | if (MACHINE_HAS_VX) { | ||
| 61 | /* Enable/disable of vector extension */ | ||
| 62 | cr_new &= ~(1UL << 17); | ||
| 63 | if (task->thread.vxrs) | ||
| 64 | cr_new |= (1UL << 17); | ||
| 65 | } | ||
| 66 | if (cr_new != cr) | 66 | if (cr_new != cr) |
| 67 | __ctl_load(cr_new, 0, 0); | 67 | __ctl_load(cr_new, 0, 0); |
| 68 | /* Set or clear transaction execution TDC bits 62 and 63. */ | 68 | if (MACHINE_HAS_TE) { |
| 69 | __ctl_store(cr, 2, 2); | 69 | /* Set/clear transaction execution TDC bits 62/63. */ |
| 70 | cr_new = cr & ~3UL; | 70 | __ctl_store(cr, 2, 2); |
| 71 | if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) { | 71 | cr_new = cr & ~3UL; |
| 72 | if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND_TEND) | 72 | if (task->thread.per_flags & PER_FLAG_TE_ABORT_RAND) { |
| 73 | cr_new |= 1UL; | 73 | if (task->thread.per_flags & |
| 74 | else | 74 | PER_FLAG_TE_ABORT_RAND_TEND) |
| 75 | cr_new |= 2UL; | 75 | cr_new |= 1UL; |
| 76 | else | ||
| 77 | cr_new |= 2UL; | ||
| 78 | } | ||
| 79 | if (cr_new != cr) | ||
| 80 | __ctl_load(cr_new, 2, 2); | ||
| 76 | } | 81 | } |
| 77 | if (cr_new != cr) | ||
| 78 | __ctl_load(cr_new, 2, 2); | ||
| 79 | } | 82 | } |
| 80 | #endif | 83 | #endif |
| 81 | /* Copy user specified PER registers */ | 84 | /* Copy user specified PER registers */ |
| @@ -84,7 +87,8 @@ void update_cr_regs(struct task_struct *task) | |||
| 84 | new.end = thread->per_user.end; | 87 | new.end = thread->per_user.end; |
| 85 | 88 | ||
| 86 | /* merge TIF_SINGLE_STEP into user specified PER registers. */ | 89 | /* merge TIF_SINGLE_STEP into user specified PER registers. */ |
| 87 | if (test_tsk_thread_flag(task, TIF_SINGLE_STEP)) { | 90 | if (test_tsk_thread_flag(task, TIF_SINGLE_STEP) || |
| 91 | test_tsk_thread_flag(task, TIF_UPROBE_SINGLESTEP)) { | ||
| 88 | if (test_tsk_thread_flag(task, TIF_BLOCK_STEP)) | 92 | if (test_tsk_thread_flag(task, TIF_BLOCK_STEP)) |
| 89 | new.control |= PER_EVENT_BRANCH; | 93 | new.control |= PER_EVENT_BRANCH; |
| 90 | else | 94 | else |
| @@ -93,6 +97,8 @@ void update_cr_regs(struct task_struct *task) | |||
| 93 | new.control |= PER_CONTROL_SUSPENSION; | 97 | new.control |= PER_CONTROL_SUSPENSION; |
| 94 | new.control |= PER_EVENT_TRANSACTION_END; | 98 | new.control |= PER_EVENT_TRANSACTION_END; |
| 95 | #endif | 99 | #endif |
| 100 | if (test_tsk_thread_flag(task, TIF_UPROBE_SINGLESTEP)) | ||
| 101 | new.control |= PER_EVENT_IFETCH; | ||
| 96 | new.start = 0; | 102 | new.start = 0; |
| 97 | new.end = PSW_ADDR_INSN; | 103 | new.end = PSW_ADDR_INSN; |
| 98 | } | 104 | } |
| @@ -923,7 +929,15 @@ static int s390_fpregs_get(struct task_struct *target, | |||
| 923 | save_fp_ctl(&target->thread.fp_regs.fpc); | 929 | save_fp_ctl(&target->thread.fp_regs.fpc); |
| 924 | save_fp_regs(target->thread.fp_regs.fprs); | 930 | save_fp_regs(target->thread.fp_regs.fprs); |
| 925 | } | 931 | } |
| 932 | #ifdef CONFIG_64BIT | ||
| 933 | else if (target->thread.vxrs) { | ||
| 934 | int i; | ||
| 926 | 935 | ||
| 936 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
| 937 | target->thread.fp_regs.fprs[i] = | ||
| 938 | *(freg_t *)(target->thread.vxrs + i); | ||
| 939 | } | ||
| 940 | #endif | ||
| 927 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, | 941 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, |
| 928 | &target->thread.fp_regs, 0, -1); | 942 | &target->thread.fp_regs, 0, -1); |
| 929 | } | 943 | } |
| @@ -957,9 +971,20 @@ static int s390_fpregs_set(struct task_struct *target, | |||
| 957 | target->thread.fp_regs.fprs, | 971 | target->thread.fp_regs.fprs, |
| 958 | offsetof(s390_fp_regs, fprs), -1); | 972 | offsetof(s390_fp_regs, fprs), -1); |
| 959 | 973 | ||
| 960 | if (rc == 0 && target == current) { | 974 | if (rc == 0) { |
| 961 | restore_fp_ctl(&target->thread.fp_regs.fpc); | 975 | if (target == current) { |
| 962 | restore_fp_regs(target->thread.fp_regs.fprs); | 976 | restore_fp_ctl(&target->thread.fp_regs.fpc); |
| 977 | restore_fp_regs(target->thread.fp_regs.fprs); | ||
| 978 | } | ||
| 979 | #ifdef CONFIG_64BIT | ||
| 980 | else if (target->thread.vxrs) { | ||
| 981 | int i; | ||
| 982 | |||
| 983 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
| 984 | *(freg_t *)(target->thread.vxrs + i) = | ||
| 985 | target->thread.fp_regs.fprs[i]; | ||
| 986 | } | ||
| 987 | #endif | ||
| 963 | } | 988 | } |
| 964 | 989 | ||
| 965 | return rc; | 990 | return rc; |
| @@ -1015,6 +1040,95 @@ static int s390_tdb_set(struct task_struct *target, | |||
| 1015 | return 0; | 1040 | return 0; |
| 1016 | } | 1041 | } |
| 1017 | 1042 | ||
| 1043 | static int s390_vxrs_active(struct task_struct *target, | ||
| 1044 | const struct user_regset *regset) | ||
| 1045 | { | ||
| 1046 | return !!target->thread.vxrs; | ||
| 1047 | } | ||
| 1048 | |||
| 1049 | static int s390_vxrs_low_get(struct task_struct *target, | ||
| 1050 | const struct user_regset *regset, | ||
| 1051 | unsigned int pos, unsigned int count, | ||
| 1052 | void *kbuf, void __user *ubuf) | ||
| 1053 | { | ||
| 1054 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
| 1055 | int i; | ||
| 1056 | |||
| 1057 | if (target->thread.vxrs) { | ||
| 1058 | if (target == current) | ||
| 1059 | save_vx_regs(target->thread.vxrs); | ||
| 1060 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
| 1061 | vxrs[i] = *((__u64 *)(target->thread.vxrs + i) + 1); | ||
| 1062 | } else | ||
| 1063 | memset(vxrs, 0, sizeof(vxrs)); | ||
| 1064 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); | ||
| 1065 | } | ||
| 1066 | |||
| 1067 | static int s390_vxrs_low_set(struct task_struct *target, | ||
| 1068 | const struct user_regset *regset, | ||
| 1069 | unsigned int pos, unsigned int count, | ||
| 1070 | const void *kbuf, const void __user *ubuf) | ||
| 1071 | { | ||
| 1072 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
| 1073 | int i, rc; | ||
| 1074 | |||
| 1075 | if (!target->thread.vxrs) { | ||
| 1076 | rc = alloc_vector_registers(target); | ||
| 1077 | if (rc) | ||
| 1078 | return rc; | ||
| 1079 | } else if (target == current) | ||
| 1080 | save_vx_regs(target->thread.vxrs); | ||
| 1081 | |||
| 1082 | rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); | ||
| 1083 | if (rc == 0) { | ||
| 1084 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
| 1085 | *((__u64 *)(target->thread.vxrs + i) + 1) = vxrs[i]; | ||
| 1086 | if (target == current) | ||
| 1087 | restore_vx_regs(target->thread.vxrs); | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | return rc; | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | static int s390_vxrs_high_get(struct task_struct *target, | ||
| 1094 | const struct user_regset *regset, | ||
| 1095 | unsigned int pos, unsigned int count, | ||
| 1096 | void *kbuf, void __user *ubuf) | ||
| 1097 | { | ||
| 1098 | __vector128 vxrs[__NUM_VXRS_HIGH]; | ||
| 1099 | |||
| 1100 | if (target->thread.vxrs) { | ||
| 1101 | if (target == current) | ||
| 1102 | save_vx_regs(target->thread.vxrs); | ||
| 1103 | memcpy(vxrs, target->thread.vxrs + __NUM_VXRS_LOW, | ||
| 1104 | sizeof(vxrs)); | ||
| 1105 | } else | ||
| 1106 | memset(vxrs, 0, sizeof(vxrs)); | ||
| 1107 | return user_regset_copyout(&pos, &count, &kbuf, &ubuf, vxrs, 0, -1); | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | static int s390_vxrs_high_set(struct task_struct *target, | ||
| 1111 | const struct user_regset *regset, | ||
| 1112 | unsigned int pos, unsigned int count, | ||
| 1113 | const void *kbuf, const void __user *ubuf) | ||
| 1114 | { | ||
| 1115 | int rc; | ||
| 1116 | |||
| 1117 | if (!target->thread.vxrs) { | ||
| 1118 | rc = alloc_vector_registers(target); | ||
| 1119 | if (rc) | ||
| 1120 | return rc; | ||
| 1121 | } else if (target == current) | ||
| 1122 | save_vx_regs(target->thread.vxrs); | ||
| 1123 | |||
| 1124 | rc = user_regset_copyin(&pos, &count, &kbuf, &ubuf, | ||
| 1125 | target->thread.vxrs + __NUM_VXRS_LOW, 0, -1); | ||
| 1126 | if (rc == 0 && target == current) | ||
| 1127 | restore_vx_regs(target->thread.vxrs); | ||
| 1128 | |||
| 1129 | return rc; | ||
| 1130 | } | ||
| 1131 | |||
| 1018 | #endif | 1132 | #endif |
| 1019 | 1133 | ||
| 1020 | static int s390_system_call_get(struct task_struct *target, | 1134 | static int s390_system_call_get(struct task_struct *target, |
| @@ -1038,7 +1152,7 @@ static int s390_system_call_set(struct task_struct *target, | |||
| 1038 | } | 1152 | } |
| 1039 | 1153 | ||
| 1040 | static const struct user_regset s390_regsets[] = { | 1154 | static const struct user_regset s390_regsets[] = { |
| 1041 | [REGSET_GENERAL] = { | 1155 | { |
| 1042 | .core_note_type = NT_PRSTATUS, | 1156 | .core_note_type = NT_PRSTATUS, |
| 1043 | .n = sizeof(s390_regs) / sizeof(long), | 1157 | .n = sizeof(s390_regs) / sizeof(long), |
| 1044 | .size = sizeof(long), | 1158 | .size = sizeof(long), |
| @@ -1046,7 +1160,7 @@ static const struct user_regset s390_regsets[] = { | |||
| 1046 | .get = s390_regs_get, | 1160 | .get = s390_regs_get, |
| 1047 | .set = s390_regs_set, | 1161 | .set = s390_regs_set, |
| 1048 | }, | 1162 | }, |
| 1049 | [REGSET_FP] = { | 1163 | { |
| 1050 | .core_note_type = NT_PRFPREG, | 1164 | .core_note_type = NT_PRFPREG, |
| 1051 | .n = sizeof(s390_fp_regs) / sizeof(long), | 1165 | .n = sizeof(s390_fp_regs) / sizeof(long), |
| 1052 | .size = sizeof(long), | 1166 | .size = sizeof(long), |
| @@ -1054,8 +1168,16 @@ static const struct user_regset s390_regsets[] = { | |||
| 1054 | .get = s390_fpregs_get, | 1168 | .get = s390_fpregs_get, |
| 1055 | .set = s390_fpregs_set, | 1169 | .set = s390_fpregs_set, |
| 1056 | }, | 1170 | }, |
| 1171 | { | ||
| 1172 | .core_note_type = NT_S390_SYSTEM_CALL, | ||
| 1173 | .n = 1, | ||
| 1174 | .size = sizeof(unsigned int), | ||
| 1175 | .align = sizeof(unsigned int), | ||
| 1176 | .get = s390_system_call_get, | ||
| 1177 | .set = s390_system_call_set, | ||
| 1178 | }, | ||
| 1057 | #ifdef CONFIG_64BIT | 1179 | #ifdef CONFIG_64BIT |
| 1058 | [REGSET_LAST_BREAK] = { | 1180 | { |
| 1059 | .core_note_type = NT_S390_LAST_BREAK, | 1181 | .core_note_type = NT_S390_LAST_BREAK, |
| 1060 | .n = 1, | 1182 | .n = 1, |
| 1061 | .size = sizeof(long), | 1183 | .size = sizeof(long), |
| @@ -1063,7 +1185,7 @@ static const struct user_regset s390_regsets[] = { | |||
| 1063 | .get = s390_last_break_get, | 1185 | .get = s390_last_break_get, |
| 1064 | .set = s390_last_break_set, | 1186 | .set = s390_last_break_set, |
| 1065 | }, | 1187 | }, |
| 1066 | [REGSET_TDB] = { | 1188 | { |
| 1067 | .core_note_type = NT_S390_TDB, | 1189 | .core_note_type = NT_S390_TDB, |
| 1068 | .n = 1, | 1190 | .n = 1, |
| 1069 | .size = 256, | 1191 | .size = 256, |
| @@ -1071,15 +1193,25 @@ static const struct user_regset s390_regsets[] = { | |||
| 1071 | .get = s390_tdb_get, | 1193 | .get = s390_tdb_get, |
| 1072 | .set = s390_tdb_set, | 1194 | .set = s390_tdb_set, |
| 1073 | }, | 1195 | }, |
| 1074 | #endif | 1196 | { |
| 1075 | [REGSET_SYSTEM_CALL] = { | 1197 | .core_note_type = NT_S390_VXRS_LOW, |
| 1076 | .core_note_type = NT_S390_SYSTEM_CALL, | 1198 | .n = __NUM_VXRS_LOW, |
| 1077 | .n = 1, | 1199 | .size = sizeof(__u64), |
| 1078 | .size = sizeof(unsigned int), | 1200 | .align = sizeof(__u64), |
| 1079 | .align = sizeof(unsigned int), | 1201 | .active = s390_vxrs_active, |
| 1080 | .get = s390_system_call_get, | 1202 | .get = s390_vxrs_low_get, |
| 1081 | .set = s390_system_call_set, | 1203 | .set = s390_vxrs_low_set, |
| 1082 | }, | 1204 | }, |
| 1205 | { | ||
| 1206 | .core_note_type = NT_S390_VXRS_HIGH, | ||
| 1207 | .n = __NUM_VXRS_HIGH, | ||
| 1208 | .size = sizeof(__vector128), | ||
| 1209 | .align = sizeof(__vector128), | ||
| 1210 | .active = s390_vxrs_active, | ||
| 1211 | .get = s390_vxrs_high_get, | ||
| 1212 | .set = s390_vxrs_high_set, | ||
| 1213 | }, | ||
| 1214 | #endif | ||
| 1083 | }; | 1215 | }; |
| 1084 | 1216 | ||
| 1085 | static const struct user_regset_view user_s390_view = { | 1217 | static const struct user_regset_view user_s390_view = { |
| @@ -1244,7 +1376,7 @@ static int s390_compat_last_break_set(struct task_struct *target, | |||
| 1244 | } | 1376 | } |
| 1245 | 1377 | ||
| 1246 | static const struct user_regset s390_compat_regsets[] = { | 1378 | static const struct user_regset s390_compat_regsets[] = { |
| 1247 | [REGSET_GENERAL] = { | 1379 | { |
| 1248 | .core_note_type = NT_PRSTATUS, | 1380 | .core_note_type = NT_PRSTATUS, |
| 1249 | .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), | 1381 | .n = sizeof(s390_compat_regs) / sizeof(compat_long_t), |
| 1250 | .size = sizeof(compat_long_t), | 1382 | .size = sizeof(compat_long_t), |
| @@ -1252,7 +1384,7 @@ static const struct user_regset s390_compat_regsets[] = { | |||
| 1252 | .get = s390_compat_regs_get, | 1384 | .get = s390_compat_regs_get, |
| 1253 | .set = s390_compat_regs_set, | 1385 | .set = s390_compat_regs_set, |
| 1254 | }, | 1386 | }, |
| 1255 | [REGSET_FP] = { | 1387 | { |
| 1256 | .core_note_type = NT_PRFPREG, | 1388 | .core_note_type = NT_PRFPREG, |
| 1257 | .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), | 1389 | .n = sizeof(s390_fp_regs) / sizeof(compat_long_t), |
| 1258 | .size = sizeof(compat_long_t), | 1390 | .size = sizeof(compat_long_t), |
| @@ -1260,7 +1392,15 @@ static const struct user_regset s390_compat_regsets[] = { | |||
| 1260 | .get = s390_fpregs_get, | 1392 | .get = s390_fpregs_get, |
| 1261 | .set = s390_fpregs_set, | 1393 | .set = s390_fpregs_set, |
| 1262 | }, | 1394 | }, |
| 1263 | [REGSET_LAST_BREAK] = { | 1395 | { |
| 1396 | .core_note_type = NT_S390_SYSTEM_CALL, | ||
| 1397 | .n = 1, | ||
| 1398 | .size = sizeof(compat_uint_t), | ||
| 1399 | .align = sizeof(compat_uint_t), | ||
| 1400 | .get = s390_system_call_get, | ||
| 1401 | .set = s390_system_call_set, | ||
| 1402 | }, | ||
| 1403 | { | ||
| 1264 | .core_note_type = NT_S390_LAST_BREAK, | 1404 | .core_note_type = NT_S390_LAST_BREAK, |
| 1265 | .n = 1, | 1405 | .n = 1, |
| 1266 | .size = sizeof(long), | 1406 | .size = sizeof(long), |
| @@ -1268,7 +1408,7 @@ static const struct user_regset s390_compat_regsets[] = { | |||
| 1268 | .get = s390_compat_last_break_get, | 1408 | .get = s390_compat_last_break_get, |
| 1269 | .set = s390_compat_last_break_set, | 1409 | .set = s390_compat_last_break_set, |
| 1270 | }, | 1410 | }, |
| 1271 | [REGSET_TDB] = { | 1411 | { |
| 1272 | .core_note_type = NT_S390_TDB, | 1412 | .core_note_type = NT_S390_TDB, |
| 1273 | .n = 1, | 1413 | .n = 1, |
| 1274 | .size = 256, | 1414 | .size = 256, |
| @@ -1276,15 +1416,25 @@ static const struct user_regset s390_compat_regsets[] = { | |||
| 1276 | .get = s390_tdb_get, | 1416 | .get = s390_tdb_get, |
| 1277 | .set = s390_tdb_set, | 1417 | .set = s390_tdb_set, |
| 1278 | }, | 1418 | }, |
| 1279 | [REGSET_SYSTEM_CALL] = { | 1419 | { |
| 1280 | .core_note_type = NT_S390_SYSTEM_CALL, | 1420 | .core_note_type = NT_S390_VXRS_LOW, |
| 1281 | .n = 1, | 1421 | .n = __NUM_VXRS_LOW, |
| 1282 | .size = sizeof(compat_uint_t), | 1422 | .size = sizeof(__u64), |
| 1283 | .align = sizeof(compat_uint_t), | 1423 | .align = sizeof(__u64), |
| 1284 | .get = s390_system_call_get, | 1424 | .active = s390_vxrs_active, |
| 1285 | .set = s390_system_call_set, | 1425 | .get = s390_vxrs_low_get, |
| 1426 | .set = s390_vxrs_low_set, | ||
| 1427 | }, | ||
| 1428 | { | ||
| 1429 | .core_note_type = NT_S390_VXRS_HIGH, | ||
| 1430 | .n = __NUM_VXRS_HIGH, | ||
| 1431 | .size = sizeof(__vector128), | ||
| 1432 | .align = sizeof(__vector128), | ||
| 1433 | .active = s390_vxrs_active, | ||
| 1434 | .get = s390_vxrs_high_get, | ||
| 1435 | .set = s390_vxrs_high_set, | ||
| 1286 | }, | 1436 | }, |
| 1287 | [REGSET_GENERAL_EXTENDED] = { | 1437 | { |
| 1288 | .core_note_type = NT_S390_HIGH_GPRS, | 1438 | .core_note_type = NT_S390_HIGH_GPRS, |
| 1289 | .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), | 1439 | .n = sizeof(s390_compat_regs_high) / sizeof(compat_long_t), |
| 1290 | .size = sizeof(compat_long_t), | 1440 | .size = sizeof(compat_long_t), |
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 82bc113e8c1d..e80d9ff9a56d 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c | |||
| @@ -343,6 +343,9 @@ static void __init setup_lowcore(void) | |||
| 343 | __ctl_set_bit(14, 29); | 343 | __ctl_set_bit(14, 29); |
| 344 | } | 344 | } |
| 345 | #else | 345 | #else |
| 346 | if (MACHINE_HAS_VX) | ||
| 347 | lc->vector_save_area_addr = | ||
| 348 | (unsigned long) &lc->vector_save_area; | ||
| 346 | lc->vdso_per_cpu_data = (unsigned long) &lc->paste[0]; | 349 | lc->vdso_per_cpu_data = (unsigned long) &lc->paste[0]; |
| 347 | #endif | 350 | #endif |
| 348 | lc->sync_enter_timer = S390_lowcore.sync_enter_timer; | 351 | lc->sync_enter_timer = S390_lowcore.sync_enter_timer; |
| @@ -452,8 +455,8 @@ static void __init setup_memory_end(void) | |||
| 452 | #ifdef CONFIG_64BIT | 455 | #ifdef CONFIG_64BIT |
| 453 | vmalloc_size = VMALLOC_END ?: (128UL << 30) - MODULES_LEN; | 456 | vmalloc_size = VMALLOC_END ?: (128UL << 30) - MODULES_LEN; |
| 454 | tmp = (memory_end ?: max_physmem_end) / PAGE_SIZE; | 457 | tmp = (memory_end ?: max_physmem_end) / PAGE_SIZE; |
| 455 | tmp = tmp * (sizeof(struct page) + PAGE_SIZE) + vmalloc_size; | 458 | tmp = tmp * (sizeof(struct page) + PAGE_SIZE); |
| 456 | if (tmp <= (1UL << 42)) | 459 | if (tmp + vmalloc_size + MODULES_LEN <= (1UL << 42)) |
| 457 | vmax = 1UL << 42; /* 3-level kernel page table */ | 460 | vmax = 1UL << 42; /* 3-level kernel page table */ |
| 458 | else | 461 | else |
| 459 | vmax = 1UL << 53; /* 4-level kernel page table */ | 462 | vmax = 1UL << 53; /* 4-level kernel page table */ |
| @@ -765,6 +768,12 @@ static void __init setup_hwcaps(void) | |||
| 765 | */ | 768 | */ |
| 766 | if (test_facility(50) && test_facility(73)) | 769 | if (test_facility(50) && test_facility(73)) |
| 767 | elf_hwcap |= HWCAP_S390_TE; | 770 | elf_hwcap |= HWCAP_S390_TE; |
| 771 | |||
| 772 | /* | ||
| 773 | * Vector extension HWCAP_S390_VXRS is bit 11. | ||
| 774 | */ | ||
| 775 | if (test_facility(129)) | ||
| 776 | elf_hwcap |= HWCAP_S390_VXRS; | ||
| 768 | #endif | 777 | #endif |
| 769 | 778 | ||
| 770 | get_cpu_id(&cpu_id); | 779 | get_cpu_id(&cpu_id); |
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c index 469c4c6d9182..0c1a0ff0a558 100644 --- a/arch/s390/kernel/signal.c +++ b/arch/s390/kernel/signal.c | |||
| @@ -31,30 +31,117 @@ | |||
| 31 | #include <asm/switch_to.h> | 31 | #include <asm/switch_to.h> |
| 32 | #include "entry.h" | 32 | #include "entry.h" |
| 33 | 33 | ||
| 34 | typedef struct | 34 | /* |
| 35 | * Layout of an old-style signal-frame: | ||
| 36 | * ----------------------------------------- | ||
| 37 | * | save area (_SIGNAL_FRAMESIZE) | | ||
| 38 | * ----------------------------------------- | ||
| 39 | * | struct sigcontext | | ||
| 40 | * | oldmask | | ||
| 41 | * | _sigregs * | | ||
| 42 | * ----------------------------------------- | ||
| 43 | * | _sigregs with | | ||
| 44 | * | _s390_regs_common | | ||
| 45 | * | _s390_fp_regs | | ||
| 46 | * ----------------------------------------- | ||
| 47 | * | int signo | | ||
| 48 | * ----------------------------------------- | ||
| 49 | * | _sigregs_ext with | | ||
| 50 | * | gprs_high 64 byte (opt) | | ||
| 51 | * | vxrs_low 128 byte (opt) | | ||
| 52 | * | vxrs_high 256 byte (opt) | | ||
| 53 | * | reserved 128 byte (opt) | | ||
| 54 | * ----------------------------------------- | ||
| 55 | * | __u16 svc_insn | | ||
| 56 | * ----------------------------------------- | ||
| 57 | * The svc_insn entry with the sigreturn system call opcode does not | ||
| 58 | * have a fixed position and moves if gprs_high or vxrs exist. | ||
| 59 | * Future extensions will be added to _sigregs_ext. | ||
| 60 | */ | ||
| 61 | struct sigframe | ||
| 35 | { | 62 | { |
| 36 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; | 63 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; |
| 37 | struct sigcontext sc; | 64 | struct sigcontext sc; |
| 38 | _sigregs sregs; | 65 | _sigregs sregs; |
| 39 | int signo; | 66 | int signo; |
| 40 | __u8 retcode[S390_SYSCALL_SIZE]; | 67 | _sigregs_ext sregs_ext; |
| 41 | } sigframe; | 68 | __u16 svc_insn; /* Offset of svc_insn is NOT fixed! */ |
| 69 | }; | ||
| 42 | 70 | ||
| 43 | typedef struct | 71 | /* |
| 72 | * Layout of an rt signal-frame: | ||
| 73 | * ----------------------------------------- | ||
| 74 | * | save area (_SIGNAL_FRAMESIZE) | | ||
| 75 | * ----------------------------------------- | ||
| 76 | * | svc __NR_rt_sigreturn 2 byte | | ||
| 77 | * ----------------------------------------- | ||
| 78 | * | struct siginfo | | ||
| 79 | * ----------------------------------------- | ||
| 80 | * | struct ucontext_extended with | | ||
| 81 | * | unsigned long uc_flags | | ||
| 82 | * | struct ucontext *uc_link | | ||
| 83 | * | stack_t uc_stack | | ||
| 84 | * | _sigregs uc_mcontext with | | ||
| 85 | * | _s390_regs_common | | ||
| 86 | * | _s390_fp_regs | | ||
| 87 | * | sigset_t uc_sigmask | | ||
| 88 | * | _sigregs_ext uc_mcontext_ext | | ||
| 89 | * | gprs_high 64 byte (opt) | | ||
| 90 | * | vxrs_low 128 byte (opt) | | ||
| 91 | * | vxrs_high 256 byte (opt)| | ||
| 92 | * | reserved 128 byte (opt) | | ||
| 93 | * ----------------------------------------- | ||
| 94 | * Future extensions will be added to _sigregs_ext. | ||
| 95 | */ | ||
| 96 | struct rt_sigframe | ||
| 44 | { | 97 | { |
| 45 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; | 98 | __u8 callee_used_stack[__SIGNAL_FRAMESIZE]; |
| 46 | __u8 retcode[S390_SYSCALL_SIZE]; | 99 | __u16 svc_insn; |
| 47 | struct siginfo info; | 100 | struct siginfo info; |
| 48 | struct ucontext uc; | 101 | struct ucontext_extended uc; |
| 49 | } rt_sigframe; | 102 | }; |
| 103 | |||
| 104 | /* Store registers needed to create the signal frame */ | ||
| 105 | static void store_sigregs(void) | ||
| 106 | { | ||
| 107 | save_access_regs(current->thread.acrs); | ||
| 108 | save_fp_ctl(¤t->thread.fp_regs.fpc); | ||
| 109 | #ifdef CONFIG_64BIT | ||
| 110 | if (current->thread.vxrs) { | ||
| 111 | int i; | ||
| 112 | |||
| 113 | save_vx_regs(current->thread.vxrs); | ||
| 114 | for (i = 0; i < __NUM_FPRS; i++) | ||
| 115 | current->thread.fp_regs.fprs[i] = | ||
| 116 | *(freg_t *)(current->thread.vxrs + i); | ||
| 117 | } else | ||
| 118 | #endif | ||
| 119 | save_fp_regs(current->thread.fp_regs.fprs); | ||
| 120 | } | ||
| 121 | |||
| 122 | /* Load registers after signal return */ | ||
| 123 | static void load_sigregs(void) | ||
| 124 | { | ||
| 125 | restore_access_regs(current->thread.acrs); | ||
| 126 | /* restore_fp_ctl is done in restore_sigregs */ | ||
| 127 | #ifdef CONFIG_64BIT | ||
| 128 | if (current->thread.vxrs) { | ||
| 129 | int i; | ||
| 130 | |||
| 131 | for (i = 0; i < __NUM_FPRS; i++) | ||
| 132 | *(freg_t *)(current->thread.vxrs + i) = | ||
| 133 | current->thread.fp_regs.fprs[i]; | ||
| 134 | restore_vx_regs(current->thread.vxrs); | ||
| 135 | } else | ||
| 136 | #endif | ||
| 137 | restore_fp_regs(current->thread.fp_regs.fprs); | ||
| 138 | } | ||
| 50 | 139 | ||
| 51 | /* Returns non-zero on fault. */ | 140 | /* Returns non-zero on fault. */ |
| 52 | static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) | 141 | static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) |
| 53 | { | 142 | { |
| 54 | _sigregs user_sregs; | 143 | _sigregs user_sregs; |
| 55 | 144 | ||
| 56 | save_access_regs(current->thread.acrs); | ||
| 57 | |||
| 58 | /* Copy a 'clean' PSW mask to the user to avoid leaking | 145 | /* Copy a 'clean' PSW mask to the user to avoid leaking |
| 59 | information about whether PER is currently on. */ | 146 | information about whether PER is currently on. */ |
| 60 | user_sregs.regs.psw.mask = PSW_USER_BITS | | 147 | user_sregs.regs.psw.mask = PSW_USER_BITS | |
| @@ -63,12 +150,6 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs) | |||
| 63 | memcpy(&user_sregs.regs.gprs, ®s->gprs, sizeof(sregs->regs.gprs)); | 150 | memcpy(&user_sregs.regs.gprs, ®s->gprs, sizeof(sregs->regs.gprs)); |
| 64 | memcpy(&user_sregs.regs.acrs, current->thread.acrs, | 151 | memcpy(&user_sregs.regs.acrs, current->thread.acrs, |
| 65 | sizeof(user_sregs.regs.acrs)); | 152 | sizeof(user_sregs.regs.acrs)); |
| 66 | /* | ||
| 67 | * We have to store the fp registers to current->thread.fp_regs | ||
| 68 | * to merge them with the emulated registers. | ||
| 69 | */ | ||
| 70 | save_fp_ctl(¤t->thread.fp_regs.fpc); | ||
| 71 | save_fp_regs(current->thread.fp_regs.fprs); | ||
| 72 | memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, | 153 | memcpy(&user_sregs.fpregs, ¤t->thread.fp_regs, |
| 73 | sizeof(user_sregs.fpregs)); | 154 | sizeof(user_sregs.fpregs)); |
| 74 | if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs))) | 155 | if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs))) |
| @@ -107,20 +188,64 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs) | |||
| 107 | memcpy(®s->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); | 188 | memcpy(®s->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs)); |
| 108 | memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, | 189 | memcpy(¤t->thread.acrs, &user_sregs.regs.acrs, |
| 109 | sizeof(current->thread.acrs)); | 190 | sizeof(current->thread.acrs)); |
| 110 | restore_access_regs(current->thread.acrs); | ||
| 111 | 191 | ||
| 112 | memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, | 192 | memcpy(¤t->thread.fp_regs, &user_sregs.fpregs, |
| 113 | sizeof(current->thread.fp_regs)); | 193 | sizeof(current->thread.fp_regs)); |
| 114 | 194 | ||
| 115 | restore_fp_regs(current->thread.fp_regs.fprs); | ||
| 116 | clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ | 195 | clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ |
| 117 | return 0; | 196 | return 0; |
| 118 | } | 197 | } |
| 119 | 198 | ||
| 199 | /* Returns non-zero on fault. */ | ||
| 200 | static int save_sigregs_ext(struct pt_regs *regs, | ||
| 201 | _sigregs_ext __user *sregs_ext) | ||
| 202 | { | ||
| 203 | #ifdef CONFIG_64BIT | ||
| 204 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
| 205 | int i; | ||
| 206 | |||
| 207 | /* Save vector registers to signal stack */ | ||
| 208 | if (current->thread.vxrs) { | ||
| 209 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
| 210 | vxrs[i] = *((__u64 *)(current->thread.vxrs + i) + 1); | ||
| 211 | if (__copy_to_user(&sregs_ext->vxrs_low, vxrs, | ||
| 212 | sizeof(sregs_ext->vxrs_low)) || | ||
| 213 | __copy_to_user(&sregs_ext->vxrs_high, | ||
| 214 | current->thread.vxrs + __NUM_VXRS_LOW, | ||
| 215 | sizeof(sregs_ext->vxrs_high))) | ||
| 216 | return -EFAULT; | ||
| 217 | } | ||
| 218 | #endif | ||
| 219 | return 0; | ||
| 220 | } | ||
| 221 | |||
| 222 | static int restore_sigregs_ext(struct pt_regs *regs, | ||
| 223 | _sigregs_ext __user *sregs_ext) | ||
| 224 | { | ||
| 225 | #ifdef CONFIG_64BIT | ||
| 226 | __u64 vxrs[__NUM_VXRS_LOW]; | ||
| 227 | int i; | ||
| 228 | |||
| 229 | /* Restore vector registers from signal stack */ | ||
| 230 | if (current->thread.vxrs) { | ||
| 231 | if (__copy_from_user(vxrs, &sregs_ext->vxrs_low, | ||
| 232 | sizeof(sregs_ext->vxrs_low)) || | ||
| 233 | __copy_from_user(current->thread.vxrs + __NUM_VXRS_LOW, | ||
| 234 | &sregs_ext->vxrs_high, | ||
| 235 | sizeof(sregs_ext->vxrs_high))) | ||
| 236 | return -EFAULT; | ||
| 237 | for (i = 0; i < __NUM_VXRS_LOW; i++) | ||
| 238 | *((__u64 *)(current->thread.vxrs + i) + 1) = vxrs[i]; | ||
| 239 | } | ||
| 240 | #endif | ||
| 241 | return 0; | ||
| 242 | } | ||
| 243 | |||
| 120 | SYSCALL_DEFINE0(sigreturn) | 244 | SYSCALL_DEFINE0(sigreturn) |
| 121 | { | 245 | { |
| 122 | struct pt_regs *regs = task_pt_regs(current); | 246 | struct pt_regs *regs = task_pt_regs(current); |
| 123 | sigframe __user *frame = (sigframe __user *)regs->gprs[15]; | 247 | struct sigframe __user *frame = |
| 248 | (struct sigframe __user *) regs->gprs[15]; | ||
| 124 | sigset_t set; | 249 | sigset_t set; |
| 125 | 250 | ||
| 126 | if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) | 251 | if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE)) |
| @@ -128,6 +253,9 @@ SYSCALL_DEFINE0(sigreturn) | |||
| 128 | set_current_blocked(&set); | 253 | set_current_blocked(&set); |
| 129 | if (restore_sigregs(regs, &frame->sregs)) | 254 | if (restore_sigregs(regs, &frame->sregs)) |
| 130 | goto badframe; | 255 | goto badframe; |
| 256 | if (restore_sigregs_ext(regs, &frame->sregs_ext)) | ||
| 257 | goto badframe; | ||
| 258 | load_sigregs(); | ||
| 131 | return regs->gprs[2]; | 259 | return regs->gprs[2]; |
| 132 | badframe: | 260 | badframe: |
| 133 | force_sig(SIGSEGV, current); | 261 | force_sig(SIGSEGV, current); |
| @@ -137,16 +265,20 @@ badframe: | |||
| 137 | SYSCALL_DEFINE0(rt_sigreturn) | 265 | SYSCALL_DEFINE0(rt_sigreturn) |
| 138 | { | 266 | { |
| 139 | struct pt_regs *regs = task_pt_regs(current); | 267 | struct pt_regs *regs = task_pt_regs(current); |
| 140 | rt_sigframe __user *frame = (rt_sigframe __user *)regs->gprs[15]; | 268 | struct rt_sigframe __user *frame = |
| 269 | (struct rt_sigframe __user *)regs->gprs[15]; | ||
| 141 | sigset_t set; | 270 | sigset_t set; |
| 142 | 271 | ||
| 143 | if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) | 272 | if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set))) |
| 144 | goto badframe; | 273 | goto badframe; |
| 145 | set_current_blocked(&set); | 274 | set_current_blocked(&set); |
| 275 | if (restore_altstack(&frame->uc.uc_stack)) | ||
| 276 | goto badframe; | ||
| 146 | if (restore_sigregs(regs, &frame->uc.uc_mcontext)) | 277 | if (restore_sigregs(regs, &frame->uc.uc_mcontext)) |
| 147 | goto badframe; | 278 | goto badframe; |
| 148 | if (restore_altstack(&frame->uc.uc_stack)) | 279 | if (restore_sigregs_ext(regs, &frame->uc.uc_mcontext_ext)) |
| 149 | goto badframe; | 280 | goto badframe; |
| 281 | load_sigregs(); | ||
| 150 | return regs->gprs[2]; | 282 | return regs->gprs[2]; |
| 151 | badframe: | 283 | badframe: |
| 152 | force_sig(SIGSEGV, current); | 284 | force_sig(SIGSEGV, current); |
| @@ -154,11 +286,6 @@ badframe: | |||
| 154 | } | 286 | } |
| 155 | 287 | ||
| 156 | /* | 288 | /* |
| 157 | * Set up a signal frame. | ||
| 158 | */ | ||
| 159 | |||
| 160 | |||
| 161 | /* | ||
| 162 | * Determine which stack to use.. | 289 | * Determine which stack to use.. |
| 163 | */ | 290 | */ |
| 164 | static inline void __user * | 291 | static inline void __user * |
| @@ -195,39 +322,63 @@ static inline int map_signal(int sig) | |||
| 195 | static int setup_frame(int sig, struct k_sigaction *ka, | 322 | static int setup_frame(int sig, struct k_sigaction *ka, |
| 196 | sigset_t *set, struct pt_regs * regs) | 323 | sigset_t *set, struct pt_regs * regs) |
| 197 | { | 324 | { |
| 198 | sigframe __user *frame; | 325 | struct sigframe __user *frame; |
| 199 | 326 | struct sigcontext sc; | |
| 200 | frame = get_sigframe(ka, regs, sizeof(sigframe)); | 327 | unsigned long restorer; |
| 328 | size_t frame_size; | ||
| 201 | 329 | ||
| 330 | /* | ||
| 331 | * gprs_high are only present for a 31-bit task running on | ||
| 332 | * a 64-bit kernel (see compat_signal.c) but the space for | ||
| 333 | * gprs_high need to be allocated if vector registers are | ||
| 334 | * included in the signal frame on a 31-bit system. | ||
| 335 | */ | ||
| 336 | frame_size = sizeof(*frame) - sizeof(frame->sregs_ext); | ||
| 337 | if (MACHINE_HAS_VX) | ||
| 338 | frame_size += sizeof(frame->sregs_ext); | ||
| 339 | frame = get_sigframe(ka, regs, frame_size); | ||
| 202 | if (frame == (void __user *) -1UL) | 340 | if (frame == (void __user *) -1UL) |
| 203 | return -EFAULT; | 341 | return -EFAULT; |
| 204 | 342 | ||
| 205 | if (__copy_to_user(&frame->sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE)) | 343 | /* Set up backchain. */ |
| 344 | if (__put_user(regs->gprs[15], (addr_t __user *) frame)) | ||
| 206 | return -EFAULT; | 345 | return -EFAULT; |
| 207 | 346 | ||
| 347 | /* Create struct sigcontext on the signal stack */ | ||
| 348 | memcpy(&sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE); | ||
| 349 | sc.sregs = (_sigregs __user __force *) &frame->sregs; | ||
| 350 | if (__copy_to_user(&frame->sc, &sc, sizeof(frame->sc))) | ||
| 351 | return -EFAULT; | ||
| 352 | |||
| 353 | /* Store registers needed to create the signal frame */ | ||
| 354 | store_sigregs(); | ||
| 355 | |||
| 356 | /* Create _sigregs on the signal stack */ | ||
| 208 | if (save_sigregs(regs, &frame->sregs)) | 357 | if (save_sigregs(regs, &frame->sregs)) |
| 209 | return -EFAULT; | 358 | return -EFAULT; |
| 210 | if (__put_user(&frame->sregs, &frame->sc.sregs)) | 359 | |
| 360 | /* Place signal number on stack to allow backtrace from handler. */ | ||
| 361 | if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) | ||
| 362 | return -EFAULT; | ||
| 363 | |||
| 364 | /* Create _sigregs_ext on the signal stack */ | ||
| 365 | if (save_sigregs_ext(regs, &frame->sregs_ext)) | ||
| 211 | return -EFAULT; | 366 | return -EFAULT; |
| 212 | 367 | ||
| 213 | /* Set up to return from userspace. If provided, use a stub | 368 | /* Set up to return from userspace. If provided, use a stub |
| 214 | already in userspace. */ | 369 | already in userspace. */ |
| 215 | if (ka->sa.sa_flags & SA_RESTORER) { | 370 | if (ka->sa.sa_flags & SA_RESTORER) { |
| 216 | regs->gprs[14] = (unsigned long) | 371 | restorer = (unsigned long) ka->sa.sa_restorer | PSW_ADDR_AMODE; |
| 217 | ka->sa.sa_restorer | PSW_ADDR_AMODE; | ||
| 218 | } else { | 372 | } else { |
| 219 | regs->gprs[14] = (unsigned long) | 373 | /* Signal frame without vector registers are short ! */ |
| 220 | frame->retcode | PSW_ADDR_AMODE; | 374 | __u16 __user *svc = (void *) frame + frame_size - 2; |
| 221 | if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, | 375 | if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, svc)) |
| 222 | (u16 __user *)(frame->retcode))) | ||
| 223 | return -EFAULT; | 376 | return -EFAULT; |
| 377 | restorer = (unsigned long) svc | PSW_ADDR_AMODE; | ||
| 224 | } | 378 | } |
| 225 | 379 | ||
| 226 | /* Set up backchain. */ | ||
| 227 | if (__put_user(regs->gprs[15], (addr_t __user *) frame)) | ||
| 228 | return -EFAULT; | ||
| 229 | |||
| 230 | /* Set up registers for signal handler */ | 380 | /* Set up registers for signal handler */ |
| 381 | regs->gprs[14] = restorer; | ||
| 231 | regs->gprs[15] = (unsigned long) frame; | 382 | regs->gprs[15] = (unsigned long) frame; |
| 232 | /* Force default amode and default user address space control. */ | 383 | /* Force default amode and default user address space control. */ |
| 233 | regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | | 384 | regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | |
| @@ -247,54 +398,69 @@ static int setup_frame(int sig, struct k_sigaction *ka, | |||
| 247 | regs->gprs[5] = regs->int_parm_long; | 398 | regs->gprs[5] = regs->int_parm_long; |
| 248 | regs->gprs[6] = task_thread_info(current)->last_break; | 399 | regs->gprs[6] = task_thread_info(current)->last_break; |
| 249 | } | 400 | } |
| 250 | |||
| 251 | /* Place signal number on stack to allow backtrace from handler. */ | ||
| 252 | if (__put_user(regs->gprs[2], (int __user *) &frame->signo)) | ||
| 253 | return -EFAULT; | ||
| 254 | return 0; | 401 | return 0; |
| 255 | } | 402 | } |
| 256 | 403 | ||
| 257 | static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, | 404 | static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, |
| 258 | struct pt_regs *regs) | 405 | struct pt_regs *regs) |
| 259 | { | 406 | { |
| 260 | int err = 0; | 407 | struct rt_sigframe __user *frame; |
| 261 | rt_sigframe __user *frame; | 408 | unsigned long uc_flags, restorer; |
| 262 | 409 | size_t frame_size; | |
| 263 | frame = get_sigframe(&ksig->ka, regs, sizeof(rt_sigframe)); | ||
| 264 | 410 | ||
| 411 | frame_size = sizeof(struct rt_sigframe) - sizeof(_sigregs_ext); | ||
| 412 | /* | ||
| 413 | * gprs_high are only present for a 31-bit task running on | ||
| 414 | * a 64-bit kernel (see compat_signal.c) but the space for | ||
| 415 | * gprs_high need to be allocated if vector registers are | ||
| 416 | * included in the signal frame on a 31-bit system. | ||
| 417 | */ | ||
| 418 | uc_flags = 0; | ||
| 419 | #ifdef CONFIG_64BIT | ||
| 420 | if (MACHINE_HAS_VX) { | ||
| 421 | frame_size += sizeof(_sigregs_ext); | ||
| 422 | if (current->thread.vxrs) | ||
| 423 | uc_flags |= UC_VXRS; | ||
| 424 | } | ||
| 425 | #endif | ||
| 426 | frame = get_sigframe(&ksig->ka, regs, frame_size); | ||
| 265 | if (frame == (void __user *) -1UL) | 427 | if (frame == (void __user *) -1UL) |
| 266 | return -EFAULT; | 428 | return -EFAULT; |
| 267 | 429 | ||
| 268 | if (copy_siginfo_to_user(&frame->info, &ksig->info)) | 430 | /* Set up backchain. */ |
| 269 | return -EFAULT; | 431 | if (__put_user(regs->gprs[15], (addr_t __user *) frame)) |
| 270 | |||
| 271 | /* Create the ucontext. */ | ||
| 272 | err |= __put_user(0, &frame->uc.uc_flags); | ||
| 273 | err |= __put_user(NULL, &frame->uc.uc_link); | ||
| 274 | err |= __save_altstack(&frame->uc.uc_stack, regs->gprs[15]); | ||
| 275 | err |= save_sigregs(regs, &frame->uc.uc_mcontext); | ||
| 276 | err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
| 277 | if (err) | ||
| 278 | return -EFAULT; | 432 | return -EFAULT; |
| 279 | 433 | ||
| 280 | /* Set up to return from userspace. If provided, use a stub | 434 | /* Set up to return from userspace. If provided, use a stub |
| 281 | already in userspace. */ | 435 | already in userspace. */ |
| 282 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { | 436 | if (ksig->ka.sa.sa_flags & SA_RESTORER) { |
| 283 | regs->gprs[14] = (unsigned long) | 437 | restorer = (unsigned long) |
| 284 | ksig->ka.sa.sa_restorer | PSW_ADDR_AMODE; | 438 | ksig->ka.sa.sa_restorer | PSW_ADDR_AMODE; |
| 285 | } else { | 439 | } else { |
| 286 | regs->gprs[14] = (unsigned long) | 440 | __u16 __user *svc = &frame->svc_insn; |
| 287 | frame->retcode | PSW_ADDR_AMODE; | 441 | if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, svc)) |
| 288 | if (__put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, | ||
| 289 | (u16 __user *)(frame->retcode))) | ||
| 290 | return -EFAULT; | 442 | return -EFAULT; |
| 443 | restorer = (unsigned long) svc | PSW_ADDR_AMODE; | ||
| 291 | } | 444 | } |
| 292 | 445 | ||
| 293 | /* Set up backchain. */ | 446 | /* Create siginfo on the signal stack */ |
| 294 | if (__put_user(regs->gprs[15], (addr_t __user *) frame)) | 447 | if (copy_siginfo_to_user(&frame->info, &ksig->info)) |
| 448 | return -EFAULT; | ||
| 449 | |||
| 450 | /* Store registers needed to create the signal frame */ | ||
| 451 | store_sigregs(); | ||
| 452 | |||
| 453 | /* Create ucontext on the signal stack. */ | ||
| 454 | if (__put_user(uc_flags, &frame->uc.uc_flags) || | ||
| 455 | __put_user(NULL, &frame->uc.uc_link) || | ||
| 456 | __save_altstack(&frame->uc.uc_stack, regs->gprs[15]) || | ||
| 457 | save_sigregs(regs, &frame->uc.uc_mcontext) || | ||
| 458 | __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)) || | ||
| 459 | save_sigregs_ext(regs, &frame->uc.uc_mcontext_ext)) | ||
| 295 | return -EFAULT; | 460 | return -EFAULT; |
| 296 | 461 | ||
| 297 | /* Set up registers for signal handler */ | 462 | /* Set up registers for signal handler */ |
| 463 | regs->gprs[14] = restorer; | ||
| 298 | regs->gprs[15] = (unsigned long) frame; | 464 | regs->gprs[15] = (unsigned long) frame; |
| 299 | /* Force default amode and default user address space control. */ | 465 | /* Force default amode and default user address space control. */ |
| 300 | regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | | 466 | regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA | |
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 243c7e512600..6fd9e60101f1 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c | |||
| @@ -45,6 +45,7 @@ | |||
| 45 | #include <asm/debug.h> | 45 | #include <asm/debug.h> |
| 46 | #include <asm/os_info.h> | 46 | #include <asm/os_info.h> |
| 47 | #include <asm/sigp.h> | 47 | #include <asm/sigp.h> |
| 48 | #include <asm/idle.h> | ||
| 48 | #include "entry.h" | 49 | #include "entry.h" |
| 49 | 50 | ||
| 50 | enum { | 51 | enum { |
| @@ -82,7 +83,8 @@ DEFINE_MUTEX(smp_cpu_state_mutex); | |||
| 82 | /* | 83 | /* |
| 83 | * Signal processor helper functions. | 84 | * Signal processor helper functions. |
| 84 | */ | 85 | */ |
| 85 | static inline int __pcpu_sigp_relax(u16 addr, u8 order, u32 parm, u32 *status) | 86 | static inline int __pcpu_sigp_relax(u16 addr, u8 order, unsigned long parm, |
| 87 | u32 *status) | ||
| 86 | { | 88 | { |
| 87 | int cc; | 89 | int cc; |
| 88 | 90 | ||
| @@ -178,6 +180,9 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu) | |||
| 178 | goto out; | 180 | goto out; |
| 179 | } | 181 | } |
| 180 | #else | 182 | #else |
| 183 | if (MACHINE_HAS_VX) | ||
| 184 | lc->vector_save_area_addr = | ||
| 185 | (unsigned long) &lc->vector_save_area; | ||
| 181 | if (vdso_alloc_per_cpu(lc)) | 186 | if (vdso_alloc_per_cpu(lc)) |
| 182 | goto out; | 187 | goto out; |
| 183 | #endif | 188 | #endif |
| @@ -333,12 +338,6 @@ int smp_vcpu_scheduled(int cpu) | |||
| 333 | return pcpu_running(pcpu_devices + cpu); | 338 | return pcpu_running(pcpu_devices + cpu); |
| 334 | } | 339 | } |
| 335 | 340 | ||
| 336 | void smp_yield(void) | ||
| 337 | { | ||
| 338 | if (MACHINE_HAS_DIAG44) | ||
| 339 | asm volatile("diag 0,0,0x44"); | ||
| 340 | } | ||
| 341 | |||
| 342 | void smp_yield_cpu(int cpu) | 341 | void smp_yield_cpu(int cpu) |
| 343 | { | 342 | { |
| 344 | if (MACHINE_HAS_DIAG9C) | 343 | if (MACHINE_HAS_DIAG9C) |
| @@ -517,35 +516,53 @@ EXPORT_SYMBOL(smp_ctl_clear_bit); | |||
| 517 | static void __init smp_get_save_area(int cpu, u16 address) | 516 | static void __init smp_get_save_area(int cpu, u16 address) |
| 518 | { | 517 | { |
| 519 | void *lc = pcpu_devices[0].lowcore; | 518 | void *lc = pcpu_devices[0].lowcore; |
| 520 | struct save_area *save_area; | 519 | struct save_area_ext *sa_ext; |
| 520 | unsigned long vx_sa; | ||
| 521 | 521 | ||
| 522 | if (is_kdump_kernel()) | 522 | if (is_kdump_kernel()) |
| 523 | return; | 523 | return; |
| 524 | if (!OLDMEM_BASE && (address == boot_cpu_address || | 524 | if (!OLDMEM_BASE && (address == boot_cpu_address || |
| 525 | ipl_info.type != IPL_TYPE_FCP_DUMP)) | 525 | ipl_info.type != IPL_TYPE_FCP_DUMP)) |
| 526 | return; | 526 | return; |
| 527 | save_area = dump_save_area_create(cpu); | 527 | sa_ext = dump_save_area_create(cpu); |
| 528 | if (!save_area) | 528 | if (!sa_ext) |
| 529 | panic("could not allocate memory for save area\n"); | 529 | panic("could not allocate memory for save area\n"); |
| 530 | if (address == boot_cpu_address) { | 530 | if (address == boot_cpu_address) { |
| 531 | /* Copy the registers of the boot cpu. */ | 531 | /* Copy the registers of the boot cpu. */ |
| 532 | copy_oldmem_page(1, (void *) save_area, sizeof(*save_area), | 532 | copy_oldmem_page(1, (void *) &sa_ext->sa, sizeof(sa_ext->sa), |
| 533 | SAVE_AREA_BASE - PAGE_SIZE, 0); | 533 | SAVE_AREA_BASE - PAGE_SIZE, 0); |
| 534 | if (MACHINE_HAS_VX) | ||
| 535 | save_vx_regs_safe(sa_ext->vx_regs); | ||
| 534 | return; | 536 | return; |
| 535 | } | 537 | } |
| 536 | /* Get the registers of a non-boot cpu. */ | 538 | /* Get the registers of a non-boot cpu. */ |
| 537 | __pcpu_sigp_relax(address, SIGP_STOP_AND_STORE_STATUS, 0, NULL); | 539 | __pcpu_sigp_relax(address, SIGP_STOP_AND_STORE_STATUS, 0, NULL); |
| 538 | memcpy_real(save_area, lc + SAVE_AREA_BASE, sizeof(*save_area)); | 540 | memcpy_real(&sa_ext->sa, lc + SAVE_AREA_BASE, sizeof(sa_ext->sa)); |
| 541 | if (!MACHINE_HAS_VX) | ||
| 542 | return; | ||
| 543 | /* Get the VX registers */ | ||
| 544 | vx_sa = __get_free_page(GFP_KERNEL); | ||
| 545 | if (!vx_sa) | ||
| 546 | panic("could not allocate memory for VX save area\n"); | ||
| 547 | __pcpu_sigp_relax(address, SIGP_STORE_ADDITIONAL_STATUS, vx_sa, NULL); | ||
| 548 | memcpy(sa_ext->vx_regs, (void *) vx_sa, sizeof(sa_ext->vx_regs)); | ||
| 549 | free_page(vx_sa); | ||
| 539 | } | 550 | } |
| 540 | 551 | ||
| 541 | int smp_store_status(int cpu) | 552 | int smp_store_status(int cpu) |
| 542 | { | 553 | { |
| 554 | unsigned long vx_sa; | ||
| 543 | struct pcpu *pcpu; | 555 | struct pcpu *pcpu; |
| 544 | 556 | ||
| 545 | pcpu = pcpu_devices + cpu; | 557 | pcpu = pcpu_devices + cpu; |
| 546 | if (__pcpu_sigp_relax(pcpu->address, SIGP_STOP_AND_STORE_STATUS, | 558 | if (__pcpu_sigp_relax(pcpu->address, SIGP_STOP_AND_STORE_STATUS, |
| 547 | 0, NULL) != SIGP_CC_ORDER_CODE_ACCEPTED) | 559 | 0, NULL) != SIGP_CC_ORDER_CODE_ACCEPTED) |
| 548 | return -EIO; | 560 | return -EIO; |
| 561 | if (!MACHINE_HAS_VX) | ||
| 562 | return 0; | ||
| 563 | vx_sa = __pa(pcpu->lowcore->vector_save_area_addr); | ||
| 564 | __pcpu_sigp_relax(pcpu->address, SIGP_STORE_ADDITIONAL_STATUS, | ||
| 565 | vx_sa, NULL); | ||
| 549 | return 0; | 566 | return 0; |
| 550 | } | 567 | } |
| 551 | 568 | ||
| @@ -667,7 +684,7 @@ static void smp_start_secondary(void *cpuvoid) | |||
| 667 | cpu_init(); | 684 | cpu_init(); |
| 668 | preempt_disable(); | 685 | preempt_disable(); |
| 669 | init_cpu_timer(); | 686 | init_cpu_timer(); |
| 670 | init_cpu_vtimer(); | 687 | vtime_init(); |
| 671 | pfault_init(); | 688 | pfault_init(); |
| 672 | notify_cpu_starting(smp_processor_id()); | 689 | notify_cpu_starting(smp_processor_id()); |
| 673 | set_cpu_online(smp_processor_id(), true); | 690 | set_cpu_online(smp_processor_id(), true); |
| @@ -726,6 +743,7 @@ int __cpu_disable(void) | |||
| 726 | cregs[6] &= ~0xff000000UL; /* disable all I/O interrupts */ | 743 | cregs[6] &= ~0xff000000UL; /* disable all I/O interrupts */ |
| 727 | cregs[14] &= ~0x1f000000UL; /* disable most machine checks */ | 744 | cregs[14] &= ~0x1f000000UL; /* disable most machine checks */ |
| 728 | __ctl_load(cregs, 0, 15); | 745 | __ctl_load(cregs, 0, 15); |
| 746 | clear_cpu_flag(CIF_NOHZ_DELAY); | ||
| 729 | return 0; | 747 | return 0; |
| 730 | } | 748 | } |
| 731 | 749 | ||
| @@ -898,42 +916,6 @@ static struct attribute_group cpu_common_attr_group = { | |||
| 898 | .attrs = cpu_common_attrs, | 916 | .attrs = cpu_common_attrs, |
| 899 | }; | 917 | }; |
| 900 | 918 | ||
| 901 | static ssize_t show_idle_count(struct device *dev, | ||
| 902 | struct device_attribute *attr, char *buf) | ||
| 903 | { | ||
| 904 | struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); | ||
| 905 | unsigned long long idle_count; | ||
| 906 | unsigned int sequence; | ||
| 907 | |||
| 908 | do { | ||
| 909 | sequence = ACCESS_ONCE(idle->sequence); | ||
| 910 | idle_count = ACCESS_ONCE(idle->idle_count); | ||
| 911 | if (ACCESS_ONCE(idle->clock_idle_enter)) | ||
| 912 | idle_count++; | ||
| 913 | } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); | ||
| 914 | return sprintf(buf, "%llu\n", idle_count); | ||
| 915 | } | ||
| 916 | static DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); | ||
| 917 | |||
| 918 | static ssize_t show_idle_time(struct device *dev, | ||
| 919 | struct device_attribute *attr, char *buf) | ||
| 920 | { | ||
| 921 | struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); | ||
| 922 | unsigned long long now, idle_time, idle_enter, idle_exit; | ||
| 923 | unsigned int sequence; | ||
| 924 | |||
| 925 | do { | ||
| 926 | now = get_tod_clock(); | ||
| 927 | sequence = ACCESS_ONCE(idle->sequence); | ||
| 928 | idle_time = ACCESS_ONCE(idle->idle_time); | ||
| 929 | idle_enter = ACCESS_ONCE(idle->clock_idle_enter); | ||
| 930 | idle_exit = ACCESS_ONCE(idle->clock_idle_exit); | ||
| 931 | } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); | ||
| 932 | idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0; | ||
| 933 | return sprintf(buf, "%llu\n", idle_time >> 12); | ||
| 934 | } | ||
| 935 | static DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); | ||
| 936 | |||
| 937 | static struct attribute *cpu_online_attrs[] = { | 919 | static struct attribute *cpu_online_attrs[] = { |
| 938 | &dev_attr_idle_count.attr, | 920 | &dev_attr_idle_count.attr, |
| 939 | &dev_attr_idle_time_us.attr, | 921 | &dev_attr_idle_time_us.attr, |
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 4cef607f3711..69e980de0f62 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c | |||
| @@ -232,6 +232,19 @@ void update_vsyscall(struct timekeeper *tk) | |||
| 232 | vdso_data->wtom_clock_nsec -= nsecps; | 232 | vdso_data->wtom_clock_nsec -= nsecps; |
| 233 | vdso_data->wtom_clock_sec++; | 233 | vdso_data->wtom_clock_sec++; |
| 234 | } | 234 | } |
| 235 | |||
| 236 | vdso_data->xtime_coarse_sec = tk->xtime_sec; | ||
| 237 | vdso_data->xtime_coarse_nsec = | ||
| 238 | (long)(tk->tkr.xtime_nsec >> tk->tkr.shift); | ||
| 239 | vdso_data->wtom_coarse_sec = | ||
| 240 | vdso_data->xtime_coarse_sec + tk->wall_to_monotonic.tv_sec; | ||
| 241 | vdso_data->wtom_coarse_nsec = | ||
| 242 | vdso_data->xtime_coarse_nsec + tk->wall_to_monotonic.tv_nsec; | ||
| 243 | while (vdso_data->wtom_coarse_nsec >= NSEC_PER_SEC) { | ||
| 244 | vdso_data->wtom_coarse_nsec -= NSEC_PER_SEC; | ||
| 245 | vdso_data->wtom_coarse_sec++; | ||
| 246 | } | ||
| 247 | |||
| 235 | vdso_data->tk_mult = tk->tkr.mult; | 248 | vdso_data->tk_mult = tk->tkr.mult; |
| 236 | vdso_data->tk_shift = tk->tkr.shift; | 249 | vdso_data->tk_shift = tk->tkr.shift; |
| 237 | smp_wmb(); | 250 | smp_wmb(); |
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index 355a16c55702..b93bed76ea94 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c | |||
| @@ -464,15 +464,17 @@ static struct sched_domain_topology_level s390_topology[] = { | |||
| 464 | 464 | ||
| 465 | static int __init topology_init(void) | 465 | static int __init topology_init(void) |
| 466 | { | 466 | { |
| 467 | if (!MACHINE_HAS_TOPOLOGY) { | 467 | if (MACHINE_HAS_TOPOLOGY) |
| 468 | set_topology_timer(); | ||
| 469 | else | ||
| 468 | topology_update_polarization_simple(); | 470 | topology_update_polarization_simple(); |
| 469 | goto out; | ||
| 470 | } | ||
| 471 | set_topology_timer(); | ||
| 472 | out: | ||
| 473 | |||
| 474 | set_sched_topology(s390_topology); | ||
| 475 | |||
| 476 | return device_create_file(cpu_subsys.dev_root, &dev_attr_dispatching); | 471 | return device_create_file(cpu_subsys.dev_root, &dev_attr_dispatching); |
| 477 | } | 472 | } |
| 478 | device_initcall(topology_init); | 473 | device_initcall(topology_init); |
| 474 | |||
| 475 | static int __init early_topology_init(void) | ||
| 476 | { | ||
| 477 | set_sched_topology(s390_topology); | ||
| 478 | return 0; | ||
| 479 | } | ||
| 480 | early_initcall(early_topology_init); | ||
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c index c5762324d9ee..9ff5ecba26ab 100644 --- a/arch/s390/kernel/traps.c +++ b/arch/s390/kernel/traps.c | |||
| @@ -18,6 +18,8 @@ | |||
| 18 | #include <linux/ptrace.h> | 18 | #include <linux/ptrace.h> |
| 19 | #include <linux/sched.h> | 19 | #include <linux/sched.h> |
| 20 | #include <linux/mm.h> | 20 | #include <linux/mm.h> |
| 21 | #include <linux/slab.h> | ||
| 22 | #include <asm/switch_to.h> | ||
| 21 | #include "entry.h" | 23 | #include "entry.h" |
| 22 | 24 | ||
| 23 | int show_unhandled_signals = 1; | 25 | int show_unhandled_signals = 1; |
| @@ -58,15 +60,10 @@ int is_valid_bugaddr(unsigned long addr) | |||
| 58 | return 1; | 60 | return 1; |
| 59 | } | 61 | } |
| 60 | 62 | ||
| 61 | static void __kprobes do_trap(struct pt_regs *regs, | 63 | void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str) |
| 62 | int si_signo, int si_code, char *str) | ||
| 63 | { | 64 | { |
| 64 | siginfo_t info; | 65 | siginfo_t info; |
| 65 | 66 | ||
| 66 | if (notify_die(DIE_TRAP, str, regs, 0, | ||
| 67 | regs->int_code, si_signo) == NOTIFY_STOP) | ||
| 68 | return; | ||
| 69 | |||
| 70 | if (user_mode(regs)) { | 67 | if (user_mode(regs)) { |
| 71 | info.si_signo = si_signo; | 68 | info.si_signo = si_signo; |
| 72 | info.si_errno = 0; | 69 | info.si_errno = 0; |
| @@ -90,6 +87,15 @@ static void __kprobes do_trap(struct pt_regs *regs, | |||
| 90 | } | 87 | } |
| 91 | } | 88 | } |
| 92 | 89 | ||
| 90 | static void __kprobes do_trap(struct pt_regs *regs, int si_signo, int si_code, | ||
| 91 | char *str) | ||
| 92 | { | ||
| 93 | if (notify_die(DIE_TRAP, str, regs, 0, | ||
| 94 | regs->int_code, si_signo) == NOTIFY_STOP) | ||
| 95 | return; | ||
| 96 | do_report_trap(regs, si_signo, si_code, str); | ||
| 97 | } | ||
| 98 | |||
| 93 | void __kprobes do_per_trap(struct pt_regs *regs) | 99 | void __kprobes do_per_trap(struct pt_regs *regs) |
| 94 | { | 100 | { |
| 95 | siginfo_t info; | 101 | siginfo_t info; |
| @@ -178,6 +184,7 @@ void __kprobes illegal_op(struct pt_regs *regs) | |||
| 178 | siginfo_t info; | 184 | siginfo_t info; |
| 179 | __u8 opcode[6]; | 185 | __u8 opcode[6]; |
| 180 | __u16 __user *location; | 186 | __u16 __user *location; |
| 187 | int is_uprobe_insn = 0; | ||
| 181 | int signal = 0; | 188 | int signal = 0; |
| 182 | 189 | ||
| 183 | location = get_trap_ip(regs); | 190 | location = get_trap_ip(regs); |
| @@ -194,6 +201,10 @@ void __kprobes illegal_op(struct pt_regs *regs) | |||
| 194 | force_sig_info(SIGTRAP, &info, current); | 201 | force_sig_info(SIGTRAP, &info, current); |
| 195 | } else | 202 | } else |
| 196 | signal = SIGILL; | 203 | signal = SIGILL; |
| 204 | #ifdef CONFIG_UPROBES | ||
| 205 | } else if (*((__u16 *) opcode) == UPROBE_SWBP_INSN) { | ||
| 206 | is_uprobe_insn = 1; | ||
| 207 | #endif | ||
| 197 | #ifdef CONFIG_MATHEMU | 208 | #ifdef CONFIG_MATHEMU |
| 198 | } else if (opcode[0] == 0xb3) { | 209 | } else if (opcode[0] == 0xb3) { |
| 199 | if (get_user(*((__u16 *) (opcode+2)), location+1)) | 210 | if (get_user(*((__u16 *) (opcode+2)), location+1)) |
| @@ -219,11 +230,13 @@ void __kprobes illegal_op(struct pt_regs *regs) | |||
| 219 | #endif | 230 | #endif |
| 220 | } else | 231 | } else |
| 221 | signal = SIGILL; | 232 | signal = SIGILL; |
| 222 | } else { | 233 | } |
| 223 | /* | 234 | /* |
| 224 | * If we get an illegal op in kernel mode, send it through the | 235 | * We got either an illegal op in kernel mode, or user space trapped |
| 225 | * kprobes notifier. If kprobes doesn't pick it up, SIGILL | 236 | * on a uprobes illegal instruction. See if kprobes or uprobes picks |
| 226 | */ | 237 | * it up. If not, SIGILL. |
| 238 | */ | ||
| 239 | if (is_uprobe_insn || !user_mode(regs)) { | ||
| 227 | if (notify_die(DIE_BPT, "bpt", regs, 0, | 240 | if (notify_die(DIE_BPT, "bpt", regs, 0, |
| 228 | 3, SIGTRAP) != NOTIFY_STOP) | 241 | 3, SIGTRAP) != NOTIFY_STOP) |
| 229 | signal = SIGILL; | 242 | signal = SIGILL; |
| @@ -292,6 +305,74 @@ DO_ERROR_INFO(specification_exception, SIGILL, ILL_ILLOPN, | |||
| 292 | "specification exception"); | 305 | "specification exception"); |
| 293 | #endif | 306 | #endif |
| 294 | 307 | ||
| 308 | #ifdef CONFIG_64BIT | ||
| 309 | int alloc_vector_registers(struct task_struct *tsk) | ||
| 310 | { | ||
| 311 | __vector128 *vxrs; | ||
| 312 | int i; | ||
| 313 | |||
| 314 | /* Allocate vector register save area. */ | ||
| 315 | vxrs = kzalloc(sizeof(__vector128) * __NUM_VXRS, | ||
| 316 | GFP_KERNEL|__GFP_REPEAT); | ||
| 317 | if (!vxrs) | ||
| 318 | return -ENOMEM; | ||
| 319 | preempt_disable(); | ||
| 320 | if (tsk == current) | ||
| 321 | save_fp_regs(tsk->thread.fp_regs.fprs); | ||
| 322 | /* Copy the 16 floating point registers */ | ||
| 323 | for (i = 0; i < 16; i++) | ||
| 324 | *(freg_t *) &vxrs[i] = tsk->thread.fp_regs.fprs[i]; | ||
| 325 | tsk->thread.vxrs = vxrs; | ||
| 326 | if (tsk == current) { | ||
| 327 | __ctl_set_bit(0, 17); | ||
| 328 | restore_vx_regs(vxrs); | ||
| 329 | } | ||
| 330 | preempt_enable(); | ||
| 331 | return 0; | ||
| 332 | } | ||
| 333 | |||
| 334 | void vector_exception(struct pt_regs *regs) | ||
| 335 | { | ||
| 336 | int si_code, vic; | ||
| 337 | |||
| 338 | if (!MACHINE_HAS_VX) { | ||
| 339 | do_trap(regs, SIGILL, ILL_ILLOPN, "illegal operation"); | ||
| 340 | return; | ||
| 341 | } | ||
| 342 | |||
| 343 | /* get vector interrupt code from fpc */ | ||
| 344 | asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc)); | ||
| 345 | vic = (current->thread.fp_regs.fpc & 0xf00) >> 8; | ||
| 346 | switch (vic) { | ||
| 347 | case 1: /* invalid vector operation */ | ||
| 348 | si_code = FPE_FLTINV; | ||
| 349 | break; | ||
| 350 | case 2: /* division by zero */ | ||
| 351 | si_code = FPE_FLTDIV; | ||
| 352 | break; | ||
| 353 | case 3: /* overflow */ | ||
| 354 | si_code = FPE_FLTOVF; | ||
| 355 | break; | ||
| 356 | case 4: /* underflow */ | ||
| 357 | si_code = FPE_FLTUND; | ||
| 358 | break; | ||
| 359 | case 5: /* inexact */ | ||
| 360 | si_code = FPE_FLTRES; | ||
| 361 | break; | ||
| 362 | default: /* unknown cause */ | ||
| 363 | si_code = 0; | ||
| 364 | } | ||
| 365 | do_trap(regs, SIGFPE, si_code, "vector exception"); | ||
| 366 | } | ||
| 367 | |||
| 368 | static int __init disable_vector_extension(char *str) | ||
| 369 | { | ||
| 370 | S390_lowcore.machine_flags &= ~MACHINE_FLAG_VX; | ||
| 371 | return 1; | ||
| 372 | } | ||
| 373 | __setup("novx", disable_vector_extension); | ||
| 374 | #endif | ||
| 375 | |||
| 295 | void data_exception(struct pt_regs *regs) | 376 | void data_exception(struct pt_regs *regs) |
| 296 | { | 377 | { |
| 297 | __u16 __user *location; | 378 | __u16 __user *location; |
| @@ -357,6 +438,18 @@ void data_exception(struct pt_regs *regs) | |||
| 357 | } | 438 | } |
| 358 | } | 439 | } |
| 359 | #endif | 440 | #endif |
| 441 | #ifdef CONFIG_64BIT | ||
| 442 | /* Check for vector register enablement */ | ||
| 443 | if (MACHINE_HAS_VX && !current->thread.vxrs && | ||
| 444 | (current->thread.fp_regs.fpc & FPC_DXC_MASK) == 0xfe00) { | ||
| 445 | alloc_vector_registers(current); | ||
| 446 | /* Vector data exception is suppressing, rewind psw. */ | ||
| 447 | regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16); | ||
| 448 | clear_pt_regs_flag(regs, PIF_PER_TRAP); | ||
| 449 | return; | ||
| 450 | } | ||
| 451 | #endif | ||
| 452 | |||
| 360 | if (current->thread.fp_regs.fpc & FPC_DXC_MASK) | 453 | if (current->thread.fp_regs.fpc & FPC_DXC_MASK) |
| 361 | signal = SIGFPE; | 454 | signal = SIGFPE; |
| 362 | else | 455 | else |
diff --git a/arch/s390/kernel/uprobes.c b/arch/s390/kernel/uprobes.c new file mode 100644 index 000000000000..956f4f7a591c --- /dev/null +++ b/arch/s390/kernel/uprobes.c | |||
| @@ -0,0 +1,332 @@ | |||
| 1 | /* | ||
| 2 | * User-space Probes (UProbes) for s390 | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2014 | ||
| 5 | * Author(s): Jan Willeke, | ||
| 6 | */ | ||
| 7 | |||
| 8 | #include <linux/kprobes.h> | ||
| 9 | #include <linux/uaccess.h> | ||
| 10 | #include <linux/uprobes.h> | ||
| 11 | #include <linux/compat.h> | ||
| 12 | #include <linux/kdebug.h> | ||
| 13 | #include <asm/switch_to.h> | ||
| 14 | #include <asm/facility.h> | ||
| 15 | #include <asm/dis.h> | ||
| 16 | #include "entry.h" | ||
| 17 | |||
| 18 | #define UPROBE_TRAP_NR UINT_MAX | ||
| 19 | |||
| 20 | int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, | ||
| 21 | unsigned long addr) | ||
| 22 | { | ||
| 23 | return probe_is_prohibited_opcode(auprobe->insn); | ||
| 24 | } | ||
| 25 | |||
| 26 | int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
| 27 | { | ||
| 28 | if (psw_bits(regs->psw).eaba == PSW_AMODE_24BIT) | ||
| 29 | return -EINVAL; | ||
| 30 | if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_AMODE_31BIT) | ||
| 31 | return -EINVAL; | ||
| 32 | clear_pt_regs_flag(regs, PIF_PER_TRAP); | ||
| 33 | auprobe->saved_per = psw_bits(regs->psw).r; | ||
| 34 | auprobe->saved_int_code = regs->int_code; | ||
| 35 | regs->int_code = UPROBE_TRAP_NR; | ||
| 36 | regs->psw.addr = current->utask->xol_vaddr; | ||
| 37 | set_tsk_thread_flag(current, TIF_UPROBE_SINGLESTEP); | ||
| 38 | update_cr_regs(current); | ||
| 39 | return 0; | ||
| 40 | } | ||
| 41 | |||
| 42 | bool arch_uprobe_xol_was_trapped(struct task_struct *tsk) | ||
| 43 | { | ||
| 44 | struct pt_regs *regs = task_pt_regs(tsk); | ||
| 45 | |||
| 46 | if (regs->int_code != UPROBE_TRAP_NR) | ||
| 47 | return true; | ||
| 48 | return false; | ||
| 49 | } | ||
| 50 | |||
| 51 | int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
| 52 | { | ||
| 53 | int fixup = probe_get_fixup_type(auprobe->insn); | ||
| 54 | struct uprobe_task *utask = current->utask; | ||
| 55 | |||
| 56 | clear_tsk_thread_flag(current, TIF_UPROBE_SINGLESTEP); | ||
| 57 | update_cr_regs(current); | ||
| 58 | psw_bits(regs->psw).r = auprobe->saved_per; | ||
| 59 | regs->int_code = auprobe->saved_int_code; | ||
| 60 | |||
| 61 | if (fixup & FIXUP_PSW_NORMAL) | ||
| 62 | regs->psw.addr += utask->vaddr - utask->xol_vaddr; | ||
| 63 | if (fixup & FIXUP_RETURN_REGISTER) { | ||
| 64 | int reg = (auprobe->insn[0] & 0xf0) >> 4; | ||
| 65 | |||
| 66 | regs->gprs[reg] += utask->vaddr - utask->xol_vaddr; | ||
| 67 | } | ||
| 68 | if (fixup & FIXUP_BRANCH_NOT_TAKEN) { | ||
| 69 | int ilen = insn_length(auprobe->insn[0] >> 8); | ||
| 70 | |||
| 71 | if (regs->psw.addr - utask->xol_vaddr == ilen) | ||
| 72 | regs->psw.addr = utask->vaddr + ilen; | ||
| 73 | } | ||
| 74 | /* If per tracing was active generate trap */ | ||
| 75 | if (regs->psw.mask & PSW_MASK_PER) | ||
| 76 | do_per_trap(regs); | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, | ||
| 81 | void *data) | ||
| 82 | { | ||
| 83 | struct die_args *args = data; | ||
| 84 | struct pt_regs *regs = args->regs; | ||
| 85 | |||
| 86 | if (!user_mode(regs)) | ||
| 87 | return NOTIFY_DONE; | ||
| 88 | if (regs->int_code & 0x200) /* Trap during transaction */ | ||
| 89 | return NOTIFY_DONE; | ||
| 90 | switch (val) { | ||
| 91 | case DIE_BPT: | ||
| 92 | if (uprobe_pre_sstep_notifier(regs)) | ||
| 93 | return NOTIFY_STOP; | ||
| 94 | break; | ||
| 95 | case DIE_SSTEP: | ||
| 96 | if (uprobe_post_sstep_notifier(regs)) | ||
| 97 | return NOTIFY_STOP; | ||
| 98 | default: | ||
| 99 | break; | ||
| 100 | } | ||
| 101 | return NOTIFY_DONE; | ||
| 102 | } | ||
| 103 | |||
| 104 | void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
| 105 | { | ||
| 106 | clear_thread_flag(TIF_UPROBE_SINGLESTEP); | ||
| 107 | regs->int_code = auprobe->saved_int_code; | ||
| 108 | regs->psw.addr = current->utask->vaddr; | ||
| 109 | } | ||
| 110 | |||
| 111 | unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline, | ||
| 112 | struct pt_regs *regs) | ||
| 113 | { | ||
| 114 | unsigned long orig; | ||
| 115 | |||
| 116 | orig = regs->gprs[14]; | ||
| 117 | regs->gprs[14] = trampoline; | ||
| 118 | return orig; | ||
| 119 | } | ||
| 120 | |||
| 121 | /* Instruction Emulation */ | ||
| 122 | |||
| 123 | static void adjust_psw_addr(psw_t *psw, unsigned long len) | ||
| 124 | { | ||
| 125 | psw->addr = __rewind_psw(*psw, -len); | ||
| 126 | } | ||
| 127 | |||
| 128 | #define EMU_ILLEGAL_OP 1 | ||
| 129 | #define EMU_SPECIFICATION 2 | ||
| 130 | #define EMU_ADDRESSING 3 | ||
| 131 | |||
| 132 | #define emu_load_ril(ptr, output) \ | ||
| 133 | ({ \ | ||
| 134 | unsigned int mask = sizeof(*(ptr)) - 1; \ | ||
| 135 | __typeof__(*(ptr)) input; \ | ||
| 136 | int __rc = 0; \ | ||
| 137 | \ | ||
| 138 | if (!test_facility(34)) \ | ||
| 139 | __rc = EMU_ILLEGAL_OP; \ | ||
| 140 | else if ((u64 __force)ptr & mask) \ | ||
| 141 | __rc = EMU_SPECIFICATION; \ | ||
| 142 | else if (get_user(input, ptr)) \ | ||
| 143 | __rc = EMU_ADDRESSING; \ | ||
| 144 | else \ | ||
| 145 | *(output) = input; \ | ||
| 146 | __rc; \ | ||
| 147 | }) | ||
| 148 | |||
| 149 | #define emu_store_ril(ptr, input) \ | ||
| 150 | ({ \ | ||
| 151 | unsigned int mask = sizeof(*(ptr)) - 1; \ | ||
| 152 | int __rc = 0; \ | ||
| 153 | \ | ||
| 154 | if (!test_facility(34)) \ | ||
| 155 | __rc = EMU_ILLEGAL_OP; \ | ||
| 156 | else if ((u64 __force)ptr & mask) \ | ||
| 157 | __rc = EMU_SPECIFICATION; \ | ||
| 158 | else if (put_user(*(input), ptr)) \ | ||
| 159 | __rc = EMU_ADDRESSING; \ | ||
| 160 | __rc; \ | ||
| 161 | }) | ||
| 162 | |||
| 163 | #define emu_cmp_ril(regs, ptr, cmp) \ | ||
| 164 | ({ \ | ||
| 165 | unsigned int mask = sizeof(*(ptr)) - 1; \ | ||
| 166 | __typeof__(*(ptr)) input; \ | ||
| 167 | int __rc = 0; \ | ||
| 168 | \ | ||
| 169 | if (!test_facility(34)) \ | ||
| 170 | __rc = EMU_ILLEGAL_OP; \ | ||
| 171 | else if ((u64 __force)ptr & mask) \ | ||
| 172 | __rc = EMU_SPECIFICATION; \ | ||
| 173 | else if (get_user(input, ptr)) \ | ||
| 174 | __rc = EMU_ADDRESSING; \ | ||
| 175 | else if (input > *(cmp)) \ | ||
| 176 | psw_bits((regs)->psw).cc = 1; \ | ||
| 177 | else if (input < *(cmp)) \ | ||
| 178 | psw_bits((regs)->psw).cc = 2; \ | ||
| 179 | else \ | ||
| 180 | psw_bits((regs)->psw).cc = 0; \ | ||
| 181 | __rc; \ | ||
| 182 | }) | ||
| 183 | |||
| 184 | struct insn_ril { | ||
| 185 | u8 opc0; | ||
| 186 | u8 reg : 4; | ||
| 187 | u8 opc1 : 4; | ||
| 188 | s32 disp; | ||
| 189 | } __packed; | ||
| 190 | |||
| 191 | union split_register { | ||
| 192 | u64 u64; | ||
| 193 | u32 u32[2]; | ||
| 194 | u16 u16[4]; | ||
| 195 | s64 s64; | ||
| 196 | s32 s32[2]; | ||
| 197 | s16 s16[4]; | ||
| 198 | }; | ||
| 199 | |||
| 200 | /* | ||
| 201 | * pc relative instructions are emulated, since parameters may not be | ||
| 202 | * accessible from the xol area due to range limitations. | ||
| 203 | */ | ||
| 204 | static void handle_insn_ril(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
| 205 | { | ||
| 206 | union split_register *rx; | ||
| 207 | struct insn_ril *insn; | ||
| 208 | unsigned int ilen; | ||
| 209 | void *uptr; | ||
| 210 | int rc = 0; | ||
| 211 | |||
| 212 | insn = (struct insn_ril *) &auprobe->insn; | ||
| 213 | rx = (union split_register *) ®s->gprs[insn->reg]; | ||
| 214 | uptr = (void *)(regs->psw.addr + (insn->disp * 2)); | ||
| 215 | ilen = insn_length(insn->opc0); | ||
| 216 | |||
| 217 | switch (insn->opc0) { | ||
| 218 | case 0xc0: | ||
| 219 | switch (insn->opc1) { | ||
| 220 | case 0x00: /* larl */ | ||
| 221 | rx->u64 = (unsigned long)uptr; | ||
| 222 | break; | ||
| 223 | } | ||
| 224 | break; | ||
| 225 | case 0xc4: | ||
| 226 | switch (insn->opc1) { | ||
| 227 | case 0x02: /* llhrl */ | ||
| 228 | rc = emu_load_ril((u16 __user *)uptr, &rx->u32[1]); | ||
| 229 | break; | ||
| 230 | case 0x04: /* lghrl */ | ||
| 231 | rc = emu_load_ril((s16 __user *)uptr, &rx->u64); | ||
| 232 | break; | ||
| 233 | case 0x05: /* lhrl */ | ||
| 234 | rc = emu_load_ril((s16 __user *)uptr, &rx->u32[1]); | ||
| 235 | break; | ||
| 236 | case 0x06: /* llghrl */ | ||
| 237 | rc = emu_load_ril((u16 __user *)uptr, &rx->u64); | ||
| 238 | break; | ||
| 239 | case 0x08: /* lgrl */ | ||
| 240 | rc = emu_load_ril((u64 __user *)uptr, &rx->u64); | ||
| 241 | break; | ||
| 242 | case 0x0c: /* lgfrl */ | ||
| 243 | rc = emu_load_ril((s32 __user *)uptr, &rx->u64); | ||
| 244 | break; | ||
| 245 | case 0x0d: /* lrl */ | ||
| 246 | rc = emu_load_ril((u32 __user *)uptr, &rx->u32[1]); | ||
| 247 | break; | ||
| 248 | case 0x0e: /* llgfrl */ | ||
| 249 | rc = emu_load_ril((u32 __user *)uptr, &rx->u64); | ||
| 250 | break; | ||
| 251 | case 0x07: /* sthrl */ | ||
| 252 | rc = emu_store_ril((u16 __user *)uptr, &rx->u16[3]); | ||
| 253 | break; | ||
| 254 | case 0x0b: /* stgrl */ | ||
| 255 | rc = emu_store_ril((u64 __user *)uptr, &rx->u64); | ||
| 256 | break; | ||
| 257 | case 0x0f: /* strl */ | ||
| 258 | rc = emu_store_ril((u32 __user *)uptr, &rx->u32[1]); | ||
| 259 | break; | ||
| 260 | } | ||
| 261 | break; | ||
| 262 | case 0xc6: | ||
| 263 | switch (insn->opc1) { | ||
| 264 | case 0x02: /* pfdrl */ | ||
| 265 | if (!test_facility(34)) | ||
| 266 | rc = EMU_ILLEGAL_OP; | ||
| 267 | break; | ||
| 268 | case 0x04: /* cghrl */ | ||
| 269 | rc = emu_cmp_ril(regs, (s16 __user *)uptr, &rx->s64); | ||
| 270 | break; | ||
| 271 | case 0x05: /* chrl */ | ||
| 272 | rc = emu_cmp_ril(regs, (s16 __user *)uptr, &rx->s32[1]); | ||
| 273 | break; | ||
| 274 | case 0x06: /* clghrl */ | ||
| 275 | rc = emu_cmp_ril(regs, (u16 __user *)uptr, &rx->u64); | ||
| 276 | break; | ||
| 277 | case 0x07: /* clhrl */ | ||
| 278 | rc = emu_cmp_ril(regs, (u16 __user *)uptr, &rx->u32[1]); | ||
| 279 | break; | ||
| 280 | case 0x08: /* cgrl */ | ||
| 281 | rc = emu_cmp_ril(regs, (s64 __user *)uptr, &rx->s64); | ||
| 282 | break; | ||
| 283 | case 0x0a: /* clgrl */ | ||
| 284 | rc = emu_cmp_ril(regs, (u64 __user *)uptr, &rx->u64); | ||
| 285 | break; | ||
| 286 | case 0x0c: /* cgfrl */ | ||
| 287 | rc = emu_cmp_ril(regs, (s32 __user *)uptr, &rx->s64); | ||
| 288 | break; | ||
| 289 | case 0x0d: /* crl */ | ||
| 290 | rc = emu_cmp_ril(regs, (s32 __user *)uptr, &rx->s32[1]); | ||
| 291 | break; | ||
| 292 | case 0x0e: /* clgfrl */ | ||
| 293 | rc = emu_cmp_ril(regs, (u32 __user *)uptr, &rx->u64); | ||
| 294 | break; | ||
| 295 | case 0x0f: /* clrl */ | ||
| 296 | rc = emu_cmp_ril(regs, (u32 __user *)uptr, &rx->u32[1]); | ||
| 297 | break; | ||
| 298 | } | ||
| 299 | break; | ||
| 300 | } | ||
| 301 | adjust_psw_addr(®s->psw, ilen); | ||
| 302 | switch (rc) { | ||
| 303 | case EMU_ILLEGAL_OP: | ||
| 304 | regs->int_code = ilen << 16 | 0x0001; | ||
| 305 | do_report_trap(regs, SIGILL, ILL_ILLOPC, NULL); | ||
| 306 | break; | ||
| 307 | case EMU_SPECIFICATION: | ||
| 308 | regs->int_code = ilen << 16 | 0x0006; | ||
| 309 | do_report_trap(regs, SIGILL, ILL_ILLOPC , NULL); | ||
| 310 | break; | ||
| 311 | case EMU_ADDRESSING: | ||
| 312 | regs->int_code = ilen << 16 | 0x0005; | ||
| 313 | do_report_trap(regs, SIGSEGV, SEGV_MAPERR, NULL); | ||
| 314 | break; | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 318 | bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) | ||
| 319 | { | ||
| 320 | if ((psw_bits(regs->psw).eaba == PSW_AMODE_24BIT) || | ||
| 321 | ((psw_bits(regs->psw).eaba == PSW_AMODE_31BIT) && | ||
| 322 | !is_compat_task())) { | ||
| 323 | regs->psw.addr = __rewind_psw(regs->psw, UPROBE_SWBP_INSN_SIZE); | ||
| 324 | do_report_trap(regs, SIGILL, ILL_ILLADR, NULL); | ||
| 325 | return true; | ||
| 326 | } | ||
| 327 | if (probe_is_insn_relative_long(auprobe->insn)) { | ||
| 328 | handle_insn_ril(auprobe, regs); | ||
| 329 | return true; | ||
| 330 | } | ||
| 331 | return false; | ||
| 332 | } | ||
diff --git a/arch/s390/kernel/vdso32/clock_getres.S b/arch/s390/kernel/vdso32/clock_getres.S index 36aaa25d05da..eca3f001f081 100644 --- a/arch/s390/kernel/vdso32/clock_getres.S +++ b/arch/s390/kernel/vdso32/clock_getres.S | |||
| @@ -19,14 +19,20 @@ | |||
| 19 | .type __kernel_clock_getres,@function | 19 | .type __kernel_clock_getres,@function |
| 20 | __kernel_clock_getres: | 20 | __kernel_clock_getres: |
| 21 | .cfi_startproc | 21 | .cfi_startproc |
| 22 | basr %r1,0 | ||
| 23 | la %r1,4f-.(%r1) | ||
| 22 | chi %r2,__CLOCK_REALTIME | 24 | chi %r2,__CLOCK_REALTIME |
| 23 | je 0f | 25 | je 0f |
| 24 | chi %r2,__CLOCK_MONOTONIC | 26 | chi %r2,__CLOCK_MONOTONIC |
| 27 | je 0f | ||
| 28 | la %r1,5f-4f(%r1) | ||
| 29 | chi %r2,__CLOCK_REALTIME_COARSE | ||
| 30 | je 0f | ||
| 31 | chi %r2,__CLOCK_MONOTONIC_COARSE | ||
| 25 | jne 3f | 32 | jne 3f |
| 26 | 0: ltr %r3,%r3 | 33 | 0: ltr %r3,%r3 |
| 27 | jz 2f /* res == NULL */ | 34 | jz 2f /* res == NULL */ |
| 28 | basr %r1,0 | 35 | 1: l %r0,0(%r1) |
| 29 | 1: l %r0,4f-1b(%r1) | ||
| 30 | xc 0(4,%r3),0(%r3) /* set tp->tv_sec to zero */ | 36 | xc 0(4,%r3),0(%r3) /* set tp->tv_sec to zero */ |
| 31 | st %r0,4(%r3) /* store tp->tv_usec */ | 37 | st %r0,4(%r3) /* store tp->tv_usec */ |
| 32 | 2: lhi %r2,0 | 38 | 2: lhi %r2,0 |
| @@ -35,5 +41,6 @@ __kernel_clock_getres: | |||
| 35 | svc 0 | 41 | svc 0 |
| 36 | br %r14 | 42 | br %r14 |
| 37 | 4: .long __CLOCK_REALTIME_RES | 43 | 4: .long __CLOCK_REALTIME_RES |
| 44 | 5: .long __CLOCK_COARSE_RES | ||
| 38 | .cfi_endproc | 45 | .cfi_endproc |
| 39 | .size __kernel_clock_getres,.-__kernel_clock_getres | 46 | .size __kernel_clock_getres,.-__kernel_clock_getres |
diff --git a/arch/s390/kernel/vdso32/clock_gettime.S b/arch/s390/kernel/vdso32/clock_gettime.S index 7cf18f8d4cb4..48c2206a3956 100644 --- a/arch/s390/kernel/vdso32/clock_gettime.S +++ b/arch/s390/kernel/vdso32/clock_gettime.S | |||
| @@ -21,8 +21,12 @@ __kernel_clock_gettime: | |||
| 21 | .cfi_startproc | 21 | .cfi_startproc |
| 22 | basr %r5,0 | 22 | basr %r5,0 |
| 23 | 0: al %r5,21f-0b(%r5) /* get &_vdso_data */ | 23 | 0: al %r5,21f-0b(%r5) /* get &_vdso_data */ |
| 24 | chi %r2,__CLOCK_REALTIME_COARSE | ||
| 25 | je 10f | ||
| 24 | chi %r2,__CLOCK_REALTIME | 26 | chi %r2,__CLOCK_REALTIME |
| 25 | je 11f | 27 | je 11f |
| 28 | chi %r2,__CLOCK_MONOTONIC_COARSE | ||
| 29 | je 9f | ||
| 26 | chi %r2,__CLOCK_MONOTONIC | 30 | chi %r2,__CLOCK_MONOTONIC |
| 27 | jne 19f | 31 | jne 19f |
| 28 | 32 | ||
| @@ -30,8 +34,8 @@ __kernel_clock_gettime: | |||
| 30 | 1: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ | 34 | 1: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ |
| 31 | tml %r4,0x0001 /* pending update ? loop */ | 35 | tml %r4,0x0001 /* pending update ? loop */ |
| 32 | jnz 1b | 36 | jnz 1b |
| 33 | stck 24(%r15) /* Store TOD clock */ | 37 | stcke 24(%r15) /* Store TOD clock */ |
| 34 | lm %r0,%r1,24(%r15) | 38 | lm %r0,%r1,25(%r15) |
| 35 | s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ | 39 | s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ |
| 36 | sl %r1,__VDSO_XTIME_STAMP+4(%r5) | 40 | sl %r1,__VDSO_XTIME_STAMP+4(%r5) |
| 37 | brc 3,2f | 41 | brc 3,2f |
| @@ -68,12 +72,32 @@ __kernel_clock_gettime: | |||
| 68 | lhi %r2,0 | 72 | lhi %r2,0 |
| 69 | br %r14 | 73 | br %r14 |
| 70 | 74 | ||
| 75 | /* CLOCK_MONOTONIC_COARSE */ | ||
| 76 | 9: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ | ||
| 77 | tml %r4,0x0001 /* pending update ? loop */ | ||
| 78 | jnz 9b | ||
| 79 | l %r2,__VDSO_WTOM_CRS_SEC+4(%r5) | ||
| 80 | l %r1,__VDSO_WTOM_CRS_NSEC+4(%r5) | ||
| 81 | cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */ | ||
| 82 | jne 9b | ||
| 83 | j 8b | ||
| 84 | |||
| 85 | /* CLOCK_REALTIME_COARSE */ | ||
| 86 | 10: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ | ||
| 87 | tml %r4,0x0001 /* pending update ? loop */ | ||
| 88 | jnz 10b | ||
| 89 | l %r2,__VDSO_XTIME_CRS_SEC+4(%r5) | ||
| 90 | l %r1,__VDSO_XTIME_CRS_NSEC+4(%r5) | ||
| 91 | cl %r4,__VDSO_UPD_COUNT+4(%r5) /* check update counter */ | ||
| 92 | jne 10b | ||
| 93 | j 17f | ||
| 94 | |||
| 71 | /* CLOCK_REALTIME */ | 95 | /* CLOCK_REALTIME */ |
| 72 | 11: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ | 96 | 11: l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ |
| 73 | tml %r4,0x0001 /* pending update ? loop */ | 97 | tml %r4,0x0001 /* pending update ? loop */ |
| 74 | jnz 11b | 98 | jnz 11b |
| 75 | stck 24(%r15) /* Store TOD clock */ | 99 | stcke 24(%r15) /* Store TOD clock */ |
| 76 | lm %r0,%r1,24(%r15) | 100 | lm %r0,%r1,25(%r15) |
| 77 | s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ | 101 | s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ |
| 78 | sl %r1,__VDSO_XTIME_STAMP+4(%r5) | 102 | sl %r1,__VDSO_XTIME_STAMP+4(%r5) |
| 79 | brc 3,12f | 103 | brc 3,12f |
diff --git a/arch/s390/kernel/vdso32/gettimeofday.S b/arch/s390/kernel/vdso32/gettimeofday.S index fd621a950f7c..60def5f562db 100644 --- a/arch/s390/kernel/vdso32/gettimeofday.S +++ b/arch/s390/kernel/vdso32/gettimeofday.S | |||
| @@ -29,8 +29,8 @@ __kernel_gettimeofday: | |||
| 29 | l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ | 29 | l %r4,__VDSO_UPD_COUNT+4(%r5) /* load update counter */ |
| 30 | tml %r4,0x0001 /* pending update ? loop */ | 30 | tml %r4,0x0001 /* pending update ? loop */ |
| 31 | jnz 1b | 31 | jnz 1b |
| 32 | stck 24(%r15) /* Store TOD clock */ | 32 | stcke 24(%r15) /* Store TOD clock */ |
| 33 | lm %r0,%r1,24(%r15) | 33 | lm %r0,%r1,25(%r15) |
| 34 | s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ | 34 | s %r0,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ |
| 35 | sl %r1,__VDSO_XTIME_STAMP+4(%r5) | 35 | sl %r1,__VDSO_XTIME_STAMP+4(%r5) |
| 36 | brc 3,3f | 36 | brc 3,3f |
diff --git a/arch/s390/kernel/vdso64/clock_getres.S b/arch/s390/kernel/vdso64/clock_getres.S index 34deba7c7ed1..c8513deb8c66 100644 --- a/arch/s390/kernel/vdso64/clock_getres.S +++ b/arch/s390/kernel/vdso64/clock_getres.S | |||
| @@ -19,6 +19,12 @@ | |||
| 19 | .type __kernel_clock_getres,@function | 19 | .type __kernel_clock_getres,@function |
| 20 | __kernel_clock_getres: | 20 | __kernel_clock_getres: |
| 21 | .cfi_startproc | 21 | .cfi_startproc |
| 22 | larl %r1,4f | ||
| 23 | cghi %r2,__CLOCK_REALTIME_COARSE | ||
| 24 | je 0f | ||
| 25 | cghi %r2,__CLOCK_MONOTONIC_COARSE | ||
| 26 | je 0f | ||
| 27 | larl %r1,3f | ||
| 22 | cghi %r2,__CLOCK_REALTIME | 28 | cghi %r2,__CLOCK_REALTIME |
| 23 | je 0f | 29 | je 0f |
| 24 | cghi %r2,__CLOCK_MONOTONIC | 30 | cghi %r2,__CLOCK_MONOTONIC |
| @@ -32,7 +38,6 @@ __kernel_clock_getres: | |||
| 32 | jz 2f | 38 | jz 2f |
| 33 | 0: ltgr %r3,%r3 | 39 | 0: ltgr %r3,%r3 |
| 34 | jz 1f /* res == NULL */ | 40 | jz 1f /* res == NULL */ |
| 35 | larl %r1,3f | ||
| 36 | lg %r0,0(%r1) | 41 | lg %r0,0(%r1) |
| 37 | xc 0(8,%r3),0(%r3) /* set tp->tv_sec to zero */ | 42 | xc 0(8,%r3),0(%r3) /* set tp->tv_sec to zero */ |
| 38 | stg %r0,8(%r3) /* store tp->tv_usec */ | 43 | stg %r0,8(%r3) /* store tp->tv_usec */ |
| @@ -42,5 +47,6 @@ __kernel_clock_getres: | |||
| 42 | svc 0 | 47 | svc 0 |
| 43 | br %r14 | 48 | br %r14 |
| 44 | 3: .quad __CLOCK_REALTIME_RES | 49 | 3: .quad __CLOCK_REALTIME_RES |
| 50 | 4: .quad __CLOCK_COARSE_RES | ||
| 45 | .cfi_endproc | 51 | .cfi_endproc |
| 46 | .size __kernel_clock_getres,.-__kernel_clock_getres | 52 | .size __kernel_clock_getres,.-__kernel_clock_getres |
diff --git a/arch/s390/kernel/vdso64/clock_gettime.S b/arch/s390/kernel/vdso64/clock_gettime.S index 3f34e09db5f4..9d9761f8e110 100644 --- a/arch/s390/kernel/vdso64/clock_gettime.S +++ b/arch/s390/kernel/vdso64/clock_gettime.S | |||
| @@ -20,12 +20,16 @@ | |||
| 20 | __kernel_clock_gettime: | 20 | __kernel_clock_gettime: |
| 21 | .cfi_startproc | 21 | .cfi_startproc |
| 22 | larl %r5,_vdso_data | 22 | larl %r5,_vdso_data |
| 23 | cghi %r2,__CLOCK_REALTIME_COARSE | ||
| 24 | je 4f | ||
| 23 | cghi %r2,__CLOCK_REALTIME | 25 | cghi %r2,__CLOCK_REALTIME |
| 24 | je 5f | 26 | je 5f |
| 25 | cghi %r2,__CLOCK_THREAD_CPUTIME_ID | 27 | cghi %r2,__CLOCK_THREAD_CPUTIME_ID |
| 26 | je 9f | 28 | je 9f |
| 27 | cghi %r2,-2 /* Per-thread CPUCLOCK with PID=0, VIRT=1 */ | 29 | cghi %r2,-2 /* Per-thread CPUCLOCK with PID=0, VIRT=1 */ |
| 28 | je 9f | 30 | je 9f |
| 31 | cghi %r2,__CLOCK_MONOTONIC_COARSE | ||
| 32 | je 3f | ||
| 29 | cghi %r2,__CLOCK_MONOTONIC | 33 | cghi %r2,__CLOCK_MONOTONIC |
| 30 | jne 12f | 34 | jne 12f |
| 31 | 35 | ||
| @@ -33,10 +37,10 @@ __kernel_clock_gettime: | |||
| 33 | 0: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ | 37 | 0: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ |
| 34 | tmll %r4,0x0001 /* pending update ? loop */ | 38 | tmll %r4,0x0001 /* pending update ? loop */ |
| 35 | jnz 0b | 39 | jnz 0b |
| 36 | stck 48(%r15) /* Store TOD clock */ | 40 | stcke 48(%r15) /* Store TOD clock */ |
| 37 | lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */ | 41 | lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */ |
| 38 | lg %r0,__VDSO_WTOM_SEC(%r5) | 42 | lg %r0,__VDSO_WTOM_SEC(%r5) |
| 39 | lg %r1,48(%r15) | 43 | lg %r1,49(%r15) |
| 40 | sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ | 44 | sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ |
| 41 | msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ | 45 | msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ |
| 42 | alg %r1,__VDSO_WTOM_NSEC(%r5) | 46 | alg %r1,__VDSO_WTOM_NSEC(%r5) |
| @@ -54,13 +58,33 @@ __kernel_clock_gettime: | |||
| 54 | lghi %r2,0 | 58 | lghi %r2,0 |
| 55 | br %r14 | 59 | br %r14 |
| 56 | 60 | ||
| 61 | /* CLOCK_MONOTONIC_COARSE */ | ||
| 62 | 3: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ | ||
| 63 | tmll %r4,0x0001 /* pending update ? loop */ | ||
| 64 | jnz 3b | ||
| 65 | lg %r0,__VDSO_WTOM_CRS_SEC(%r5) | ||
| 66 | lg %r1,__VDSO_WTOM_CRS_NSEC(%r5) | ||
| 67 | clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */ | ||
| 68 | jne 3b | ||
| 69 | j 2b | ||
| 70 | |||
| 71 | /* CLOCK_REALTIME_COARSE */ | ||
| 72 | 4: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ | ||
| 73 | tmll %r4,0x0001 /* pending update ? loop */ | ||
| 74 | jnz 4b | ||
| 75 | lg %r0,__VDSO_XTIME_CRS_SEC(%r5) | ||
| 76 | lg %r1,__VDSO_XTIME_CRS_NSEC(%r5) | ||
| 77 | clg %r4,__VDSO_UPD_COUNT(%r5) /* check update counter */ | ||
| 78 | jne 4b | ||
| 79 | j 7f | ||
| 80 | |||
| 57 | /* CLOCK_REALTIME */ | 81 | /* CLOCK_REALTIME */ |
| 58 | 5: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ | 82 | 5: lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ |
| 59 | tmll %r4,0x0001 /* pending update ? loop */ | 83 | tmll %r4,0x0001 /* pending update ? loop */ |
| 60 | jnz 5b | 84 | jnz 5b |
| 61 | stck 48(%r15) /* Store TOD clock */ | 85 | stcke 48(%r15) /* Store TOD clock */ |
| 62 | lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */ | 86 | lgf %r2,__VDSO_TK_SHIFT(%r5) /* Timekeeper shift */ |
| 63 | lg %r1,48(%r15) | 87 | lg %r1,49(%r15) |
| 64 | sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ | 88 | sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ |
| 65 | msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ | 89 | msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ |
| 66 | alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */ | 90 | alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */ |
diff --git a/arch/s390/kernel/vdso64/gettimeofday.S b/arch/s390/kernel/vdso64/gettimeofday.S index d0860d1d0ccc..7a344995a97f 100644 --- a/arch/s390/kernel/vdso64/gettimeofday.S +++ b/arch/s390/kernel/vdso64/gettimeofday.S | |||
| @@ -28,8 +28,8 @@ __kernel_gettimeofday: | |||
| 28 | lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ | 28 | lg %r4,__VDSO_UPD_COUNT(%r5) /* load update counter */ |
| 29 | tmll %r4,0x0001 /* pending update ? loop */ | 29 | tmll %r4,0x0001 /* pending update ? loop */ |
| 30 | jnz 0b | 30 | jnz 0b |
| 31 | stck 48(%r15) /* Store TOD clock */ | 31 | stcke 48(%r15) /* Store TOD clock */ |
| 32 | lg %r1,48(%r15) | 32 | lg %r1,49(%r15) |
| 33 | sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ | 33 | sg %r1,__VDSO_XTIME_STAMP(%r5) /* TOD - cycle_last */ |
| 34 | msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ | 34 | msgf %r1,__VDSO_TK_MULT(%r5) /* * tk->mult */ |
| 35 | alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */ | 35 | alg %r1,__VDSO_XTIME_NSEC(%r5) /* + tk->xtime_nsec */ |
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 8c34363d6f1e..416f2a323ba5 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c | |||
| @@ -6,27 +6,18 @@ | |||
| 6 | */ | 6 | */ |
| 7 | 7 | ||
| 8 | #include <linux/kernel_stat.h> | 8 | #include <linux/kernel_stat.h> |
| 9 | #include <linux/notifier.h> | ||
| 10 | #include <linux/kprobes.h> | ||
| 11 | #include <linux/export.h> | 9 | #include <linux/export.h> |
| 12 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
| 13 | #include <linux/timex.h> | 11 | #include <linux/timex.h> |
| 14 | #include <linux/types.h> | 12 | #include <linux/types.h> |
| 15 | #include <linux/time.h> | 13 | #include <linux/time.h> |
| 16 | #include <linux/cpu.h> | ||
| 17 | #include <linux/smp.h> | ||
| 18 | 14 | ||
| 19 | #include <asm/irq_regs.h> | ||
| 20 | #include <asm/cputime.h> | 15 | #include <asm/cputime.h> |
| 21 | #include <asm/vtimer.h> | 16 | #include <asm/vtimer.h> |
| 22 | #include <asm/vtime.h> | 17 | #include <asm/vtime.h> |
| 23 | #include <asm/irq.h> | ||
| 24 | #include "entry.h" | ||
| 25 | 18 | ||
| 26 | static void virt_timer_expire(void); | 19 | static void virt_timer_expire(void); |
| 27 | 20 | ||
| 28 | DEFINE_PER_CPU(struct s390_idle_data, s390_idle); | ||
| 29 | |||
| 30 | static LIST_HEAD(virt_timer_list); | 21 | static LIST_HEAD(virt_timer_list); |
| 31 | static DEFINE_SPINLOCK(virt_timer_lock); | 22 | static DEFINE_SPINLOCK(virt_timer_lock); |
| 32 | static atomic64_t virt_timer_current; | 23 | static atomic64_t virt_timer_current; |
| @@ -152,49 +143,6 @@ void vtime_account_system(struct task_struct *tsk) | |||
| 152 | __attribute__((alias("vtime_account_irq_enter"))); | 143 | __attribute__((alias("vtime_account_irq_enter"))); |
| 153 | EXPORT_SYMBOL_GPL(vtime_account_system); | 144 | EXPORT_SYMBOL_GPL(vtime_account_system); |
| 154 | 145 | ||
| 155 | void __kprobes vtime_stop_cpu(void) | ||
| 156 | { | ||
| 157 | struct s390_idle_data *idle = &__get_cpu_var(s390_idle); | ||
| 158 | unsigned long long idle_time; | ||
| 159 | unsigned long psw_mask; | ||
| 160 | |||
| 161 | trace_hardirqs_on(); | ||
| 162 | |||
| 163 | /* Wait for external, I/O or machine check interrupt. */ | ||
| 164 | psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT | | ||
| 165 | PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; | ||
| 166 | idle->nohz_delay = 0; | ||
| 167 | |||
| 168 | /* Call the assembler magic in entry.S */ | ||
| 169 | psw_idle(idle, psw_mask); | ||
| 170 | |||
| 171 | /* Account time spent with enabled wait psw loaded as idle time. */ | ||
| 172 | idle->sequence++; | ||
| 173 | smp_wmb(); | ||
| 174 | idle_time = idle->clock_idle_exit - idle->clock_idle_enter; | ||
| 175 | idle->clock_idle_enter = idle->clock_idle_exit = 0ULL; | ||
| 176 | idle->idle_time += idle_time; | ||
| 177 | idle->idle_count++; | ||
| 178 | account_idle_time(idle_time); | ||
| 179 | smp_wmb(); | ||
| 180 | idle->sequence++; | ||
| 181 | } | ||
| 182 | |||
| 183 | cputime64_t s390_get_idle_time(int cpu) | ||
| 184 | { | ||
| 185 | struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); | ||
| 186 | unsigned long long now, idle_enter, idle_exit; | ||
| 187 | unsigned int sequence; | ||
| 188 | |||
| 189 | do { | ||
| 190 | now = get_tod_clock(); | ||
| 191 | sequence = ACCESS_ONCE(idle->sequence); | ||
| 192 | idle_enter = ACCESS_ONCE(idle->clock_idle_enter); | ||
| 193 | idle_exit = ACCESS_ONCE(idle->clock_idle_exit); | ||
| 194 | } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); | ||
| 195 | return idle_enter ? ((idle_exit ?: now) - idle_enter) : 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | /* | 146 | /* |
| 199 | * Sorted add to a list. List is linear searched until first bigger | 147 | * Sorted add to a list. List is linear searched until first bigger |
| 200 | * element is found. | 148 | * element is found. |
| @@ -372,31 +320,8 @@ EXPORT_SYMBOL(del_virt_timer); | |||
| 372 | /* | 320 | /* |
| 373 | * Start the virtual CPU timer on the current CPU. | 321 | * Start the virtual CPU timer on the current CPU. |
| 374 | */ | 322 | */ |
| 375 | void init_cpu_vtimer(void) | 323 | void vtime_init(void) |
| 376 | { | 324 | { |
| 377 | /* set initial cpu timer */ | 325 | /* set initial cpu timer */ |
| 378 | set_vtimer(VTIMER_MAX_SLICE); | 326 | set_vtimer(VTIMER_MAX_SLICE); |
| 379 | } | 327 | } |
| 380 | |||
| 381 | static int s390_nohz_notify(struct notifier_block *self, unsigned long action, | ||
| 382 | void *hcpu) | ||
| 383 | { | ||
| 384 | struct s390_idle_data *idle; | ||
| 385 | long cpu = (long) hcpu; | ||
| 386 | |||
| 387 | idle = &per_cpu(s390_idle, cpu); | ||
| 388 | switch (action & ~CPU_TASKS_FROZEN) { | ||
| 389 | case CPU_DYING: | ||
| 390 | idle->nohz_delay = 0; | ||
| 391 | default: | ||
| 392 | break; | ||
| 393 | } | ||
| 394 | return NOTIFY_OK; | ||
| 395 | } | ||
| 396 | |||
| 397 | void __init vtime_init(void) | ||
| 398 | { | ||
| 399 | /* Enable cpu timer interrupts on the boot cpu. */ | ||
| 400 | init_cpu_vtimer(); | ||
| 401 | cpu_notifier(s390_nohz_notify, 0); | ||
| 402 | } | ||
diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile index c6d752e8bf28..a01df233856f 100644 --- a/arch/s390/lib/Makefile +++ b/arch/s390/lib/Makefile | |||
| @@ -6,3 +6,5 @@ lib-y += delay.o string.o uaccess.o find.o | |||
| 6 | obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o mem32.o | 6 | obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o mem32.o |
| 7 | obj-$(CONFIG_64BIT) += mem64.o | 7 | obj-$(CONFIG_64BIT) += mem64.o |
| 8 | lib-$(CONFIG_SMP) += spinlock.o | 8 | lib-$(CONFIG_SMP) += spinlock.o |
| 9 | lib-$(CONFIG_KPROBES) += probes.o | ||
| 10 | lib-$(CONFIG_UPROBES) += probes.o | ||
diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c index a9f3d0042d58..16dc42d83f93 100644 --- a/arch/s390/lib/delay.c +++ b/arch/s390/lib/delay.c | |||
| @@ -43,7 +43,7 @@ static void __udelay_disabled(unsigned long long usecs) | |||
| 43 | lockdep_off(); | 43 | lockdep_off(); |
| 44 | do { | 44 | do { |
| 45 | set_clock_comparator(end); | 45 | set_clock_comparator(end); |
| 46 | vtime_stop_cpu(); | 46 | enabled_wait(); |
| 47 | } while (get_tod_clock_fast() < end); | 47 | } while (get_tod_clock_fast() < end); |
| 48 | lockdep_on(); | 48 | lockdep_on(); |
| 49 | __ctl_load(cr0, 0, 0); | 49 | __ctl_load(cr0, 0, 0); |
| @@ -62,7 +62,7 @@ static void __udelay_enabled(unsigned long long usecs) | |||
| 62 | clock_saved = local_tick_disable(); | 62 | clock_saved = local_tick_disable(); |
| 63 | set_clock_comparator(end); | 63 | set_clock_comparator(end); |
| 64 | } | 64 | } |
| 65 | vtime_stop_cpu(); | 65 | enabled_wait(); |
| 66 | if (clock_saved) | 66 | if (clock_saved) |
| 67 | local_tick_enable(clock_saved); | 67 | local_tick_enable(clock_saved); |
| 68 | } while (get_tod_clock_fast() < end); | 68 | } while (get_tod_clock_fast() < end); |
diff --git a/arch/s390/lib/probes.c b/arch/s390/lib/probes.c new file mode 100644 index 000000000000..c5d64a099719 --- /dev/null +++ b/arch/s390/lib/probes.c | |||
| @@ -0,0 +1,159 @@ | |||
| 1 | /* | ||
| 2 | * Common helper functions for kprobes and uprobes | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2014 | ||
| 5 | */ | ||
| 6 | |||
| 7 | #include <linux/kprobes.h> | ||
| 8 | #include <asm/dis.h> | ||
| 9 | |||
| 10 | int probe_is_prohibited_opcode(u16 *insn) | ||
| 11 | { | ||
| 12 | if (!is_known_insn((unsigned char *)insn)) | ||
| 13 | return -EINVAL; | ||
| 14 | switch (insn[0] >> 8) { | ||
| 15 | case 0x0c: /* bassm */ | ||
| 16 | case 0x0b: /* bsm */ | ||
| 17 | case 0x83: /* diag */ | ||
| 18 | case 0x44: /* ex */ | ||
| 19 | case 0xac: /* stnsm */ | ||
| 20 | case 0xad: /* stosm */ | ||
| 21 | return -EINVAL; | ||
| 22 | case 0xc6: | ||
| 23 | switch (insn[0] & 0x0f) { | ||
| 24 | case 0x00: /* exrl */ | ||
| 25 | return -EINVAL; | ||
| 26 | } | ||
| 27 | } | ||
| 28 | switch (insn[0]) { | ||
| 29 | case 0x0101: /* pr */ | ||
| 30 | case 0xb25a: /* bsa */ | ||
| 31 | case 0xb240: /* bakr */ | ||
| 32 | case 0xb258: /* bsg */ | ||
| 33 | case 0xb218: /* pc */ | ||
| 34 | case 0xb228: /* pt */ | ||
| 35 | case 0xb98d: /* epsw */ | ||
| 36 | case 0xe560: /* tbegin */ | ||
| 37 | case 0xe561: /* tbeginc */ | ||
| 38 | case 0xb2f8: /* tend */ | ||
| 39 | return -EINVAL; | ||
| 40 | } | ||
| 41 | return 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | int probe_get_fixup_type(u16 *insn) | ||
| 45 | { | ||
| 46 | /* default fixup method */ | ||
| 47 | int fixup = FIXUP_PSW_NORMAL; | ||
| 48 | |||
| 49 | switch (insn[0] >> 8) { | ||
| 50 | case 0x05: /* balr */ | ||
| 51 | case 0x0d: /* basr */ | ||
| 52 | fixup = FIXUP_RETURN_REGISTER; | ||
| 53 | /* if r2 = 0, no branch will be taken */ | ||
| 54 | if ((insn[0] & 0x0f) == 0) | ||
| 55 | fixup |= FIXUP_BRANCH_NOT_TAKEN; | ||
| 56 | break; | ||
| 57 | case 0x06: /* bctr */ | ||
| 58 | case 0x07: /* bcr */ | ||
| 59 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 60 | break; | ||
| 61 | case 0x45: /* bal */ | ||
| 62 | case 0x4d: /* bas */ | ||
| 63 | fixup = FIXUP_RETURN_REGISTER; | ||
| 64 | break; | ||
| 65 | case 0x47: /* bc */ | ||
| 66 | case 0x46: /* bct */ | ||
| 67 | case 0x86: /* bxh */ | ||
| 68 | case 0x87: /* bxle */ | ||
| 69 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 70 | break; | ||
| 71 | case 0x82: /* lpsw */ | ||
| 72 | fixup = FIXUP_NOT_REQUIRED; | ||
| 73 | break; | ||
| 74 | case 0xb2: /* lpswe */ | ||
| 75 | if ((insn[0] & 0xff) == 0xb2) | ||
| 76 | fixup = FIXUP_NOT_REQUIRED; | ||
| 77 | break; | ||
| 78 | case 0xa7: /* bras */ | ||
| 79 | if ((insn[0] & 0x0f) == 0x05) | ||
| 80 | fixup |= FIXUP_RETURN_REGISTER; | ||
| 81 | break; | ||
| 82 | case 0xc0: | ||
| 83 | if ((insn[0] & 0x0f) == 0x05) /* brasl */ | ||
| 84 | fixup |= FIXUP_RETURN_REGISTER; | ||
| 85 | break; | ||
| 86 | case 0xeb: | ||
| 87 | switch (insn[2] & 0xff) { | ||
| 88 | case 0x44: /* bxhg */ | ||
| 89 | case 0x45: /* bxleg */ | ||
| 90 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 91 | break; | ||
| 92 | } | ||
| 93 | break; | ||
| 94 | case 0xe3: /* bctg */ | ||
| 95 | if ((insn[2] & 0xff) == 0x46) | ||
| 96 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 97 | break; | ||
| 98 | case 0xec: | ||
| 99 | switch (insn[2] & 0xff) { | ||
| 100 | case 0xe5: /* clgrb */ | ||
| 101 | case 0xe6: /* cgrb */ | ||
| 102 | case 0xf6: /* crb */ | ||
| 103 | case 0xf7: /* clrb */ | ||
| 104 | case 0xfc: /* cgib */ | ||
| 105 | case 0xfd: /* cglib */ | ||
| 106 | case 0xfe: /* cib */ | ||
| 107 | case 0xff: /* clib */ | ||
| 108 | fixup = FIXUP_BRANCH_NOT_TAKEN; | ||
| 109 | break; | ||
| 110 | } | ||
| 111 | break; | ||
| 112 | } | ||
| 113 | return fixup; | ||
| 114 | } | ||
| 115 | |||
| 116 | int probe_is_insn_relative_long(u16 *insn) | ||
| 117 | { | ||
| 118 | /* Check if we have a RIL-b or RIL-c format instruction which | ||
| 119 | * we need to modify in order to avoid instruction emulation. */ | ||
| 120 | switch (insn[0] >> 8) { | ||
| 121 | case 0xc0: | ||
| 122 | if ((insn[0] & 0x0f) == 0x00) /* larl */ | ||
| 123 | return true; | ||
| 124 | break; | ||
| 125 | case 0xc4: | ||
| 126 | switch (insn[0] & 0x0f) { | ||
| 127 | case 0x02: /* llhrl */ | ||
| 128 | case 0x04: /* lghrl */ | ||
| 129 | case 0x05: /* lhrl */ | ||
| 130 | case 0x06: /* llghrl */ | ||
| 131 | case 0x07: /* sthrl */ | ||
| 132 | case 0x08: /* lgrl */ | ||
| 133 | case 0x0b: /* stgrl */ | ||
| 134 | case 0x0c: /* lgfrl */ | ||
| 135 | case 0x0d: /* lrl */ | ||
| 136 | case 0x0e: /* llgfrl */ | ||
| 137 | case 0x0f: /* strl */ | ||
| 138 | return true; | ||
| 139 | } | ||
| 140 | break; | ||
| 141 | case 0xc6: | ||
| 142 | switch (insn[0] & 0x0f) { | ||
| 143 | case 0x02: /* pfdrl */ | ||
| 144 | case 0x04: /* cghrl */ | ||
| 145 | case 0x05: /* chrl */ | ||
| 146 | case 0x06: /* clghrl */ | ||
| 147 | case 0x07: /* clhrl */ | ||
| 148 | case 0x08: /* cgrl */ | ||
| 149 | case 0x0a: /* clgrl */ | ||
| 150 | case 0x0c: /* cgfrl */ | ||
| 151 | case 0x0d: /* crl */ | ||
| 152 | case 0x0e: /* clgfrl */ | ||
| 153 | case 0x0f: /* clrl */ | ||
| 154 | return true; | ||
| 155 | } | ||
| 156 | break; | ||
| 157 | } | ||
| 158 | return false; | ||
| 159 | } | ||
diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c index 5b0e445bc3f3..034a35a3e9c1 100644 --- a/arch/s390/lib/spinlock.c +++ b/arch/s390/lib/spinlock.c | |||
| @@ -98,17 +98,6 @@ void arch_spin_lock_wait_flags(arch_spinlock_t *lp, unsigned long flags) | |||
| 98 | } | 98 | } |
| 99 | EXPORT_SYMBOL(arch_spin_lock_wait_flags); | 99 | EXPORT_SYMBOL(arch_spin_lock_wait_flags); |
| 100 | 100 | ||
| 101 | void arch_spin_relax(arch_spinlock_t *lp) | ||
| 102 | { | ||
| 103 | unsigned int cpu = lp->lock; | ||
| 104 | if (cpu != 0) { | ||
| 105 | if (MACHINE_IS_VM || MACHINE_IS_KVM || | ||
| 106 | !smp_vcpu_scheduled(~cpu)) | ||
| 107 | smp_yield_cpu(~cpu); | ||
| 108 | } | ||
| 109 | } | ||
| 110 | EXPORT_SYMBOL(arch_spin_relax); | ||
| 111 | |||
| 112 | int arch_spin_trylock_retry(arch_spinlock_t *lp) | 101 | int arch_spin_trylock_retry(arch_spinlock_t *lp) |
| 113 | { | 102 | { |
| 114 | int count; | 103 | int count; |
| @@ -122,15 +111,21 @@ EXPORT_SYMBOL(arch_spin_trylock_retry); | |||
| 122 | 111 | ||
| 123 | void _raw_read_lock_wait(arch_rwlock_t *rw) | 112 | void _raw_read_lock_wait(arch_rwlock_t *rw) |
| 124 | { | 113 | { |
| 125 | unsigned int old; | 114 | unsigned int owner, old; |
| 126 | int count = spin_retry; | 115 | int count = spin_retry; |
| 127 | 116 | ||
| 117 | #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES | ||
| 118 | __RAW_LOCK(&rw->lock, -1, __RAW_OP_ADD); | ||
| 119 | #endif | ||
| 120 | owner = 0; | ||
| 128 | while (1) { | 121 | while (1) { |
| 129 | if (count-- <= 0) { | 122 | if (count-- <= 0) { |
| 130 | smp_yield(); | 123 | if (owner && !smp_vcpu_scheduled(~owner)) |
| 124 | smp_yield_cpu(~owner); | ||
| 131 | count = spin_retry; | 125 | count = spin_retry; |
| 132 | } | 126 | } |
| 133 | old = ACCESS_ONCE(rw->lock); | 127 | old = ACCESS_ONCE(rw->lock); |
| 128 | owner = ACCESS_ONCE(rw->owner); | ||
| 134 | if ((int) old < 0) | 129 | if ((int) old < 0) |
| 135 | continue; | 130 | continue; |
| 136 | if (_raw_compare_and_swap(&rw->lock, old, old + 1)) | 131 | if (_raw_compare_and_swap(&rw->lock, old, old + 1)) |
| @@ -139,28 +134,6 @@ void _raw_read_lock_wait(arch_rwlock_t *rw) | |||
| 139 | } | 134 | } |
| 140 | EXPORT_SYMBOL(_raw_read_lock_wait); | 135 | EXPORT_SYMBOL(_raw_read_lock_wait); |
| 141 | 136 | ||
| 142 | void _raw_read_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags) | ||
| 143 | { | ||
| 144 | unsigned int old; | ||
| 145 | int count = spin_retry; | ||
| 146 | |||
| 147 | local_irq_restore(flags); | ||
| 148 | while (1) { | ||
| 149 | if (count-- <= 0) { | ||
| 150 | smp_yield(); | ||
| 151 | count = spin_retry; | ||
| 152 | } | ||
| 153 | old = ACCESS_ONCE(rw->lock); | ||
| 154 | if ((int) old < 0) | ||
| 155 | continue; | ||
| 156 | local_irq_disable(); | ||
| 157 | if (_raw_compare_and_swap(&rw->lock, old, old + 1)) | ||
| 158 | return; | ||
| 159 | local_irq_restore(flags); | ||
| 160 | } | ||
| 161 | } | ||
| 162 | EXPORT_SYMBOL(_raw_read_lock_wait_flags); | ||
| 163 | |||
| 164 | int _raw_read_trylock_retry(arch_rwlock_t *rw) | 137 | int _raw_read_trylock_retry(arch_rwlock_t *rw) |
| 165 | { | 138 | { |
| 166 | unsigned int old; | 139 | unsigned int old; |
| @@ -177,46 +150,62 @@ int _raw_read_trylock_retry(arch_rwlock_t *rw) | |||
| 177 | } | 150 | } |
| 178 | EXPORT_SYMBOL(_raw_read_trylock_retry); | 151 | EXPORT_SYMBOL(_raw_read_trylock_retry); |
| 179 | 152 | ||
| 180 | void _raw_write_lock_wait(arch_rwlock_t *rw) | 153 | #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES |
| 154 | |||
| 155 | void _raw_write_lock_wait(arch_rwlock_t *rw, unsigned int prev) | ||
| 181 | { | 156 | { |
| 182 | unsigned int old; | 157 | unsigned int owner, old; |
| 183 | int count = spin_retry; | 158 | int count = spin_retry; |
| 184 | 159 | ||
| 160 | owner = 0; | ||
| 185 | while (1) { | 161 | while (1) { |
| 186 | if (count-- <= 0) { | 162 | if (count-- <= 0) { |
| 187 | smp_yield(); | 163 | if (owner && !smp_vcpu_scheduled(~owner)) |
| 164 | smp_yield_cpu(~owner); | ||
| 188 | count = spin_retry; | 165 | count = spin_retry; |
| 189 | } | 166 | } |
| 190 | old = ACCESS_ONCE(rw->lock); | 167 | old = ACCESS_ONCE(rw->lock); |
| 191 | if (old) | 168 | owner = ACCESS_ONCE(rw->owner); |
| 192 | continue; | 169 | smp_rmb(); |
| 193 | if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) | 170 | if ((int) old >= 0) { |
| 194 | return; | 171 | prev = __RAW_LOCK(&rw->lock, 0x80000000, __RAW_OP_OR); |
| 172 | old = prev; | ||
| 173 | } | ||
| 174 | if ((old & 0x7fffffff) == 0 && (int) prev >= 0) | ||
| 175 | break; | ||
| 195 | } | 176 | } |
| 196 | } | 177 | } |
| 197 | EXPORT_SYMBOL(_raw_write_lock_wait); | 178 | EXPORT_SYMBOL(_raw_write_lock_wait); |
| 198 | 179 | ||
| 199 | void _raw_write_lock_wait_flags(arch_rwlock_t *rw, unsigned long flags) | 180 | #else /* CONFIG_HAVE_MARCH_Z196_FEATURES */ |
| 181 | |||
| 182 | void _raw_write_lock_wait(arch_rwlock_t *rw) | ||
| 200 | { | 183 | { |
| 201 | unsigned int old; | 184 | unsigned int owner, old, prev; |
| 202 | int count = spin_retry; | 185 | int count = spin_retry; |
| 203 | 186 | ||
| 204 | local_irq_restore(flags); | 187 | prev = 0x80000000; |
| 188 | owner = 0; | ||
| 205 | while (1) { | 189 | while (1) { |
| 206 | if (count-- <= 0) { | 190 | if (count-- <= 0) { |
| 207 | smp_yield(); | 191 | if (owner && !smp_vcpu_scheduled(~owner)) |
| 192 | smp_yield_cpu(~owner); | ||
| 208 | count = spin_retry; | 193 | count = spin_retry; |
| 209 | } | 194 | } |
| 210 | old = ACCESS_ONCE(rw->lock); | 195 | old = ACCESS_ONCE(rw->lock); |
| 211 | if (old) | 196 | owner = ACCESS_ONCE(rw->owner); |
| 212 | continue; | 197 | if ((int) old >= 0 && |
| 213 | local_irq_disable(); | 198 | _raw_compare_and_swap(&rw->lock, old, old | 0x80000000)) |
| 214 | if (_raw_compare_and_swap(&rw->lock, 0, 0x80000000)) | 199 | prev = old; |
| 215 | return; | 200 | else |
| 216 | local_irq_restore(flags); | 201 | smp_rmb(); |
| 202 | if ((old & 0x7fffffff) == 0 && (int) prev >= 0) | ||
| 203 | break; | ||
| 217 | } | 204 | } |
| 218 | } | 205 | } |
| 219 | EXPORT_SYMBOL(_raw_write_lock_wait_flags); | 206 | EXPORT_SYMBOL(_raw_write_lock_wait); |
| 207 | |||
| 208 | #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */ | ||
| 220 | 209 | ||
| 221 | int _raw_write_trylock_retry(arch_rwlock_t *rw) | 210 | int _raw_write_trylock_retry(arch_rwlock_t *rw) |
| 222 | { | 211 | { |
| @@ -233,3 +222,13 @@ int _raw_write_trylock_retry(arch_rwlock_t *rw) | |||
| 233 | return 0; | 222 | return 0; |
| 234 | } | 223 | } |
| 235 | EXPORT_SYMBOL(_raw_write_trylock_retry); | 224 | EXPORT_SYMBOL(_raw_write_trylock_retry); |
| 225 | |||
| 226 | void arch_lock_relax(unsigned int cpu) | ||
| 227 | { | ||
| 228 | if (!cpu) | ||
| 229 | return; | ||
| 230 | if (MACHINE_IS_LPAR && smp_vcpu_scheduled(~cpu)) | ||
| 231 | return; | ||
| 232 | smp_yield_cpu(~cpu); | ||
| 233 | } | ||
| 234 | EXPORT_SYMBOL(arch_lock_relax); | ||
diff --git a/arch/s390/mm/dump_pagetables.c b/arch/s390/mm/dump_pagetables.c index 46d517c3c763..d46cadeda204 100644 --- a/arch/s390/mm/dump_pagetables.c +++ b/arch/s390/mm/dump_pagetables.c | |||
| @@ -54,7 +54,6 @@ static void print_prot(struct seq_file *m, unsigned int pr, int level) | |||
| 54 | return; | 54 | return; |
| 55 | } | 55 | } |
| 56 | seq_printf(m, "%s", pr & _PAGE_PROTECT ? "RO " : "RW "); | 56 | seq_printf(m, "%s", pr & _PAGE_PROTECT ? "RO " : "RW "); |
| 57 | seq_printf(m, "%s", pr & _PAGE_CO ? "CO " : " "); | ||
| 58 | seq_putc(m, '\n'); | 57 | seq_putc(m, '\n'); |
| 59 | } | 58 | } |
| 60 | 59 | ||
| @@ -129,7 +128,7 @@ static void walk_pte_level(struct seq_file *m, struct pg_state *st, | |||
| 129 | } | 128 | } |
| 130 | 129 | ||
| 131 | #ifdef CONFIG_64BIT | 130 | #ifdef CONFIG_64BIT |
| 132 | #define _PMD_PROT_MASK (_SEGMENT_ENTRY_PROTECT | _SEGMENT_ENTRY_CO) | 131 | #define _PMD_PROT_MASK _SEGMENT_ENTRY_PROTECT |
| 133 | #else | 132 | #else |
| 134 | #define _PMD_PROT_MASK 0 | 133 | #define _PMD_PROT_MASK 0 |
| 135 | #endif | 134 | #endif |
| @@ -157,7 +156,7 @@ static void walk_pmd_level(struct seq_file *m, struct pg_state *st, | |||
| 157 | } | 156 | } |
| 158 | 157 | ||
| 159 | #ifdef CONFIG_64BIT | 158 | #ifdef CONFIG_64BIT |
| 160 | #define _PUD_PROT_MASK (_REGION3_ENTRY_RO | _REGION3_ENTRY_CO) | 159 | #define _PUD_PROT_MASK _REGION3_ENTRY_RO |
| 161 | #else | 160 | #else |
| 162 | #define _PUD_PROT_MASK 0 | 161 | #define _PUD_PROT_MASK 0 |
| 163 | #endif | 162 | #endif |
diff --git a/arch/s390/mm/hugetlbpage.c b/arch/s390/mm/hugetlbpage.c index 389bc17934b7..3c80d2e38f03 100644 --- a/arch/s390/mm/hugetlbpage.c +++ b/arch/s390/mm/hugetlbpage.c | |||
| @@ -88,7 +88,7 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, | |||
| 88 | pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; | 88 | pmd_val(pmd) &= ~_SEGMENT_ENTRY_ORIGIN; |
| 89 | pmd_val(pmd) |= pte_page(pte)[1].index; | 89 | pmd_val(pmd) |= pte_page(pte)[1].index; |
| 90 | } else | 90 | } else |
| 91 | pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE | _SEGMENT_ENTRY_CO; | 91 | pmd_val(pmd) |= _SEGMENT_ENTRY_LARGE; |
| 92 | *(pmd_t *) ptep = pmd; | 92 | *(pmd_t *) ptep = pmd; |
| 93 | } | 93 | } |
| 94 | 94 | ||
diff --git a/arch/s390/mm/pageattr.c b/arch/s390/mm/pageattr.c index 8400f494623f..3fef3b299665 100644 --- a/arch/s390/mm/pageattr.c +++ b/arch/s390/mm/pageattr.c | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <linux/module.h> | 6 | #include <linux/module.h> |
| 7 | #include <linux/mm.h> | 7 | #include <linux/mm.h> |
| 8 | #include <asm/cacheflush.h> | 8 | #include <asm/cacheflush.h> |
| 9 | #include <asm/facility.h> | ||
| 9 | #include <asm/pgtable.h> | 10 | #include <asm/pgtable.h> |
| 10 | #include <asm/page.h> | 11 | #include <asm/page.h> |
| 11 | 12 | ||
| @@ -103,27 +104,50 @@ int set_memory_x(unsigned long addr, int numpages) | |||
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | #ifdef CONFIG_DEBUG_PAGEALLOC | 106 | #ifdef CONFIG_DEBUG_PAGEALLOC |
| 107 | |||
| 108 | static void ipte_range(pte_t *pte, unsigned long address, int nr) | ||
| 109 | { | ||
| 110 | int i; | ||
| 111 | |||
| 112 | if (test_facility(13) && IS_ENABLED(CONFIG_64BIT)) { | ||
| 113 | __ptep_ipte_range(address, nr - 1, pte); | ||
| 114 | return; | ||
| 115 | } | ||
| 116 | for (i = 0; i < nr; i++) { | ||
| 117 | __ptep_ipte(address, pte); | ||
| 118 | address += PAGE_SIZE; | ||
| 119 | pte++; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | |||
| 106 | void kernel_map_pages(struct page *page, int numpages, int enable) | 123 | void kernel_map_pages(struct page *page, int numpages, int enable) |
| 107 | { | 124 | { |
| 108 | unsigned long address; | 125 | unsigned long address; |
| 126 | int nr, i, j; | ||
| 109 | pgd_t *pgd; | 127 | pgd_t *pgd; |
| 110 | pud_t *pud; | 128 | pud_t *pud; |
| 111 | pmd_t *pmd; | 129 | pmd_t *pmd; |
| 112 | pte_t *pte; | 130 | pte_t *pte; |
| 113 | int i; | ||
| 114 | 131 | ||
| 115 | for (i = 0; i < numpages; i++) { | 132 | for (i = 0; i < numpages;) { |
| 116 | address = page_to_phys(page + i); | 133 | address = page_to_phys(page + i); |
| 117 | pgd = pgd_offset_k(address); | 134 | pgd = pgd_offset_k(address); |
| 118 | pud = pud_offset(pgd, address); | 135 | pud = pud_offset(pgd, address); |
| 119 | pmd = pmd_offset(pud, address); | 136 | pmd = pmd_offset(pud, address); |
| 120 | pte = pte_offset_kernel(pmd, address); | 137 | pte = pte_offset_kernel(pmd, address); |
| 121 | if (!enable) { | 138 | nr = (unsigned long)pte >> ilog2(sizeof(long)); |
| 122 | __ptep_ipte(address, pte); | 139 | nr = PTRS_PER_PTE - (nr & (PTRS_PER_PTE - 1)); |
| 123 | pte_val(*pte) = _PAGE_INVALID; | 140 | nr = min(numpages - i, nr); |
| 124 | continue; | 141 | if (enable) { |
| 142 | for (j = 0; j < nr; j++) { | ||
| 143 | pte_val(*pte) = __pa(address); | ||
| 144 | address += PAGE_SIZE; | ||
| 145 | pte++; | ||
| 146 | } | ||
| 147 | } else { | ||
| 148 | ipte_range(pte, address, nr); | ||
| 125 | } | 149 | } |
| 126 | pte_val(*pte) = __pa(address); | 150 | i += nr; |
| 127 | } | 151 | } |
| 128 | } | 152 | } |
| 129 | 153 | ||
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c index fdbd7888cb07..b1593c2f751a 100644 --- a/arch/s390/mm/vmem.c +++ b/arch/s390/mm/vmem.c | |||
| @@ -236,8 +236,7 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) | |||
| 236 | if (!new_page) | 236 | if (!new_page) |
| 237 | goto out; | 237 | goto out; |
| 238 | pmd_val(*pm_dir) = __pa(new_page) | | 238 | pmd_val(*pm_dir) = __pa(new_page) | |
| 239 | _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE | | 239 | _SEGMENT_ENTRY | _SEGMENT_ENTRY_LARGE; |
| 240 | _SEGMENT_ENTRY_CO; | ||
| 241 | address = (address + PMD_SIZE) & PMD_MASK; | 240 | address = (address + PMD_SIZE) & PMD_MASK; |
| 242 | continue; | 241 | continue; |
| 243 | } | 242 | } |
| @@ -253,9 +252,9 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) | |||
| 253 | 252 | ||
| 254 | pt_dir = pte_offset_kernel(pm_dir, address); | 253 | pt_dir = pte_offset_kernel(pm_dir, address); |
| 255 | if (pte_none(*pt_dir)) { | 254 | if (pte_none(*pt_dir)) { |
| 256 | unsigned long new_page; | 255 | void *new_page; |
| 257 | 256 | ||
| 258 | new_page =__pa(vmem_alloc_pages(0)); | 257 | new_page = vmemmap_alloc_block(PAGE_SIZE, node); |
| 259 | if (!new_page) | 258 | if (!new_page) |
| 260 | goto out; | 259 | goto out; |
| 261 | pte_val(*pt_dir) = | 260 | pte_val(*pt_dir) = |
| @@ -263,7 +262,6 @@ int __meminit vmemmap_populate(unsigned long start, unsigned long end, int node) | |||
| 263 | } | 262 | } |
| 264 | address += PAGE_SIZE; | 263 | address += PAGE_SIZE; |
| 265 | } | 264 | } |
| 266 | memset((void *)start, 0, end - start); | ||
| 267 | ret = 0; | 265 | ret = 0; |
| 268 | out: | 266 | out: |
| 269 | return ret; | 267 | return ret; |
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 5df05f26b7d9..329db997ee66 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c | |||
| @@ -1660,6 +1660,14 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, | |||
| 1660 | device->discipline->check_for_device_change(device, cqr, irb); | 1660 | device->discipline->check_for_device_change(device, cqr, irb); |
| 1661 | dasd_put_device(device); | 1661 | dasd_put_device(device); |
| 1662 | } | 1662 | } |
| 1663 | |||
| 1664 | /* check for for attention message */ | ||
| 1665 | if (scsw_dstat(&irb->scsw) & DEV_STAT_ATTENTION) { | ||
| 1666 | device = dasd_device_from_cdev_locked(cdev); | ||
| 1667 | device->discipline->check_attention(device, irb->esw.esw1.lpum); | ||
| 1668 | dasd_put_device(device); | ||
| 1669 | } | ||
| 1670 | |||
| 1663 | if (!cqr) | 1671 | if (!cqr) |
| 1664 | return; | 1672 | return; |
| 1665 | 1673 | ||
| @@ -2261,8 +2269,8 @@ static inline int _wait_for_wakeup_queue(struct list_head *ccw_queue) | |||
| 2261 | static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) | 2269 | static int _dasd_sleep_on_queue(struct list_head *ccw_queue, int interruptible) |
| 2262 | { | 2270 | { |
| 2263 | struct dasd_device *device; | 2271 | struct dasd_device *device; |
| 2264 | int rc; | ||
| 2265 | struct dasd_ccw_req *cqr, *n; | 2272 | struct dasd_ccw_req *cqr, *n; |
| 2273 | int rc; | ||
| 2266 | 2274 | ||
| 2267 | retry: | 2275 | retry: |
| 2268 | list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { | 2276 | list_for_each_entry_safe(cqr, n, ccw_queue, blocklist) { |
| @@ -2310,21 +2318,26 @@ retry: | |||
| 2310 | /* | 2318 | /* |
| 2311 | * for alias devices simplify error recovery and | 2319 | * for alias devices simplify error recovery and |
| 2312 | * return to upper layer | 2320 | * return to upper layer |
| 2321 | * do not skip ERP requests | ||
| 2313 | */ | 2322 | */ |
| 2314 | if (cqr->startdev != cqr->basedev && | 2323 | if (cqr->startdev != cqr->basedev && !cqr->refers && |
| 2315 | (cqr->status == DASD_CQR_TERMINATED || | 2324 | (cqr->status == DASD_CQR_TERMINATED || |
| 2316 | cqr->status == DASD_CQR_NEED_ERP)) | 2325 | cqr->status == DASD_CQR_NEED_ERP)) |
| 2317 | return -EAGAIN; | 2326 | return -EAGAIN; |
| 2318 | else { | 2327 | |
| 2319 | /* normal recovery for basedev IO */ | 2328 | /* normal recovery for basedev IO */ |
| 2320 | if (__dasd_sleep_on_erp(cqr)) { | 2329 | if (__dasd_sleep_on_erp(cqr)) { |
| 2321 | if (!cqr->status == DASD_CQR_TERMINATED && | 2330 | goto retry; |
| 2322 | !cqr->status == DASD_CQR_NEED_ERP) | 2331 | /* remember that ERP was needed */ |
| 2323 | break; | 2332 | rc = 1; |
| 2324 | rc = 1; | 2333 | /* skip processing for active cqr */ |
| 2325 | } | 2334 | if (cqr->status != DASD_CQR_TERMINATED && |
| 2335 | cqr->status != DASD_CQR_NEED_ERP) | ||
| 2336 | break; | ||
| 2326 | } | 2337 | } |
| 2327 | } | 2338 | } |
| 2339 | |||
| 2340 | /* start ERP requests in upper loop */ | ||
| 2328 | if (rc) | 2341 | if (rc) |
| 2329 | goto retry; | 2342 | goto retry; |
| 2330 | 2343 | ||
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 14ba80bfa571..8286f742436b 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c | |||
| @@ -1432,6 +1432,29 @@ static ssize_t dasd_reservation_state_store(struct device *dev, | |||
| 1432 | static DEVICE_ATTR(last_known_reservation_state, 0644, | 1432 | static DEVICE_ATTR(last_known_reservation_state, 0644, |
| 1433 | dasd_reservation_state_show, dasd_reservation_state_store); | 1433 | dasd_reservation_state_show, dasd_reservation_state_store); |
| 1434 | 1434 | ||
| 1435 | static ssize_t dasd_pm_show(struct device *dev, | ||
| 1436 | struct device_attribute *attr, char *buf) | ||
| 1437 | { | ||
| 1438 | struct dasd_device *device; | ||
| 1439 | u8 opm, nppm, cablepm, cuirpm, hpfpm; | ||
| 1440 | |||
| 1441 | device = dasd_device_from_cdev(to_ccwdev(dev)); | ||
| 1442 | if (IS_ERR(device)) | ||
| 1443 | return sprintf(buf, "0\n"); | ||
| 1444 | |||
| 1445 | opm = device->path_data.opm; | ||
| 1446 | nppm = device->path_data.npm; | ||
| 1447 | cablepm = device->path_data.cablepm; | ||
| 1448 | cuirpm = device->path_data.cuirpm; | ||
| 1449 | hpfpm = device->path_data.hpfpm; | ||
| 1450 | dasd_put_device(device); | ||
| 1451 | |||
| 1452 | return sprintf(buf, "%02x %02x %02x %02x %02x\n", opm, nppm, | ||
| 1453 | cablepm, cuirpm, hpfpm); | ||
| 1454 | } | ||
| 1455 | |||
| 1456 | static DEVICE_ATTR(path_masks, 0444, dasd_pm_show, NULL); | ||
| 1457 | |||
| 1435 | static struct attribute * dasd_attrs[] = { | 1458 | static struct attribute * dasd_attrs[] = { |
| 1436 | &dev_attr_readonly.attr, | 1459 | &dev_attr_readonly.attr, |
| 1437 | &dev_attr_discipline.attr, | 1460 | &dev_attr_discipline.attr, |
| @@ -1450,6 +1473,7 @@ static struct attribute * dasd_attrs[] = { | |||
| 1450 | &dev_attr_reservation_policy.attr, | 1473 | &dev_attr_reservation_policy.attr, |
| 1451 | &dev_attr_last_known_reservation_state.attr, | 1474 | &dev_attr_last_known_reservation_state.attr, |
| 1452 | &dev_attr_safe_offline.attr, | 1475 | &dev_attr_safe_offline.attr, |
| 1476 | &dev_attr_path_masks.attr, | ||
| 1453 | NULL, | 1477 | NULL, |
| 1454 | }; | 1478 | }; |
| 1455 | 1479 | ||
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 51dea7baf02c..d47f5b99623a 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c | |||
| @@ -29,6 +29,8 @@ | |||
| 29 | #include <asm/cio.h> | 29 | #include <asm/cio.h> |
| 30 | #include <asm/ccwdev.h> | 30 | #include <asm/ccwdev.h> |
| 31 | #include <asm/itcw.h> | 31 | #include <asm/itcw.h> |
| 32 | #include <asm/schid.h> | ||
| 33 | #include <asm/chpid.h> | ||
| 32 | 34 | ||
| 33 | #include "dasd_int.h" | 35 | #include "dasd_int.h" |
| 34 | #include "dasd_eckd.h" | 36 | #include "dasd_eckd.h" |
| @@ -112,6 +114,12 @@ struct path_verification_work_data { | |||
| 112 | static struct path_verification_work_data *path_verification_worker; | 114 | static struct path_verification_work_data *path_verification_worker; |
| 113 | static DEFINE_MUTEX(dasd_path_verification_mutex); | 115 | static DEFINE_MUTEX(dasd_path_verification_mutex); |
| 114 | 116 | ||
| 117 | struct check_attention_work_data { | ||
| 118 | struct work_struct worker; | ||
| 119 | struct dasd_device *device; | ||
| 120 | __u8 lpum; | ||
| 121 | }; | ||
| 122 | |||
| 115 | /* initial attempt at a probe function. this can be simplified once | 123 | /* initial attempt at a probe function. this can be simplified once |
| 116 | * the other detection code is gone */ | 124 | * the other detection code is gone */ |
| 117 | static int | 125 | static int |
| @@ -1126,6 +1134,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) | |||
| 1126 | "device %s instead of %s\n", lpm, | 1134 | "device %s instead of %s\n", lpm, |
| 1127 | print_path_uid, print_device_uid); | 1135 | print_path_uid, print_device_uid); |
| 1128 | path_err = -EINVAL; | 1136 | path_err = -EINVAL; |
| 1137 | path_data->cablepm |= lpm; | ||
| 1129 | continue; | 1138 | continue; |
| 1130 | } | 1139 | } |
| 1131 | 1140 | ||
| @@ -1141,6 +1150,13 @@ static int dasd_eckd_read_conf(struct dasd_device *device) | |||
| 1141 | break; | 1150 | break; |
| 1142 | } | 1151 | } |
| 1143 | path_data->opm |= lpm; | 1152 | path_data->opm |= lpm; |
| 1153 | /* | ||
| 1154 | * if the path is used | ||
| 1155 | * it should not be in one of the negative lists | ||
| 1156 | */ | ||
| 1157 | path_data->cablepm &= ~lpm; | ||
| 1158 | path_data->hpfpm &= ~lpm; | ||
| 1159 | path_data->cuirpm &= ~lpm; | ||
| 1144 | 1160 | ||
| 1145 | if (conf_data != private->conf_data) | 1161 | if (conf_data != private->conf_data) |
| 1146 | kfree(conf_data); | 1162 | kfree(conf_data); |
| @@ -1230,7 +1246,7 @@ static void do_path_verification_work(struct work_struct *work) | |||
| 1230 | struct dasd_eckd_private path_private; | 1246 | struct dasd_eckd_private path_private; |
| 1231 | struct dasd_uid *uid; | 1247 | struct dasd_uid *uid; |
| 1232 | __u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE]; | 1248 | __u8 path_rcd_buf[DASD_ECKD_RCD_DATA_SIZE]; |
| 1233 | __u8 lpm, opm, npm, ppm, epm; | 1249 | __u8 lpm, opm, npm, ppm, epm, hpfpm, cablepm; |
| 1234 | unsigned long flags; | 1250 | unsigned long flags; |
| 1235 | char print_uid[60]; | 1251 | char print_uid[60]; |
| 1236 | int rc; | 1252 | int rc; |
| @@ -1248,6 +1264,9 @@ static void do_path_verification_work(struct work_struct *work) | |||
| 1248 | npm = 0; | 1264 | npm = 0; |
| 1249 | ppm = 0; | 1265 | ppm = 0; |
| 1250 | epm = 0; | 1266 | epm = 0; |
| 1267 | hpfpm = 0; | ||
| 1268 | cablepm = 0; | ||
| 1269 | |||
| 1251 | for (lpm = 0x80; lpm; lpm >>= 1) { | 1270 | for (lpm = 0x80; lpm; lpm >>= 1) { |
| 1252 | if (!(lpm & data->tbvpm)) | 1271 | if (!(lpm & data->tbvpm)) |
| 1253 | continue; | 1272 | continue; |
| @@ -1289,6 +1308,7 @@ static void do_path_verification_work(struct work_struct *work) | |||
| 1289 | opm &= ~lpm; | 1308 | opm &= ~lpm; |
| 1290 | npm &= ~lpm; | 1309 | npm &= ~lpm; |
| 1291 | ppm &= ~lpm; | 1310 | ppm &= ~lpm; |
| 1311 | hpfpm |= lpm; | ||
| 1292 | continue; | 1312 | continue; |
| 1293 | } | 1313 | } |
| 1294 | 1314 | ||
| @@ -1350,6 +1370,7 @@ static void do_path_verification_work(struct work_struct *work) | |||
| 1350 | opm &= ~lpm; | 1370 | opm &= ~lpm; |
| 1351 | npm &= ~lpm; | 1371 | npm &= ~lpm; |
| 1352 | ppm &= ~lpm; | 1372 | ppm &= ~lpm; |
| 1373 | cablepm |= lpm; | ||
| 1353 | continue; | 1374 | continue; |
| 1354 | } | 1375 | } |
| 1355 | } | 1376 | } |
| @@ -1364,12 +1385,21 @@ static void do_path_verification_work(struct work_struct *work) | |||
| 1364 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | 1385 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); |
| 1365 | if (!device->path_data.opm && opm) { | 1386 | if (!device->path_data.opm && opm) { |
| 1366 | device->path_data.opm = opm; | 1387 | device->path_data.opm = opm; |
| 1388 | device->path_data.cablepm &= ~opm; | ||
| 1389 | device->path_data.cuirpm &= ~opm; | ||
| 1390 | device->path_data.hpfpm &= ~opm; | ||
| 1367 | dasd_generic_path_operational(device); | 1391 | dasd_generic_path_operational(device); |
| 1368 | } else | 1392 | } else { |
| 1369 | device->path_data.opm |= opm; | 1393 | device->path_data.opm |= opm; |
| 1394 | device->path_data.cablepm &= ~opm; | ||
| 1395 | device->path_data.cuirpm &= ~opm; | ||
| 1396 | device->path_data.hpfpm &= ~opm; | ||
| 1397 | } | ||
| 1370 | device->path_data.npm |= npm; | 1398 | device->path_data.npm |= npm; |
| 1371 | device->path_data.ppm |= ppm; | 1399 | device->path_data.ppm |= ppm; |
| 1372 | device->path_data.tbvpm |= epm; | 1400 | device->path_data.tbvpm |= epm; |
| 1401 | device->path_data.cablepm |= cablepm; | ||
| 1402 | device->path_data.hpfpm |= hpfpm; | ||
| 1373 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | 1403 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); |
| 1374 | } | 1404 | } |
| 1375 | 1405 | ||
| @@ -4475,6 +4505,343 @@ out_err: | |||
| 4475 | return -1; | 4505 | return -1; |
| 4476 | } | 4506 | } |
| 4477 | 4507 | ||
| 4508 | static int dasd_eckd_read_message_buffer(struct dasd_device *device, | ||
| 4509 | struct dasd_rssd_messages *messages, | ||
| 4510 | __u8 lpum) | ||
| 4511 | { | ||
| 4512 | struct dasd_rssd_messages *message_buf; | ||
| 4513 | struct dasd_psf_prssd_data *prssdp; | ||
| 4514 | struct dasd_eckd_private *private; | ||
| 4515 | struct dasd_ccw_req *cqr; | ||
| 4516 | struct ccw1 *ccw; | ||
| 4517 | int rc; | ||
| 4518 | |||
| 4519 | private = (struct dasd_eckd_private *) device->private; | ||
| 4520 | cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ + 1 /* RSSD */, | ||
| 4521 | (sizeof(struct dasd_psf_prssd_data) + | ||
| 4522 | sizeof(struct dasd_rssd_messages)), | ||
| 4523 | device); | ||
| 4524 | if (IS_ERR(cqr)) { | ||
| 4525 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", | ||
| 4526 | "Could not allocate read message buffer request"); | ||
| 4527 | return PTR_ERR(cqr); | ||
| 4528 | } | ||
| 4529 | |||
| 4530 | cqr->startdev = device; | ||
| 4531 | cqr->memdev = device; | ||
| 4532 | cqr->block = NULL; | ||
| 4533 | cqr->retries = 256; | ||
| 4534 | cqr->expires = 10 * HZ; | ||
| 4535 | |||
| 4536 | /* we need to check for messages on exactly this path */ | ||
| 4537 | set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); | ||
| 4538 | cqr->lpm = lpum; | ||
| 4539 | |||
| 4540 | /* Prepare for Read Subsystem Data */ | ||
| 4541 | prssdp = (struct dasd_psf_prssd_data *) cqr->data; | ||
| 4542 | memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data)); | ||
| 4543 | prssdp->order = PSF_ORDER_PRSSD; | ||
| 4544 | prssdp->suborder = 0x03; /* Message Buffer */ | ||
| 4545 | /* all other bytes of prssdp must be zero */ | ||
| 4546 | |||
| 4547 | ccw = cqr->cpaddr; | ||
| 4548 | ccw->cmd_code = DASD_ECKD_CCW_PSF; | ||
| 4549 | ccw->count = sizeof(struct dasd_psf_prssd_data); | ||
| 4550 | ccw->flags |= CCW_FLAG_CC; | ||
| 4551 | ccw->flags |= CCW_FLAG_SLI; | ||
| 4552 | ccw->cda = (__u32)(addr_t) prssdp; | ||
| 4553 | |||
| 4554 | /* Read Subsystem Data - message buffer */ | ||
| 4555 | message_buf = (struct dasd_rssd_messages *) (prssdp + 1); | ||
| 4556 | memset(message_buf, 0, sizeof(struct dasd_rssd_messages)); | ||
| 4557 | |||
| 4558 | ccw++; | ||
| 4559 | ccw->cmd_code = DASD_ECKD_CCW_RSSD; | ||
| 4560 | ccw->count = sizeof(struct dasd_rssd_messages); | ||
| 4561 | ccw->flags |= CCW_FLAG_SLI; | ||
| 4562 | ccw->cda = (__u32)(addr_t) message_buf; | ||
| 4563 | |||
| 4564 | cqr->buildclk = get_tod_clock(); | ||
| 4565 | cqr->status = DASD_CQR_FILLED; | ||
| 4566 | rc = dasd_sleep_on_immediatly(cqr); | ||
| 4567 | if (rc == 0) { | ||
| 4568 | prssdp = (struct dasd_psf_prssd_data *) cqr->data; | ||
| 4569 | message_buf = (struct dasd_rssd_messages *) | ||
| 4570 | (prssdp + 1); | ||
| 4571 | memcpy(messages, message_buf, | ||
| 4572 | sizeof(struct dasd_rssd_messages)); | ||
| 4573 | } else | ||
| 4574 | DBF_EVENT_DEVID(DBF_WARNING, device->cdev, | ||
| 4575 | "Reading messages failed with rc=%d\n" | ||
| 4576 | , rc); | ||
| 4577 | dasd_sfree_request(cqr, cqr->memdev); | ||
| 4578 | return rc; | ||
| 4579 | } | ||
| 4580 | |||
| 4581 | /* | ||
| 4582 | * Perform Subsystem Function - CUIR response | ||
| 4583 | */ | ||
| 4584 | static int | ||
| 4585 | dasd_eckd_psf_cuir_response(struct dasd_device *device, int response, | ||
| 4586 | __u32 message_id, | ||
| 4587 | struct channel_path_desc *desc, | ||
| 4588 | struct subchannel_id sch_id) | ||
| 4589 | { | ||
| 4590 | struct dasd_psf_cuir_response *psf_cuir; | ||
| 4591 | struct dasd_ccw_req *cqr; | ||
| 4592 | struct ccw1 *ccw; | ||
| 4593 | int rc; | ||
| 4594 | |||
| 4595 | cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* PSF */ , | ||
| 4596 | sizeof(struct dasd_psf_cuir_response), | ||
| 4597 | device); | ||
| 4598 | |||
| 4599 | if (IS_ERR(cqr)) { | ||
| 4600 | DBF_DEV_EVENT(DBF_WARNING, device, "%s", | ||
| 4601 | "Could not allocate PSF-CUIR request"); | ||
| 4602 | return PTR_ERR(cqr); | ||
| 4603 | } | ||
| 4604 | |||
| 4605 | psf_cuir = (struct dasd_psf_cuir_response *)cqr->data; | ||
| 4606 | psf_cuir->order = PSF_ORDER_CUIR_RESPONSE; | ||
| 4607 | psf_cuir->cc = response; | ||
| 4608 | if (desc) | ||
| 4609 | psf_cuir->chpid = desc->chpid; | ||
| 4610 | psf_cuir->message_id = message_id; | ||
| 4611 | psf_cuir->cssid = sch_id.cssid; | ||
| 4612 | psf_cuir->ssid = sch_id.ssid; | ||
| 4613 | |||
| 4614 | ccw = cqr->cpaddr; | ||
| 4615 | ccw->cmd_code = DASD_ECKD_CCW_PSF; | ||
| 4616 | ccw->cda = (__u32)(addr_t)psf_cuir; | ||
| 4617 | ccw->count = sizeof(struct dasd_psf_cuir_response); | ||
| 4618 | |||
| 4619 | cqr->startdev = device; | ||
| 4620 | cqr->memdev = device; | ||
| 4621 | cqr->block = NULL; | ||
| 4622 | cqr->retries = 256; | ||
| 4623 | cqr->expires = 10*HZ; | ||
| 4624 | cqr->buildclk = get_tod_clock(); | ||
| 4625 | cqr->status = DASD_CQR_FILLED; | ||
| 4626 | |||
| 4627 | rc = dasd_sleep_on(cqr); | ||
| 4628 | |||
| 4629 | dasd_sfree_request(cqr, cqr->memdev); | ||
| 4630 | return rc; | ||
| 4631 | } | ||
| 4632 | |||
| 4633 | static int dasd_eckd_cuir_change_state(struct dasd_device *device, __u8 lpum) | ||
| 4634 | { | ||
| 4635 | unsigned long flags; | ||
| 4636 | __u8 tbcpm; | ||
| 4637 | |||
| 4638 | spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); | ||
| 4639 | tbcpm = device->path_data.opm & ~lpum; | ||
| 4640 | if (tbcpm) { | ||
| 4641 | device->path_data.opm = tbcpm; | ||
| 4642 | device->path_data.cuirpm |= lpum; | ||
| 4643 | } | ||
| 4644 | spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); | ||
| 4645 | return tbcpm ? 0 : PSF_CUIR_LAST_PATH; | ||
| 4646 | } | ||
| 4647 | |||
| 4648 | /* | ||
| 4649 | * walk through all devices and quiesce them | ||
| 4650 | * if it is the last path return error | ||
| 4651 | * | ||
| 4652 | * if only part of the devices are quiesced and an error | ||
| 4653 | * occurs no onlining necessary, the storage server will | ||
| 4654 | * notify the already set offline devices again | ||
| 4655 | */ | ||
| 4656 | static int dasd_eckd_cuir_quiesce(struct dasd_device *device, __u8 lpum, | ||
| 4657 | struct channel_path_desc *desc, | ||
| 4658 | struct subchannel_id sch_id) | ||
| 4659 | { | ||
| 4660 | struct alias_pav_group *pavgroup, *tempgroup; | ||
| 4661 | struct dasd_eckd_private *private; | ||
| 4662 | struct dasd_device *dev, *n; | ||
| 4663 | int rc; | ||
| 4664 | |||
| 4665 | private = (struct dasd_eckd_private *) device->private; | ||
| 4666 | rc = 0; | ||
| 4667 | |||
| 4668 | /* active devices */ | ||
| 4669 | list_for_each_entry_safe(dev, n, | ||
| 4670 | &private->lcu->active_devices, | ||
| 4671 | alias_list) { | ||
| 4672 | rc = dasd_eckd_cuir_change_state(dev, lpum); | ||
| 4673 | if (rc) | ||
| 4674 | goto out; | ||
| 4675 | } | ||
| 4676 | |||
| 4677 | /* inactive devices */ | ||
| 4678 | list_for_each_entry_safe(dev, n, | ||
| 4679 | &private->lcu->inactive_devices, | ||
| 4680 | alias_list) { | ||
| 4681 | rc = dasd_eckd_cuir_change_state(dev, lpum); | ||
| 4682 | if (rc) | ||
| 4683 | goto out; | ||
| 4684 | } | ||
| 4685 | |||
| 4686 | /* devices in PAV groups */ | ||
| 4687 | list_for_each_entry_safe(pavgroup, tempgroup, | ||
| 4688 | &private->lcu->grouplist, group) { | ||
| 4689 | list_for_each_entry_safe(dev, n, &pavgroup->baselist, | ||
| 4690 | alias_list) { | ||
| 4691 | rc = dasd_eckd_cuir_change_state(dev, lpum); | ||
| 4692 | if (rc) | ||
| 4693 | goto out; | ||
| 4694 | } | ||
| 4695 | list_for_each_entry_safe(dev, n, &pavgroup->aliaslist, | ||
| 4696 | alias_list) { | ||
| 4697 | rc = dasd_eckd_cuir_change_state(dev, lpum); | ||
| 4698 | if (rc) | ||
| 4699 | goto out; | ||
| 4700 | } | ||
| 4701 | } | ||
| 4702 | |||
| 4703 | pr_warn("Service on the storage server caused path %x.%02x to go offline", | ||
| 4704 | sch_id.cssid, desc ? desc->chpid : 0); | ||
| 4705 | rc = PSF_CUIR_COMPLETED; | ||
| 4706 | out: | ||
| 4707 | return rc; | ||
| 4708 | } | ||
| 4709 | |||
| 4710 | static int dasd_eckd_cuir_resume(struct dasd_device *device, __u8 lpum, | ||
| 4711 | struct channel_path_desc *desc, | ||
| 4712 | struct subchannel_id sch_id) | ||
| 4713 | { | ||
| 4714 | struct alias_pav_group *pavgroup, *tempgroup; | ||
| 4715 | struct dasd_eckd_private *private; | ||
| 4716 | struct dasd_device *dev, *n; | ||
| 4717 | |||
| 4718 | pr_info("Path %x.%02x is back online after service on the storage server", | ||
| 4719 | sch_id.cssid, desc ? desc->chpid : 0); | ||
| 4720 | private = (struct dasd_eckd_private *) device->private; | ||
| 4721 | |||
| 4722 | /* | ||
| 4723 | * the path may have been added through a generic path event before | ||
| 4724 | * only trigger path verification if the path is not already in use | ||
| 4725 | */ | ||
| 4726 | |||
| 4727 | list_for_each_entry_safe(dev, n, | ||
| 4728 | &private->lcu->active_devices, | ||
| 4729 | alias_list) { | ||
| 4730 | if (!(dev->path_data.opm & lpum)) { | ||
| 4731 | dev->path_data.tbvpm |= lpum; | ||
| 4732 | dasd_schedule_device_bh(dev); | ||
| 4733 | } | ||
| 4734 | } | ||
| 4735 | |||
| 4736 | list_for_each_entry_safe(dev, n, | ||
| 4737 | &private->lcu->inactive_devices, | ||
| 4738 | alias_list) { | ||
| 4739 | if (!(dev->path_data.opm & lpum)) { | ||
| 4740 | dev->path_data.tbvpm |= lpum; | ||
| 4741 | dasd_schedule_device_bh(dev); | ||
| 4742 | } | ||
| 4743 | } | ||
| 4744 | |||
| 4745 | /* devices in PAV groups */ | ||
| 4746 | list_for_each_entry_safe(pavgroup, tempgroup, | ||
| 4747 | &private->lcu->grouplist, | ||
| 4748 | group) { | ||
| 4749 | list_for_each_entry_safe(dev, n, | ||
| 4750 | &pavgroup->baselist, | ||
| 4751 | alias_list) { | ||
| 4752 | if (!(dev->path_data.opm & lpum)) { | ||
| 4753 | dev->path_data.tbvpm |= lpum; | ||
| 4754 | dasd_schedule_device_bh(dev); | ||
| 4755 | } | ||
| 4756 | } | ||
| 4757 | list_for_each_entry_safe(dev, n, | ||
| 4758 | &pavgroup->aliaslist, | ||
| 4759 | alias_list) { | ||
| 4760 | if (!(dev->path_data.opm & lpum)) { | ||
| 4761 | dev->path_data.tbvpm |= lpum; | ||
| 4762 | dasd_schedule_device_bh(dev); | ||
| 4763 | } | ||
| 4764 | } | ||
| 4765 | } | ||
| 4766 | return PSF_CUIR_COMPLETED; | ||
| 4767 | } | ||
| 4768 | |||
| 4769 | static void dasd_eckd_handle_cuir(struct dasd_device *device, void *messages, | ||
| 4770 | __u8 lpum) | ||
| 4771 | { | ||
| 4772 | struct dasd_cuir_message *cuir = messages; | ||
| 4773 | struct channel_path_desc *desc; | ||
| 4774 | struct subchannel_id sch_id; | ||
| 4775 | int pos, response; | ||
| 4776 | ccw_device_get_schid(device->cdev, &sch_id); | ||
| 4777 | |||
| 4778 | /* get position of path in mask */ | ||
| 4779 | pos = 8 - ffs(lpum); | ||
| 4780 | /* get channel path descriptor from this position */ | ||
| 4781 | desc = ccw_device_get_chp_desc(device->cdev, pos); | ||
| 4782 | |||
| 4783 | if (cuir->code == CUIR_QUIESCE) { | ||
| 4784 | /* quiesce */ | ||
| 4785 | response = dasd_eckd_cuir_quiesce(device, lpum, desc, sch_id); | ||
| 4786 | } else if (cuir->code == CUIR_RESUME) { | ||
| 4787 | /* resume */ | ||
| 4788 | response = dasd_eckd_cuir_resume(device, lpum, desc, sch_id); | ||
| 4789 | } else | ||
| 4790 | response = PSF_CUIR_NOT_SUPPORTED; | ||
| 4791 | |||
| 4792 | dasd_eckd_psf_cuir_response(device, response, cuir->message_id, | ||
| 4793 | desc, sch_id); | ||
| 4794 | |||
| 4795 | /* free descriptor copy */ | ||
| 4796 | kfree(desc); | ||
| 4797 | } | ||
| 4798 | |||
| 4799 | static void dasd_eckd_check_attention_work(struct work_struct *work) | ||
| 4800 | { | ||
| 4801 | struct check_attention_work_data *data; | ||
| 4802 | struct dasd_rssd_messages *messages; | ||
| 4803 | struct dasd_device *device; | ||
| 4804 | int rc; | ||
| 4805 | |||
| 4806 | data = container_of(work, struct check_attention_work_data, worker); | ||
| 4807 | device = data->device; | ||
| 4808 | |||
| 4809 | messages = kzalloc(sizeof(*messages), GFP_KERNEL); | ||
| 4810 | if (!messages) { | ||
| 4811 | DBF_DEV_EVENT(DBF_WARNING, device, "%s", | ||
| 4812 | "Could not allocate attention message buffer"); | ||
| 4813 | goto out; | ||
| 4814 | } | ||
| 4815 | |||
| 4816 | rc = dasd_eckd_read_message_buffer(device, messages, data->lpum); | ||
| 4817 | if (rc) | ||
| 4818 | goto out; | ||
| 4819 | |||
| 4820 | if (messages->length == ATTENTION_LENGTH_CUIR && | ||
| 4821 | messages->format == ATTENTION_FORMAT_CUIR) | ||
| 4822 | dasd_eckd_handle_cuir(device, messages, data->lpum); | ||
| 4823 | |||
| 4824 | out: | ||
| 4825 | dasd_put_device(device); | ||
| 4826 | kfree(messages); | ||
| 4827 | kfree(data); | ||
| 4828 | } | ||
| 4829 | |||
| 4830 | static int dasd_eckd_check_attention(struct dasd_device *device, __u8 lpum) | ||
| 4831 | { | ||
| 4832 | struct check_attention_work_data *data; | ||
| 4833 | |||
| 4834 | data = kzalloc(sizeof(*data), GFP_ATOMIC); | ||
| 4835 | if (!data) | ||
| 4836 | return -ENOMEM; | ||
| 4837 | INIT_WORK(&data->worker, dasd_eckd_check_attention_work); | ||
| 4838 | dasd_get_device(device); | ||
| 4839 | data->device = device; | ||
| 4840 | data->lpum = lpum; | ||
| 4841 | schedule_work(&data->worker); | ||
| 4842 | return 0; | ||
| 4843 | } | ||
| 4844 | |||
| 4478 | static struct ccw_driver dasd_eckd_driver = { | 4845 | static struct ccw_driver dasd_eckd_driver = { |
| 4479 | .driver = { | 4846 | .driver = { |
| 4480 | .name = "dasd-eckd", | 4847 | .name = "dasd-eckd", |
| @@ -4539,6 +4906,7 @@ static struct dasd_discipline dasd_eckd_discipline = { | |||
| 4539 | .reload = dasd_eckd_reload_device, | 4906 | .reload = dasd_eckd_reload_device, |
| 4540 | .get_uid = dasd_eckd_get_uid, | 4907 | .get_uid = dasd_eckd_get_uid, |
| 4541 | .kick_validate = dasd_eckd_kick_validate_server, | 4908 | .kick_validate = dasd_eckd_kick_validate_server, |
| 4909 | .check_attention = dasd_eckd_check_attention, | ||
| 4542 | }; | 4910 | }; |
| 4543 | 4911 | ||
| 4544 | static int __init | 4912 | static int __init |
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 2555e494591f..ddab7df36e25 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h | |||
| @@ -51,8 +51,35 @@ | |||
| 51 | /* | 51 | /* |
| 52 | * Perform Subsystem Function / Sub-Orders | 52 | * Perform Subsystem Function / Sub-Orders |
| 53 | */ | 53 | */ |
| 54 | #define PSF_ORDER_PRSSD 0x18 | 54 | #define PSF_ORDER_PRSSD 0x18 |
| 55 | #define PSF_ORDER_SSC 0x1D | 55 | #define PSF_ORDER_CUIR_RESPONSE 0x1A |
| 56 | #define PSF_ORDER_SSC 0x1D | ||
| 57 | |||
| 58 | /* | ||
| 59 | * CUIR response condition codes | ||
| 60 | */ | ||
| 61 | #define PSF_CUIR_INVALID 0x00 | ||
| 62 | #define PSF_CUIR_COMPLETED 0x01 | ||
| 63 | #define PSF_CUIR_NOT_SUPPORTED 0x02 | ||
| 64 | #define PSF_CUIR_ERROR_IN_REQ 0x03 | ||
| 65 | #define PSF_CUIR_DENIED 0x04 | ||
| 66 | #define PSF_CUIR_LAST_PATH 0x05 | ||
| 67 | #define PSF_CUIR_DEVICE_ONLINE 0x06 | ||
| 68 | #define PSF_CUIR_VARY_FAILURE 0x07 | ||
| 69 | #define PSF_CUIR_SOFTWARE_FAILURE 0x08 | ||
| 70 | #define PSF_CUIR_NOT_RECOGNIZED 0x09 | ||
| 71 | |||
| 72 | /* | ||
| 73 | * CUIR codes | ||
| 74 | */ | ||
| 75 | #define CUIR_QUIESCE 0x01 | ||
| 76 | #define CUIR_RESUME 0x02 | ||
| 77 | |||
| 78 | /* | ||
| 79 | * attention message definitions | ||
| 80 | */ | ||
| 81 | #define ATTENTION_LENGTH_CUIR 0x0e | ||
| 82 | #define ATTENTION_FORMAT_CUIR 0x01 | ||
| 56 | 83 | ||
| 57 | /* | 84 | /* |
| 58 | * Size that is reportet for large volumes in the old 16-bit no_cyl field | 85 | * Size that is reportet for large volumes in the old 16-bit no_cyl field |
| @@ -342,6 +369,38 @@ struct dasd_rssd_features { | |||
| 342 | char feature[256]; | 369 | char feature[256]; |
| 343 | } __attribute__((packed)); | 370 | } __attribute__((packed)); |
| 344 | 371 | ||
| 372 | struct dasd_rssd_messages { | ||
| 373 | __u16 length; | ||
| 374 | __u8 format; | ||
| 375 | __u8 code; | ||
| 376 | __u32 message_id; | ||
| 377 | __u8 flags; | ||
| 378 | char messages[4087]; | ||
| 379 | } __packed; | ||
| 380 | |||
| 381 | struct dasd_cuir_message { | ||
| 382 | __u16 length; | ||
| 383 | __u8 format; | ||
| 384 | __u8 code; | ||
| 385 | __u32 message_id; | ||
| 386 | __u8 flags; | ||
| 387 | __u8 neq_map[3]; | ||
| 388 | __u8 ned_map; | ||
| 389 | __u8 record_selector; | ||
| 390 | } __packed; | ||
| 391 | |||
| 392 | struct dasd_psf_cuir_response { | ||
| 393 | __u8 order; | ||
| 394 | __u8 flags; | ||
| 395 | __u8 cc; | ||
| 396 | __u8 chpid; | ||
| 397 | __u16 device_nr; | ||
| 398 | __u16 reserved; | ||
| 399 | __u32 message_id; | ||
| 400 | __u64 system_id; | ||
| 401 | __u8 cssid; | ||
| 402 | __u8 ssid; | ||
| 403 | } __packed; | ||
| 345 | 404 | ||
| 346 | /* | 405 | /* |
| 347 | * Perform Subsystem Function - Prepare for Read Subsystem Data | 406 | * Perform Subsystem Function - Prepare for Read Subsystem Data |
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index c20170166909..8b5d4100abf7 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h | |||
| @@ -357,6 +357,7 @@ struct dasd_discipline { | |||
| 357 | 357 | ||
| 358 | int (*get_uid) (struct dasd_device *, struct dasd_uid *); | 358 | int (*get_uid) (struct dasd_device *, struct dasd_uid *); |
| 359 | void (*kick_validate) (struct dasd_device *); | 359 | void (*kick_validate) (struct dasd_device *); |
| 360 | int (*check_attention)(struct dasd_device *, __u8); | ||
| 360 | }; | 361 | }; |
| 361 | 362 | ||
| 362 | extern struct dasd_discipline *dasd_diag_discipline_pointer; | 363 | extern struct dasd_discipline *dasd_diag_discipline_pointer; |
| @@ -382,6 +383,10 @@ struct dasd_path { | |||
| 382 | __u8 tbvpm; | 383 | __u8 tbvpm; |
| 383 | __u8 ppm; | 384 | __u8 ppm; |
| 384 | __u8 npm; | 385 | __u8 npm; |
| 386 | /* paths that are not used because of a special condition */ | ||
| 387 | __u8 cablepm; /* miss-cabled */ | ||
| 388 | __u8 hpfpm; /* the HPF requirements of the other paths are not met */ | ||
| 389 | __u8 cuirpm; /* CUIR varied offline */ | ||
| 385 | }; | 390 | }; |
| 386 | 391 | ||
| 387 | struct dasd_profile_info { | 392 | struct dasd_profile_info { |
| @@ -501,7 +506,10 @@ struct dasd_block { | |||
| 501 | struct dasd_profile profile; | 506 | struct dasd_profile profile; |
| 502 | }; | 507 | }; |
| 503 | 508 | ||
| 504 | 509 | struct dasd_attention_data { | |
| 510 | struct dasd_device *device; | ||
| 511 | __u8 lpum; | ||
| 512 | }; | ||
| 505 | 513 | ||
| 506 | /* reasons why device (ccw_device_start) was stopped */ | 514 | /* reasons why device (ccw_device_start) was stopped */ |
| 507 | #define DASD_STOPPED_NOT_ACC 1 /* not accessible */ | 515 | #define DASD_STOPPED_NOT_ACC 1 /* not accessible */ |
diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 71bf959732fe..dc24ecfac2d1 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig | |||
| @@ -102,6 +102,19 @@ config SCLP_ASYNC | |||
| 102 | want for inform other people about your kernel panics, | 102 | want for inform other people about your kernel panics, |
| 103 | need this feature and intend to run your kernel in LPAR. | 103 | need this feature and intend to run your kernel in LPAR. |
| 104 | 104 | ||
| 105 | config HMC_DRV | ||
| 106 | def_tristate m | ||
| 107 | prompt "Support for file transfers from HMC drive CD/DVD-ROM" | ||
| 108 | depends on 64BIT | ||
| 109 | select CRC16 | ||
| 110 | help | ||
| 111 | This option enables support for file transfers from a Hardware | ||
| 112 | Management Console (HMC) drive CD/DVD-ROM. It is available as a | ||
| 113 | module, called 'hmcdrv', and also as kernel built-in. There is one | ||
| 114 | optional parameter for this module: cachesize=N, which modifies the | ||
| 115 | transfer cache size from it's default value 0.5MB to N bytes. If N | ||
| 116 | is zero, then no caching is performed. | ||
| 117 | |||
| 105 | config S390_TAPE | 118 | config S390_TAPE |
| 106 | def_tristate m | 119 | def_tristate m |
| 107 | prompt "S/390 tape device support" | 120 | prompt "S/390 tape device support" |
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 78b6ace7edcb..6fa9364d1c07 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile | |||
| @@ -33,3 +33,6 @@ obj-$(CONFIG_S390_VMUR) += vmur.o | |||
| 33 | 33 | ||
| 34 | zcore_mod-objs := sclp_sdias.o zcore.o | 34 | zcore_mod-objs := sclp_sdias.o zcore.o |
| 35 | obj-$(CONFIG_CRASH_DUMP) += zcore_mod.o | 35 | obj-$(CONFIG_CRASH_DUMP) += zcore_mod.o |
| 36 | |||
| 37 | hmcdrv-objs := hmcdrv_mod.o hmcdrv_dev.o hmcdrv_ftp.o hmcdrv_cache.o diag_ftp.o sclp_ftp.o | ||
| 38 | obj-$(CONFIG_HMC_DRV) += hmcdrv.o | ||
diff --git a/drivers/s390/char/diag_ftp.c b/drivers/s390/char/diag_ftp.c new file mode 100644 index 000000000000..93889632fdf9 --- /dev/null +++ b/drivers/s390/char/diag_ftp.c | |||
| @@ -0,0 +1,237 @@ | |||
| 1 | /* | ||
| 2 | * DIAGNOSE X'2C4' instruction based HMC FTP services, useable on z/VM | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2013 | ||
| 5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 6 | * | ||
| 7 | */ | ||
| 8 | |||
| 9 | #define KMSG_COMPONENT "hmcdrv" | ||
| 10 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
| 11 | |||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/mm.h> | ||
| 14 | #include <linux/irq.h> | ||
| 15 | #include <linux/wait.h> | ||
| 16 | #include <linux/string.h> | ||
| 17 | #include <asm/ctl_reg.h> | ||
| 18 | |||
| 19 | #include "hmcdrv_ftp.h" | ||
| 20 | #include "diag_ftp.h" | ||
| 21 | |||
| 22 | /* DIAGNOSE X'2C4' return codes in Ry */ | ||
| 23 | #define DIAG_FTP_RET_OK 0 /* HMC FTP started successfully */ | ||
| 24 | #define DIAG_FTP_RET_EBUSY 4 /* HMC FTP service currently busy */ | ||
| 25 | #define DIAG_FTP_RET_EIO 8 /* HMC FTP service I/O error */ | ||
| 26 | /* and an artificial extension */ | ||
| 27 | #define DIAG_FTP_RET_EPERM 2 /* HMC FTP service privilege error */ | ||
| 28 | |||
| 29 | /* FTP service status codes (after INTR at guest real location 133) */ | ||
| 30 | #define DIAG_FTP_STAT_OK 0U /* request completed successfully */ | ||
| 31 | #define DIAG_FTP_STAT_PGCC 4U /* program check condition */ | ||
| 32 | #define DIAG_FTP_STAT_PGIOE 8U /* paging I/O error */ | ||
| 33 | #define DIAG_FTP_STAT_TIMEOUT 12U /* timeout */ | ||
| 34 | #define DIAG_FTP_STAT_EBASE 16U /* base of error codes from SCLP */ | ||
| 35 | #define DIAG_FTP_STAT_LDFAIL (DIAG_FTP_STAT_EBASE + 1U) /* failed */ | ||
| 36 | #define DIAG_FTP_STAT_LDNPERM (DIAG_FTP_STAT_EBASE + 2U) /* not allowed */ | ||
| 37 | #define DIAG_FTP_STAT_LDRUNS (DIAG_FTP_STAT_EBASE + 3U) /* runs */ | ||
| 38 | #define DIAG_FTP_STAT_LDNRUNS (DIAG_FTP_STAT_EBASE + 4U) /* not runs */ | ||
| 39 | |||
| 40 | /** | ||
| 41 | * struct diag_ftp_ldfpl - load file FTP parameter list (LDFPL) | ||
| 42 | * @bufaddr: real buffer address (at 4k boundary) | ||
| 43 | * @buflen: length of buffer | ||
| 44 | * @offset: dir/file offset | ||
| 45 | * @intparm: interruption parameter (unused) | ||
| 46 | * @transferred: bytes transferred | ||
| 47 | * @fsize: file size, filled on GET | ||
| 48 | * @failaddr: failing address | ||
| 49 | * @spare: padding | ||
| 50 | * @fident: file name - ASCII | ||
| 51 | */ | ||
| 52 | struct diag_ftp_ldfpl { | ||
| 53 | u64 bufaddr; | ||
| 54 | u64 buflen; | ||
| 55 | u64 offset; | ||
| 56 | u64 intparm; | ||
| 57 | u64 transferred; | ||
| 58 | u64 fsize; | ||
| 59 | u64 failaddr; | ||
| 60 | u64 spare; | ||
| 61 | u8 fident[HMCDRV_FTP_FIDENT_MAX]; | ||
| 62 | } __packed; | ||
| 63 | |||
| 64 | static DECLARE_COMPLETION(diag_ftp_rx_complete); | ||
| 65 | static int diag_ftp_subcode; | ||
| 66 | |||
| 67 | /** | ||
| 68 | * diag_ftp_handler() - FTP services IRQ handler | ||
| 69 | * @extirq: external interrupt (sub-) code | ||
| 70 | * @param32: 32-bit interruption parameter from &struct diag_ftp_ldfpl | ||
| 71 | * @param64: unused (for 64-bit interrupt parameters) | ||
| 72 | */ | ||
| 73 | static void diag_ftp_handler(struct ext_code extirq, | ||
| 74 | unsigned int param32, | ||
| 75 | unsigned long param64) | ||
| 76 | { | ||
| 77 | if ((extirq.subcode >> 8) != 8) | ||
| 78 | return; /* not a FTP services sub-code */ | ||
| 79 | |||
| 80 | inc_irq_stat(IRQEXT_FTP); | ||
| 81 | diag_ftp_subcode = extirq.subcode & 0xffU; | ||
| 82 | complete(&diag_ftp_rx_complete); | ||
| 83 | } | ||
| 84 | |||
| 85 | /** | ||
| 86 | * diag_ftp_2c4() - DIAGNOSE X'2C4' service call | ||
| 87 | * @fpl: pointer to prepared LDFPL | ||
| 88 | * @cmd: FTP command to be executed | ||
| 89 | * | ||
| 90 | * Performs a DIAGNOSE X'2C4' call with (input/output) FTP parameter list | ||
| 91 | * @fpl and FTP function code @cmd. In case of an error the function does | ||
| 92 | * nothing and returns an (negative) error code. | ||
| 93 | * | ||
| 94 | * Notes: | ||
| 95 | * 1. This function only initiates a transfer, so the caller must wait | ||
| 96 | * for completion (asynchronous execution). | ||
| 97 | * 2. The FTP parameter list @fpl must be aligned to a double-word boundary. | ||
| 98 | * 3. fpl->bufaddr must be a real address, 4k aligned | ||
| 99 | */ | ||
| 100 | static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl, | ||
| 101 | enum hmcdrv_ftp_cmdid cmd) | ||
| 102 | { | ||
| 103 | int rc; | ||
| 104 | |||
| 105 | asm volatile( | ||
| 106 | " diag %[addr],%[cmd],0x2c4\n" | ||
| 107 | "0: j 2f\n" | ||
| 108 | "1: la %[rc],%[err]\n" | ||
| 109 | "2:\n" | ||
| 110 | EX_TABLE(0b, 1b) | ||
| 111 | : [rc] "=d" (rc), "+m" (*fpl) | ||
| 112 | : [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)), | ||
| 113 | [err] "i" (DIAG_FTP_RET_EPERM) | ||
| 114 | : "cc"); | ||
| 115 | |||
| 116 | switch (rc) { | ||
| 117 | case DIAG_FTP_RET_OK: | ||
| 118 | return 0; | ||
| 119 | case DIAG_FTP_RET_EBUSY: | ||
| 120 | return -EBUSY; | ||
| 121 | case DIAG_FTP_RET_EPERM: | ||
| 122 | return -EPERM; | ||
| 123 | case DIAG_FTP_RET_EIO: | ||
| 124 | default: | ||
| 125 | return -EIO; | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | /** | ||
| 130 | * diag_ftp_cmd() - executes a DIAG X'2C4' FTP command, targeting a HMC | ||
| 131 | * @ftp: pointer to FTP command specification | ||
| 132 | * @fsize: return of file size (or NULL if undesirable) | ||
| 133 | * | ||
| 134 | * Attention: Notice that this function is not reentrant - so the caller | ||
| 135 | * must ensure locking. | ||
| 136 | * | ||
| 137 | * Return: number of bytes read/written or a (negative) error code | ||
| 138 | */ | ||
| 139 | ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) | ||
| 140 | { | ||
| 141 | struct diag_ftp_ldfpl *ldfpl; | ||
| 142 | ssize_t len; | ||
| 143 | #ifdef DEBUG | ||
| 144 | unsigned long start_jiffies; | ||
| 145 | |||
| 146 | pr_debug("starting DIAG X'2C4' on '%s', requesting %zd bytes\n", | ||
| 147 | ftp->fname, ftp->len); | ||
| 148 | start_jiffies = jiffies; | ||
| 149 | #endif | ||
| 150 | init_completion(&diag_ftp_rx_complete); | ||
| 151 | |||
| 152 | ldfpl = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
| 153 | if (!ldfpl) { | ||
| 154 | len = -ENOMEM; | ||
| 155 | goto out; | ||
| 156 | } | ||
| 157 | |||
| 158 | len = strlcpy(ldfpl->fident, ftp->fname, sizeof(ldfpl->fident)); | ||
| 159 | if (len >= HMCDRV_FTP_FIDENT_MAX) { | ||
| 160 | len = -EINVAL; | ||
| 161 | goto out_free; | ||
| 162 | } | ||
| 163 | |||
| 164 | ldfpl->transferred = 0; | ||
| 165 | ldfpl->fsize = 0; | ||
| 166 | ldfpl->offset = ftp->ofs; | ||
| 167 | ldfpl->buflen = ftp->len; | ||
| 168 | ldfpl->bufaddr = virt_to_phys(ftp->buf); | ||
| 169 | |||
| 170 | len = diag_ftp_2c4(ldfpl, ftp->id); | ||
| 171 | if (len) | ||
| 172 | goto out_free; | ||
| 173 | |||
| 174 | /* | ||
| 175 | * There is no way to cancel the running diag X'2C4', the code | ||
| 176 | * needs to wait unconditionally until the transfer is complete. | ||
| 177 | */ | ||
| 178 | wait_for_completion(&diag_ftp_rx_complete); | ||
| 179 | |||
| 180 | #ifdef DEBUG | ||
| 181 | pr_debug("completed DIAG X'2C4' after %lu ms\n", | ||
| 182 | (jiffies - start_jiffies) * 1000 / HZ); | ||
| 183 | pr_debug("status of DIAG X'2C4' is %u, with %lld/%lld bytes\n", | ||
| 184 | diag_ftp_subcode, ldfpl->transferred, ldfpl->fsize); | ||
| 185 | #endif | ||
| 186 | |||
| 187 | switch (diag_ftp_subcode) { | ||
| 188 | case DIAG_FTP_STAT_OK: /* success */ | ||
| 189 | len = ldfpl->transferred; | ||
| 190 | if (fsize) | ||
| 191 | *fsize = ldfpl->fsize; | ||
| 192 | break; | ||
| 193 | case DIAG_FTP_STAT_LDNPERM: | ||
| 194 | len = -EPERM; | ||
| 195 | break; | ||
| 196 | case DIAG_FTP_STAT_LDRUNS: | ||
| 197 | len = -EBUSY; | ||
| 198 | break; | ||
| 199 | case DIAG_FTP_STAT_LDFAIL: | ||
| 200 | len = -ENOENT; /* no such file or media */ | ||
| 201 | break; | ||
| 202 | default: | ||
| 203 | len = -EIO; | ||
| 204 | break; | ||
| 205 | } | ||
| 206 | |||
| 207 | out_free: | ||
| 208 | free_page((unsigned long) ldfpl); | ||
| 209 | out: | ||
| 210 | return len; | ||
| 211 | } | ||
| 212 | |||
| 213 | /** | ||
| 214 | * diag_ftp_startup() - startup of FTP services, when running on z/VM | ||
| 215 | * | ||
| 216 | * Return: 0 on success, else an (negative) error code | ||
| 217 | */ | ||
| 218 | int diag_ftp_startup(void) | ||
| 219 | { | ||
| 220 | int rc; | ||
| 221 | |||
| 222 | rc = register_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler); | ||
| 223 | if (rc) | ||
| 224 | return rc; | ||
| 225 | |||
| 226 | ctl_set_bit(0, 63 - 22); | ||
| 227 | return 0; | ||
| 228 | } | ||
| 229 | |||
| 230 | /** | ||
| 231 | * diag_ftp_shutdown() - shutdown of FTP services, when running on z/VM | ||
| 232 | */ | ||
| 233 | void diag_ftp_shutdown(void) | ||
| 234 | { | ||
| 235 | ctl_clear_bit(0, 63 - 22); | ||
| 236 | unregister_external_irq(EXT_IRQ_CP_SERVICE, diag_ftp_handler); | ||
| 237 | } | ||
diff --git a/drivers/s390/char/diag_ftp.h b/drivers/s390/char/diag_ftp.h new file mode 100644 index 000000000000..3abd2614053a --- /dev/null +++ b/drivers/s390/char/diag_ftp.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* | ||
| 2 | * DIAGNOSE X'2C4' instruction based SE/HMC FTP Services, useable on z/VM | ||
| 3 | * | ||
| 4 | * Notice that all functions exported here are not reentrant. | ||
| 5 | * So usage should be exclusive, ensured by the caller (e.g. using a | ||
| 6 | * mutex). | ||
| 7 | * | ||
| 8 | * Copyright IBM Corp. 2013 | ||
| 9 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef __DIAG_FTP_H__ | ||
| 13 | #define __DIAG_FTP_H__ | ||
| 14 | |||
| 15 | #include "hmcdrv_ftp.h" | ||
| 16 | |||
| 17 | int diag_ftp_startup(void); | ||
| 18 | void diag_ftp_shutdown(void); | ||
| 19 | ssize_t diag_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize); | ||
| 20 | |||
| 21 | #endif /* __DIAG_FTP_H__ */ | ||
diff --git a/drivers/s390/char/hmcdrv_cache.c b/drivers/s390/char/hmcdrv_cache.c new file mode 100644 index 000000000000..4cda5ada143a --- /dev/null +++ b/drivers/s390/char/hmcdrv_cache.c | |||
| @@ -0,0 +1,252 @@ | |||
| 1 | /* | ||
| 2 | * SE/HMC Drive (Read) Cache Functions | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2013 | ||
| 5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 6 | * | ||
| 7 | */ | ||
| 8 | |||
| 9 | #define KMSG_COMPONENT "hmcdrv" | ||
| 10 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
| 11 | |||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/mm.h> | ||
| 14 | #include <linux/jiffies.h> | ||
| 15 | |||
| 16 | #include "hmcdrv_ftp.h" | ||
| 17 | #include "hmcdrv_cache.h" | ||
| 18 | |||
| 19 | #define HMCDRV_CACHE_TIMEOUT 30 /* aging timeout in seconds */ | ||
| 20 | |||
| 21 | /** | ||
| 22 | * struct hmcdrv_cache_entry - file cache (only used on read/dir) | ||
| 23 | * @id: FTP command ID | ||
| 24 | * @content: kernel-space buffer, 4k aligned | ||
| 25 | * @len: size of @content cache (0 if caching disabled) | ||
| 26 | * @ofs: start of content within file (-1 if no cached content) | ||
| 27 | * @fname: file name | ||
| 28 | * @fsize: file size | ||
| 29 | * @timeout: cache timeout in jiffies | ||
| 30 | * | ||
| 31 | * Notice that the first three members (id, fname, fsize) are cached on all | ||
| 32 | * read/dir requests. But content is cached only under some preconditions. | ||
| 33 | * Uncached content is signalled by a negative value of @ofs. | ||
| 34 | */ | ||
| 35 | struct hmcdrv_cache_entry { | ||
| 36 | enum hmcdrv_ftp_cmdid id; | ||
| 37 | char fname[HMCDRV_FTP_FIDENT_MAX]; | ||
| 38 | size_t fsize; | ||
| 39 | loff_t ofs; | ||
| 40 | unsigned long timeout; | ||
| 41 | void *content; | ||
| 42 | size_t len; | ||
| 43 | }; | ||
| 44 | |||
| 45 | static int hmcdrv_cache_order; /* cache allocated page order */ | ||
| 46 | |||
| 47 | static struct hmcdrv_cache_entry hmcdrv_cache_file = { | ||
| 48 | .fsize = SIZE_MAX, | ||
| 49 | .ofs = -1, | ||
| 50 | .len = 0, | ||
| 51 | .fname = {'\0'} | ||
| 52 | }; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * hmcdrv_cache_get() - looks for file data/content in read cache | ||
| 56 | * @ftp: pointer to FTP command specification | ||
| 57 | * | ||
| 58 | * Return: number of bytes read from cache or a negative number if nothing | ||
| 59 | * in content cache (for the file/cmd specified in @ftp) | ||
| 60 | */ | ||
| 61 | static ssize_t hmcdrv_cache_get(const struct hmcdrv_ftp_cmdspec *ftp) | ||
| 62 | { | ||
| 63 | loff_t pos; /* position in cache (signed) */ | ||
| 64 | ssize_t len; | ||
| 65 | |||
| 66 | if ((ftp->id != hmcdrv_cache_file.id) || | ||
| 67 | strcmp(hmcdrv_cache_file.fname, ftp->fname)) | ||
| 68 | return -1; | ||
| 69 | |||
| 70 | if (ftp->ofs >= hmcdrv_cache_file.fsize) /* EOF ? */ | ||
| 71 | return 0; | ||
| 72 | |||
| 73 | if ((hmcdrv_cache_file.ofs < 0) || /* has content? */ | ||
| 74 | time_after(jiffies, hmcdrv_cache_file.timeout)) | ||
| 75 | return -1; | ||
| 76 | |||
| 77 | /* there seems to be cached content - calculate the maximum number | ||
| 78 | * of bytes that can be returned (regarding file size and offset) | ||
| 79 | */ | ||
| 80 | len = hmcdrv_cache_file.fsize - ftp->ofs; | ||
| 81 | |||
| 82 | if (len > ftp->len) | ||
| 83 | len = ftp->len; | ||
| 84 | |||
| 85 | /* check if the requested chunk falls into our cache (which starts | ||
| 86 | * at offset 'hmcdrv_cache_file.ofs' in the file of interest) | ||
| 87 | */ | ||
| 88 | pos = ftp->ofs - hmcdrv_cache_file.ofs; | ||
| 89 | |||
| 90 | if ((pos >= 0) && | ||
| 91 | ((pos + len) <= hmcdrv_cache_file.len)) { | ||
| 92 | |||
| 93 | memcpy(ftp->buf, | ||
| 94 | hmcdrv_cache_file.content + pos, | ||
| 95 | len); | ||
| 96 | pr_debug("using cached content of '%s', returning %zd/%zd bytes\n", | ||
| 97 | hmcdrv_cache_file.fname, len, | ||
| 98 | hmcdrv_cache_file.fsize); | ||
| 99 | |||
| 100 | return len; | ||
| 101 | } | ||
| 102 | |||
| 103 | return -1; | ||
| 104 | } | ||
| 105 | |||
| 106 | /** | ||
| 107 | * hmcdrv_cache_do() - do a HMC drive CD/DVD transfer with cache update | ||
| 108 | * @ftp: pointer to FTP command specification | ||
| 109 | * @func: FTP transfer function to be used | ||
| 110 | * | ||
| 111 | * Return: number of bytes read/written or a (negative) error code | ||
| 112 | */ | ||
| 113 | static ssize_t hmcdrv_cache_do(const struct hmcdrv_ftp_cmdspec *ftp, | ||
| 114 | hmcdrv_cache_ftpfunc func) | ||
| 115 | { | ||
| 116 | ssize_t len; | ||
| 117 | |||
| 118 | /* only cache content if the read/dir cache really exists | ||
| 119 | * (hmcdrv_cache_file.len > 0), is large enough to handle the | ||
| 120 | * request (hmcdrv_cache_file.len >= ftp->len) and there is a need | ||
| 121 | * to do so (ftp->len > 0) | ||
| 122 | */ | ||
| 123 | if ((ftp->len > 0) && (hmcdrv_cache_file.len >= ftp->len)) { | ||
| 124 | |||
| 125 | /* because the cache is not located at ftp->buf, we have to | ||
| 126 | * assemble a new HMC drive FTP cmd specification (pointing | ||
| 127 | * to our cache, and using the increased size) | ||
| 128 | */ | ||
| 129 | struct hmcdrv_ftp_cmdspec cftp = *ftp; /* make a copy */ | ||
| 130 | cftp.buf = hmcdrv_cache_file.content; /* and update */ | ||
| 131 | cftp.len = hmcdrv_cache_file.len; /* buffer data */ | ||
| 132 | |||
| 133 | len = func(&cftp, &hmcdrv_cache_file.fsize); /* now do */ | ||
| 134 | |||
| 135 | if (len > 0) { | ||
| 136 | pr_debug("caching %zd bytes content for '%s'\n", | ||
| 137 | len, ftp->fname); | ||
| 138 | |||
| 139 | if (len > ftp->len) | ||
| 140 | len = ftp->len; | ||
| 141 | |||
| 142 | hmcdrv_cache_file.ofs = ftp->ofs; | ||
| 143 | hmcdrv_cache_file.timeout = jiffies + | ||
| 144 | HMCDRV_CACHE_TIMEOUT * HZ; | ||
| 145 | memcpy(ftp->buf, hmcdrv_cache_file.content, len); | ||
| 146 | } | ||
| 147 | } else { | ||
| 148 | len = func(ftp, &hmcdrv_cache_file.fsize); | ||
| 149 | hmcdrv_cache_file.ofs = -1; /* invalidate content */ | ||
| 150 | } | ||
| 151 | |||
| 152 | if (len > 0) { | ||
| 153 | /* cache some file info (FTP command, file name and file | ||
| 154 | * size) unconditionally | ||
| 155 | */ | ||
| 156 | strlcpy(hmcdrv_cache_file.fname, ftp->fname, | ||
| 157 | HMCDRV_FTP_FIDENT_MAX); | ||
| 158 | hmcdrv_cache_file.id = ftp->id; | ||
| 159 | pr_debug("caching cmd %d, file size %zu for '%s'\n", | ||
| 160 | ftp->id, hmcdrv_cache_file.fsize, ftp->fname); | ||
| 161 | } | ||
| 162 | |||
| 163 | return len; | ||
| 164 | } | ||
| 165 | |||
| 166 | /** | ||
| 167 | * hmcdrv_cache_cmd() - perform a cached HMC drive CD/DVD transfer | ||
| 168 | * @ftp: pointer to FTP command specification | ||
| 169 | * @func: FTP transfer function to be used | ||
| 170 | * | ||
| 171 | * Attention: Notice that this function is not reentrant - so the caller | ||
| 172 | * must ensure exclusive execution. | ||
| 173 | * | ||
| 174 | * Return: number of bytes read/written or a (negative) error code | ||
| 175 | */ | ||
| 176 | ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp, | ||
| 177 | hmcdrv_cache_ftpfunc func) | ||
| 178 | { | ||
| 179 | ssize_t len; | ||
| 180 | |||
| 181 | if ((ftp->id == HMCDRV_FTP_DIR) || /* read cache */ | ||
| 182 | (ftp->id == HMCDRV_FTP_NLIST) || | ||
| 183 | (ftp->id == HMCDRV_FTP_GET)) { | ||
| 184 | |||
| 185 | len = hmcdrv_cache_get(ftp); | ||
| 186 | |||
| 187 | if (len >= 0) /* got it from cache ? */ | ||
| 188 | return len; /* yes */ | ||
| 189 | |||
| 190 | len = hmcdrv_cache_do(ftp, func); | ||
| 191 | |||
| 192 | if (len >= 0) | ||
| 193 | return len; | ||
| 194 | |||
| 195 | } else { | ||
| 196 | len = func(ftp, NULL); /* simply do original command */ | ||
| 197 | } | ||
| 198 | |||
| 199 | /* invalidate the (read) cache in case there was a write operation | ||
| 200 | * or an error on read/dir | ||
| 201 | */ | ||
| 202 | hmcdrv_cache_file.id = HMCDRV_FTP_NOOP; | ||
| 203 | hmcdrv_cache_file.fsize = LLONG_MAX; | ||
| 204 | hmcdrv_cache_file.ofs = -1; | ||
| 205 | |||
| 206 | return len; | ||
| 207 | } | ||
| 208 | |||
| 209 | /** | ||
| 210 | * hmcdrv_cache_startup() - startup of HMC drive cache | ||
| 211 | * @cachesize: cache size | ||
| 212 | * | ||
| 213 | * Return: 0 on success, else a (negative) error code | ||
| 214 | */ | ||
| 215 | int hmcdrv_cache_startup(size_t cachesize) | ||
| 216 | { | ||
| 217 | if (cachesize > 0) { /* perform caching ? */ | ||
| 218 | hmcdrv_cache_order = get_order(cachesize); | ||
| 219 | hmcdrv_cache_file.content = | ||
| 220 | (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, | ||
| 221 | hmcdrv_cache_order); | ||
| 222 | |||
| 223 | if (!hmcdrv_cache_file.content) { | ||
| 224 | pr_err("Allocating the requested cache size of %zu bytes failed\n", | ||
| 225 | cachesize); | ||
| 226 | return -ENOMEM; | ||
| 227 | } | ||
| 228 | |||
| 229 | pr_debug("content cache enabled, size is %zu bytes\n", | ||
| 230 | cachesize); | ||
| 231 | } | ||
| 232 | |||
| 233 | hmcdrv_cache_file.len = cachesize; | ||
| 234 | return 0; | ||
| 235 | } | ||
| 236 | |||
| 237 | /** | ||
| 238 | * hmcdrv_cache_shutdown() - shutdown of HMC drive cache | ||
| 239 | */ | ||
| 240 | void hmcdrv_cache_shutdown(void) | ||
| 241 | { | ||
| 242 | if (hmcdrv_cache_file.content) { | ||
| 243 | free_pages((unsigned long) hmcdrv_cache_file.content, | ||
| 244 | hmcdrv_cache_order); | ||
| 245 | hmcdrv_cache_file.content = NULL; | ||
| 246 | } | ||
| 247 | |||
| 248 | hmcdrv_cache_file.id = HMCDRV_FTP_NOOP; | ||
| 249 | hmcdrv_cache_file.fsize = LLONG_MAX; | ||
| 250 | hmcdrv_cache_file.ofs = -1; | ||
| 251 | hmcdrv_cache_file.len = 0; /* no cache */ | ||
| 252 | } | ||
diff --git a/drivers/s390/char/hmcdrv_cache.h b/drivers/s390/char/hmcdrv_cache.h new file mode 100644 index 000000000000..a14b57526781 --- /dev/null +++ b/drivers/s390/char/hmcdrv_cache.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* | ||
| 2 | * SE/HMC Drive (Read) Cache Functions | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2013 | ||
| 5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef __HMCDRV_CACHE_H__ | ||
| 9 | #define __HMCDRV_CACHE_H__ | ||
| 10 | |||
| 11 | #include <linux/mmzone.h> | ||
| 12 | #include "hmcdrv_ftp.h" | ||
| 13 | |||
| 14 | #define HMCDRV_CACHE_SIZE_DFLT (MAX_ORDER_NR_PAGES * PAGE_SIZE / 2UL) | ||
| 15 | |||
| 16 | typedef ssize_t (*hmcdrv_cache_ftpfunc)(const struct hmcdrv_ftp_cmdspec *ftp, | ||
| 17 | size_t *fsize); | ||
| 18 | |||
| 19 | ssize_t hmcdrv_cache_cmd(const struct hmcdrv_ftp_cmdspec *ftp, | ||
| 20 | hmcdrv_cache_ftpfunc func); | ||
| 21 | int hmcdrv_cache_startup(size_t cachesize); | ||
| 22 | void hmcdrv_cache_shutdown(void); | ||
| 23 | |||
| 24 | #endif /* __HMCDRV_CACHE_H__ */ | ||
diff --git a/drivers/s390/char/hmcdrv_dev.c b/drivers/s390/char/hmcdrv_dev.c new file mode 100644 index 000000000000..0c5176179c17 --- /dev/null +++ b/drivers/s390/char/hmcdrv_dev.c | |||
| @@ -0,0 +1,370 @@ | |||
| 1 | /* | ||
| 2 | * HMC Drive CD/DVD Device | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2013 | ||
| 5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 6 | * | ||
| 7 | * This file provides a Linux "misc" character device for access to an | ||
| 8 | * assigned HMC drive CD/DVD-ROM. It works as follows: First create the | ||
| 9 | * device by calling hmcdrv_dev_init(). After open() a lseek(fd, 0, | ||
| 10 | * SEEK_END) indicates that a new FTP command follows (not needed on the | ||
| 11 | * first command after open). Then write() the FTP command ASCII string | ||
| 12 | * to it, e.g. "dir /" or "nls <directory>" or "get <filename>". At the | ||
| 13 | * end read() the response. | ||
| 14 | */ | ||
| 15 | |||
| 16 | #define KMSG_COMPONENT "hmcdrv" | ||
| 17 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
| 18 | |||
| 19 | #include <linux/kernel.h> | ||
| 20 | #include <linux/module.h> | ||
| 21 | #include <linux/slab.h> | ||
| 22 | #include <linux/fs.h> | ||
| 23 | #include <linux/cdev.h> | ||
| 24 | #include <linux/miscdevice.h> | ||
| 25 | #include <linux/device.h> | ||
| 26 | #include <linux/capability.h> | ||
| 27 | #include <linux/delay.h> | ||
| 28 | #include <linux/uaccess.h> | ||
| 29 | |||
| 30 | #include "hmcdrv_dev.h" | ||
| 31 | #include "hmcdrv_ftp.h" | ||
| 32 | |||
| 33 | /* If the following macro is defined, then the HMC device creates it's own | ||
| 34 | * separated device class (and dynamically assigns a major number). If not | ||
| 35 | * defined then the HMC device is assigned to the "misc" class devices. | ||
| 36 | * | ||
| 37 | #define HMCDRV_DEV_CLASS "hmcftp" | ||
| 38 | */ | ||
| 39 | |||
| 40 | #define HMCDRV_DEV_NAME "hmcdrv" | ||
| 41 | #define HMCDRV_DEV_BUSY_DELAY 500 /* delay between -EBUSY trials in ms */ | ||
| 42 | #define HMCDRV_DEV_BUSY_RETRIES 3 /* number of retries on -EBUSY */ | ||
| 43 | |||
| 44 | struct hmcdrv_dev_node { | ||
| 45 | |||
| 46 | #ifdef HMCDRV_DEV_CLASS | ||
| 47 | struct cdev dev; /* character device structure */ | ||
| 48 | umode_t mode; /* mode of device node (unused, zero) */ | ||
| 49 | #else | ||
| 50 | struct miscdevice dev; /* "misc" device structure */ | ||
| 51 | #endif | ||
| 52 | |||
| 53 | }; | ||
| 54 | |||
| 55 | static int hmcdrv_dev_open(struct inode *inode, struct file *fp); | ||
| 56 | static int hmcdrv_dev_release(struct inode *inode, struct file *fp); | ||
| 57 | static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence); | ||
| 58 | static ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf, | ||
| 59 | size_t len, loff_t *pos); | ||
| 60 | static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf, | ||
| 61 | size_t len, loff_t *pos); | ||
| 62 | static ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset, | ||
| 63 | char __user *buf, size_t len); | ||
| 64 | |||
| 65 | /* | ||
| 66 | * device operations | ||
| 67 | */ | ||
| 68 | static const struct file_operations hmcdrv_dev_fops = { | ||
| 69 | .open = hmcdrv_dev_open, | ||
| 70 | .llseek = hmcdrv_dev_seek, | ||
| 71 | .release = hmcdrv_dev_release, | ||
| 72 | .read = hmcdrv_dev_read, | ||
| 73 | .write = hmcdrv_dev_write, | ||
| 74 | }; | ||
| 75 | |||
| 76 | static struct hmcdrv_dev_node hmcdrv_dev; /* HMC device struct (static) */ | ||
| 77 | |||
| 78 | #ifdef HMCDRV_DEV_CLASS | ||
| 79 | |||
| 80 | static struct class *hmcdrv_dev_class; /* device class pointer */ | ||
| 81 | static dev_t hmcdrv_dev_no; /* device number (major/minor) */ | ||
| 82 | |||
| 83 | /** | ||
| 84 | * hmcdrv_dev_name() - provides a naming hint for a device node in /dev | ||
| 85 | * @dev: device for which the naming/mode hint is | ||
| 86 | * @mode: file mode for device node created in /dev | ||
| 87 | * | ||
| 88 | * See: devtmpfs.c, function devtmpfs_create_node() | ||
| 89 | * | ||
| 90 | * Return: recommended device file name in /dev | ||
| 91 | */ | ||
| 92 | static char *hmcdrv_dev_name(struct device *dev, umode_t *mode) | ||
| 93 | { | ||
| 94 | char *nodename = NULL; | ||
| 95 | const char *devname = dev_name(dev); /* kernel device name */ | ||
| 96 | |||
| 97 | if (devname) | ||
| 98 | nodename = kasprintf(GFP_KERNEL, "%s", devname); | ||
| 99 | |||
| 100 | /* on device destroy (rmmod) the mode pointer may be NULL | ||
| 101 | */ | ||
| 102 | if (mode) | ||
| 103 | *mode = hmcdrv_dev.mode; | ||
| 104 | |||
| 105 | return nodename; | ||
| 106 | } | ||
| 107 | |||
| 108 | #endif /* HMCDRV_DEV_CLASS */ | ||
| 109 | |||
| 110 | /* | ||
| 111 | * open() | ||
| 112 | */ | ||
| 113 | static int hmcdrv_dev_open(struct inode *inode, struct file *fp) | ||
| 114 | { | ||
| 115 | int rc; | ||
| 116 | |||
| 117 | /* check for non-blocking access, which is really unsupported | ||
| 118 | */ | ||
| 119 | if (fp->f_flags & O_NONBLOCK) | ||
| 120 | return -EINVAL; | ||
| 121 | |||
| 122 | /* Because it makes no sense to open this device read-only (then a | ||
| 123 | * FTP command cannot be emitted), we respond with an error. | ||
| 124 | */ | ||
| 125 | if ((fp->f_flags & O_ACCMODE) == O_RDONLY) | ||
| 126 | return -EINVAL; | ||
| 127 | |||
| 128 | /* prevent unloading this module as long as anyone holds the | ||
| 129 | * device file open - so increment the reference count here | ||
| 130 | */ | ||
| 131 | if (!try_module_get(THIS_MODULE)) | ||
| 132 | return -ENODEV; | ||
| 133 | |||
| 134 | fp->private_data = NULL; /* no command yet */ | ||
| 135 | rc = hmcdrv_ftp_startup(); | ||
| 136 | if (rc) | ||
| 137 | module_put(THIS_MODULE); | ||
| 138 | |||
| 139 | pr_debug("open file '/dev/%s' with return code %d\n", | ||
| 140 | fp->f_dentry->d_name.name, rc); | ||
| 141 | return rc; | ||
| 142 | } | ||
| 143 | |||
| 144 | /* | ||
| 145 | * release() | ||
| 146 | */ | ||
| 147 | static int hmcdrv_dev_release(struct inode *inode, struct file *fp) | ||
| 148 | { | ||
| 149 | pr_debug("closing file '/dev/%s'\n", fp->f_dentry->d_name.name); | ||
| 150 | kfree(fp->private_data); | ||
| 151 | fp->private_data = NULL; | ||
| 152 | hmcdrv_ftp_shutdown(); | ||
| 153 | module_put(THIS_MODULE); | ||
| 154 | return 0; | ||
| 155 | } | ||
| 156 | |||
| 157 | /* | ||
| 158 | * lseek() | ||
| 159 | */ | ||
| 160 | static loff_t hmcdrv_dev_seek(struct file *fp, loff_t pos, int whence) | ||
| 161 | { | ||
| 162 | switch (whence) { | ||
| 163 | case SEEK_CUR: /* relative to current file position */ | ||
| 164 | pos += fp->f_pos; /* new position stored in 'pos' */ | ||
| 165 | break; | ||
| 166 | |||
| 167 | case SEEK_SET: /* absolute (relative to beginning of file) */ | ||
| 168 | break; /* SEEK_SET */ | ||
| 169 | |||
| 170 | /* We use SEEK_END as a special indicator for a SEEK_SET | ||
| 171 | * (set absolute position), combined with a FTP command | ||
| 172 | * clear. | ||
| 173 | */ | ||
| 174 | case SEEK_END: | ||
| 175 | if (fp->private_data) { | ||
| 176 | kfree(fp->private_data); | ||
| 177 | fp->private_data = NULL; | ||
| 178 | } | ||
| 179 | |||
| 180 | break; /* SEEK_END */ | ||
| 181 | |||
| 182 | default: /* SEEK_DATA, SEEK_HOLE: unsupported */ | ||
| 183 | return -EINVAL; | ||
| 184 | } | ||
| 185 | |||
| 186 | if (pos < 0) | ||
| 187 | return -EINVAL; | ||
| 188 | |||
| 189 | if (fp->f_pos != pos) | ||
| 190 | ++fp->f_version; | ||
| 191 | |||
| 192 | fp->f_pos = pos; | ||
| 193 | return pos; | ||
| 194 | } | ||
| 195 | |||
| 196 | /* | ||
| 197 | * transfer (helper function) | ||
| 198 | */ | ||
| 199 | static ssize_t hmcdrv_dev_transfer(char __kernel *cmd, loff_t offset, | ||
| 200 | char __user *buf, size_t len) | ||
| 201 | { | ||
| 202 | ssize_t retlen; | ||
| 203 | unsigned trials = HMCDRV_DEV_BUSY_RETRIES; | ||
| 204 | |||
| 205 | do { | ||
| 206 | retlen = hmcdrv_ftp_cmd(cmd, offset, buf, len); | ||
| 207 | |||
| 208 | if (retlen != -EBUSY) | ||
| 209 | break; | ||
| 210 | |||
| 211 | msleep(HMCDRV_DEV_BUSY_DELAY); | ||
| 212 | |||
| 213 | } while (--trials > 0); | ||
| 214 | |||
| 215 | return retlen; | ||
| 216 | } | ||
| 217 | |||
| 218 | /* | ||
| 219 | * read() | ||
| 220 | */ | ||
| 221 | static ssize_t hmcdrv_dev_read(struct file *fp, char __user *ubuf, | ||
| 222 | size_t len, loff_t *pos) | ||
| 223 | { | ||
| 224 | ssize_t retlen; | ||
| 225 | |||
| 226 | if (((fp->f_flags & O_ACCMODE) == O_WRONLY) || | ||
| 227 | (fp->private_data == NULL)) { /* no FTP cmd defined ? */ | ||
| 228 | return -EBADF; | ||
| 229 | } | ||
| 230 | |||
| 231 | retlen = hmcdrv_dev_transfer((char *) fp->private_data, | ||
| 232 | *pos, ubuf, len); | ||
| 233 | |||
| 234 | pr_debug("read from file '/dev/%s' at %lld returns %zd/%zu\n", | ||
| 235 | fp->f_dentry->d_name.name, (long long) *pos, retlen, len); | ||
| 236 | |||
| 237 | if (retlen > 0) | ||
| 238 | *pos += retlen; | ||
| 239 | |||
| 240 | return retlen; | ||
| 241 | } | ||
| 242 | |||
| 243 | /* | ||
| 244 | * write() | ||
| 245 | */ | ||
| 246 | static ssize_t hmcdrv_dev_write(struct file *fp, const char __user *ubuf, | ||
| 247 | size_t len, loff_t *pos) | ||
| 248 | { | ||
| 249 | ssize_t retlen; | ||
| 250 | |||
| 251 | pr_debug("writing file '/dev/%s' at pos. %lld with length %zd\n", | ||
| 252 | fp->f_dentry->d_name.name, (long long) *pos, len); | ||
| 253 | |||
| 254 | if (!fp->private_data) { /* first expect a cmd write */ | ||
| 255 | fp->private_data = kmalloc(len + 1, GFP_KERNEL); | ||
| 256 | |||
| 257 | if (!fp->private_data) | ||
| 258 | return -ENOMEM; | ||
| 259 | |||
| 260 | if (!copy_from_user(fp->private_data, ubuf, len)) { | ||
| 261 | ((char *)fp->private_data)[len] = '\0'; | ||
| 262 | return len; | ||
| 263 | } | ||
| 264 | |||
| 265 | kfree(fp->private_data); | ||
| 266 | fp->private_data = NULL; | ||
| 267 | return -EFAULT; | ||
| 268 | } | ||
| 269 | |||
| 270 | retlen = hmcdrv_dev_transfer((char *) fp->private_data, | ||
| 271 | *pos, (char __user *) ubuf, len); | ||
| 272 | if (retlen > 0) | ||
| 273 | *pos += retlen; | ||
| 274 | |||
| 275 | pr_debug("write to file '/dev/%s' returned %zd\n", | ||
| 276 | fp->f_dentry->d_name.name, retlen); | ||
| 277 | |||
| 278 | return retlen; | ||
| 279 | } | ||
| 280 | |||
| 281 | /** | ||
| 282 | * hmcdrv_dev_init() - creates a HMC drive CD/DVD device | ||
| 283 | * | ||
| 284 | * This function creates a HMC drive CD/DVD kernel device and an associated | ||
| 285 | * device under /dev, using a dynamically allocated major number. | ||
| 286 | * | ||
| 287 | * Return: 0 on success, else an error code. | ||
| 288 | */ | ||
| 289 | int hmcdrv_dev_init(void) | ||
| 290 | { | ||
| 291 | int rc; | ||
| 292 | |||
| 293 | #ifdef HMCDRV_DEV_CLASS | ||
| 294 | struct device *dev; | ||
| 295 | |||
| 296 | rc = alloc_chrdev_region(&hmcdrv_dev_no, 0, 1, HMCDRV_DEV_NAME); | ||
| 297 | |||
| 298 | if (rc) | ||
| 299 | goto out_err; | ||
| 300 | |||
| 301 | cdev_init(&hmcdrv_dev.dev, &hmcdrv_dev_fops); | ||
| 302 | hmcdrv_dev.dev.owner = THIS_MODULE; | ||
| 303 | rc = cdev_add(&hmcdrv_dev.dev, hmcdrv_dev_no, 1); | ||
| 304 | |||
| 305 | if (rc) | ||
| 306 | goto out_unreg; | ||
| 307 | |||
| 308 | /* At this point the character device exists in the kernel (see | ||
| 309 | * /proc/devices), but not under /dev nor /sys/devices/virtual. So | ||
| 310 | * we have to create an associated class (see /sys/class). | ||
| 311 | */ | ||
| 312 | hmcdrv_dev_class = class_create(THIS_MODULE, HMCDRV_DEV_CLASS); | ||
| 313 | |||
| 314 | if (IS_ERR(hmcdrv_dev_class)) { | ||
| 315 | rc = PTR_ERR(hmcdrv_dev_class); | ||
| 316 | goto out_devdel; | ||
| 317 | } | ||
| 318 | |||
| 319 | /* Finally a device node in /dev has to be established (as 'mkdev' | ||
| 320 | * does from the command line). Notice that assignment of a device | ||
| 321 | * node name/mode function is optional (only for mode != 0600). | ||
| 322 | */ | ||
| 323 | hmcdrv_dev.mode = 0; /* "unset" */ | ||
| 324 | hmcdrv_dev_class->devnode = hmcdrv_dev_name; | ||
| 325 | |||
| 326 | dev = device_create(hmcdrv_dev_class, NULL, hmcdrv_dev_no, NULL, | ||
| 327 | "%s", HMCDRV_DEV_NAME); | ||
| 328 | if (!IS_ERR(dev)) | ||
| 329 | return 0; | ||
| 330 | |||
| 331 | rc = PTR_ERR(dev); | ||
| 332 | class_destroy(hmcdrv_dev_class); | ||
| 333 | hmcdrv_dev_class = NULL; | ||
| 334 | |||
| 335 | out_devdel: | ||
| 336 | cdev_del(&hmcdrv_dev.dev); | ||
| 337 | |||
| 338 | out_unreg: | ||
| 339 | unregister_chrdev_region(hmcdrv_dev_no, 1); | ||
| 340 | |||
| 341 | out_err: | ||
| 342 | |||
| 343 | #else /* !HMCDRV_DEV_CLASS */ | ||
| 344 | hmcdrv_dev.dev.minor = MISC_DYNAMIC_MINOR; | ||
| 345 | hmcdrv_dev.dev.name = HMCDRV_DEV_NAME; | ||
| 346 | hmcdrv_dev.dev.fops = &hmcdrv_dev_fops; | ||
| 347 | hmcdrv_dev.dev.mode = 0; /* finally produces 0600 */ | ||
| 348 | rc = misc_register(&hmcdrv_dev.dev); | ||
| 349 | #endif /* HMCDRV_DEV_CLASS */ | ||
| 350 | |||
| 351 | return rc; | ||
| 352 | } | ||
| 353 | |||
| 354 | /** | ||
| 355 | * hmcdrv_dev_exit() - destroys a HMC drive CD/DVD device | ||
| 356 | */ | ||
| 357 | void hmcdrv_dev_exit(void) | ||
| 358 | { | ||
| 359 | #ifdef HMCDRV_DEV_CLASS | ||
| 360 | if (!IS_ERR_OR_NULL(hmcdrv_dev_class)) { | ||
| 361 | device_destroy(hmcdrv_dev_class, hmcdrv_dev_no); | ||
| 362 | class_destroy(hmcdrv_dev_class); | ||
| 363 | } | ||
| 364 | |||
| 365 | cdev_del(&hmcdrv_dev.dev); | ||
| 366 | unregister_chrdev_region(hmcdrv_dev_no, 1); | ||
| 367 | #else /* !HMCDRV_DEV_CLASS */ | ||
| 368 | misc_deregister(&hmcdrv_dev.dev); | ||
| 369 | #endif /* HMCDRV_DEV_CLASS */ | ||
| 370 | } | ||
diff --git a/drivers/s390/char/hmcdrv_dev.h b/drivers/s390/char/hmcdrv_dev.h new file mode 100644 index 000000000000..cb17f07e02de --- /dev/null +++ b/drivers/s390/char/hmcdrv_dev.h | |||
| @@ -0,0 +1,14 @@ | |||
| 1 | /* | ||
| 2 | * SE/HMC Drive FTP Device | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2013 | ||
| 5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef __HMCDRV_DEV_H__ | ||
| 9 | #define __HMCDRV_DEV_H__ | ||
| 10 | |||
| 11 | int hmcdrv_dev_init(void); | ||
| 12 | void hmcdrv_dev_exit(void); | ||
| 13 | |||
| 14 | #endif /* __HMCDRV_DEV_H__ */ | ||
diff --git a/drivers/s390/char/hmcdrv_ftp.c b/drivers/s390/char/hmcdrv_ftp.c new file mode 100644 index 000000000000..4bd63322fc29 --- /dev/null +++ b/drivers/s390/char/hmcdrv_ftp.c | |||
| @@ -0,0 +1,343 @@ | |||
| 1 | /* | ||
| 2 | * HMC Drive FTP Services | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2013 | ||
| 5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 6 | */ | ||
| 7 | |||
| 8 | #define KMSG_COMPONENT "hmcdrv" | ||
| 9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
| 10 | |||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/uaccess.h> | ||
| 14 | #include <linux/export.h> | ||
| 15 | |||
| 16 | #include <linux/ctype.h> | ||
| 17 | #include <linux/crc16.h> | ||
| 18 | |||
| 19 | #include "hmcdrv_ftp.h" | ||
| 20 | #include "hmcdrv_cache.h" | ||
| 21 | #include "sclp_ftp.h" | ||
| 22 | #include "diag_ftp.h" | ||
| 23 | |||
| 24 | /** | ||
| 25 | * struct hmcdrv_ftp_ops - HMC drive FTP operations | ||
| 26 | * @startup: startup function | ||
| 27 | * @shutdown: shutdown function | ||
| 28 | * @cmd: FTP transfer function | ||
| 29 | */ | ||
| 30 | struct hmcdrv_ftp_ops { | ||
| 31 | int (*startup)(void); | ||
| 32 | void (*shutdown)(void); | ||
| 33 | ssize_t (*transfer)(const struct hmcdrv_ftp_cmdspec *ftp, | ||
| 34 | size_t *fsize); | ||
| 35 | }; | ||
| 36 | |||
| 37 | static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len); | ||
| 38 | static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp); | ||
| 39 | |||
| 40 | static struct hmcdrv_ftp_ops *hmcdrv_ftp_funcs; /* current operations */ | ||
| 41 | static DEFINE_MUTEX(hmcdrv_ftp_mutex); /* mutex for hmcdrv_ftp_funcs */ | ||
| 42 | static unsigned hmcdrv_ftp_refcnt; /* start/shutdown reference counter */ | ||
| 43 | |||
| 44 | /** | ||
| 45 | * hmcdrv_ftp_cmd_getid() - determine FTP command ID from a command string | ||
| 46 | * @cmd: FTP command string (NOT zero-terminated) | ||
| 47 | * @len: length of FTP command string in @cmd | ||
| 48 | */ | ||
| 49 | static enum hmcdrv_ftp_cmdid hmcdrv_ftp_cmd_getid(const char *cmd, int len) | ||
| 50 | { | ||
| 51 | /* HMC FTP command descriptor */ | ||
| 52 | struct hmcdrv_ftp_cmd_desc { | ||
| 53 | const char *str; /* command string */ | ||
| 54 | enum hmcdrv_ftp_cmdid cmd; /* associated command as enum */ | ||
| 55 | }; | ||
| 56 | |||
| 57 | /* Description of all HMC drive FTP commands | ||
| 58 | * | ||
| 59 | * Notes: | ||
| 60 | * 1. Array size should be a prime number. | ||
| 61 | * 2. Do not change the order of commands in table (because the | ||
| 62 | * index is determined by CRC % ARRAY_SIZE). | ||
| 63 | * 3. Original command 'nlist' was renamed, else the CRC would | ||
| 64 | * collide with 'append' (see point 2). | ||
| 65 | */ | ||
| 66 | static const struct hmcdrv_ftp_cmd_desc ftpcmds[7] = { | ||
| 67 | |||
| 68 | {.str = "get", /* [0] get (CRC = 0x68eb) */ | ||
| 69 | .cmd = HMCDRV_FTP_GET}, | ||
| 70 | {.str = "dir", /* [1] dir (CRC = 0x6a9e) */ | ||
| 71 | .cmd = HMCDRV_FTP_DIR}, | ||
| 72 | {.str = "delete", /* [2] delete (CRC = 0x53ae) */ | ||
| 73 | .cmd = HMCDRV_FTP_DELETE}, | ||
| 74 | {.str = "nls", /* [3] nls (CRC = 0xf87c) */ | ||
| 75 | .cmd = HMCDRV_FTP_NLIST}, | ||
| 76 | {.str = "put", /* [4] put (CRC = 0xac56) */ | ||
| 77 | .cmd = HMCDRV_FTP_PUT}, | ||
| 78 | {.str = "append", /* [5] append (CRC = 0xf56e) */ | ||
| 79 | .cmd = HMCDRV_FTP_APPEND}, | ||
| 80 | {.str = NULL} /* [6] unused */ | ||
| 81 | }; | ||
| 82 | |||
| 83 | const struct hmcdrv_ftp_cmd_desc *pdesc; | ||
| 84 | |||
| 85 | u16 crc = 0xffffU; | ||
| 86 | |||
| 87 | if (len == 0) | ||
| 88 | return HMCDRV_FTP_NOOP; /* error indiactor */ | ||
| 89 | |||
| 90 | crc = crc16(crc, cmd, len); | ||
| 91 | pdesc = ftpcmds + (crc % ARRAY_SIZE(ftpcmds)); | ||
| 92 | pr_debug("FTP command '%s' has CRC 0x%04x, at table pos. %lu\n", | ||
| 93 | cmd, crc, (crc % ARRAY_SIZE(ftpcmds))); | ||
| 94 | |||
| 95 | if (!pdesc->str || strncmp(pdesc->str, cmd, len)) | ||
| 96 | return HMCDRV_FTP_NOOP; | ||
| 97 | |||
| 98 | pr_debug("FTP command '%s' found, with ID %d\n", | ||
| 99 | pdesc->str, pdesc->cmd); | ||
| 100 | |||
| 101 | return pdesc->cmd; | ||
| 102 | } | ||
| 103 | |||
| 104 | /** | ||
| 105 | * hmcdrv_ftp_parse() - HMC drive FTP command parser | ||
| 106 | * @cmd: FTP command string "<cmd> <filename>" | ||
| 107 | * @ftp: Pointer to FTP command specification buffer (output) | ||
| 108 | * | ||
| 109 | * Return: 0 on success, else a (negative) error code | ||
| 110 | */ | ||
| 111 | static int hmcdrv_ftp_parse(char *cmd, struct hmcdrv_ftp_cmdspec *ftp) | ||
| 112 | { | ||
| 113 | char *start; | ||
| 114 | int argc = 0; | ||
| 115 | |||
| 116 | ftp->id = HMCDRV_FTP_NOOP; | ||
| 117 | ftp->fname = NULL; | ||
| 118 | |||
| 119 | while (*cmd != '\0') { | ||
| 120 | |||
| 121 | while (isspace(*cmd)) | ||
| 122 | ++cmd; | ||
| 123 | |||
| 124 | if (*cmd == '\0') | ||
| 125 | break; | ||
| 126 | |||
| 127 | start = cmd; | ||
| 128 | |||
| 129 | switch (argc) { | ||
| 130 | case 0: /* 1st argument (FTP command) */ | ||
| 131 | while ((*cmd != '\0') && !isspace(*cmd)) | ||
| 132 | ++cmd; | ||
| 133 | ftp->id = hmcdrv_ftp_cmd_getid(start, cmd - start); | ||
| 134 | break; | ||
| 135 | case 1: /* 2nd / last argument (rest of line) */ | ||
| 136 | while ((*cmd != '\0') && !iscntrl(*cmd)) | ||
| 137 | ++cmd; | ||
| 138 | ftp->fname = start; | ||
| 139 | /* fall through */ | ||
| 140 | default: | ||
| 141 | *cmd = '\0'; | ||
| 142 | break; | ||
| 143 | } /* switch */ | ||
| 144 | |||
| 145 | ++argc; | ||
| 146 | } /* while */ | ||
| 147 | |||
| 148 | if (!ftp->fname || (ftp->id == HMCDRV_FTP_NOOP)) | ||
| 149 | return -EINVAL; | ||
| 150 | |||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | |||
| 154 | /** | ||
| 155 | * hmcdrv_ftp_do() - perform a HMC drive FTP, with data from kernel-space | ||
| 156 | * @ftp: pointer to FTP command specification | ||
| 157 | * | ||
| 158 | * Return: number of bytes read/written or a negative error code | ||
| 159 | */ | ||
| 160 | ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp) | ||
| 161 | { | ||
| 162 | ssize_t len; | ||
| 163 | |||
| 164 | mutex_lock(&hmcdrv_ftp_mutex); | ||
| 165 | |||
| 166 | if (hmcdrv_ftp_funcs && hmcdrv_ftp_refcnt) { | ||
| 167 | pr_debug("starting transfer, cmd %d for '%s' at %lld with %zd bytes\n", | ||
| 168 | ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); | ||
| 169 | len = hmcdrv_cache_cmd(ftp, hmcdrv_ftp_funcs->transfer); | ||
| 170 | } else { | ||
| 171 | len = -ENXIO; | ||
| 172 | } | ||
| 173 | |||
| 174 | mutex_unlock(&hmcdrv_ftp_mutex); | ||
| 175 | return len; | ||
| 176 | } | ||
| 177 | EXPORT_SYMBOL(hmcdrv_ftp_do); | ||
| 178 | |||
| 179 | /** | ||
| 180 | * hmcdrv_ftp_probe() - probe for the HMC drive FTP service | ||
| 181 | * | ||
| 182 | * Return: 0 if service is available, else an (negative) error code | ||
| 183 | */ | ||
| 184 | int hmcdrv_ftp_probe(void) | ||
| 185 | { | ||
| 186 | int rc; | ||
| 187 | |||
| 188 | struct hmcdrv_ftp_cmdspec ftp = { | ||
| 189 | .id = HMCDRV_FTP_NOOP, | ||
| 190 | .ofs = 0, | ||
| 191 | .fname = "", | ||
| 192 | .len = PAGE_SIZE | ||
| 193 | }; | ||
| 194 | |||
| 195 | ftp.buf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
| 196 | |||
| 197 | if (!ftp.buf) | ||
| 198 | return -ENOMEM; | ||
| 199 | |||
| 200 | rc = hmcdrv_ftp_startup(); | ||
| 201 | |||
| 202 | if (rc) | ||
| 203 | return rc; | ||
| 204 | |||
| 205 | rc = hmcdrv_ftp_do(&ftp); | ||
| 206 | free_page((unsigned long) ftp.buf); | ||
| 207 | hmcdrv_ftp_shutdown(); | ||
| 208 | |||
| 209 | switch (rc) { | ||
| 210 | case -ENOENT: /* no such file/media or currently busy, */ | ||
| 211 | case -EBUSY: /* but service seems to be available */ | ||
| 212 | rc = 0; | ||
| 213 | break; | ||
| 214 | default: /* leave 'rc' as it is for [0, -EPERM, -E...] */ | ||
| 215 | if (rc > 0) | ||
| 216 | rc = 0; /* clear length (success) */ | ||
| 217 | break; | ||
| 218 | } /* switch */ | ||
| 219 | |||
| 220 | return rc; | ||
| 221 | } | ||
| 222 | EXPORT_SYMBOL(hmcdrv_ftp_probe); | ||
| 223 | |||
| 224 | /** | ||
| 225 | * hmcdrv_ftp_cmd() - Perform a HMC drive FTP, with data from user-space | ||
| 226 | * | ||
| 227 | * @cmd: FTP command string "<cmd> <filename>" | ||
| 228 | * @offset: file position to read/write | ||
| 229 | * @buf: user-space buffer for read/written directory/file | ||
| 230 | * @len: size of @buf (read/dir) or number of bytes to write | ||
| 231 | * | ||
| 232 | * This function must not be called before hmcdrv_ftp_startup() was called. | ||
| 233 | * | ||
| 234 | * Return: number of bytes read/written or a negative error code | ||
| 235 | */ | ||
| 236 | ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset, | ||
| 237 | char __user *buf, size_t len) | ||
| 238 | { | ||
| 239 | int order; | ||
| 240 | |||
| 241 | struct hmcdrv_ftp_cmdspec ftp = {.len = len, .ofs = offset}; | ||
| 242 | ssize_t retlen = hmcdrv_ftp_parse(cmd, &ftp); | ||
| 243 | |||
| 244 | if (retlen) | ||
| 245 | return retlen; | ||
| 246 | |||
| 247 | order = get_order(ftp.len); | ||
| 248 | ftp.buf = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, order); | ||
| 249 | |||
| 250 | if (!ftp.buf) | ||
| 251 | return -ENOMEM; | ||
| 252 | |||
| 253 | switch (ftp.id) { | ||
| 254 | case HMCDRV_FTP_DIR: | ||
| 255 | case HMCDRV_FTP_NLIST: | ||
| 256 | case HMCDRV_FTP_GET: | ||
| 257 | retlen = hmcdrv_ftp_do(&ftp); | ||
| 258 | |||
| 259 | if ((retlen >= 0) && | ||
| 260 | copy_to_user(buf, ftp.buf, retlen)) | ||
| 261 | retlen = -EFAULT; | ||
| 262 | break; | ||
| 263 | |||
| 264 | case HMCDRV_FTP_PUT: | ||
| 265 | case HMCDRV_FTP_APPEND: | ||
| 266 | if (!copy_from_user(ftp.buf, buf, ftp.len)) | ||
| 267 | retlen = hmcdrv_ftp_do(&ftp); | ||
| 268 | else | ||
| 269 | retlen = -EFAULT; | ||
| 270 | break; | ||
| 271 | |||
| 272 | case HMCDRV_FTP_DELETE: | ||
| 273 | retlen = hmcdrv_ftp_do(&ftp); | ||
| 274 | break; | ||
| 275 | |||
| 276 | default: | ||
| 277 | retlen = -EOPNOTSUPP; | ||
| 278 | break; | ||
| 279 | } | ||
| 280 | |||
| 281 | free_pages((unsigned long) ftp.buf, order); | ||
| 282 | return retlen; | ||
| 283 | } | ||
| 284 | |||
| 285 | /** | ||
| 286 | * hmcdrv_ftp_startup() - startup of HMC drive FTP functionality for a | ||
| 287 | * dedicated (owner) instance | ||
| 288 | * | ||
| 289 | * Return: 0 on success, else an (negative) error code | ||
| 290 | */ | ||
| 291 | int hmcdrv_ftp_startup(void) | ||
| 292 | { | ||
| 293 | static struct hmcdrv_ftp_ops hmcdrv_ftp_zvm = { | ||
| 294 | .startup = diag_ftp_startup, | ||
| 295 | .shutdown = diag_ftp_shutdown, | ||
| 296 | .transfer = diag_ftp_cmd | ||
| 297 | }; | ||
| 298 | |||
| 299 | static struct hmcdrv_ftp_ops hmcdrv_ftp_lpar = { | ||
| 300 | .startup = sclp_ftp_startup, | ||
| 301 | .shutdown = sclp_ftp_shutdown, | ||
| 302 | .transfer = sclp_ftp_cmd | ||
| 303 | }; | ||
| 304 | |||
| 305 | int rc = 0; | ||
| 306 | |||
| 307 | mutex_lock(&hmcdrv_ftp_mutex); /* block transfers while start-up */ | ||
| 308 | |||
| 309 | if (hmcdrv_ftp_refcnt == 0) { | ||
| 310 | if (MACHINE_IS_VM) | ||
| 311 | hmcdrv_ftp_funcs = &hmcdrv_ftp_zvm; | ||
| 312 | else if (MACHINE_IS_LPAR || MACHINE_IS_KVM) | ||
| 313 | hmcdrv_ftp_funcs = &hmcdrv_ftp_lpar; | ||
| 314 | else | ||
| 315 | rc = -EOPNOTSUPP; | ||
| 316 | |||
| 317 | if (hmcdrv_ftp_funcs) | ||
| 318 | rc = hmcdrv_ftp_funcs->startup(); | ||
| 319 | } | ||
| 320 | |||
| 321 | if (!rc) | ||
| 322 | ++hmcdrv_ftp_refcnt; | ||
| 323 | |||
| 324 | mutex_unlock(&hmcdrv_ftp_mutex); | ||
| 325 | return rc; | ||
| 326 | } | ||
| 327 | EXPORT_SYMBOL(hmcdrv_ftp_startup); | ||
| 328 | |||
| 329 | /** | ||
| 330 | * hmcdrv_ftp_shutdown() - shutdown of HMC drive FTP functionality for a | ||
| 331 | * dedicated (owner) instance | ||
| 332 | */ | ||
| 333 | void hmcdrv_ftp_shutdown(void) | ||
| 334 | { | ||
| 335 | mutex_lock(&hmcdrv_ftp_mutex); | ||
| 336 | --hmcdrv_ftp_refcnt; | ||
| 337 | |||
| 338 | if ((hmcdrv_ftp_refcnt == 0) && hmcdrv_ftp_funcs) | ||
| 339 | hmcdrv_ftp_funcs->shutdown(); | ||
| 340 | |||
| 341 | mutex_unlock(&hmcdrv_ftp_mutex); | ||
| 342 | } | ||
| 343 | EXPORT_SYMBOL(hmcdrv_ftp_shutdown); | ||
diff --git a/drivers/s390/char/hmcdrv_ftp.h b/drivers/s390/char/hmcdrv_ftp.h new file mode 100644 index 000000000000..f3643a7b3676 --- /dev/null +++ b/drivers/s390/char/hmcdrv_ftp.h | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | /* | ||
| 2 | * SE/HMC Drive FTP Services | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2013 | ||
| 5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 6 | */ | ||
| 7 | |||
| 8 | #ifndef __HMCDRV_FTP_H__ | ||
| 9 | #define __HMCDRV_FTP_H__ | ||
| 10 | |||
| 11 | #include <linux/types.h> /* size_t, loff_t */ | ||
| 12 | |||
| 13 | /* | ||
| 14 | * HMC drive FTP Service max. length of path (w/ EOS) | ||
| 15 | */ | ||
| 16 | #define HMCDRV_FTP_FIDENT_MAX 192 | ||
| 17 | |||
| 18 | /** | ||
| 19 | * enum hmcdrv_ftp_cmdid - HMC drive FTP commands | ||
| 20 | * @HMCDRV_FTP_NOOP: do nothing (only for probing) | ||
| 21 | * @HMCDRV_FTP_GET: read a file | ||
| 22 | * @HMCDRV_FTP_PUT: (over-) write a file | ||
| 23 | * @HMCDRV_FTP_APPEND: append to a file | ||
| 24 | * @HMCDRV_FTP_DIR: list directory long (ls -l) | ||
| 25 | * @HMCDRV_FTP_NLIST: list files, no directories (name list) | ||
| 26 | * @HMCDRV_FTP_DELETE: delete a file | ||
| 27 | * @HMCDRV_FTP_CANCEL: cancel operation (SCLP/LPAR only) | ||
| 28 | */ | ||
| 29 | enum hmcdrv_ftp_cmdid { | ||
| 30 | HMCDRV_FTP_NOOP = 0, | ||
| 31 | HMCDRV_FTP_GET = 1, | ||
| 32 | HMCDRV_FTP_PUT = 2, | ||
| 33 | HMCDRV_FTP_APPEND = 3, | ||
| 34 | HMCDRV_FTP_DIR = 4, | ||
| 35 | HMCDRV_FTP_NLIST = 5, | ||
| 36 | HMCDRV_FTP_DELETE = 6, | ||
| 37 | HMCDRV_FTP_CANCEL = 7 | ||
| 38 | }; | ||
| 39 | |||
| 40 | /** | ||
| 41 | * struct hmcdrv_ftp_cmdspec - FTP command specification | ||
| 42 | * @id: FTP command ID | ||
| 43 | * @ofs: offset in file | ||
| 44 | * @fname: filename (ASCII), null-terminated | ||
| 45 | * @buf: kernel-space transfer data buffer, 4k aligned | ||
| 46 | * @len: (max) number of bytes to transfer from/to @buf | ||
| 47 | */ | ||
| 48 | struct hmcdrv_ftp_cmdspec { | ||
| 49 | enum hmcdrv_ftp_cmdid id; | ||
| 50 | loff_t ofs; | ||
| 51 | const char *fname; | ||
| 52 | void __kernel *buf; | ||
| 53 | size_t len; | ||
| 54 | }; | ||
| 55 | |||
| 56 | int hmcdrv_ftp_startup(void); | ||
| 57 | void hmcdrv_ftp_shutdown(void); | ||
| 58 | int hmcdrv_ftp_probe(void); | ||
| 59 | ssize_t hmcdrv_ftp_do(const struct hmcdrv_ftp_cmdspec *ftp); | ||
| 60 | ssize_t hmcdrv_ftp_cmd(char __kernel *cmd, loff_t offset, | ||
| 61 | char __user *buf, size_t len); | ||
| 62 | |||
| 63 | #endif /* __HMCDRV_FTP_H__ */ | ||
diff --git a/drivers/s390/char/hmcdrv_mod.c b/drivers/s390/char/hmcdrv_mod.c new file mode 100644 index 000000000000..505c6a78ee1a --- /dev/null +++ b/drivers/s390/char/hmcdrv_mod.c | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | /* | ||
| 2 | * HMC Drive DVD Module | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2013 | ||
| 5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 6 | */ | ||
| 7 | |||
| 8 | #define KMSG_COMPONENT "hmcdrv" | ||
| 9 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
| 10 | |||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/moduleparam.h> | ||
| 14 | #include <linux/version.h> | ||
| 15 | #include <linux/stat.h> | ||
| 16 | |||
| 17 | #include "hmcdrv_ftp.h" | ||
| 18 | #include "hmcdrv_dev.h" | ||
| 19 | #include "hmcdrv_cache.h" | ||
| 20 | |||
| 21 | MODULE_LICENSE("GPL"); | ||
| 22 | MODULE_AUTHOR("Copyright 2013 IBM Corporation"); | ||
| 23 | MODULE_DESCRIPTION("HMC drive DVD access"); | ||
| 24 | |||
| 25 | /* | ||
| 26 | * module parameter 'cachesize' | ||
| 27 | */ | ||
| 28 | static size_t hmcdrv_mod_cachesize = HMCDRV_CACHE_SIZE_DFLT; | ||
| 29 | module_param_named(cachesize, hmcdrv_mod_cachesize, ulong, S_IRUGO); | ||
| 30 | |||
| 31 | /** | ||
| 32 | * hmcdrv_mod_init() - module init function | ||
| 33 | */ | ||
| 34 | static int __init hmcdrv_mod_init(void) | ||
| 35 | { | ||
| 36 | int rc = hmcdrv_ftp_probe(); /* perform w/o cache */ | ||
| 37 | |||
| 38 | if (rc) | ||
| 39 | return rc; | ||
| 40 | |||
| 41 | rc = hmcdrv_cache_startup(hmcdrv_mod_cachesize); | ||
| 42 | |||
| 43 | if (rc) | ||
| 44 | return rc; | ||
| 45 | |||
| 46 | rc = hmcdrv_dev_init(); | ||
| 47 | |||
| 48 | if (rc) | ||
| 49 | hmcdrv_cache_shutdown(); | ||
| 50 | |||
| 51 | return rc; | ||
| 52 | } | ||
| 53 | |||
| 54 | /** | ||
| 55 | * hmcdrv_mod_exit() - module exit function | ||
| 56 | */ | ||
| 57 | static void __exit hmcdrv_mod_exit(void) | ||
| 58 | { | ||
| 59 | hmcdrv_dev_exit(); | ||
| 60 | hmcdrv_cache_shutdown(); | ||
| 61 | } | ||
| 62 | |||
| 63 | module_init(hmcdrv_mod_init); | ||
| 64 | module_exit(hmcdrv_mod_exit); | ||
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index a68b5ec7d042..a88069f8c677 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | 19 | ||
| 20 | #define EVTYP_OPCMD 0x01 | 20 | #define EVTYP_OPCMD 0x01 |
| 21 | #define EVTYP_MSG 0x02 | 21 | #define EVTYP_MSG 0x02 |
| 22 | #define EVTYP_DIAG_TEST 0x07 | ||
| 22 | #define EVTYP_STATECHANGE 0x08 | 23 | #define EVTYP_STATECHANGE 0x08 |
| 23 | #define EVTYP_PMSGCMD 0x09 | 24 | #define EVTYP_PMSGCMD 0x09 |
| 24 | #define EVTYP_CNTLPROGOPCMD 0x20 | 25 | #define EVTYP_CNTLPROGOPCMD 0x20 |
| @@ -32,6 +33,7 @@ | |||
| 32 | 33 | ||
| 33 | #define EVTYP_OPCMD_MASK 0x80000000 | 34 | #define EVTYP_OPCMD_MASK 0x80000000 |
| 34 | #define EVTYP_MSG_MASK 0x40000000 | 35 | #define EVTYP_MSG_MASK 0x40000000 |
| 36 | #define EVTYP_DIAG_TEST_MASK 0x02000000 | ||
| 35 | #define EVTYP_STATECHANGE_MASK 0x01000000 | 37 | #define EVTYP_STATECHANGE_MASK 0x01000000 |
| 36 | #define EVTYP_PMSGCMD_MASK 0x00800000 | 38 | #define EVTYP_PMSGCMD_MASK 0x00800000 |
| 37 | #define EVTYP_CTLPROGOPCMD_MASK 0x00000001 | 39 | #define EVTYP_CTLPROGOPCMD_MASK 0x00000001 |
diff --git a/drivers/s390/char/sclp_diag.h b/drivers/s390/char/sclp_diag.h new file mode 100644 index 000000000000..59c4afa5e670 --- /dev/null +++ b/drivers/s390/char/sclp_diag.h | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | /* | ||
| 2 | * Copyright IBM Corp. 2013 | ||
| 3 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 4 | */ | ||
| 5 | |||
| 6 | #ifndef _SCLP_DIAG_H | ||
| 7 | #define _SCLP_DIAG_H | ||
| 8 | |||
| 9 | #include <linux/types.h> | ||
| 10 | |||
| 11 | /* return codes for Diagnostic Test FTP Service, as indicated in member | ||
| 12 | * sclp_diag_ftp::ldflg | ||
| 13 | */ | ||
| 14 | #define SCLP_DIAG_FTP_OK 0x80U /* success */ | ||
| 15 | #define SCLP_DIAG_FTP_LDFAIL 0x01U /* load failed */ | ||
| 16 | #define SCLP_DIAG_FTP_LDNPERM 0x02U /* not allowed */ | ||
| 17 | #define SCLP_DIAG_FTP_LDRUNS 0x03U /* LD runs */ | ||
| 18 | #define SCLP_DIAG_FTP_LDNRUNS 0x04U /* LD does not run */ | ||
| 19 | |||
| 20 | #define SCLP_DIAG_FTP_XPCX 0x80 /* PCX communication code */ | ||
| 21 | #define SCLP_DIAG_FTP_ROUTE 4 /* routing code for new FTP service */ | ||
| 22 | |||
| 23 | /* | ||
| 24 | * length of Diagnostic Test FTP Service event buffer | ||
| 25 | */ | ||
| 26 | #define SCLP_DIAG_FTP_EVBUF_LEN \ | ||
| 27 | (offsetof(struct sclp_diag_evbuf, mdd) + \ | ||
| 28 | sizeof(struct sclp_diag_ftp)) | ||
| 29 | |||
| 30 | /** | ||
| 31 | * struct sclp_diag_ftp - Diagnostic Test FTP Service model-dependent data | ||
| 32 | * @pcx: code for PCX communication (should be 0x80) | ||
| 33 | * @ldflg: load flag (see defines above) | ||
| 34 | * @cmd: FTP command | ||
| 35 | * @pgsize: page size (0 = 4kB, 1 = large page size) | ||
| 36 | * @srcflg: source flag | ||
| 37 | * @spare: reserved (zeroes) | ||
| 38 | * @offset: file offset | ||
| 39 | * @fsize: file size | ||
| 40 | * @length: buffer size resp. bytes transferred | ||
| 41 | * @failaddr: failing address | ||
| 42 | * @bufaddr: buffer address, virtual | ||
| 43 | * @asce: region or segment table designation | ||
| 44 | * @fident: file name (ASCII, zero-terminated) | ||
| 45 | */ | ||
| 46 | struct sclp_diag_ftp { | ||
| 47 | u8 pcx; | ||
| 48 | u8 ldflg; | ||
| 49 | u8 cmd; | ||
| 50 | u8 pgsize; | ||
| 51 | u8 srcflg; | ||
| 52 | u8 spare; | ||
| 53 | u64 offset; | ||
| 54 | u64 fsize; | ||
| 55 | u64 length; | ||
| 56 | u64 failaddr; | ||
| 57 | u64 bufaddr; | ||
| 58 | u64 asce; | ||
| 59 | |||
| 60 | u8 fident[256]; | ||
| 61 | } __packed; | ||
| 62 | |||
| 63 | /** | ||
| 64 | * struct sclp_diag_evbuf - Diagnostic Test (ET7) Event Buffer | ||
| 65 | * @hdr: event buffer header | ||
| 66 | * @route: diagnostic route | ||
| 67 | * @mdd: model-dependent data (@route dependent) | ||
| 68 | */ | ||
| 69 | struct sclp_diag_evbuf { | ||
| 70 | struct evbuf_header hdr; | ||
| 71 | u16 route; | ||
| 72 | |||
| 73 | union { | ||
| 74 | struct sclp_diag_ftp ftp; | ||
| 75 | } mdd; | ||
| 76 | } __packed; | ||
| 77 | |||
| 78 | /** | ||
| 79 | * struct sclp_diag_sccb - Diagnostic Test (ET7) SCCB | ||
| 80 | * @hdr: SCCB header | ||
| 81 | * @evbuf: event buffer | ||
| 82 | */ | ||
| 83 | struct sclp_diag_sccb { | ||
| 84 | |||
| 85 | struct sccb_header hdr; | ||
| 86 | struct sclp_diag_evbuf evbuf; | ||
| 87 | } __packed; | ||
| 88 | |||
| 89 | #endif /* _SCLP_DIAG_H */ | ||
diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index 1918d9dff45d..5bd6cb145a87 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c | |||
| @@ -281,7 +281,7 @@ out: | |||
| 281 | 281 | ||
| 282 | static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb) | 282 | static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb) |
| 283 | { | 283 | { |
| 284 | if (!(sccb->sclp_send_mask & (EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK))) | 284 | if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK)) |
| 285 | return 0; | 285 | return 0; |
| 286 | if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) | 286 | if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK))) |
| 287 | return 0; | 287 | return 0; |
diff --git a/drivers/s390/char/sclp_ftp.c b/drivers/s390/char/sclp_ftp.c new file mode 100644 index 000000000000..6561cc5b2d5d --- /dev/null +++ b/drivers/s390/char/sclp_ftp.c | |||
| @@ -0,0 +1,275 @@ | |||
| 1 | /* | ||
| 2 | * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR | ||
| 3 | * | ||
| 4 | * Copyright IBM Corp. 2013 | ||
| 5 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 6 | * | ||
| 7 | */ | ||
| 8 | |||
| 9 | #define KMSG_COMPONENT "hmcdrv" | ||
| 10 | #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt | ||
| 11 | |||
| 12 | #include <linux/kernel.h> | ||
| 13 | #include <linux/mm.h> | ||
| 14 | #include <linux/slab.h> | ||
| 15 | #include <linux/io.h> | ||
| 16 | #include <linux/wait.h> | ||
| 17 | #include <linux/string.h> | ||
| 18 | #include <linux/jiffies.h> | ||
| 19 | #include <asm/sysinfo.h> | ||
| 20 | #include <asm/ebcdic.h> | ||
| 21 | |||
| 22 | #include "sclp.h" | ||
| 23 | #include "sclp_diag.h" | ||
| 24 | #include "sclp_ftp.h" | ||
| 25 | |||
| 26 | static DECLARE_COMPLETION(sclp_ftp_rx_complete); | ||
| 27 | static u8 sclp_ftp_ldflg; | ||
| 28 | static u64 sclp_ftp_fsize; | ||
| 29 | static u64 sclp_ftp_length; | ||
| 30 | |||
| 31 | /** | ||
| 32 | * sclp_ftp_txcb() - Diagnostic Test FTP services SCLP command callback | ||
| 33 | */ | ||
| 34 | static void sclp_ftp_txcb(struct sclp_req *req, void *data) | ||
| 35 | { | ||
| 36 | struct completion *completion = data; | ||
| 37 | |||
| 38 | #ifdef DEBUG | ||
| 39 | pr_debug("SCLP (ET7) TX-IRQ, SCCB @ 0x%p: %*phN\n", | ||
| 40 | req->sccb, 24, req->sccb); | ||
| 41 | #endif | ||
| 42 | complete(completion); | ||
| 43 | } | ||
| 44 | |||
| 45 | /** | ||
| 46 | * sclp_ftp_rxcb() - Diagnostic Test FTP services receiver event callback | ||
| 47 | */ | ||
| 48 | static void sclp_ftp_rxcb(struct evbuf_header *evbuf) | ||
| 49 | { | ||
| 50 | struct sclp_diag_evbuf *diag = (struct sclp_diag_evbuf *) evbuf; | ||
| 51 | |||
| 52 | /* | ||
| 53 | * Check for Diagnostic Test FTP Service | ||
| 54 | */ | ||
| 55 | if (evbuf->type != EVTYP_DIAG_TEST || | ||
| 56 | diag->route != SCLP_DIAG_FTP_ROUTE || | ||
| 57 | diag->mdd.ftp.pcx != SCLP_DIAG_FTP_XPCX || | ||
| 58 | evbuf->length < SCLP_DIAG_FTP_EVBUF_LEN) | ||
| 59 | return; | ||
| 60 | |||
| 61 | #ifdef DEBUG | ||
| 62 | pr_debug("SCLP (ET7) RX-IRQ, Event @ 0x%p: %*phN\n", | ||
| 63 | evbuf, 24, evbuf); | ||
| 64 | #endif | ||
| 65 | |||
| 66 | /* | ||
| 67 | * Because the event buffer is located in a page which is owned | ||
| 68 | * by the SCLP core, all data of interest must be copied. The | ||
| 69 | * error indication is in 'sclp_ftp_ldflg' | ||
| 70 | */ | ||
| 71 | sclp_ftp_ldflg = diag->mdd.ftp.ldflg; | ||
| 72 | sclp_ftp_fsize = diag->mdd.ftp.fsize; | ||
| 73 | sclp_ftp_length = diag->mdd.ftp.length; | ||
| 74 | |||
| 75 | complete(&sclp_ftp_rx_complete); | ||
| 76 | } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * sclp_ftp_et7() - start a Diagnostic Test FTP Service SCLP request | ||
| 80 | * @ftp: pointer to FTP descriptor | ||
| 81 | * | ||
| 82 | * Return: 0 on success, else a (negative) error code | ||
| 83 | */ | ||
| 84 | static int sclp_ftp_et7(const struct hmcdrv_ftp_cmdspec *ftp) | ||
| 85 | { | ||
| 86 | struct completion completion; | ||
| 87 | struct sclp_diag_sccb *sccb; | ||
| 88 | struct sclp_req *req; | ||
| 89 | size_t len; | ||
| 90 | int rc; | ||
| 91 | |||
| 92 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
| 93 | sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); | ||
| 94 | if (!req || !sccb) { | ||
| 95 | rc = -ENOMEM; | ||
| 96 | goto out_free; | ||
| 97 | } | ||
| 98 | |||
| 99 | sccb->hdr.length = SCLP_DIAG_FTP_EVBUF_LEN + | ||
| 100 | sizeof(struct sccb_header); | ||
| 101 | sccb->evbuf.hdr.type = EVTYP_DIAG_TEST; | ||
| 102 | sccb->evbuf.hdr.length = SCLP_DIAG_FTP_EVBUF_LEN; | ||
| 103 | sccb->evbuf.hdr.flags = 0; /* clear processed-buffer */ | ||
| 104 | sccb->evbuf.route = SCLP_DIAG_FTP_ROUTE; | ||
| 105 | sccb->evbuf.mdd.ftp.pcx = SCLP_DIAG_FTP_XPCX; | ||
| 106 | sccb->evbuf.mdd.ftp.srcflg = 0; | ||
| 107 | sccb->evbuf.mdd.ftp.pgsize = 0; | ||
| 108 | sccb->evbuf.mdd.ftp.asce = _ASCE_REAL_SPACE; | ||
| 109 | sccb->evbuf.mdd.ftp.ldflg = SCLP_DIAG_FTP_LDFAIL; | ||
| 110 | sccb->evbuf.mdd.ftp.fsize = 0; | ||
| 111 | sccb->evbuf.mdd.ftp.cmd = ftp->id; | ||
| 112 | sccb->evbuf.mdd.ftp.offset = ftp->ofs; | ||
| 113 | sccb->evbuf.mdd.ftp.length = ftp->len; | ||
| 114 | sccb->evbuf.mdd.ftp.bufaddr = virt_to_phys(ftp->buf); | ||
| 115 | |||
| 116 | len = strlcpy(sccb->evbuf.mdd.ftp.fident, ftp->fname, | ||
| 117 | HMCDRV_FTP_FIDENT_MAX); | ||
| 118 | if (len >= HMCDRV_FTP_FIDENT_MAX) { | ||
| 119 | rc = -EINVAL; | ||
| 120 | goto out_free; | ||
| 121 | } | ||
| 122 | |||
| 123 | req->command = SCLP_CMDW_WRITE_EVENT_DATA; | ||
| 124 | req->sccb = sccb; | ||
| 125 | req->status = SCLP_REQ_FILLED; | ||
| 126 | req->callback = sclp_ftp_txcb; | ||
| 127 | req->callback_data = &completion; | ||
| 128 | |||
| 129 | init_completion(&completion); | ||
| 130 | |||
| 131 | rc = sclp_add_request(req); | ||
| 132 | if (rc) | ||
| 133 | goto out_free; | ||
| 134 | |||
| 135 | /* Wait for end of ftp sclp command. */ | ||
| 136 | wait_for_completion(&completion); | ||
| 137 | |||
| 138 | #ifdef DEBUG | ||
| 139 | pr_debug("status of SCLP (ET7) request is 0x%04x (0x%02x)\n", | ||
| 140 | sccb->hdr.response_code, sccb->evbuf.hdr.flags); | ||
| 141 | #endif | ||
| 142 | |||
| 143 | /* | ||
| 144 | * Check if sclp accepted the request. The data transfer runs | ||
| 145 | * asynchronously and the completion is indicated with an | ||
| 146 | * sclp ET7 event. | ||
| 147 | */ | ||
| 148 | if (req->status != SCLP_REQ_DONE || | ||
| 149 | (sccb->evbuf.hdr.flags & 0x80) == 0 || /* processed-buffer */ | ||
| 150 | (sccb->hdr.response_code & 0xffU) != 0x20U) { | ||
| 151 | rc = -EIO; | ||
| 152 | } | ||
| 153 | |||
| 154 | out_free: | ||
| 155 | free_page((unsigned long) sccb); | ||
| 156 | kfree(req); | ||
| 157 | return rc; | ||
| 158 | } | ||
| 159 | |||
| 160 | /** | ||
| 161 | * sclp_ftp_cmd() - executes a HMC related SCLP Diagnose (ET7) FTP command | ||
| 162 | * @ftp: pointer to FTP command specification | ||
| 163 | * @fsize: return of file size (or NULL if undesirable) | ||
| 164 | * | ||
| 165 | * Attention: Notice that this function is not reentrant - so the caller | ||
| 166 | * must ensure locking. | ||
| 167 | * | ||
| 168 | * Return: number of bytes read/written or a (negative) error code | ||
| 169 | */ | ||
| 170 | ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize) | ||
| 171 | { | ||
| 172 | ssize_t len; | ||
| 173 | #ifdef DEBUG | ||
| 174 | unsigned long start_jiffies; | ||
| 175 | |||
| 176 | pr_debug("starting SCLP (ET7), cmd %d for '%s' at %lld with %zd bytes\n", | ||
| 177 | ftp->id, ftp->fname, (long long) ftp->ofs, ftp->len); | ||
| 178 | start_jiffies = jiffies; | ||
| 179 | #endif | ||
| 180 | |||
| 181 | init_completion(&sclp_ftp_rx_complete); | ||
| 182 | |||
| 183 | /* Start ftp sclp command. */ | ||
| 184 | len = sclp_ftp_et7(ftp); | ||
| 185 | if (len) | ||
| 186 | goto out_unlock; | ||
| 187 | |||
| 188 | /* | ||
| 189 | * There is no way to cancel the sclp ET7 request, the code | ||
| 190 | * needs to wait unconditionally until the transfer is complete. | ||
| 191 | */ | ||
| 192 | wait_for_completion(&sclp_ftp_rx_complete); | ||
| 193 | |||
| 194 | #ifdef DEBUG | ||
| 195 | pr_debug("completed SCLP (ET7) request after %lu ms (all)\n", | ||
| 196 | (jiffies - start_jiffies) * 1000 / HZ); | ||
| 197 | pr_debug("return code of SCLP (ET7) FTP Service is 0x%02x, with %lld/%lld bytes\n", | ||
| 198 | sclp_ftp_ldflg, sclp_ftp_length, sclp_ftp_fsize); | ||
| 199 | #endif | ||
| 200 | |||
| 201 | switch (sclp_ftp_ldflg) { | ||
| 202 | case SCLP_DIAG_FTP_OK: | ||
| 203 | len = sclp_ftp_length; | ||
| 204 | if (fsize) | ||
| 205 | *fsize = sclp_ftp_fsize; | ||
| 206 | break; | ||
| 207 | case SCLP_DIAG_FTP_LDNPERM: | ||
| 208 | len = -EPERM; | ||
| 209 | break; | ||
| 210 | case SCLP_DIAG_FTP_LDRUNS: | ||
| 211 | len = -EBUSY; | ||
| 212 | break; | ||
| 213 | case SCLP_DIAG_FTP_LDFAIL: | ||
| 214 | len = -ENOENT; | ||
| 215 | break; | ||
| 216 | default: | ||
| 217 | len = -EIO; | ||
| 218 | break; | ||
| 219 | } | ||
| 220 | |||
| 221 | out_unlock: | ||
| 222 | return len; | ||
| 223 | } | ||
| 224 | |||
| 225 | /* | ||
| 226 | * ET7 event listener | ||
| 227 | */ | ||
| 228 | static struct sclp_register sclp_ftp_event = { | ||
| 229 | .send_mask = EVTYP_DIAG_TEST_MASK, /* want tx events */ | ||
| 230 | .receive_mask = EVTYP_DIAG_TEST_MASK, /* want rx events */ | ||
| 231 | .receiver_fn = sclp_ftp_rxcb, /* async callback (rx) */ | ||
| 232 | .state_change_fn = NULL, | ||
| 233 | .pm_event_fn = NULL, | ||
| 234 | }; | ||
| 235 | |||
| 236 | /** | ||
| 237 | * sclp_ftp_startup() - startup of FTP services, when running on LPAR | ||
| 238 | */ | ||
| 239 | int sclp_ftp_startup(void) | ||
| 240 | { | ||
| 241 | #ifdef DEBUG | ||
| 242 | unsigned long info; | ||
| 243 | #endif | ||
| 244 | int rc; | ||
| 245 | |||
| 246 | rc = sclp_register(&sclp_ftp_event); | ||
| 247 | if (rc) | ||
| 248 | return rc; | ||
| 249 | |||
| 250 | #ifdef DEBUG | ||
| 251 | info = get_zeroed_page(GFP_KERNEL); | ||
| 252 | |||
| 253 | if (info != 0) { | ||
| 254 | struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info; | ||
| 255 | |||
| 256 | if (!stsi(info222, 2, 2, 2)) { /* get SYSIB 2.2.2 */ | ||
| 257 | info222->name[sizeof(info222->name) - 1] = '\0'; | ||
| 258 | EBCASC_500(info222->name, sizeof(info222->name) - 1); | ||
| 259 | pr_debug("SCLP (ET7) FTP Service working on LPAR %u (%s)\n", | ||
| 260 | info222->lpar_number, info222->name); | ||
| 261 | } | ||
| 262 | |||
| 263 | free_page(info); | ||
| 264 | } | ||
| 265 | #endif /* DEBUG */ | ||
| 266 | return 0; | ||
| 267 | } | ||
| 268 | |||
| 269 | /** | ||
| 270 | * sclp_ftp_shutdown() - shutdown of FTP services, when running on LPAR | ||
| 271 | */ | ||
| 272 | void sclp_ftp_shutdown(void) | ||
| 273 | { | ||
| 274 | sclp_unregister(&sclp_ftp_event); | ||
| 275 | } | ||
diff --git a/drivers/s390/char/sclp_ftp.h b/drivers/s390/char/sclp_ftp.h new file mode 100644 index 000000000000..98ba3183e7d9 --- /dev/null +++ b/drivers/s390/char/sclp_ftp.h | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | /* | ||
| 2 | * SCLP Event Type (ET) 7 - Diagnostic Test FTP Services, useable on LPAR | ||
| 3 | * | ||
| 4 | * Notice that all functions exported here are not reentrant. | ||
| 5 | * So usage should be exclusive, ensured by the caller (e.g. using a | ||
| 6 | * mutex). | ||
| 7 | * | ||
| 8 | * Copyright IBM Corp. 2013 | ||
| 9 | * Author(s): Ralf Hoppe (rhoppe@de.ibm.com) | ||
| 10 | */ | ||
| 11 | |||
| 12 | #ifndef __SCLP_FTP_H__ | ||
| 13 | #define __SCLP_FTP_H__ | ||
| 14 | |||
| 15 | #include "hmcdrv_ftp.h" | ||
| 16 | |||
| 17 | int sclp_ftp_startup(void); | ||
| 18 | void sclp_ftp_shutdown(void); | ||
| 19 | ssize_t sclp_ftp_cmd(const struct hmcdrv_ftp_cmdspec *ftp, size_t *fsize); | ||
| 20 | |||
| 21 | #endif /* __SCLP_FTP_H__ */ | ||
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index 3b13d58fe87b..35a84af875ee 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c | |||
| @@ -33,7 +33,7 @@ static void sclp_rw_pm_event(struct sclp_register *reg, | |||
| 33 | 33 | ||
| 34 | /* Event type structure for write message and write priority message */ | 34 | /* Event type structure for write message and write priority message */ |
| 35 | static struct sclp_register sclp_rw_event = { | 35 | static struct sclp_register sclp_rw_event = { |
| 36 | .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK, | 36 | .send_mask = EVTYP_MSG_MASK, |
| 37 | .pm_event_fn = sclp_rw_pm_event, | 37 | .pm_event_fn = sclp_rw_pm_event, |
| 38 | }; | 38 | }; |
| 39 | 39 | ||
| @@ -456,14 +456,9 @@ sclp_emit_buffer(struct sclp_buffer *buffer, | |||
| 456 | return -EIO; | 456 | return -EIO; |
| 457 | 457 | ||
| 458 | sccb = buffer->sccb; | 458 | sccb = buffer->sccb; |
| 459 | if (sclp_rw_event.sclp_receive_mask & EVTYP_MSG_MASK) | 459 | /* Use normal write message */ |
| 460 | /* Use normal write message */ | 460 | sccb->msg_buf.header.type = EVTYP_MSG; |
| 461 | sccb->msg_buf.header.type = EVTYP_MSG; | 461 | |
| 462 | else if (sclp_rw_event.sclp_receive_mask & EVTYP_PMSGCMD_MASK) | ||
| 463 | /* Use write priority message */ | ||
| 464 | sccb->msg_buf.header.type = EVTYP_PMSGCMD; | ||
| 465 | else | ||
| 466 | return -EOPNOTSUPP; | ||
| 467 | buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA; | 462 | buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA; |
| 468 | buffer->request.status = SCLP_REQ_FILLED; | 463 | buffer->request.status = SCLP_REQ_FILLED; |
| 469 | buffer->request.callback = sclp_writedata_callback; | 464 | buffer->request.callback = sclp_writedata_callback; |
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index b9a9f721716d..ae67386c03d3 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c | |||
| @@ -206,10 +206,6 @@ sclp_vt220_callback(struct sclp_req *request, void *data) | |||
| 206 | static int | 206 | static int |
| 207 | __sclp_vt220_emit(struct sclp_vt220_request *request) | 207 | __sclp_vt220_emit(struct sclp_vt220_request *request) |
| 208 | { | 208 | { |
| 209 | if (!(sclp_vt220_register.sclp_receive_mask & EVTYP_VT220MSG_MASK)) { | ||
| 210 | request->sclp_req.status = SCLP_REQ_FAILED; | ||
| 211 | return -EIO; | ||
| 212 | } | ||
| 213 | request->sclp_req.command = SCLP_CMDW_WRITE_EVENT_DATA; | 209 | request->sclp_req.command = SCLP_CMDW_WRITE_EVENT_DATA; |
| 214 | request->sclp_req.status = SCLP_REQ_FILLED; | 210 | request->sclp_req.status = SCLP_REQ_FILLED; |
| 215 | request->sclp_req.callback = sclp_vt220_callback; | 211 | request->sclp_req.callback = sclp_vt220_callback; |
diff --git a/drivers/s390/char/tape_char.c b/drivers/s390/char/tape_char.c index 6dc60725de92..77f9b9c2f701 100644 --- a/drivers/s390/char/tape_char.c +++ b/drivers/s390/char/tape_char.c | |||
| @@ -402,7 +402,9 @@ __tapechar_ioctl(struct tape_device *device, | |||
| 402 | memset(&get, 0, sizeof(get)); | 402 | memset(&get, 0, sizeof(get)); |
| 403 | get.mt_type = MT_ISUNKNOWN; | 403 | get.mt_type = MT_ISUNKNOWN; |
| 404 | get.mt_resid = 0 /* device->devstat.rescnt */; | 404 | get.mt_resid = 0 /* device->devstat.rescnt */; |
| 405 | get.mt_dsreg = device->tape_state; | 405 | get.mt_dsreg = |
| 406 | ((device->char_data.block_size << MT_ST_BLKSIZE_SHIFT) | ||
| 407 | & MT_ST_BLKSIZE_MASK); | ||
| 406 | /* FIXME: mt_gstat, mt_erreg, mt_fileno */ | 408 | /* FIXME: mt_gstat, mt_erreg, mt_fileno */ |
| 407 | get.mt_gstat = 0; | 409 | get.mt_gstat = 0; |
| 408 | get.mt_erreg = 0; | 410 | get.mt_erreg = 0; |
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c index 1884653e4472..efcf48481c5f 100644 --- a/drivers/s390/char/zcore.c +++ b/drivers/s390/char/zcore.c | |||
| @@ -28,6 +28,7 @@ | |||
| 28 | #include <asm/processor.h> | 28 | #include <asm/processor.h> |
| 29 | #include <asm/irqflags.h> | 29 | #include <asm/irqflags.h> |
| 30 | #include <asm/checksum.h> | 30 | #include <asm/checksum.h> |
| 31 | #include <asm/switch_to.h> | ||
| 31 | #include "sclp.h" | 32 | #include "sclp.h" |
| 32 | 33 | ||
| 33 | #define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) | 34 | #define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) |
| @@ -149,18 +150,21 @@ static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count) | |||
| 149 | 150 | ||
| 150 | static int __init init_cpu_info(enum arch_id arch) | 151 | static int __init init_cpu_info(enum arch_id arch) |
| 151 | { | 152 | { |
| 152 | struct save_area *sa; | 153 | struct save_area_ext *sa_ext; |
| 153 | 154 | ||
| 154 | /* get info for boot cpu from lowcore, stored in the HSA */ | 155 | /* get info for boot cpu from lowcore, stored in the HSA */ |
| 155 | 156 | ||
| 156 | sa = dump_save_area_create(0); | 157 | sa_ext = dump_save_area_create(0); |
| 157 | if (!sa) | 158 | if (!sa_ext) |
| 158 | return -ENOMEM; | 159 | return -ENOMEM; |
| 159 | if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) { | 160 | if (memcpy_hsa_kernel(&sa_ext->sa, sys_info.sa_base, |
| 161 | sys_info.sa_size) < 0) { | ||
| 160 | TRACE("could not copy from HSA\n"); | 162 | TRACE("could not copy from HSA\n"); |
| 161 | kfree(sa); | 163 | kfree(sa_ext); |
| 162 | return -EIO; | 164 | return -EIO; |
| 163 | } | 165 | } |
| 166 | if (MACHINE_HAS_VX) | ||
| 167 | save_vx_regs_safe(sa_ext->vx_regs); | ||
| 164 | return 0; | 168 | return 0; |
| 165 | } | 169 | } |
| 166 | 170 | ||
| @@ -258,7 +262,7 @@ static int zcore_add_lc(char __user *buf, unsigned long start, size_t count) | |||
| 258 | unsigned long sa_start, sa_end; /* save area range */ | 262 | unsigned long sa_start, sa_end; /* save area range */ |
| 259 | unsigned long prefix; | 263 | unsigned long prefix; |
| 260 | unsigned long sa_off, len, buf_off; | 264 | unsigned long sa_off, len, buf_off; |
| 261 | struct save_area *save_area = dump_save_areas.areas[i]; | 265 | struct save_area *save_area = &dump_save_areas.areas[i]->sa; |
| 262 | 266 | ||
| 263 | prefix = save_area->pref_reg; | 267 | prefix = save_area->pref_reg; |
| 264 | sa_start = prefix + sys_info.sa_base; | 268 | sa_start = prefix + sys_info.sa_base; |
| @@ -612,7 +616,7 @@ static void __init zcore_header_init(int arch, struct zcore_header *hdr, | |||
| 612 | hdr->tod = get_tod_clock(); | 616 | hdr->tod = get_tod_clock(); |
| 613 | get_cpu_id(&hdr->cpu_id); | 617 | get_cpu_id(&hdr->cpu_id); |
| 614 | for (i = 0; i < dump_save_areas.count; i++) { | 618 | for (i = 0; i < dump_save_areas.count; i++) { |
| 615 | prefix = dump_save_areas.areas[i]->pref_reg; | 619 | prefix = dump_save_areas.areas[i]->sa.pref_reg; |
| 616 | hdr->real_cpu_cnt++; | 620 | hdr->real_cpu_cnt++; |
| 617 | if (!prefix) | 621 | if (!prefix) |
| 618 | continue; | 622 | continue; |
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index 00bfbee0af9e..56eb4ee4deba 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c | |||
| @@ -87,7 +87,7 @@ static irqreturn_t do_airq_interrupt(int irq, void *dummy) | |||
| 87 | struct airq_struct *airq; | 87 | struct airq_struct *airq; |
| 88 | struct hlist_head *head; | 88 | struct hlist_head *head; |
| 89 | 89 | ||
| 90 | __this_cpu_write(s390_idle.nohz_delay, 1); | 90 | set_cpu_flag(CIF_NOHZ_DELAY); |
| 91 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; | 91 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; |
| 92 | head = &airq_lists[tpi_info->isc]; | 92 | head = &airq_lists[tpi_info->isc]; |
| 93 | rcu_read_lock(); | 93 | rcu_read_lock(); |
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index 2905d8b0ec95..d5a6f287d2fe 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c | |||
| @@ -561,7 +561,7 @@ static irqreturn_t do_cio_interrupt(int irq, void *dummy) | |||
| 561 | struct subchannel *sch; | 561 | struct subchannel *sch; |
| 562 | struct irb *irb; | 562 | struct irb *irb; |
| 563 | 563 | ||
| 564 | __this_cpu_write(s390_idle.nohz_delay, 1); | 564 | set_cpu_flag(CIF_NOHZ_DELAY); |
| 565 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; | 565 | tpi_info = (struct tpi_info *) &get_irq_regs()->int_code; |
| 566 | irb = &__get_cpu_var(cio_irb); | 566 | irb = &__get_cpu_var(cio_irb); |
| 567 | sch = (struct subchannel *)(unsigned long) tpi_info->intparm; | 567 | sch = (struct subchannel *)(unsigned long) tpi_info->intparm; |
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 4038437ff033..99485415dcc2 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c | |||
| @@ -664,6 +664,17 @@ static ssize_t ap_hwtype_show(struct device *dev, | |||
| 664 | } | 664 | } |
| 665 | 665 | ||
| 666 | static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL); | 666 | static DEVICE_ATTR(hwtype, 0444, ap_hwtype_show, NULL); |
| 667 | |||
| 668 | static ssize_t ap_raw_hwtype_show(struct device *dev, | ||
| 669 | struct device_attribute *attr, char *buf) | ||
| 670 | { | ||
| 671 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
| 672 | |||
| 673 | return snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->raw_hwtype); | ||
| 674 | } | ||
| 675 | |||
| 676 | static DEVICE_ATTR(raw_hwtype, 0444, ap_raw_hwtype_show, NULL); | ||
| 677 | |||
| 667 | static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr, | 678 | static ssize_t ap_depth_show(struct device *dev, struct device_attribute *attr, |
| 668 | char *buf) | 679 | char *buf) |
| 669 | { | 680 | { |
| @@ -734,6 +745,7 @@ static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL); | |||
| 734 | 745 | ||
| 735 | static struct attribute *ap_dev_attrs[] = { | 746 | static struct attribute *ap_dev_attrs[] = { |
| 736 | &dev_attr_hwtype.attr, | 747 | &dev_attr_hwtype.attr, |
| 748 | &dev_attr_raw_hwtype.attr, | ||
| 737 | &dev_attr_depth.attr, | 749 | &dev_attr_depth.attr, |
| 738 | &dev_attr_request_count.attr, | 750 | &dev_attr_request_count.attr, |
| 739 | &dev_attr_requestq_count.attr, | 751 | &dev_attr_requestq_count.attr, |
| @@ -1188,6 +1200,10 @@ static int ap_select_domain(void) | |||
| 1188 | ap_qid_t qid; | 1200 | ap_qid_t qid; |
| 1189 | int rc, i, j; | 1201 | int rc, i, j; |
| 1190 | 1202 | ||
| 1203 | /* IF APXA isn't installed, only 16 domains could be defined */ | ||
| 1204 | if (!ap_configuration->ap_extended && (ap_domain_index > 15)) | ||
| 1205 | return -EINVAL; | ||
| 1206 | |||
| 1191 | /* | 1207 | /* |
| 1192 | * We want to use a single domain. Either the one specified with | 1208 | * We want to use a single domain. Either the one specified with |
| 1193 | * the "domain=" parameter or the domain with the maximum number | 1209 | * the "domain=" parameter or the domain with the maximum number |
| @@ -1413,9 +1429,13 @@ static void ap_scan_bus(struct work_struct *unused) | |||
| 1413 | continue; | 1429 | continue; |
| 1414 | } | 1430 | } |
| 1415 | break; | 1431 | break; |
| 1432 | case 11: | ||
| 1433 | ap_dev->device_type = 10; | ||
| 1434 | break; | ||
| 1416 | default: | 1435 | default: |
| 1417 | ap_dev->device_type = device_type; | 1436 | ap_dev->device_type = device_type; |
| 1418 | } | 1437 | } |
| 1438 | ap_dev->raw_hwtype = device_type; | ||
| 1419 | 1439 | ||
| 1420 | rc = ap_query_functions(qid, &device_functions); | 1440 | rc = ap_query_functions(qid, &device_functions); |
| 1421 | if (!rc) | 1441 | if (!rc) |
| @@ -1900,9 +1920,15 @@ static void ap_reset_all(void) | |||
| 1900 | { | 1920 | { |
| 1901 | int i, j; | 1921 | int i, j; |
| 1902 | 1922 | ||
| 1903 | for (i = 0; i < AP_DOMAINS; i++) | 1923 | for (i = 0; i < AP_DOMAINS; i++) { |
| 1904 | for (j = 0; j < AP_DEVICES; j++) | 1924 | if (!ap_test_config_domain(i)) |
| 1925 | continue; | ||
| 1926 | for (j = 0; j < AP_DEVICES; j++) { | ||
| 1927 | if (!ap_test_config_card_id(j)) | ||
| 1928 | continue; | ||
| 1905 | ap_reset_queue(AP_MKQID(j, i)); | 1929 | ap_reset_queue(AP_MKQID(j, i)); |
| 1930 | } | ||
| 1931 | } | ||
| 1906 | } | 1932 | } |
| 1907 | 1933 | ||
| 1908 | static struct reset_call ap_reset_call = { | 1934 | static struct reset_call ap_reset_call = { |
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 6405ae24a7a6..055a0f956d17 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h | |||
| @@ -31,7 +31,7 @@ | |||
| 31 | #include <linux/types.h> | 31 | #include <linux/types.h> |
| 32 | 32 | ||
| 33 | #define AP_DEVICES 64 /* Number of AP devices. */ | 33 | #define AP_DEVICES 64 /* Number of AP devices. */ |
| 34 | #define AP_DOMAINS 16 /* Number of AP domains. */ | 34 | #define AP_DOMAINS 256 /* Number of AP domains. */ |
| 35 | #define AP_MAX_RESET 90 /* Maximum number of resets. */ | 35 | #define AP_MAX_RESET 90 /* Maximum number of resets. */ |
| 36 | #define AP_RESET_TIMEOUT (HZ*0.7) /* Time in ticks for reset timeouts. */ | 36 | #define AP_RESET_TIMEOUT (HZ*0.7) /* Time in ticks for reset timeouts. */ |
| 37 | #define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */ | 37 | #define AP_CONFIG_TIME 30 /* Time in seconds between AP bus rescans. */ |
| @@ -45,9 +45,9 @@ extern int ap_domain_index; | |||
| 45 | */ | 45 | */ |
| 46 | typedef unsigned int ap_qid_t; | 46 | typedef unsigned int ap_qid_t; |
| 47 | 47 | ||
| 48 | #define AP_MKQID(_device,_queue) (((_device) & 63) << 8 | ((_queue) & 15)) | 48 | #define AP_MKQID(_device, _queue) (((_device) & 63) << 8 | ((_queue) & 255)) |
| 49 | #define AP_QID_DEVICE(_qid) (((_qid) >> 8) & 63) | 49 | #define AP_QID_DEVICE(_qid) (((_qid) >> 8) & 63) |
| 50 | #define AP_QID_QUEUE(_qid) ((_qid) & 15) | 50 | #define AP_QID_QUEUE(_qid) ((_qid) & 255) |
| 51 | 51 | ||
| 52 | /** | 52 | /** |
| 53 | * structy ap_queue_status - Holds the AP queue status. | 53 | * structy ap_queue_status - Holds the AP queue status. |
| @@ -161,6 +161,7 @@ struct ap_device { | |||
| 161 | ap_qid_t qid; /* AP queue id. */ | 161 | ap_qid_t qid; /* AP queue id. */ |
| 162 | int queue_depth; /* AP queue depth.*/ | 162 | int queue_depth; /* AP queue depth.*/ |
| 163 | int device_type; /* AP device type. */ | 163 | int device_type; /* AP device type. */ |
| 164 | int raw_hwtype; /* AP raw hardware type. */ | ||
| 164 | unsigned int functions; /* AP device function bitfield. */ | 165 | unsigned int functions; /* AP device function bitfield. */ |
| 165 | int unregistered; /* marks AP device as unregistered */ | 166 | int unregistered; /* marks AP device as unregistered */ |
| 166 | struct timer_list timeout; /* Timer for request timeouts. */ | 167 | struct timer_list timeout; /* Timer for request timeouts. */ |
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 0e18c5dcd91f..08f1830cbfc4 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c | |||
| @@ -343,10 +343,11 @@ struct zcrypt_ops *__ops_lookup(unsigned char *name, int variant) | |||
| 343 | break; | 343 | break; |
| 344 | } | 344 | } |
| 345 | } | 345 | } |
| 346 | if (!found || !try_module_get(zops->owner)) | ||
| 347 | zops = NULL; | ||
| 348 | |||
| 346 | spin_unlock_bh(&zcrypt_ops_list_lock); | 349 | spin_unlock_bh(&zcrypt_ops_list_lock); |
| 347 | 350 | ||
| 348 | if (!found) | ||
| 349 | return NULL; | ||
| 350 | return zops; | 351 | return zops; |
| 351 | } | 352 | } |
| 352 | 353 | ||
| @@ -359,8 +360,6 @@ struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *name, int variant) | |||
| 359 | request_module("%s", name); | 360 | request_module("%s", name); |
| 360 | zops = __ops_lookup(name, variant); | 361 | zops = __ops_lookup(name, variant); |
| 361 | } | 362 | } |
| 362 | if ((!zops) || (!try_module_get(zops->owner))) | ||
| 363 | return NULL; | ||
| 364 | return zops; | 363 | return zops; |
| 365 | } | 364 | } |
| 366 | EXPORT_SYMBOL(zcrypt_msgtype_request); | 365 | EXPORT_SYMBOL(zcrypt_msgtype_request); |
diff --git a/include/linux/tick.h b/include/linux/tick.h index 595ee86f5e0d..eda850ca757a 100644 --- a/include/linux/tick.h +++ b/include/linux/tick.h | |||
| @@ -108,7 +108,7 @@ extern struct tick_sched *tick_get_tick_sched(int cpu); | |||
| 108 | extern void tick_irq_enter(void); | 108 | extern void tick_irq_enter(void); |
| 109 | extern int tick_oneshot_mode_active(void); | 109 | extern int tick_oneshot_mode_active(void); |
| 110 | # ifndef arch_needs_cpu | 110 | # ifndef arch_needs_cpu |
| 111 | # define arch_needs_cpu(cpu) (0) | 111 | # define arch_needs_cpu() (0) |
| 112 | # endif | 112 | # endif |
| 113 | # else | 113 | # else |
| 114 | static inline void tick_clock_notify(void) { } | 114 | static inline void tick_clock_notify(void) { } |
diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h index ef6103bf1f9b..ea9bf2561b9e 100644 --- a/include/uapi/linux/elf.h +++ b/include/uapi/linux/elf.h | |||
| @@ -391,6 +391,8 @@ typedef struct elf64_shdr { | |||
| 391 | #define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ | 391 | #define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ |
| 392 | #define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ | 392 | #define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ |
| 393 | #define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ | 393 | #define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ |
| 394 | #define NT_S390_VXRS_LOW 0x309 /* s390 vector registers 0-15 upper half */ | ||
| 395 | #define NT_S390_VXRS_HIGH 0x30a /* s390 vector registers 16-31 */ | ||
| 394 | #define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ | 396 | #define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ |
| 395 | #define NT_ARM_TLS 0x401 /* ARM TLS register */ | 397 | #define NT_ARM_TLS 0x401 /* ARM TLS register */ |
| 396 | #define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ | 398 | #define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ |
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c index 7c1412ea2d29..a73efdf6f696 100644 --- a/kernel/time/tick-sched.c +++ b/kernel/time/tick-sched.c | |||
| @@ -586,7 +586,7 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts, | |||
| 586 | } while (read_seqretry(&jiffies_lock, seq)); | 586 | } while (read_seqretry(&jiffies_lock, seq)); |
| 587 | 587 | ||
| 588 | if (rcu_needs_cpu(cpu, &rcu_delta_jiffies) || | 588 | if (rcu_needs_cpu(cpu, &rcu_delta_jiffies) || |
| 589 | arch_needs_cpu(cpu) || irq_work_needs_cpu()) { | 589 | arch_needs_cpu() || irq_work_needs_cpu()) { |
| 590 | next_jiffies = last_jiffies + 1; | 590 | next_jiffies = last_jiffies + 1; |
| 591 | delta_jiffies = 1; | 591 | delta_jiffies = 1; |
| 592 | } else { | 592 | } else { |
diff --git a/scripts/recordmcount.c b/scripts/recordmcount.c index 650ecc83d7d7..001facfa5b74 100644 --- a/scripts/recordmcount.c +++ b/scripts/recordmcount.c | |||
| @@ -388,10 +388,6 @@ do_file(char const *const fname) | |||
| 388 | "unrecognized ET_REL file: %s\n", fname); | 388 | "unrecognized ET_REL file: %s\n", fname); |
| 389 | fail_file(); | 389 | fail_file(); |
| 390 | } | 390 | } |
| 391 | if (w2(ehdr->e_machine) == EM_S390) { | ||
| 392 | reltype = R_390_32; | ||
| 393 | mcount_adjust_32 = -4; | ||
| 394 | } | ||
| 395 | if (w2(ehdr->e_machine) == EM_MIPS) { | 391 | if (w2(ehdr->e_machine) == EM_MIPS) { |
| 396 | reltype = R_MIPS_32; | 392 | reltype = R_MIPS_32; |
| 397 | is_fake_mcount32 = MIPS32_is_fake_mcount; | 393 | is_fake_mcount32 = MIPS32_is_fake_mcount; |
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 397b6b84e8c5..d4b665610d67 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl | |||
| @@ -241,13 +241,6 @@ if ($arch eq "x86_64") { | |||
| 241 | $objcopy .= " -O elf32-i386"; | 241 | $objcopy .= " -O elf32-i386"; |
| 242 | $cc .= " -m32"; | 242 | $cc .= " -m32"; |
| 243 | 243 | ||
| 244 | } elsif ($arch eq "s390" && $bits == 32) { | ||
| 245 | $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_390_32\\s+_mcount\$"; | ||
| 246 | $mcount_adjust = -4; | ||
| 247 | $alignment = 4; | ||
| 248 | $ld .= " -m elf_s390"; | ||
| 249 | $cc .= " -m31"; | ||
| 250 | |||
| 251 | } elsif ($arch eq "s390" && $bits == 64) { | 244 | } elsif ($arch eq "s390" && $bits == 64) { |
| 252 | $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_390_(PC|PLT)32DBL\\s+_mcount\\+0x2\$"; | 245 | $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_390_(PC|PLT)32DBL\\s+_mcount\\+0x2\$"; |
| 253 | $mcount_adjust = -8; | 246 | $mcount_adjust = -8; |
