s390 updates for 7.4 merge window

- Add support for sclp-vt220 terminal resize events e.g. for QEMU
 
 - Warn if kernel command line contains non-printable EBCDIC
   characters
 
 - Improve show_code() disassembly output by using reliable markers for
   the instruction address and faulting instruction
 
 - Rework exception handling to require only a single exception table
   entry per faulting instruction: convert all inline assemblies from
   EX_TABLE to the new EX_TABLE_INSN macros. The previous exception
   handling was error prone since it usually required two EX_TABLE
   entries: one pointing at the instruction, and one pointing behind it
 
 - Disallow kprobes on instructions with exception table entries to avoid
   exception handlers decoding a breakpoint instead of the original
   instruction
 
 - Preemptible percpu ops changes:
 
   - Simplify percpu_entry() and percpu_exit() by encoding the percpu
     base, offset, and pointer registers into the lowcore marker, allowing
     percpu_exit() to update the percpu pointer on CPU migration without
     comparing the interrupted instruction
 
   - Convert this_cpu_cmpxchg(), this_cpu_xchg(), and
     this_cpu_cmpxchg128() to preemptible percpu ops; provide
     this_cpu_try_cmpxchg()
 
 - Emulate virtual timer in appldata via delayed work and remove the
   s390 specific vtimer infrastructure
 
 - Add lock contention tracepoints to rwlock slowpath
 
 - Fix incorrect return address in signal frame setup
 
 - Fix cpum_cf CPU hotplug handling
 
 - Convert various __get_free_pages() / get_zeroed_page() users to
   kmalloc() / kzalloc()
 
 - PCI:
 
   - Improve RPCIT instruction tracing
 
   - Fix double-free, use-after-free, and resource leaks in zPCI MSI
     and IRQ setup / cleanup paths
 
 - Crypto and zcrypt:
 
   - Improve reply message and length verification checks for CCA,
     EP11, and ICA messages to prevent out-of-bounds reads
 
   - Add missing array_index_nospec() for domain index uses in zcrypt to
     prevent speculative execution bypass
 
   - Fix out-of-bounds read in cca_query_crypto_facility()
 
 - Various other small fixes and improvements
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEECMNfWEw3SLnmiLkZIg7DeRspbsIFAmrSZW4ACgkQIg7DeRsp
 bsIXhg/+P39lvYFLfxl2mb7PeE/YPRnxZRhsW0eUoRMXSabigjhlCyBHlTBfJS0b
 dx+yEWLK/X7rP8kTrQyGt+JasMmBxUWpfF+FmEoBxxFmDsyV6cteFlo//a1NVeNX
 suY8QQBxwg13UOorUYB+yovvKhsRfSJoPT+wEAK52Wq39PBgSV/ulI3+naSmxfxE
 e12XdUnL8PgXb9WUx//iBzJexAurRZPZinCrL3qVUOuCkPqExmoP1TmLWItkXaSa
 6DP/rZ5z2Bh/CWMGQhLuT7btPfxWJrPFFo0oYPK7wT8DjKdPcQm7bRTjAcHj9vCI
 pZL9nTXezQYE/eRiXFmS0cU9Pwbih2NdC/cncHkGQqIfCtlrPP1uOYECO9S3YI+J
 RrCODXdQyalwW+zMNDtW5O0pLTRPI4dNLApWCbGYE92u2yws9hoT+9uzPBJRqPq7
 PAYmIVNaXaZLwySDiZVa/eemuH3kbbWnP3YXHeqQs/MmV+u/Go4DV/HYbGLIQbrG
 nkZ8qio+PNKJsPhbzjD4ev2YKTQPV+FtRYyaApHWM9wJM3ME8Dq8JYRNo6KrSa2p
 ahVLk0iK5Ath9hHQ3EIXLXtRl3eAkn7ZDJbpfg/QPCj0c4vlmtz1ixKzb0JE9QeP
 ikmSpM0FOqH8ykXlegG7/yQSZCpuIWlF8EVIeg+3yj86dloVPjg=
 =6GFl
 -----END PGP SIGNATURE-----

Merge tag 's390-7.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 updates from Heiko Carstens:

 - Add support for sclp-vt220 terminal resize events e.g. for QEMU

 - Warn if kernel command line contains non-printable EBCDIC characters

 - Improve show_code() disassembly output by using reliable markers for
   the instruction address and faulting instruction

 - Rework exception handling to require only a single exception table
   entry per faulting instruction: convert all inline assemblies from
   EX_TABLE to the new EX_TABLE_INSN macros. The previous exception
   handling was error prone since it usually required two EX_TABLE
   entries: one pointing at the instruction, and one pointing behind it

 - Disallow kprobes on instructions with exception table entries to
   avoid exception handlers decoding a breakpoint instead of the
   original instruction

 - Preemptible percpu ops changes:

    - Simplify percpu_entry() and percpu_exit() by encoding the percpu
      base, offset, and pointer registers into the lowcore marker,
      allowing percpu_exit() to update the percpu pointer on CPU
      migration without comparing the interrupted instruction

    - Convert this_cpu_cmpxchg(), this_cpu_xchg(), and
      this_cpu_cmpxchg128() to preemptible percpu ops; provide
      this_cpu_try_cmpxchg()

 - Emulate virtual timer in appldata via delayed work and remove the
   s390 specific vtimer infrastructure

 - Add lock contention tracepoints to rwlock slowpath

 - Fix incorrect return address in signal frame setup

 - Fix cpum_cf CPU hotplug handling

 - Convert various __get_free_pages() / get_zeroed_page() users to
   kmalloc() / kzalloc()

 - PCI:

    - Improve RPCIT instruction tracing

    - Fix double-free, use-after-free, and resource leaks in zPCI MSI
      and IRQ setup / cleanup paths

 - Crypto and zcrypt:

    - Improve reply message and length verification checks for CCA,
      EP11, and ICA messages to prevent out-of-bounds reads

    - Add missing array_index_nospec() for domain index uses in zcrypt
      to prevent speculative execution bypass

    - Fix out-of-bounds read in cca_query_crypto_facility()

 - Various other small fixes and improvements

* tag 's390-7.4-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (102 commits)
  s390/signal: Cleanup coding style
  s390/signal: Fix incorrect return address in signal frame
  s390/probes: Fix wrong mnemonic comment for clgib
  s390/probes: Add missing fixup for bctgr and bic
  s390/probes: Fix wrong opcode for cgrb fixup
  s390: Select ARCH_MEMORY_ORDER_TSO
  s390/mm: Cleanup pXXp_flush_lazy() routines
  s390/pci: Add function handle to RPCIT insn trace
  s390/pci: Use 32-bit fh outside of inline asm
  s390/pci: Adjust alignment in insn trace
  s390/sclp: Remove redundant memset after memblock_alloc_low()
  s390/entry: Save lowcore on invalid kernel stack
  s390/entry: Last breaking event vs invalid kernel stack
  s390/percpu: ImplemenHeiko Carstenst this_cpu_try_cmpxchg()
  s390/percpu: Drop unused output register from arch_this_cpu_to_op()
  s390/percpu: Drop unused output register from arch_this_cpu_add()
  s390/percpu: Use asm_inline for arch_this_cpu_add()
  s390/extable: Remove EX_TABLE macros and fallback extable search
  s390/bpf: Simplify exception table entry handling
  s390/sclp: Convert inline assembly to EX_TABLE_INSN
  ...
This commit is contained in:
Linus Torvalds 2026-10-20 14:28:32 -10:00
commit 375d5d5754
90 changed files with 1682 additions and 1453 deletions

View File

@ -145,6 +145,7 @@ config S390
select ARCH_INLINE_WRITE_UNLOCK_BH
select ARCH_INLINE_WRITE_UNLOCK_IRQ
select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
select ARCH_MEMORY_ORDER_TSO
select ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE
select ARCH_STACKWALK
select ARCH_SUPPORTS_ATOMIC_RMW

View File

@ -28,19 +28,14 @@
#include <linux/workqueue.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/kernel_stat.h>
#include <asm/appldata.h>
#include <asm/vtimer.h>
#include <asm/smp.h>
#include "appldata.h"
#define APPLDATA_CPU_INTERVAL 10000 /* default (CPU) time for
sampling interval in
milliseconds */
#define TOD_MICRO 0x01000 /* nr. of TOD clock units
for 1 microsecond */
/* Default CPU time for sampling interval */
#define APPLDATA_CPU_INTERVAL (10 * NSEC_PER_SEC)
/*
* /proc entries (sysctl)
@ -64,22 +59,14 @@ static const struct ctl_table appldata_table[] = {
},
};
/*
* Timer
*/
static struct vtimer_list appldata_timer;
static void appldata_work_fn(struct work_struct *work);
static DECLARE_DELAYED_WORK(appldata_work, appldata_work_fn);
static DEFINE_SPINLOCK(appldata_timer_lock);
static int appldata_interval = APPLDATA_CPU_INTERVAL;
static DEFINE_MUTEX(appldata_timer_lock);
static u64 appldata_interval = APPLDATA_CPU_INTERVAL;
static int appldata_timer_active;
/*
* Work queue
*/
static struct workqueue_struct *appldata_wq;
static void appldata_work_fn(struct work_struct *work);
static DECLARE_WORK(appldata_work, appldata_work_fn);
static u64 appldata_cputime_start;
/*
* Ops list
@ -89,14 +76,34 @@ static LIST_HEAD(appldata_ops_list);
/*************************** timer, work, DIAG *******************************/
/*
* appldata_timer_function()
*
* schedule work and reschedule timer
*/
static void appldata_timer_function(unsigned long data)
static u64 appldata_total_cpu_time_ns(void)
{
queue_work(appldata_wq, (struct work_struct *) data);
u64 total = 0;
int cpu;
for_each_possible_cpu(cpu) {
total += kcpustat_cpu(cpu).cpustat[CPUTIME_USER];
total += kcpustat_cpu(cpu).cpustat[CPUTIME_NICE];
total += kcpustat_cpu(cpu).cpustat[CPUTIME_SYSTEM];
total += kcpustat_cpu(cpu).cpustat[CPUTIME_IRQ];
total += kcpustat_cpu(cpu).cpustat[CPUTIME_SOFTIRQ];
}
return total;
}
static void appldata_schedule_work(u64 remaining)
{
unsigned int ncpus = num_online_cpus();
unsigned long delay = HZ / 10;
/*
* At most ncpus CPUs consume CPU time simultaneously, so the
* minimum wall-clock time until the remaining CPU time elapses
* is remaining / ncpus.
* Make sure the work is not scheduled more than once per 100ms.
*/
delay = max(delay, nsecs_to_jiffies(remaining / ncpus));
mod_delayed_work(system_percpu_wq, &appldata_work, delay);
}
/*
@ -106,9 +113,27 @@ static void appldata_timer_function(unsigned long data)
*/
static void appldata_work_fn(struct work_struct *work)
{
struct list_head *lh;
u64 now, elapsed, interval;
struct appldata_ops *ops;
struct list_head *lh;
bool expired = false;
now = appldata_total_cpu_time_ns();
mutex_lock(&appldata_timer_lock);
if (appldata_timer_active) {
interval = appldata_interval;
elapsed = now - appldata_cputime_start;
if (elapsed < interval) {
appldata_schedule_work(interval - elapsed);
} else {
appldata_cputime_start = now;
appldata_schedule_work(interval);
expired = true;
}
}
mutex_unlock(&appldata_timer_lock);
if (!expired)
return;
mutex_lock(&appldata_ops_mutex);
list_for_each(lh, &appldata_ops_list) {
ops = list_entry(lh, struct appldata_ops, list);
@ -162,33 +187,52 @@ int appldata_diag(char record_nr, u16 function, unsigned long buffer,
#define APPLDATA_MOD_TIMER 2
/*
* __appldata_vtimer_setup()
* __appldata_timer_setup()
*
* Add, delete or modify virtual timers on all online cpus.
* The caller needs to get the appldata_timer_lock spinlock.
* Add, delete or modify the appldata delayed work.
*/
static void __appldata_vtimer_setup(int cmd)
static void __appldata_timer_setup(int cmd)
{
u64 timer_interval = (u64) appldata_interval * 1000 * TOD_MICRO;
static DEFINE_MUTEX(appldata_cancel_lock);
u64 now, elapsed, remaining;
switch (cmd) {
case APPLDATA_ADD_TIMER:
if (appldata_timer_active)
break;
appldata_timer.expires = timer_interval;
add_virt_timer_periodic(&appldata_timer);
appldata_timer_active = 1;
mutex_lock(&appldata_cancel_lock);
mutex_lock(&appldata_timer_lock);
if (!appldata_timer_active) {
appldata_cputime_start = appldata_total_cpu_time_ns();
appldata_schedule_work(appldata_interval);
appldata_timer_active = 1;
}
mutex_unlock(&appldata_timer_lock);
mutex_unlock(&appldata_cancel_lock);
break;
case APPLDATA_DEL_TIMER:
del_virt_timer(&appldata_timer);
if (!appldata_timer_active)
mutex_lock(&appldata_cancel_lock);
mutex_lock(&appldata_timer_lock);
if (!appldata_timer_active) {
mutex_unlock(&appldata_timer_lock);
mutex_unlock(&appldata_cancel_lock);
break;
}
appldata_timer_active = 0;
mutex_unlock(&appldata_timer_lock);
cancel_delayed_work_sync(&appldata_work);
mutex_unlock(&appldata_cancel_lock);
break;
case APPLDATA_MOD_TIMER:
if (!appldata_timer_active)
break;
mod_virt_timer_periodic(&appldata_timer, timer_interval);
mutex_lock(&appldata_timer_lock);
if (appldata_timer_active) {
now = appldata_total_cpu_time_ns();
elapsed = now - appldata_cputime_start;
remaining = appldata_interval - elapsed;
if (elapsed >= appldata_interval)
remaining = 1;
appldata_schedule_work(remaining);
}
mutex_unlock(&appldata_timer_lock);
break;
}
}
@ -215,12 +259,10 @@ appldata_timer_handler(const struct ctl_table *ctl, int write,
if (rc < 0 || !write)
return rc;
spin_lock(&appldata_timer_lock);
if (timer_active)
__appldata_vtimer_setup(APPLDATA_ADD_TIMER);
__appldata_timer_setup(APPLDATA_ADD_TIMER);
else
__appldata_vtimer_setup(APPLDATA_DEL_TIMER);
spin_unlock(&appldata_timer_lock);
__appldata_timer_setup(APPLDATA_DEL_TIMER);
return 0;
}
@ -234,7 +276,7 @@ static int
appldata_interval_handler(const struct ctl_table *ctl, int write,
void *buffer, size_t *lenp, loff_t *ppos)
{
int interval = appldata_interval;
int interval = appldata_interval / NSEC_PER_MSEC;
int rc;
struct ctl_table ctl_entry = {
.procname = ctl->procname,
@ -247,10 +289,10 @@ appldata_interval_handler(const struct ctl_table *ctl, int write,
if (rc < 0 || !write)
return rc;
spin_lock(&appldata_timer_lock);
appldata_interval = interval;
__appldata_vtimer_setup(APPLDATA_MOD_TIMER);
spin_unlock(&appldata_timer_lock);
mutex_lock(&appldata_timer_lock);
appldata_interval = interval * NSEC_PER_MSEC;
mutex_unlock(&appldata_timer_lock);
__appldata_timer_setup(APPLDATA_MOD_TIMER);
return 0;
}
@ -392,19 +434,20 @@ void appldata_unregister_ops(struct appldata_ops *ops)
/******************************* init / exit *********************************/
/*
* appldata_init()
*
* init timer, register /proc entries
*/
static int appldata_cpu_online(unsigned int cpu)
{
__appldata_timer_setup(APPLDATA_MOD_TIMER);
return 0;
}
static int __init appldata_init(void)
{
init_virt_timer(&appldata_timer);
appldata_timer.function = appldata_timer_function;
appldata_timer.data = (unsigned long) &appldata_work;
appldata_wq = alloc_ordered_workqueue("appldata", 0);
if (!appldata_wq)
return -ENOMEM;
int rc;
rc = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN, "s390/appldata:online",
appldata_cpu_online, NULL);
if (rc < 0)
return rc;
register_sysctl(appldata_proc_name, appldata_table);
return 0;
}

View File

@ -40,9 +40,9 @@ static inline int __diag308(unsigned long subcode, void *addr)
union register_pair r1 = { .even = (unsigned long)addr, .odd = 0 };
asm_inline volatile(
" diag %[r1],%[subcode],0x308\n"
"0:\n"
EX_TABLE(0b, 0b)
"0: diag %[r1],%[subcode],0x308\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
: [r1] "+d" (r1.pair)
: [subcode] "d" (subcode)
: "cc", "memory");
@ -172,12 +172,23 @@ static inline int has_ebcdic_char(const char *str)
return 0;
}
static inline bool has_nonprintable_ebcdic_char(const char *str)
{
for (; *str; str++)
if (!isprint_ebc_asc(*str) && !isspace_ebc_asc(*str))
return true;
return false;
}
void setup_boot_command_line(void)
{
parmarea.command_line[COMMAND_LINE_SIZE - 1] = 0;
/* convert arch command line to ascii if necessary */
if (has_ebcdic_char(parmarea.command_line))
if (has_ebcdic_char(parmarea.command_line)) {
if (has_nonprintable_ebcdic_char(parmarea.command_line))
boot_warn("Kernel command line was treated as EBCDIC, but contains non-printable characters\n");
EBCASC(parmarea.command_line, COMMAND_LINE_SIZE);
}
/* copy arch command line */
strscpy(early_command_line, strim(parmarea.command_line));

View File

@ -3,9 +3,11 @@
#include <linux/stdarg.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <asm/pgmcheck-defs.h>
#include <asm/stacktrace.h>
#include <asm/boot_data.h>
#include <asm/lowcore.h>
#include <asm/processor.h>
#include <asm/setup.h>
#include <asm/sclp.h>
#include <asm/uv.h>
@ -40,12 +42,22 @@ static inline unsigned long extable_insn(const struct exception_table_entry *x)
return (unsigned long)&x->insn + x->insn;
}
static unsigned long boot_get_trap_ip(struct pt_regs *regs)
{
static const unsigned long nullified[2] = { PGM_CHECKS_NULLIFIED };
if (test_bit(regs->int_code & PGM_INT_CODE_MASK, nullified))
return regs->psw.addr;
return __rewind_psw(regs->psw, regs->int_code >> 16);
}
static bool ex_handler(struct pt_regs *regs)
{
const struct exception_table_entry *ex;
unsigned long ip = boot_get_trap_ip(regs);
for (ex = __start___ex_table; ex < __stop___ex_table; ex++) {
if (extable_insn(ex) != regs->psw.addr)
if (extable_insn(ex) != ip)
continue;
if (ex->type != EX_TYPE_FIXUP)
return false;

View File

@ -68,11 +68,11 @@ static int __diag260(unsigned long rx1, unsigned long rx2)
ry = 0x10; /* storage configuration */
exception = 1;
asm_inline volatile(
" diag %[rx],%[ry],0x260\n"
"0: lhi %[exc],0\n"
"0: diag %[rx],%[ry],0x260\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [exc] "+d" (exception), [ry] "+d" (ry)
: [rx] "d" (rx.pair)
: CC_CLOBBER_LIST("memory"));
@ -108,9 +108,9 @@ static int diag500_storage_limit(unsigned long *max_physmem_end)
asm_inline volatile(
" lghi %%r1,%[subcode]\n"
" lghi %%r2,0\n"
" diag %%r2,%%r4,0x500\n"
"0: lgr %[slimit],%%r2\n"
EX_TABLE(0b, 0b)
"0: diag %%r2,%%r4,0x500\n"
"1: lgr %[slimit],%%r2\n"
EX_TABLE_INSN(0b, 1b)
: [slimit] "=d" (storage_limit)
: [subcode] "i" (DIAG500_SC_STOR_LIMIT)
: "memory", "1", "2");
@ -127,11 +127,11 @@ static int tprot(unsigned long addr)
exception = 1;
asm_inline volatile(
" tprot 0(%[addr]),0\n"
"0: lhi %[exc],0\n"
"0: tprot 0(%[addr]),0\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [exc] "+d" (exception)
: [addr] "a" (addr)
: CC_CLOBBER_LIST("memory"));

View File

@ -89,10 +89,10 @@ static void detect_diag9c(void)
cpu = stap();
asm_inline volatile(
" diag %[cpu],%%r0,0x9c\n"
"0: lhi %[rc],0\n"
"0: diag %[cpu],%%r0,0x9c\n"
" lhi %[rc],0\n"
"1:\n"
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: [rc] "+d" (rc)
: [cpu] "d" (cpu)
: "cc", "memory");
@ -150,10 +150,10 @@ static int cmma_test_essa(void)
/* Test ESSA_GET_STATE */
asm_inline volatile(
" .insn rrf,0xb9ab0000,%[tmp],%[tmp],%[cmd],0\n"
"0: lhi %[rc],0\n"
"0: .insn rrf,0xb9ab0000,%[tmp],%[tmp],%[cmd],0\n"
" lhi %[rc],0\n"
"1:\n"
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: [rc] "+d" (rc), [tmp] "+d" (tmp)
: [cmd] "i" (ESSA_GET_STATE)
: "cc", "memory");

View File

@ -451,6 +451,20 @@ CONFIG_BLK_DEV_RAM_SIZE=32768
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_DEV_RBD=m
CONFIG_BLK_DEV_NVME=m
CONFIG_NVME_MULTIPATH=y
CONFIG_NVME_VERBOSE_ERRORS=y
CONFIG_NVME_RDMA=m
CONFIG_NVME_TCP=m
CONFIG_NVME_TCP_TLS=y
CONFIG_NVME_HOST_AUTH=y
CONFIG_NVME_TARGET=m
CONFIG_NVME_TARGET_DEBUGFS=y
CONFIG_NVME_TARGET_PASSTHRU=y
CONFIG_NVME_TARGET_LOOP=m
CONFIG_NVME_TARGET_RDMA=m
CONFIG_NVME_TARGET_TCP=m
CONFIG_NVME_TARGET_TCP_TLS=y
CONFIG_NVME_TARGET_AUTH=y
CONFIG_BLK_DEV_NULL_BLK=m
CONFIG_ENCLOSURE_SERVICES=m
CONFIG_GENWQE=m

View File

@ -441,6 +441,20 @@ CONFIG_BLK_DEV_RAM_SIZE=32768
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_DEV_RBD=m
CONFIG_BLK_DEV_NVME=m
CONFIG_NVME_MULTIPATH=y
CONFIG_NVME_VERBOSE_ERRORS=y
CONFIG_NVME_RDMA=m
CONFIG_NVME_TCP=m
CONFIG_NVME_TCP_TLS=y
CONFIG_NVME_HOST_AUTH=y
CONFIG_NVME_TARGET=m
CONFIG_NVME_TARGET_DEBUGFS=y
CONFIG_NVME_TARGET_PASSTHRU=y
CONFIG_NVME_TARGET_LOOP=m
CONFIG_NVME_TARGET_RDMA=m
CONFIG_NVME_TARGET_TCP=m
CONFIG_NVME_TARGET_TCP_TLS=y
CONFIG_NVME_TARGET_AUTH=y
CONFIG_BLK_DEV_NULL_BLK=m
CONFIG_ENCLOSURE_SERVICES=m
CONFIG_GENWQE=m

View File

@ -42,9 +42,9 @@ static int diag2fc(int size, char* query, void *addr)
diag_stat_inc(DIAG_STAT_X2FC);
asm volatile(
" diag %0,%1,0x2fc\n"
"0: nopr %%r7\n"
EX_TABLE(0b,0b)
"0: diag %0,%1,0x2fc\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
: "=d" (residual_cnt), "+d" (rc) : "0" (&parm_list) : "memory");
if ((rc != 0 ) && (rc != -2))

View File

@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0
generated-y += dis-defs.h
generated-y += facility-defs.h
generated-y += pgmcheck-defs.h
generated-y += syscall_table.h
generated-y += unistd_nr.h

View File

@ -91,10 +91,10 @@ static inline bool ap_instructions_available(void)
" lgr 0,%[reg0]\n" /* qid into gr0 */
" lghi 1,0\n" /* 0 into gr1 */
" lghi 2,0\n" /* 0 into gr2 */
" .insn rre,0xb2af0000,0,0\n" /* PQAP(TAPQ) */
"0: la %[reg1],1\n" /* 1 into reg1 */
"0: .insn rre,0xb2af0000,0,0\n" /* PQAP(TAPQ) */
" la %[reg1],1\n" /* 1 into reg1 */
"1:\n"
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: [reg1] "+&d" (reg1)
: [reg0] "d" (reg0)
: "cc", "0", "1", "2");
@ -272,10 +272,10 @@ static inline int ap_qci(struct ap_config_info *config)
asm volatile(
" lgr 0,%[reg0]\n" /* QCI fc into gr0 */
" lgr 2,%[reg2]\n" /* ptr to config into gr2 */
" .insn rre,0xb2af0000,0,0\n" /* PQAP(QCI) */
"0: la %[reg1],0\n" /* good case, QCI fc available */
"0: .insn rre,0xb2af0000,0,0\n" /* PQAP(QCI) */
" la %[reg1],0\n" /* good case, QCI fc available */
"1:\n"
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: [reg1] "+&d" (reg1)
: [reg0] "d" (reg0), [reg2] "d" (reg2)
: "cc", "memory", "0", "2");

View File

@ -65,31 +65,41 @@
stringify_in_c(.purgem extable_reg;) \
stringify_in_c(.previous)
#define EX_TABLE(_fault, _target) \
/*
* EX_TABLE_INSN usage rules:
*
* _fault must point to the faulting instruction, never behind it.
*
* This differs from the former EX_TABLE convention, where usually two entries
* were needed. One that pointed to the faulting instruction (for nullifying
* exceptions), and a second one that pointed behind the instruction (for
* suppressing, completing, and terminating exceptions).
*/
#define EX_TABLE_INSN(_fault, _target) \
__EX_TABLE(__ex_table, _fault, _target, EX_TYPE_FIXUP, __stringify(%%r0), __stringify(%%r0), 0)
#define EX_TABLE_AMODE31(_fault, _target) \
#define EX_TABLE_INSN_AMODE31(_fault, _target) \
__EX_TABLE(.amode31.ex_table, _fault, _target, EX_TYPE_FIXUP, __stringify(%%r0), __stringify(%%r0), 0)
#define EX_TABLE_UA_FAULT(_fault, _target, _regerr) \
#define EX_TABLE_INSN_UA_FAULT(_fault, _target, _regerr) \
__EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_FAULT, _regerr, _regerr, 0)
#define EX_TABLE_UA_LOAD_REG(_fault, _target, _regerr, _regzero) \
#define EX_TABLE_INSN_UA_LOAD_REG(_fault, _target, _regerr, _regzero) \
__EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REG, _regerr, _regzero, 0)
#define EX_TABLE_UA_LOAD_REGPAIR(_fault, _target, _regerr, _regzero) \
#define EX_TABLE_INSN_UA_LOAD_REGPAIR(_fault, _target, _regerr, _regzero) \
__EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_LOAD_REGPAIR, _regerr, _regzero, 0)
#define EX_TABLE_ZEROPAD(_fault, _target, _regdata, _regaddr) \
#define EX_TABLE_INSN_ZEROPAD(_fault, _target, _regdata, _regaddr) \
__EX_TABLE(__ex_table, _fault, _target, EX_TYPE_ZEROPAD, _regdata, _regaddr, 0)
#define EX_TABLE_FPC(_fault, _target) \
#define EX_TABLE_INSN_FPC(_fault, _target) \
__EX_TABLE(__ex_table, _fault, _target, EX_TYPE_FPC, __stringify(%%r0), __stringify(%%r0), 0)
#define EX_TABLE_UA_MVCOS_TO(_fault, _target) \
#define EX_TABLE_INSN_UA_MVCOS_TO(_fault, _target) \
__EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_MVCOS_TO, __stringify(%%r0), __stringify(%%r0), 0)
#define EX_TABLE_UA_MVCOS_FROM(_fault, _target) \
#define EX_TABLE_INSN_UA_MVCOS_FROM(_fault, _target) \
__EX_TABLE(__ex_table, _fault, _target, EX_TYPE_UA_MVCOS_FROM, __stringify(%%r0), __stringify(%%r0), 0)
#endif /* __ASM_EXTABLE_H */

View File

@ -173,9 +173,9 @@ static inline int qctri(struct cpumf_ctr_info *info)
asm_inline volatile (
"0: qctri %1\n"
"1: lhi %0,0\n"
"2:\n"
EX_TABLE(1b, 2b)
" lhi %0,0\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
: "+d" (rc), "=Q" (*info));
return rc;
}
@ -257,9 +257,9 @@ static inline int qsi(struct hws_qsi_info_block *info)
asm volatile(
"0: qsi %1\n"
"1: lhi %0,0\n"
"2:\n"
EX_TABLE(0b, 2b) EX_TABLE(1b, 2b)
" lhi %0,0\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
: "+d" (cc), "+Q" (*info));
return cc ? -EINVAL : 0;
}
@ -272,10 +272,10 @@ static inline int lsctl(struct hws_lsctl_request_block *req)
exception = 1;
asm volatile(
"0: lsctl %[req]\n"
"1: lhi %[exc],0\n"
"2:\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 2b) EX_TABLE(1b, 2b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [exc] "+d" (exception)
: [req] "Q" (*req)
: CC_CLOBBER);

View File

@ -68,9 +68,8 @@ static inline void diag10_range(unsigned long start_pfn, unsigned long num_pfn)
diag_stat_inc(DIAG_STAT_X010);
asm_inline volatile(
"0: diag %0,%1,0x10\n"
"1: nopr %%r7\n"
EX_TABLE(0b, 1b)
EX_TABLE(1b, 1b)
"1:\n"
EX_TABLE_INSN(0b, 1b)
: : "a" (start_addr), "a" (end_addr));
}

View File

@ -28,10 +28,10 @@ static inline int __diag288(unsigned int func, unsigned int timeout,
int rc = -EINVAL;
asm volatile(
" diag %[r1],%[r3],0x288\n"
"0: lhi %[rc],0\n"
"0: diag %[r1],%[r3],0x288\n"
" lhi %[rc],0\n"
"1:"
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: [rc] "+d" (rc)
: [r1] "d" (r1.pair), [r3] "d" (r3.pair)
: "cc", "memory");

View File

@ -10,6 +10,7 @@
#ifndef _EBCDIC_H
#define _EBCDIC_H
#include <linux/ctype.h>
#include <linux/types.h>
extern __u8 _ascebc_500[256]; /* ASCII -> EBCDIC 500 conversion table */
@ -18,6 +19,11 @@ extern __u8 _ascebc[256]; /* ASCII -> EBCDIC conversion table */
extern __u8 _ebcasc[256]; /* EBCDIC -> ASCII conversion table */
extern __u8 _ebc_tolower[256]; /* EBCDIC -> lowercase */
extern __u8 _ebc_toupper[256]; /* EBCDIC -> uppercase */
extern const __u8 _ebctype_asc[256]; /* EBCDIC -> character classes */
#define __ismask_ebc_asc(x) (_ebctype_asc[(int)(unsigned char)(x)])
#define isprint_ebc_asc(c) ((__ismask_ebc_asc(c)&(_P|_U|_L|_D|_SP)) != 0)
#define isspace_ebc_asc(c) ((__ismask_ebc_asc(c)&(_S)) != 0)
static inline void
codepage_convert(const __u8 *codepage, volatile char *addr, unsigned long nr)

View File

@ -2,79 +2,42 @@
#ifndef ARCH_S390_ENTRY_PERCPU_H
#define ARCH_S390_ENTRY_PERCPU_H
#include <linux/kprobes.h>
#include <linux/bitfield.h>
#include <linux/percpu.h>
#include <asm/lowcore.h>
#include <asm/ptrace.h>
#include <asm/asm-offsets.h>
static __always_inline void percpu_entry(struct pt_regs *regs)
{
struct lowcore *lc = get_lowcore();
if (user_mode(regs))
return;
regs->cpu = lc->cpu_nr;
regs->percpu_register = lc->percpu_register;
lc->percpu_register = 0;
}
static __always_inline bool percpu_code_check(struct pt_regs *regs)
{
unsigned int insn, disp;
struct kprobe *p;
if (likely(user_mode(regs) || !regs->percpu_register))
return false;
/*
* Within a percpu code section - check if the percpu base register
* needs to be updated. This is the case if the PSW does not point to
* the ADD instruction within the section.
* - AG %rx,percpu_offset_in_lowcore(%r0,%r0)
* which adds the percpu offset to the percpu base register.
*/
lockdep_assert_preemption_disabled();
again:
insn = READ_ONCE(*(u16 *)psw_bits(regs->psw).ia);
if (unlikely(insn == BREAKPOINT_INSTRUCTION)) {
p = get_kprobe((void *)psw_bits(regs->psw).ia);
/*
* If the kprobe is concurrently removed on a different CPU
* it might not be found anymore. However text must have
* been restored - try again.
*/
if (!p)
goto again;
insn = p->opcode;
}
if ((insn & 0xff0f) != 0xe300)
return true;
disp = offsetof(struct lowcore, percpu_offset);
if (machine_has_relocated_lowcore())
disp += LOWCORE_ALT_ADDRESS;
insn = (disp & 0xff000) >> 4 | (disp & 0x00fff) << 16 | 0x8;
if (*(u32 *)(psw_bits(regs->psw).ia + 2) != insn)
return true;
return false;
}
static __always_inline void percpu_exit(struct pt_regs *regs, bool needs_fixup)
static __always_inline void percpu_exit(struct pt_regs *regs)
{
unsigned char regval, regpcp, regoff, regptr;
struct lowcore *lc = get_lowcore();
unsigned char reg;
if (user_mode(regs))
if (!regs->percpu_register)
return;
reg = regs->percpu_register;
lc->percpu_register = reg;
if (likely(!needs_fixup))
return;
/* Check if process has been migrated to a different CPU. */
regval = regs->percpu_register;
lc->percpu_register = regval;
/* Migrated to a different CPU? */
if (regs->cpu == lc->cpu_nr)
return;
/* Fixup percpu base register */
regs->gprs[reg] -= __per_cpu_offset[regs->cpu];
regs->gprs[reg] += lc->percpu_offset;
regpcp = FIELD_GET(PCPU_REG_PCP, regval);
regoff = FIELD_GET(PCPU_REG_OFF, regval);
regptr = regoff + 1;
/*
* Update register 'regoff' with the current CPU's percpu offset,
* and recalculate and update current CPU's percpu variable address
* contained in register 'regptr'.
*/
regs->gprs[regoff] = lc->percpu_offset;
regs->gprs[regptr] = regs->gprs[regpcp] + regs->gprs[regoff];
}
#endif

View File

@ -5,25 +5,14 @@
#include <asm/ptrace.h>
#include <linux/compiler.h>
/*
* The exception table consists of three addresses:
*
* - Address of an instruction that is allowed to fault.
* - Address at which the program should continue.
* - Optional address of handler that takes pt_regs * argument and runs in
* interrupt context.
*
* No registers are modified, so it is entirely up to the continuation code
* to figure out what to do.
*
* All the routines below use bits of fixup code that are out of line
* with the main instruction path. This means when everything is well,
* we don't even have to jump over them. Further, they do not intrude
* on our cache or tlb entries.
/**
* struct exception_table_entry - entry in exception table
* @insn: Relative pointer to faulting instruction
* @fixup: Relative pointer to instruction where execution resumes
* @type: Exception type
* @data: handler-specific data
*/
struct exception_table_entry
{
struct exception_table_entry {
int insn, fixup;
short type, data;
};

View File

@ -104,9 +104,9 @@ static inline void fpu_lfpc_safe(unsigned int *fpc)
{
instrument_read(fpc, sizeof(*fpc));
asm_inline volatile(
" lfpc %[fpc]\n"
"0: nopr %%r7\n"
EX_TABLE_FPC(0b, 0b)
"0: lfpc %[fpc]\n"
"1:\n"
EX_TABLE_INSN_FPC(0b, 1b)
:
: [fpc] "Q" (*fpc)
: "memory");

View File

@ -23,13 +23,11 @@ __futex_atomic_##name(int oparg, int *old, u32 __user *uaddr) \
"0: l %[old],%[uaddr]\n" \
"1:"insn \
"2: cs %[old],%[new],%[uaddr]\n" \
"3: jl 1b\n" \
" jl 1b\n" \
" lhi %[rc],0\n" \
"4: sacf 768\n" \
EX_TABLE_UA_FAULT(0b, 4b, %[rc]) \
EX_TABLE_UA_FAULT(1b, 4b, %[rc]) \
EX_TABLE_UA_FAULT(2b, 4b, %[rc]) \
EX_TABLE_UA_FAULT(3b, 4b, %[rc]) \
"3: sacf 768\n" \
EX_TABLE_INSN_UA_FAULT(0b, 3b, %[rc]) \
EX_TABLE_INSN_UA_FAULT(2b, 3b, %[rc]) \
: [rc] "=d" (rc), [old] "=&d" (*old), \
[new] "=&d" (new), [uaddr] "+Q" (*uaddr) \
: [oparg] "d" (oparg) \
@ -86,10 +84,9 @@ int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32
asm_inline volatile(
" sacf 256\n"
"0: cs %[old],%[new],%[uaddr]\n"
"1: lhi %[rc],0\n"
"2: sacf 768\n"
EX_TABLE_UA_FAULT(0b, 2b, %[rc])
EX_TABLE_UA_FAULT(1b, 2b, %[rc])
" lhi %[rc],0\n"
"1: sacf 768\n"
EX_TABLE_INSN_UA_FAULT(0b, 1b, %[rc])
: [rc] "=d" (rc), [old] "+d" (oldval), [uaddr] "+Q" (*uaddr)
: [new] "d" (newval)
: "cc", "memory");

View File

@ -18,6 +18,7 @@
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/kmemleak.h>
#include <asm/dma-types.h>
#include <asm/cio.h>
@ -147,9 +148,11 @@ static inline struct idal_buffer *idal_buffer_alloc(size_t size, int page_order)
ib->data[i] = dma64_add(ib->data[i - 1], IDA_BLOCK_SIZE);
continue;
}
vaddr = (void *)__get_free_pages(GFP_KERNEL, page_order);
vaddr = kmalloc(PAGE_SIZE << page_order, GFP_KERNEL);
if (!vaddr)
goto error;
/* Only DMA addresses are retained in ib->data. */
kmemleak_ignore(vaddr);
ib->data[i] = virt_to_dma64(vaddr);
}
return ib;
@ -157,7 +160,7 @@ static inline struct idal_buffer *idal_buffer_alloc(size_t size, int page_order)
while (i >= nr_chunks) {
i -= nr_chunks;
vaddr = dma64_to_virt(ib->data[i]);
free_pages((unsigned long)vaddr, ib->page_order);
kfree(vaddr);
}
kfree(ib);
return ERR_PTR(-ENOMEM);
@ -175,7 +178,7 @@ static inline void idal_buffer_free(struct idal_buffer *ib)
nr_chunks = (PAGE_SIZE << ib->page_order) >> IDA_SIZE_SHIFT;
for (i = 0; i < nr_ptrs; i += nr_chunks) {
vaddr = dma64_to_virt(ib->data[i]);
free_pages((unsigned long)vaddr, ib->page_order);
kfree(vaddr);
}
kfree(ib);
}

View File

@ -21,7 +21,7 @@
#define LC_ORDER 1
#define LC_PAGES 2
#define LOWCORE_ALT_ADDRESS _AC(0x70000, UL)
#define LOWCORE_ALT_ADDRESS 0x70000
#ifndef __ASSEMBLER__
@ -222,6 +222,9 @@ struct lowcore {
__u8 pad_0x1900[0x2000-0x1900]; /* 0x1900 */
} __packed __aligned(8192);
#define LC_PERCPU_OFFSET offsetof(struct lowcore, percpu_offset)
#define LC_PERCPU_REGISTER offsetof(struct lowcore, percpu_register)
static __always_inline struct lowcore *get_lowcore(void)
{
struct lowcore *lc;

View File

@ -147,7 +147,7 @@ union zpci_sic_iib {
DECLARE_STATIC_KEY_FALSE(have_mio);
u8 zpci_mod_fc(u64 req, struct zpci_fib *fib, u8 *status);
int zpci_refresh_trans(u64 fn, u64 addr, u64 range);
int zpci_refresh_trans(u32 fh, u64 addr, u64 range);
int __zpci_load(u64 *data, u64 req, u64 offset);
int zpci_load(u64 *data, const volatile void __iomem *addr, unsigned long len);
int __zpci_store(u64 data, u64 req, u64 offset);

View File

@ -5,6 +5,7 @@
#include <linux/preempt.h>
#include <asm/cmpxchg.h>
#include <asm/march.h>
#include <asm/asm.h>
/*
* s390 uses its own implementation for per cpu data, the offset of
@ -14,17 +15,16 @@
#define arch_raw_cpu_ptr(_ptr) \
({ \
unsigned long lc_percpu, tcp_ptr__; \
unsigned long tcp_ptr__; \
\
tcp_ptr__ = (__force unsigned long)(_ptr); \
lc_percpu = offsetof(struct lowcore, percpu_offset); \
asm_inline volatile( \
ALTERNATIVE("ag %[__ptr__],%[offzero](%%r0)\n", \
"ag %[__ptr__],%[offalt](%%r0)\n", \
ALT_FEATURE(MFEATURE_LOWCORE)) \
: [__ptr__] "+d" (tcp_ptr__) \
: [offzero] "i" (lc_percpu), \
[offalt] "i" (lc_percpu + LOWCORE_ALT_ADDRESS), \
: [offzero] "i" (LC_PERCPU_OFFSET), \
[offalt] "i" (LC_PERCPU_OFFSET + LOWCORE_ALT_ADDRESS), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "cc"); \
(TYPEOF_UNQUAL(*(_ptr)) __force __kernel *)tcp_ptr__; \
@ -63,65 +63,114 @@
/*
* Macros to be used for percpu code section based on atomic instructions.
*
* Avoid the need to use preempt_disable() / preempt_disable() pairs and the
* Avoid the need to use preempt_disable() / preempt_enable() pairs and the
* conditional preempt_schedule_notrace() function calls which come with
* this. The idea is that this_cpu operations based on atomic instructions are
* guarded with mviy instructions:
*
* - The first mviy instruction writes the register number, which contains the
* percpu address variable to lowcore. This also indicates that a percpu
* code section is executed.
* - The first mviy instruction writes the register number of the percpu address
* variable and the even register number of a register pair (which encodes two
* registers: the even register for the percpu offset and the odd register for
* the percpu pointer) to lowcore. This also indicates that a percpu code
* section is executed.
*
* - The first mviy instruction following the mviy instruction must be the ag
* instruction which adds the percpu offset to the percpu address register.
* - The mviy instruction is followed by the lg instruction which loads the
* percpu offset into the even register of the pair and the la instruction
* which adds the percpu offset and the percpu address into the odd register
* of the pair (the percpu pointer register).
*
* - Afterwards the atomic percpu operation follows.
*
* - Then a second mviy instruction writes a zero to lowcore, which indicates
* the end of the percpu code section.
*
* - In case of an interrupt/exception/nmi the register number which was
* written to lowcore is copied to the exception frame (pt_regs), and a zero
* is written to lowcore.
* - In case of an interrupt/exception/nmi the encoded register numbers which
* were written to lowcore are copied to the exception frame (pt_regs), and a
* zero is written to lowcore.
*
* - On return to the previous context it is checked if a percpu code section
* was executed (saved register number not zero), and if the process was
* migrated to a different cpu. If the percpu offset was already added to
* the percpu address register (instruction address does _not_ point to the
* ag instruction) the content of the percpu address register is adjusted so
* it points to percpu variable of the new cpu.
* was executed (saved register value not zero), and if the process was
* migrated to a different cpu. The content of the percpu offset register
* (even register of the pair) is reloaded with the current cpu's percpu
* offset and the percpu pointer register (odd register of the pair) is
* recalculated so it points to the percpu variable of the new cpu.
*
* Inline assemblies making use of this typically have a code sequence like:
*
* MVIY_PERCPU(...) <- start of percpu code section
* AG_ALT(...) <- add percpu offset; must be the second instruction
* atomic_op <- atomic op
* MVIY_ALT(...) <- end of percpu code section
* __PCPU_BEGIN(...) <- start of percpu code section
* atomic_op <- atomic op
* __PCPU_END(...) <- end of percpu code section
*/
#define MVIY_PERCPU(disp, dispalt, reg) \
".macro GEN_MVIY disp reg\n" \
".irp rs,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15\n" \
" .ifc \\reg,%%r\\rs\n" \
" mviy \\disp(%%r0),\\rs\n" \
#define LC_ALT_ADDR __stringify(LOWCORE_ALT_ADDRESS)
#define DEFINE_GR_NUM \
".macro _GR_NUM opd, gr\n" \
".set \\opd,255\n" \
".irp rs,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15\n" \
" .ifc \\gr,%%r\\rs\n" \
" \\opd = \\rs\n" \
" .endif\n" \
".endr\n" \
".endm\n" \
ALTERNATIVE("GEN_MVIY " disp ", " reg "\n", \
"GEN_MVIY " dispalt ", " reg "\n", \
ALT_FEATURE(MFEATURE_LOWCORE)) \
".purgem GEN_MVIY\n"
".if \\opd == 255\n" \
" .error \"Illegal register number\"\n" \
".endif\n" \
".endm\n"
#define MVIY_ALT(disp, dispalt) \
ALTERNATIVE(" mviy " disp "(%%r0),0\n", \
" mviy " dispalt "(%%r0),0\n", \
#define UNDEF_GR_NUM \
".purgem _GR_NUM\n"
#define PCPU_REG_PCP_SHIFT 0
#define PCPU_REG_PCP GENMASK(3, 0)
#define PCPU_REG_OFF_SHIFT 4
#define PCPU_REG_OFF GENMASK(7, 4)
#define __PCPU_MVIY(lcreg, imm) \
ALTERNATIVE(" mviy " lcreg "(%%r0)," imm "\n", \
" mviy " lcreg "+" LC_ALT_ADDR "(%%r0)," imm "\n", \
ALT_FEATURE(MFEATURE_LOWCORE))
#define AG_ALT(disp, dispalt, reg) \
ALTERNATIVE(" ag " reg ", " disp "(%%r0)\n", \
" ag " reg ", " dispalt "(%%r0)\n", \
#define __PCPU_LG(regoff, lcoff) \
ALTERNATIVE(" lg " regoff ", " lcoff "(%%r0)\n", \
" lg " regoff ", " lcoff "+" LC_ALT_ADDR "(%%r0)\n", \
ALT_FEATURE(MFEATURE_LOWCORE))
#define __PCPU_LA(regptr, regoff, regpcp) \
" la " regptr ",0(" regoff "," regpcp ")\n"
#define __PCPU_CALC_REGVAL(regpcp, regoff) \
"(" regpcp " << " __stringify(PCPU_REG_PCP_SHIFT) ") |" \
"(" regoff " << " __stringify(PCPU_REG_OFF_SHIFT) ")"
#define __PCPU_CHECK_REGS(regpcp, regoff, regptr) \
".if " regoff " & 1\n" \
" .error \"Percpu offset register must be even\"\n" \
".endif\n" \
".if (" regpcp " == 0) || (" regoff " == 0)\n" \
" .error \"Percpu address and offset register must be non-zero\"\n"\
".endif\n" \
".if " regptr " != " regoff " + 1\n" \
" .error \"Percpu pointer and offset register must be a pair\"\n" \
".endif\n" \
".if (" regpcp " == " regoff ") || (" regpcp " == " regptr ")\n" \
" .error \"Percpu registers must be distinct\"\n" \
".endif\n"
#define __PCPU_BEGIN(lcreg, lcoff, regpcp, regoff, regptr) \
DEFINE_GR_NUM \
"_GR_NUM .Lregpcp, " regpcp "\n" \
"_GR_NUM .Lregoff, " regoff "\n" \
"_GR_NUM .Lregptr, " regptr "\n" \
UNDEF_GR_NUM \
__PCPU_CHECK_REGS(".Lregpcp", ".Lregoff", ".Lregptr") \
".set .Lregval, " __PCPU_CALC_REGVAL(".Lregpcp", ".Lregoff") "\n" \
__PCPU_MVIY(lcreg, ".Lregval") \
__PCPU_LG(regoff, lcoff) \
__PCPU_LA(regptr, regoff, regpcp)
#define __PCPU_END(lcreg) \
__PCPU_MVIY(lcreg, "0")
#ifndef MARCH_HAS_Z196_FEATURES
#define this_cpu_add_4(pcp, val) arch_this_cpu_to_op_simple(pcp, val, +)
@ -137,44 +186,38 @@
#define arch_this_cpu_add(pcp, val, op1, op2, szcast) \
do { \
unsigned long lc_pcpr, lc_pcpo; \
typedef typeof(pcp) pcp_op_T__; \
union register_pair rp__; \
pcp_op_T__ val__ = (val); \
pcp_op_T__ old__, *ptr__; \
pcp_op_T__ *ptr__; \
\
lc_pcpr = offsetof(struct lowcore, percpu_register); \
lc_pcpo = offsetof(struct lowcore, percpu_offset); \
ptr__ = PERCPU_PTR(&(pcp)); \
if (__builtin_constant_p(val__) && \
((szcast)val__ > -129) && ((szcast)val__ < 128)) { \
asm volatile( \
MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]") \
op2 " 0(%[ptr__]),%[val__]\n" \
MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]") \
: [ptr__] "+&a" (ptr__), "+m" (*ptr__), \
asm_inline volatile( \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
op2 " 0(%N[pair__]),%[val__]\n" \
__PCPU_END("%[lcreg]") \
: [pair__] "=&a" (rp__.pair), "+m" (*ptr__), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [val__] "i" ((szcast)val__), \
[disppcpr] "i" (lc_pcpr), \
[disppcpo] "i" (lc_pcpo), \
[dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS), \
[dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS), \
: [val__] "i" ((szcast)val__), [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "cc"); \
} else { \
asm volatile( \
MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]") \
op1 " %[old__],%[val__],0(%[ptr__])\n" \
MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]") \
: [old__] "=&d" (old__), \
[ptr__] "+&a" (ptr__), "+m" (*ptr__), \
asm_inline volatile( \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
op1 " %[val__],%[val__],0(%N[pair__])\n" \
__PCPU_END("%[lcreg]") \
: [val__] "+&d" (val__), [pair__] "=&a" (rp__.pair), \
"+m" (*ptr__), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [val__] "d" (val__), \
[disppcpr] "i" (lc_pcpr), \
[disppcpo] "i" (lc_pcpo), \
[dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS), \
[dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS), \
: [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "cc"); \
} \
@ -185,27 +228,23 @@ do { \
#define arch_this_cpu_add_return(pcp, val, op) \
({ \
unsigned long lc_pcpr, lc_pcpo; \
typedef typeof(pcp) pcp_op_T__; \
union register_pair rp__; \
pcp_op_T__ val__ = (val); \
pcp_op_T__ old__, *ptr__; \
\
lc_pcpr = offsetof(struct lowcore, percpu_register); \
lc_pcpo = offsetof(struct lowcore, percpu_offset); \
ptr__ = PERCPU_PTR(&(pcp)); \
asm_inline volatile( \
MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]") \
op " %[old__],%[val__],0(%[ptr__])\n" \
MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]") \
: [old__] "=&d" (old__), \
[ptr__] "+&a" (ptr__), "+m" (*ptr__), \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
op " %[old__],%[val__],0(%N[pair__])\n" \
__PCPU_END("%[lcreg]") \
: [old__] "=&d" (old__), [pair__] "=&a" (rp__.pair), \
"+m" (*ptr__), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [val__] "d" (val__), \
[disppcpr] "i" (lc_pcpr), \
[disppcpo] "i" (lc_pcpo), \
[dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS), \
[dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS), \
: [val__] "d" (val__), [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "cc"); \
old__ + val__; \
@ -216,27 +255,23 @@ do { \
#define arch_this_cpu_to_op(pcp, val, op) \
do { \
unsigned long lc_pcpr, lc_pcpo; \
typedef typeof(pcp) pcp_op_T__; \
union register_pair rp__; \
pcp_op_T__ val__ = (val); \
pcp_op_T__ old__, *ptr__; \
pcp_op_T__ *ptr__; \
\
lc_pcpr = offsetof(struct lowcore, percpu_register); \
lc_pcpo = offsetof(struct lowcore, percpu_offset); \
ptr__ = PERCPU_PTR(&(pcp)); \
asm_inline volatile( \
MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]") \
op " %[old__],%[val__],0(%[ptr__])\n" \
MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]") \
: [old__] "=&d" (old__), \
[ptr__] "+&a" (ptr__), "+m" (*ptr__), \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
op " %[val__],%[val__],0(%N[pair__])\n" \
__PCPU_END("%[lcreg]") \
: [val__] "+&d" (val__), [pair__] "=&a" (rp__.pair), \
"+m" (*ptr__), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [val__] "d" (val__), \
[disppcpr] "i" (lc_pcpr), \
[disppcpo] "i" (lc_pcpo), \
[dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS), \
[dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS), \
: [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "cc"); \
} while (0)
@ -250,24 +285,22 @@ do { \
#define arch_this_cpu_read(pcp, op) \
({ \
unsigned long lc_pcpr, lc_pcpo, res__; \
typedef typeof(pcp) pcp_op_T__; \
union register_pair rp__; \
unsigned long res__; \
pcp_op_T__ *ptr__; \
\
lc_pcpr = offsetof(struct lowcore, percpu_register); \
lc_pcpo = offsetof(struct lowcore, percpu_offset); \
ptr__ = PERCPU_PTR(&(pcp)); \
asm_inline volatile( \
MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]") \
op " %[res__],0(%[ptr__])\n" \
MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]") \
: [res__] "=&d" (res__), [ptr__] "+&a" (ptr__), \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
op " %[res__],0(%N[pair__])\n" \
__PCPU_END("%[lcreg]") \
: [res__] "=&d" (res__), [pair__] "=&a" (rp__.pair), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [disppcpr] "i" (lc_pcpr), \
[disppcpo] "i" (lc_pcpo), \
[dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS), \
[dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS), \
: [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (*ptr__), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "cc"); \
@ -281,25 +314,21 @@ do { \
#define arch_this_cpu_write(pcp, val, op) \
do { \
unsigned long lc_pcpr, lc_pcpo; \
typedef typeof(pcp) pcp_op_T__; \
union register_pair rp__; \
pcp_op_T__ *ptr__, val__ = (val); \
\
lc_pcpr = offsetof(struct lowcore, percpu_register); \
lc_pcpo = offsetof(struct lowcore, percpu_offset); \
ptr__ = PERCPU_PTR(&(pcp)); \
asm_inline volatile( \
MVIY_PERCPU("%[disppcpr]", "%[dispaltpcpr]", "%[ptr__]")\
AG_ALT("%[disppcpo]", "%[dispaltpcpo]", "%[ptr__]") \
op " %[val__],0(%[ptr__])\n" \
MVIY_ALT("%[disppcpr]", "%[dispaltpcpr]") \
: [ptr__] "+&a" (ptr__), "=m" (*ptr__), \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
op " %[val__],0(%N[pair__])\n" \
__PCPU_END("%[lcreg]") \
: [pair__] "=&a" (rp__.pair), "=m" (*ptr__), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [val__] "d" (val__), \
[disppcpr] "i" (lc_pcpr), \
[disppcpo] "i" (lc_pcpo), \
[dispaltpcpr] "i" (lc_pcpr + LOWCORE_ALT_ADDRESS), \
[dispaltpcpo] "i" (lc_pcpo + LOWCORE_ALT_ADDRESS), \
: [val__] "d" (val__), [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "cc"); \
} while (0)
@ -309,7 +338,7 @@ do { \
#define this_cpu_write_4(pcp, val) arch_this_cpu_write(pcp, val, "st")
#define this_cpu_write_8(pcp, val) arch_this_cpu_write(pcp, val, "stg")
#define arch_this_cpu_cmpxchg(pcp, oval, nval) \
#define arch_this_cpu_cmpxchg_simple(pcp, oval, nval) \
({ \
typedef typeof(pcp) pcp_op_T__; \
pcp_op_T__ ret__; \
@ -321,28 +350,100 @@ do { \
ret__; \
})
#define this_cpu_cmpxchg_1(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
#define this_cpu_cmpxchg_2(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
#define this_cpu_cmpxchg_4(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
#define this_cpu_cmpxchg_8(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
#define arch_this_cpu_cmpxchg(pcp, oval, nval, op) \
({ \
typedef typeof(pcp) pcp_op_T__; \
pcp_op_T__ old__ = (oval), new__ = (nval); \
union register_pair rp__; \
pcp_op_T__ *ptr__; \
\
ptr__ = PERCPU_PTR(&(pcp)); \
asm_inline volatile( \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
op " %[old__],%[new__],0(%N[pair__])\n" \
__PCPU_END("%[lcreg]") \
: [old__] "+&d" (old__), [pair__] "=&a" (rp__.pair), \
"+m" (*ptr__), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [new__] "d" (new__), [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "memory", "cc"); \
old__; \
})
#define this_cpu_cmpxchg_1(pcp, oval, nval) arch_this_cpu_cmpxchg_simple(pcp, oval, nval)
#define this_cpu_cmpxchg_2(pcp, oval, nval) arch_this_cpu_cmpxchg_simple(pcp, oval, nval)
#define this_cpu_cmpxchg_4(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval, "cs")
#define this_cpu_cmpxchg_8(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval, "csg")
#define this_cpu_cmpxchg64(pcp, o, n) this_cpu_cmpxchg_8(pcp, o, n)
#define this_cpu_cmpxchg128(pcp, oval, nval) \
({ \
typedef typeof(pcp) pcp_op_T__; \
u128 old__, new__, ret__; \
u128 old__ = (oval), new__ = (nval); \
union register_pair rp__; \
pcp_op_T__ *ptr__; \
old__ = oval; \
new__ = nval; \
preempt_disable_notrace(); \
ptr__ = raw_cpu_ptr(&(pcp)); \
ret__ = cmpxchg128((void *)ptr__, old__, new__); \
preempt_enable_notrace(); \
ret__; \
\
ptr__ = PERCPU_PTR(&(pcp)); \
asm_inline volatile( \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
" cdsg %[old__],%[new__],0(%N[pair__])\n" \
__PCPU_END("%[lcreg]") \
: [old__] "+&d" (old__), [pair__] "=&a" (rp__.pair), \
"+m" (*ptr__), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [new__] "d" (new__), [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "memory", "cc"); \
old__; \
})
#define arch_this_cpu_xchg(pcp, nval) \
#ifdef __HAVE_ASM_FLAG_OUTPUTS__
#define arch_this_cpu_try_cmpxchg(pcp, ovalp, nval, op) \
({ \
typedef typeof(pcp) pcp_op_T__; \
typeof(ovalp) ovalp__ = (ovalp); \
pcp_op_T__ old__ = *ovalp__; \
pcp_op_T__ new__ = (nval); \
union register_pair rp__; \
pcp_op_T__ *ptr__; \
int cc__; \
\
ptr__ = PERCPU_PTR(&(pcp)); \
asm_inline volatile( \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
op " %[old__],%[new__],0(%N[pair__])\n" \
__PCPU_END("%[lcreg]") \
: [old__] "+&d" (old__), [pair__] "=&a" (rp__.pair), \
"+m" (*ptr__), "=@cc" (cc__), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [new__] "d" (new__), [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "memory"); \
if (unlikely(cc__)) \
*ovalp__ = old__; \
likely(cc__ == 0); \
})
#define this_cpu_try_cmpxchg_4(pcp, ovalp, nval) arch_this_cpu_try_cmpxchg(pcp, ovalp, nval, "cs")
#define this_cpu_try_cmpxchg_8(pcp, ovalp, nval) arch_this_cpu_try_cmpxchg(pcp, ovalp, nval, "csg")
#define this_cpu_try_cmpxchg64(pcp, ovalp, nval) this_cpu_try_cmpxchg_8(pcp, ovalp, nval)
#endif /* __HAVE_ASM_FLAG_OUTPUTS__ */
#define arch_this_cpu_xchg_simple(pcp, nval) \
({ \
typeof(pcp) *ptr__; \
typeof(pcp) ret__; \
@ -353,10 +454,36 @@ do { \
ret__; \
})
#define this_cpu_xchg_1(pcp, nval) arch_this_cpu_xchg(pcp, nval)
#define this_cpu_xchg_2(pcp, nval) arch_this_cpu_xchg(pcp, nval)
#define this_cpu_xchg_4(pcp, nval) arch_this_cpu_xchg(pcp, nval)
#define this_cpu_xchg_8(pcp, nval) arch_this_cpu_xchg(pcp, nval)
#define arch_this_cpu_xchg(pcp, nval, ldop, csop) \
({ \
typedef typeof(pcp) pcp_op_T__; \
pcp_op_T__ old__, new__ = (nval); \
union register_pair rp__; \
pcp_op_T__ *ptr__; \
\
ptr__ = PERCPU_PTR(&(pcp)); \
asm_inline volatile( \
__PCPU_BEGIN("%[lcreg]","%[lcoff]","%[ptr__]", \
"%[pair__]","%N[pair__]") \
" " ldop " %[old__],0(%N[pair__])\n" \
"0: " csop " %[old__],%[new__],0(%N[pair__])\n"\
" jnz 0b\n" \
__PCPU_END("%[lcreg]") \
: [old__] "=&d" (old__), [pair__] "=&a" (rp__.pair), \
"+m" (*ptr__), \
"=m" (((struct lowcore *)0)->percpu_register) \
: [new__] "d" (new__), [ptr__] "a" (ptr__), \
[lcreg] "i" (LC_PERCPU_REGISTER), \
[lcoff] "i" (LC_PERCPU_OFFSET), \
"m" (((struct lowcore *)0)->percpu_offset) \
: "memory", "cc"); \
old__; \
})
#define this_cpu_xchg_1(pcp, nval) arch_this_cpu_xchg_simple(pcp, nval)
#define this_cpu_xchg_2(pcp, nval) arch_this_cpu_xchg_simple(pcp, nval)
#define this_cpu_xchg_4(pcp, nval) arch_this_cpu_xchg(pcp, nval, "l", "cs")
#define this_cpu_xchg_8(pcp, nval) arch_this_cpu_xchg(pcp, nval, "lg", "csg")
#include <asm-generic/percpu.h>

View File

@ -17,11 +17,15 @@
#define PIF_PSW_ADDR_ADJUSTED 1 /* psw address has been adjusted */
#define PIF_SYSCALL_RET_SET 2 /* return value was set via ptrace */
#define PIF_FTRACE_FULL_REGS 3 /* all register contents valid (ftrace) */
#define PIF_WARN_TRAP_SKIPPED 4 /* __WARN_trap() was skipped */
#define PIF_PGM_CHECK 5 /* inside a program check */
#define _PIF_SYSCALL BIT(PIF_SYSCALL)
#define _PIF_ADDR_PSW_ADJUSTED BIT(PIF_PSW_ADDR_ADJUSTED)
#define _PIF_SYSCALL_RET_SET BIT(PIF_SYSCALL_RET_SET)
#define _PIF_FTRACE_FULL_REGS BIT(PIF_FTRACE_FULL_REGS)
#define _PIF_WARN_TRAP_SKIPPED BIT(PIF_WARN_TRAP_SKIPPED)
#define _PIF_PGM_CHECK BIT(PIF_PGM_CHECK)
#define PSW32_MASK_PER _AC(0x40000000, UL)
#define PSW32_MASK_DAT _AC(0x04000000, UL)
@ -240,6 +244,8 @@ static inline void instruction_pointer_set(struct pt_regs *regs,
regs->psw.addr = val;
}
unsigned long get_trap_ip(struct pt_regs *regs);
int regs_query_register_offset(const char *name);
const char *regs_query_register_name(unsigned int offset);

View File

@ -43,10 +43,8 @@ raw_copy_from_user(void *to, const void __user *from, unsigned long size)
asm_inline volatile(
" lhi %%r0,%[spec]\n"
"0: mvcos %[to],%[from],%[size]\n"
"1: nopr %%r7\n"
CC_IPM(cc)
EX_TABLE_UA_MVCOS_FROM(0b, 0b)
EX_TABLE_UA_MVCOS_FROM(1b, 0b)
EX_TABLE_INSN_UA_MVCOS_FROM(0b, 0b)
: CC_OUT(cc, cc), [size] "+d" (size), [to] "=Q" (*(char *)to)
: [spec] "I" (0x81), [from] "Q" (*(const char __user *)from)
: CC_CLOBBER_LIST("memory", "0"));
@ -71,10 +69,8 @@ raw_copy_to_user(void __user *to, const void *from, unsigned long size)
asm_inline volatile(
" llilh %%r0,%[spec]\n"
"0: mvcos %[to],%[from],%[size]\n"
"1: nopr %%r7\n"
CC_IPM(cc)
EX_TABLE_UA_MVCOS_TO(0b, 0b)
EX_TABLE_UA_MVCOS_TO(1b, 0b)
EX_TABLE_INSN_UA_MVCOS_TO(0b, 0b)
: CC_OUT(cc, cc), [size] "+d" (size), [to] "=Q" (*(char __user *)to)
: [spec] "I" (0x81), [from] "Q" (*(const char *)from)
: CC_CLOBBER_LIST("memory", "0"));
@ -123,9 +119,7 @@ __put_user_##type##_noinstr(unsigned type __user *to, \
asm goto( \
" llilh %%r0,%[spec]\n" \
"0: mvcos %[to],%[from],%[size]\n" \
"1: nopr %%r7\n" \
EX_TABLE(0b, %l[Efault]) \
EX_TABLE(1b, %l[Efault]) \
EX_TABLE_INSN(0b, %l[Efault]) \
: [to] "+Q" (*to) \
: [size] "d" (size), [from] "Q" (*from), \
[spec] "I" (0x81) \
@ -150,10 +144,9 @@ __put_user_##type##_noinstr(unsigned type __user *to, \
asm_inline volatile( \
" llilh %%r0,%[spec]\n" \
"0: mvcos %[to],%[from],%[size]\n" \
"1: lhi %[rc],0\n" \
"2:\n" \
EX_TABLE_UA_FAULT(0b, 2b, %[rc]) \
EX_TABLE_UA_FAULT(1b, 2b, %[rc]) \
" lhi %[rc],0\n" \
"1:\n" \
EX_TABLE_INSN_UA_FAULT(0b, 1b, %[rc]) \
: [rc] "=d" (rc), [to] "+Q" (*to) \
: [size] "d" (size), [from] "Q" (*from), \
[spec] "I" (0x81) \
@ -238,9 +231,7 @@ __get_user_##type##_noinstr(unsigned type *to, \
asm goto( \
" lhi %%r0,%[spec]\n" \
"0: mvcos %[to],%[from],%[size]\n" \
"1: nopr %%r7\n" \
EX_TABLE(0b, %l[Efault]) \
EX_TABLE(1b, %l[Efault]) \
EX_TABLE_INSN(0b, %l[Efault]) \
: [to] "=Q" (*to) \
: [size] "d" (size), [from] "Q" (*from), \
[spec] "I" (0x81) \
@ -266,10 +257,9 @@ __get_user_##type##_noinstr(unsigned type *to, \
asm_inline volatile( \
" lhi %%r0,%[spec]\n" \
"0: mvcos %[to],%[from],%[size]\n" \
"1: lhi %[rc],0\n" \
"2:\n" \
EX_TABLE_UA_FAULT(0b, 2b, %[rc]) \
EX_TABLE_UA_FAULT(1b, 2b, %[rc]) \
" lhi %[rc],0\n" \
"1:\n" \
EX_TABLE_INSN_UA_FAULT(0b, 1b, %[rc]) \
: [rc] "=d" (rc), [to] "=Q" (*to) \
: [size] "d" (size), [from] "Q" (*from), \
[spec] "I" (0x81) \
@ -374,10 +364,8 @@ __clear_user(void __user *to, unsigned long size)
asm_inline volatile(
" llilh %%r0,%[spec]\n"
"0: mvcos %[to],%[from],%[size]\n"
"1: nopr %%r7\n"
CC_IPM(cc)
EX_TABLE_UA_MVCOS_TO(0b, 0b)
EX_TABLE_UA_MVCOS_TO(1b, 0b)
EX_TABLE_INSN_UA_MVCOS_TO(0b, 0b)
: CC_OUT(cc, cc), [size] "+d" (size), [to] "=Q" (*(char __user *)to)
: [spec] "I" (0x81), [from] "Q" (*(const char *)empty_zero_page)
: CC_CLOBBER_LIST("memory", "0"));
@ -418,9 +406,7 @@ do { \
case 8: \
asm goto( \
"0: mvc %O[_dst](%[_len],%R[_dst]),%[_src]\n" \
"1: nopr %%r7\n" \
EX_TABLE(0b, %l[err_label]) \
EX_TABLE(1b, %l[err_label]) \
EX_TABLE_INSN(0b, %l[err_label]) \
: [_dst] "=Q" (*(type *)dst) \
: [_src] "Q" (*(type *)(src)), \
[_len] "I" (sizeof(type)) \
@ -447,10 +433,9 @@ do { \
case 8: \
asm_inline volatile( \
"0: mvc 0(%[_len],%[_dst]),%[_src]\n" \
"1: lhi %[_rc],0\n" \
"2:\n" \
EX_TABLE_UA_FAULT(0b, 2b, %[_rc]) \
EX_TABLE_UA_FAULT(1b, 2b, %[_rc]) \
" lhi %[_rc],0\n" \
"1:\n" \
EX_TABLE_INSN_UA_FAULT(0b, 1b, %[_rc]) \
: [_rc] "=d" (__rc), \
"=m" (*__dst) \
: [_src] "Q" (*(type *)(src)), \

View File

@ -8,6 +8,8 @@
DECLARE_PER_CPU(u64, mt_cycles[8]);
void vtime_init(void);
static inline void update_timer_sys(void)
{
struct lowcore *lc = get_lowcore();

View File

@ -1,30 +0,0 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright IBM Corp. 2003, 2012
* Virtual CPU timer
*
* Author(s): Jan Glauber <jan.glauber@de.ibm.com>
*/
#ifndef _ASM_S390_TIMER_H
#define _ASM_S390_TIMER_H
#define VTIMER_MAX_SLICE (0x7fffffffffffffffULL)
struct vtimer_list {
struct list_head entry;
u64 expires;
u64 interval;
void (*function)(unsigned long);
unsigned long data;
};
extern void init_virt_timer(struct vtimer_list *timer);
extern void add_virt_timer(struct vtimer_list *timer);
extern void add_virt_timer_periodic(struct vtimer_list *timer);
extern int mod_virt_timer(struct vtimer_list *timer, u64 expires);
extern int mod_virt_timer_periodic(struct vtimer_list *timer, u64 expires);
extern int del_virt_timer(struct vtimer_list *timer);
extern void vtime_init(void);
#endif /* _ASM_S390_TIMER_H */

View File

@ -54,9 +54,8 @@ static inline unsigned long load_unaligned_zeropad(const void *addr)
asm_inline volatile(
"0: lg %[data],0(%[addr])\n"
"1: nopr %%r7\n"
EX_TABLE_ZEROPAD(0b, 1b, %[data], %[addr])
EX_TABLE_ZEROPAD(1b, 1b, %[data], %[addr])
"1:\n"
EX_TABLE_INSN_ZEROPAD(0b, 1b, %[data], %[addr])
: [data] "=d" (data)
: [addr] "a" (addr), "m" (*(unsigned long *)addr));
return data;

View File

@ -236,9 +236,9 @@ static int __diag320(unsigned long subcode, void *addr)
union register_pair rp = { .even = (unsigned long)addr, };
asm_inline volatile(
" diag %[rp],%[subcode],0x320\n"
"0: nopr %%r7\n"
EX_TABLE(0b, 0b)
"0: diag %[rp],%[subcode],0x320\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
: [rp] "+d" (rp.pair)
: [subcode] "d" (subcode)
: "cc", "memory");

View File

@ -196,9 +196,9 @@ static inline int __diag204(unsigned long *subcode, unsigned long size, void *ad
union register_pair rp = { .even = *subcode, .odd = size };
asm_inline volatile(
" diag %[addr],%[rp],0x204\n"
"0: nopr %%r7\n"
EX_TABLE(0b,0b)
"0: diag %[addr],%[rp],0x204\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
: [rp] "+&d" (rp.pair) : [addr] "d" (addr) : "memory");
*subcode = rp.even;
return rp.odd;
@ -287,10 +287,10 @@ int diag224(void *ptr)
diag_stat_inc(DIAG_STAT_X224);
asm_inline volatile("\n"
" diag %[type],%[addr],0x224\n"
"0: lhi %[rc],0\n"
"0: diag %[type],%[addr],0x224\n"
" lhi %[rc],0\n"
"1:\n"
EX_TABLE(0b,1b)
EX_TABLE_INSN(0b, 1b)
: [rc] "+d" (rc)
, "=m" (*(struct { char buf[PAGE_SIZE]; } *)ptr)
: [type] "d" (0), [addr] "d" (addr));

View File

@ -51,7 +51,7 @@ struct s390_operand {
struct s390_insn {
union {
const char name[5];
const char name[5] __nonstring;
struct {
unsigned char zero;
unsigned int offset;
@ -340,7 +340,7 @@ static const unsigned char formats[][6] = {
[INSTR_VSI_URDV] = { V_32, D_20, B_16, U8_8, 0, 0 },
};
static char long_insn_name[][7] = LONG_INSN_INITIALIZER;
static char long_insn_name[][7] __nonstring_array = LONG_INSN_INITIALIZER;
static struct s390_insn opcode[] = OPCODE_TABLE_INITIALIZER;
static struct s390_opcode_offset opcode_offset[] = OPCODE_OFFSET_INITIALIZER;
@ -503,7 +503,8 @@ static int copy_from_regs(struct pt_regs *regs, void *dst, void *src, int len)
void show_code(struct pt_regs *regs)
{
char *mode = user_mode(regs) ? "User" : "Krnl";
unsigned long addr, pswaddr;
unsigned long addr, pswaddr, trapaddr;
bool pgm_check;
unsigned char code[64];
char buffer[128], *ptr;
int start, end, opsize, hops, i;
@ -511,6 +512,9 @@ void show_code(struct pt_regs *regs)
pswaddr = regs->psw.addr;
if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED))
pswaddr = __forward_psw(regs->psw, regs->int_code >> 16);
pgm_check = test_pt_regs_flag(regs, PIF_PGM_CHECK);
if (pgm_check)
trapaddr = get_trap_ip(regs);
/* Get a snapshot of the 64 bytes surrounding the fault address. */
for (start = 32; start && pswaddr >= 34 - start; start -= 2) {
addr = pswaddr - 34 + start;
@ -540,34 +544,33 @@ void show_code(struct pt_regs *regs)
start += 2;
}
/* Decode the instructions. */
ptr = buffer;
ptr += sprintf(ptr, "%s Code:", mode);
printk("%s Code:\n", mode);
hops = 0;
while (start < end && hops < 8) {
ptr = buffer;
opsize = insn_length(code[start]);
if (start + opsize == 32)
*ptr++ = '*';
else if (start == 32)
*ptr++ = '>';
else
*ptr++ = ' ';
addr = pswaddr + start - 32;
if (start == 32 && test_pt_regs_flag(regs, PIF_WARN_TRAP_SKIPPED))
ptr += sprintf(ptr, " WARN RET >");
else if (pgm_check && addr == trapaddr && start == 32)
ptr += sprintf(ptr, " PGM+PSW >");
else if (pgm_check && addr == trapaddr)
ptr += sprintf(ptr, " PGM >");
else if (start == 32)
ptr += sprintf(ptr, " PSW >");
else
ptr += sprintf(ptr, " ");
ptr += sprintf(ptr, "%px: ", (void *)addr);
if (start + opsize >= end)
break;
for (i = 0; i < opsize; i++)
ptr += sprintf(ptr, "%02x", code[start + i]);
*ptr++ = '\t';
if (i < 6)
*ptr++ = '\t';
ptr += sprintf(ptr, "%*s", 13 - 2 * opsize, " ");
ptr += print_insn(ptr, code + start, addr);
start += opsize;
pr_cont("%s", buffer);
ptr = buffer;
ptr += sprintf(ptr, "\n ");
printk("%s\n", buffer);
hops++;
}
pr_cont("\n");
}
void print_fn_code(unsigned char *code, unsigned long len)

View File

@ -203,8 +203,11 @@ void __noreturn die(struct pt_regs *regs, const char *str)
console_verbose();
spin_lock_irq(&die_lock);
bust_spinlocks(1);
printk("%s: %04x ilc:%d [#%d]", str, regs->int_code & 0xffff,
regs->int_code >> 17, ++die_counter);
if (test_pt_regs_flag(regs, PIF_WARN_TRAP_SKIPPED))
printk("%s: [#%d]", str, ++die_counter);
else
printk("%s: %04x ilc:%d [#%d]", str, regs->int_code & 0xffff,
regs->int_code >> 17, ++die_counter);
pr_cont("SMP ");
if (debug_pagealloc_enabled())
pr_cont("DEBUG_PAGEALLOC");

View File

@ -153,16 +153,28 @@ static __init void setup_topology(void)
void __init __do_early_pgm_check(struct pt_regs *regs)
{
struct lowcore *lc = get_lowcore();
unsigned long ip;
regs->flags = 0;
regs->int_code = lc->pgm_int_code;
regs->int_parm_long = lc->trans_exc_code;
regs->last_break = lc->pgm_last_break;
ip = __rewind_psw(regs->psw, regs->int_code >> 16);
regs->monitor_code = lc->monitor_code;
set_pt_regs_flag(regs, PIF_PGM_CHECK);
/* Monitor Event? Might be a warning */
if ((regs->int_code & PGM_INT_CODE_MASK) == 0x40) {
if (report_bug(ip, regs) == BUG_TRAP_TYPE_WARN)
enum bug_trap_type btt;
unsigned long ip;
if (regs->monitor_code == MONCODE_BUG_ARG) {
regs->psw.addr = regs->gprs[14];
set_pt_regs_flag(regs, PIF_WARN_TRAP_SKIPPED);
btt = report_bug_entry((struct bug_entry *)regs->gprs[2], regs);
} else {
ip = __rewind_psw(regs->psw, regs->int_code >> 16);
btt = report_bug(ip, regs);
}
if (btt == BUG_TRAP_TYPE_WARN)
return;
}
if (fixup_exception(regs))
@ -173,8 +185,12 @@ void __init __do_early_pgm_check(struct pt_regs *regs)
* some basic information in case it is too early for printk().
*/
register_early_console();
early_printk("PANIC: early exception %04x PSW: %016lx %016lx\n",
regs->int_code & 0xffff, regs->psw.mask, regs->psw.addr);
if (test_pt_regs_flag(regs, PIF_WARN_TRAP_SKIPPED))
early_printk("PANIC: early exception PSW: %016lx %016lx\n",
regs->psw.mask, regs->psw.addr);
else
early_printk("PANIC: early exception %04x PSW: %016lx %016lx\n",
regs->int_code & 0xffff, regs->psw.mask, regs->psw.addr);
show_regs(regs);
disabled_wait();
}

View File

@ -392,10 +392,48 @@ __u8 _ebc_toupper[256] =
0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
/*
* ASCII subset of EBCDIC -> character classes
*/
const __u8 _ebctype_asc[256] = {
_C, _C, _C, _C, _C, _C|_S, _C, _C, /* 0x00-0x07 */
_C, _C, _C, _C|_S, _C|_S, _C|_S, _C, _C, /* 0x08-0x0f */
_C, _C, _C, _C, _C, _C|_S, _C, _C, /* 0x10-0x17 */
_C, _C, _C, _C, _C, _C, _C, _C, /* 0x18-0x1f */
_C, _C, _C, _C, _C, _C|_S, _C, _C, /* 0x20-0x27 */
_C, _C, _C, _C, _C, _C, _C, _C, /* 0x28-0x2f */
0, 0, _C, _C, _C, _C, _C, _C, /* 0x30-0x37 */
_C, _C, _C, _C, _C, _C, 0, _C, /* 0x38-0x3f */
_S|_SP, 0, 0, 0, 0, 0, 0, 0, /* 0x40-0x47 */
0, 0, 0, _P, _P, _P, _P, _P, /* 0x48-0x4f */
_P, 0, 0, 0, 0, 0, 0, 0, /* 0x50-0x57 */
0, 0, _P, _P, _P, _P, _P, 0, /* 0x58-0x5f */
_P, _P, 0, 0, 0, 0, 0, 0, /* 0x60-0x67 */
0, 0, 0, _P, _P, _P, _P, _P, /* 0x68-0x6f */
0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */
0, _P, _P, _P, _P, _P, _P, _P, /* 0x78-0x7f */
0, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L|_X, _L, /* 0x80-0x87 */
_L, _L, 0, 0, 0, 0, 0, 0, /* 0x88-0x8f */
0, _L, _L, _L, _L, _L, _L, _L, /* 0x90-0x97 */
_L, _L, 0, 0, 0, 0, 0, 0, /* 0x98-0x9f */
0, _P, _L, _L, _L, _L, _L, _L, /* 0xa0-0xa7 */
_L, _L, 0, 0, 0, 0, 0, 0, /* 0xa8-0xaf */
_P, 0, 0, 0, 0, 0, 0, 0, /* 0xb0-0xb7 */
0, 0, _P, _P, 0, 0, 0, 0, /* 0xb8-0xbf */
_P, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U|_X, _U, /* 0xc0-0xc7 */
_U, _U, 0, 0, 0, 0, 0, 0, /* 0xc8-0xcf */
_P, _U, _U, _U, _U, _U, _U, _U, /* 0xd0-0xd7 */
_U, _U, 0, 0, 0, 0, 0, 0, /* 0xd8-0xdf */
_P, 0, _U, _U, _U, _U, _U, _U, /* 0xe0-0xe7 */
_U, _U, 0, 0, 0, 0, 0, 0, /* 0xe8-0xef */
_D, _D, _D, _D, _D, _D, _D, _D, /* 0xf0-0xf7 */
_D, _D, 0, 0, 0, 0, 0, _C, /* 0xf8-0xff */
};
EXPORT_SYMBOL(_ascebc_500);
EXPORT_SYMBOL(_ebcasc_500);
EXPORT_SYMBOL(_ascebc);
EXPORT_SYMBOL(_ebcasc);
EXPORT_SYMBOL(_ebc_tolower);
EXPORT_SYMBOL(_ebc_toupper);
EXPORT_SYMBOL(_ebctype_asc);

View File

@ -613,6 +613,8 @@ SYM_CODE_END(early_pgm_check_handler)
* The synchronous or the asynchronous stack pointer is invalid. We are dead.
* No need to properly save the registers, we are going to panic anyway.
* Setup a pt_regs so that show_trace can provide a good call trace.
* Copy lowcore of the current CPU to stack_invalid_lowcore before it
* is modified by printing debug messages and crash handling.
*/
SYM_CODE_START(stack_invalid)
GET_LC %r15
@ -621,8 +623,21 @@ SYM_CODE_START(stack_invalid)
stmg %r0,%r7,__PT_R0(%r11)
stmg %r8,%r9,__PT_PSW(%r11)
mvc __PT_R8(64,%r11),0(%r14)
xc __PT_FLAGS(8,%r11),__PT_FLAGS(%r11)
GET_LC %r2
mvc __PT_ORIG_GPR2(8,%r11),__LC_PGM_LAST_BREAK(%r2)
mvc __PT_LAST_BREAK(8,%r11),__LC_PGM_LAST_BREAK(%r2)
/* copy lowcore; skip if used by another CPU */
larl %r4,stack_invalid_lowcore_used
lhi %r1,0
lhi %r3,1
cs %r1,%r3,0(%r4)
jne .Lskip
larl %r0,stack_invalid_lowcore
lghi %r1,LC_PAGES * PAGE_SIZE
lgr %r4,%r2
lgr %r5,%r1
mvcl %r0,%r4
.Lskip:
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
lgr %r2,%r11 # pass pointer to pt_regs
jg kernel_stack_invalid

View File

@ -11,6 +11,7 @@
#include <linux/types.h>
#include <linux/signal.h>
#include <asm/extable.h>
#include <asm/lowcore.h>
#include <asm/ptrace.h>
#include <asm/idle.h>
@ -64,6 +65,9 @@ long sys_s390_sthyi(unsigned long function_code, void __user *buffer, u64 __user
unsigned long stack_alloc(void);
void stack_free(unsigned long stack);
extern int stack_invalid_lowcore_used;
extern struct lowcore stack_invalid_lowcore;
extern char kprobes_insn_page[];
extern char _samode31[], _eamode31[];

View File

@ -261,7 +261,7 @@ static void hd_capacity_work_fn(struct work_struct *work)
}
trace_s390_hd_work_fn(steal_percentage, hd_entitled_cores, hd_high_capacity_cores);
mutex_unlock(&smp_cpu_state_mutex);
schedule_delayed_work(&hd_capacity_work, HD_DELAY_INTERVAL);
queue_delayed_work(system_dfl_wq, &hd_capacity_work, HD_DELAY_INTERVAL);
}
static int hiperdispatch_ctl_handler(const struct ctl_table *ctl, int write,

View File

@ -188,9 +188,9 @@ static inline int __diag308(unsigned long subcode, unsigned long addr)
r1.even = addr;
r1.odd = 0;
asm_inline volatile(
" diag %[r1],%[subcode],0x308\n"
"0: nopr %%r7\n"
EX_TABLE(0b,0b)
"0: diag %[r1],%[subcode],0x308\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
: [r1] "+&d" (r1.pair)
: [subcode] "d" (subcode)
: "cc", "memory");

View File

@ -144,9 +144,9 @@ static int irq_pending(struct pt_regs *regs)
void noinstr do_io_irq(struct pt_regs *regs)
{
bool from_idle, percpu_needs_fixup;
struct pt_regs *old_regs;
irqentry_state_t state;
bool from_idle;
percpu_entry(regs);
state = irqentry_enter(regs);
@ -174,21 +174,20 @@ void noinstr do_io_irq(struct pt_regs *regs)
do_irq_async(regs, IO_INTERRUPT);
} while (machine_is_lpar() && irq_pending(regs));
percpu_needs_fixup = percpu_code_check(regs);
irq_exit_rcu();
set_irq_regs(old_regs);
irqentry_exit(regs, state);
if (from_idle)
regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
percpu_exit(regs, percpu_needs_fixup);
percpu_exit(regs);
}
void noinstr do_ext_irq(struct pt_regs *regs)
{
bool from_idle, percpu_needs_fixup;
struct pt_regs *old_regs;
irqentry_state_t state;
bool from_idle;
percpu_entry(regs);
state = irqentry_enter(regs);
@ -214,14 +213,13 @@ void noinstr do_ext_irq(struct pt_regs *regs)
do_irq_async(regs, EXT_INTERRUPT);
percpu_needs_fixup = percpu_code_check(regs);
irq_exit_rcu();
set_irq_regs(old_regs);
irqentry_exit(regs, state);
if (from_idle)
regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
percpu_exit(regs, percpu_needs_fixup);
percpu_exit(regs);
}
static void show_msi_interrupt(struct seq_file *p, int irq)

View File

@ -85,6 +85,9 @@ static bool can_probe(unsigned long paddr)
if (!kallsyms_lookup_size_offset(paddr, NULL, &offset))
return false;
if (s390_search_extables(paddr))
return false;
/* Decode instructions */
addr = paddr - offset;
while (addr < paddr) {

View File

@ -361,7 +361,6 @@ NOKPROBE_SYMBOL(s390_backup_mcck_info);
*/
void notrace s390_do_machine_check(struct pt_regs *regs)
{
bool percpu_needs_fixup;
static int ipd_count;
static DEFINE_SPINLOCK(ipd_lock);
static unsigned long long last_ipd;
@ -495,9 +494,8 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
if (mcck_pending)
schedule_mcck_handler();
percpu_needs_fixup = percpu_code_check(regs);
irqentry_nmi_exit(regs, irq_state);
percpu_exit(regs, percpu_needs_fixup);
percpu_exit(regs);
}
NOKPROBE_SYMBOL(s390_do_machine_check);

View File

@ -1768,8 +1768,9 @@ static struct miscdevice cfset_dev = {
.mode = 0666,
};
/* Hotplug add of a CPU. Scan through all active processes and add
* that CPU to the list of CPUs supplied with ioctl(..., START, ...).
/*
* Start counter sets on a newly online CPU for each active hwctr session that
* includes this CPU in its requested mask.
*/
static int cfset_online_cpu(unsigned int cpu)
{
@ -1778,17 +1779,19 @@ static int cfset_online_cpu(unsigned int cpu)
if (!list_empty(&cfset_session.head)) {
list_for_each_entry(rp, &cfset_session.head, node) {
if (!cpumask_test_cpu(cpu, &rp->mask))
continue;
p.sets = rp->ctrset;
cfset_ioctl_on(&p);
cpumask_set_cpu(cpu, &rp->mask);
}
}
return 0;
}
/* Hotplug remove of a CPU. Scan through all active processes and clear
* that CPU from the list of CPUs supplied with ioctl(..., START, ...).
* Adjust reference counts.
/*
* Stop counter sets on an outgoing CPU for each active hwctr session that
* includes this CPU in its requested mask.
*/
static int cfset_offline_cpu(unsigned int cpu)
{
@ -1797,9 +1800,11 @@ static int cfset_offline_cpu(unsigned int cpu)
if (!list_empty(&cfset_session.head)) {
list_for_each_entry(rp, &cfset_session.head, node) {
if (!cpumask_test_cpu(cpu, &rp->mask))
continue;
p.sets = rp->ctrset;
cfset_ioctl_off(&p);
cpumask_clear_cpu(cpu, &rp->mask);
}
}
return 0;

View File

@ -35,7 +35,6 @@
#include <asm/cpu_mf.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
#include <asm/vtimer.h>
#include <asm/exec.h>
#include <asm/fpu.h>
#include <asm/irq.h>
@ -131,6 +130,12 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
p->thread.hardirq_timer = 0;
p->thread.softirq_timer = 0;
p->thread.last_break = 1;
/*
* s390 stores the svc return address in arch_data when calling
* sigreturn()/restart_syscall() via vdso. 1 means no valid address
* stored.
*/
p->restart_block.arch_data = 1;
frame->sf.back_chain = 0;
frame->sf.gprs[11 - 6] = (unsigned long)&frame->childregs;
@ -168,12 +173,6 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
p->thread.acrs[0] = (unsigned int)(tls >> 32);
p->thread.acrs[1] = (unsigned int)tls;
}
/*
* s390 stores the svc return address in arch_data when calling
* sigreturn()/restart_syscall() via vdso. 1 means no valid address
* stored.
*/
p->restart_block.arch_data = 1;
return 0;
}

View File

@ -34,6 +34,7 @@
#include <asm/ptrace.h>
#include <asm/rwonce.h>
#include <asm/fpu.h>
#include <asm/syscall.h>
#include "entry.h"
@ -345,11 +346,9 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
return -EINVAL;
}
if (test_pt_regs_flag(regs, PIF_SYSCALL) &&
addr == offsetof(struct user, regs.gprs[2])) {
struct pt_regs *regs = task_pt_regs(child);
regs->int_code = 0x20000 | (data & 0xffff);
if (addr == offsetof(struct user, regs.gprs[2])) {
if (test_pt_regs_flag(regs, PIF_SYSCALL))
syscall_set_nr(child, regs, data);
}
*(addr_t *)((addr_t) &regs->psw + addr) = data;
} else if (addr < offsetof(struct user, regs.orig_gpr2)) {

View File

@ -27,8 +27,8 @@
#include <linux/personality.h>
#include <linux/binfmts.h>
#include <linux/syscalls.h>
#include <asm/ucontext.h>
#include <linux/uaccess.h>
#include <asm/ucontext.h>
#include <asm/vdso-symbols.h>
#include <asm/access-regs.h>
#include <asm/lowcore.h>
@ -61,8 +61,7 @@
* have a fixed position and moves if gprs_high or vxrs exist.
* Future extensions will be added to _sigregs_ext.
*/
struct sigframe
{
struct sigframe {
__u8 callee_used_stack[__SIGNAL_FRAMESIZE];
struct sigcontext sc;
_sigregs sregs;
@ -96,22 +95,21 @@ struct sigframe
* -----------------------------------------
* Future extensions will be added to _sigregs_ext.
*/
struct rt_sigframe
{
struct rt_sigframe {
__u8 callee_used_stack[__SIGNAL_FRAMESIZE];
__u16 svc_insn;
struct siginfo info;
struct ucontext_extended uc;
};
/* Store registers needed to create the signal frame */
/* Store registers needed to create the signal frame. */
static void store_sigregs(void)
{
save_access_regs(current->thread.acrs);
save_user_fpu_regs();
}
/* Load registers after signal return */
/* Load registers after signal return. */
static void load_sigregs(void)
{
restore_access_regs(current->thread.acrs);
@ -122,14 +120,14 @@ static int save_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
{
_sigregs user_sregs;
/* Copy a 'clean' PSW mask to the user to avoid leaking
information about whether PER is currently on. */
user_sregs.regs.psw.mask = PSW_USER_BITS |
(regs->psw.mask & (PSW_MASK_USER | PSW_MASK_RI));
/*
* Copy a 'clean' PSW mask to the user to avoid leaking
* information about whether PER is currently on.
*/
user_sregs.regs.psw.mask = PSW_USER_BITS | (regs->psw.mask & (PSW_MASK_USER | PSW_MASK_RI));
user_sregs.regs.psw.addr = regs->psw.addr;
memcpy(&user_sregs.regs.gprs, &regs->gprs, sizeof(sregs->regs.gprs));
memcpy(&user_sregs.regs.acrs, current->thread.acrs,
sizeof(user_sregs.regs.acrs));
memcpy(&user_sregs.regs.acrs, current->thread.acrs, sizeof(user_sregs.regs.acrs));
fpregs_store(&user_sregs.fpregs, &current->thread.ufpu);
if (__copy_to_user(sregs, &user_sregs, sizeof(_sigregs)))
return -EFAULT;
@ -140,44 +138,37 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
{
_sigregs user_sregs;
/* Always make any pending restarted system call return -EINTR */
/* Always make any pending restarted system call return -EINTR. */
current->restart_block.fn = do_no_restart_syscall;
if (__copy_from_user(&user_sregs, sregs, sizeof(user_sregs)))
return -EFAULT;
if (!is_ri_task(current) && (user_sregs.regs.psw.mask & PSW_MASK_RI))
return -EINVAL;
/* Use regs->psw.mask instead of PSW_USER_BITS to preserve PER bit. */
regs->psw.mask = (regs->psw.mask & ~(PSW_MASK_USER | PSW_MASK_RI)) |
(user_sregs.regs.psw.mask & (PSW_MASK_USER | PSW_MASK_RI));
regs->psw.mask &= ~(PSW_MASK_USER | PSW_MASK_RI);
regs->psw.mask |= user_sregs.regs.psw.mask & (PSW_MASK_USER | PSW_MASK_RI);
/* Check for invalid user address space control. */
if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_HOME)
regs->psw.mask = PSW_ASC_PRIMARY |
(regs->psw.mask & ~PSW_MASK_ASC);
/* Check for invalid amode */
regs->psw.mask = PSW_ASC_PRIMARY | (regs->psw.mask & ~PSW_MASK_ASC);
/* Check for invalid amode. */
if (regs->psw.mask & PSW_MASK_EA)
regs->psw.mask |= PSW_MASK_BA;
regs->psw.addr = user_sregs.regs.psw.addr;
memcpy(&regs->gprs, &user_sregs.regs.gprs, sizeof(sregs->regs.gprs));
memcpy(&current->thread.acrs, &user_sregs.regs.acrs,
sizeof(current->thread.acrs));
memcpy(&current->thread.acrs, &user_sregs.regs.acrs, sizeof(current->thread.acrs));
fpregs_load(&user_sregs.fpregs, &current->thread.ufpu);
clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
/* No longer in a system call. */
clear_pt_regs_flag(regs, PIF_SYSCALL);
return 0;
}
/* Returns non-zero on fault. */
static int save_sigregs_ext(struct pt_regs *regs,
_sigregs_ext __user *sregs_ext)
static int save_sigregs_ext(struct pt_regs *regs, _sigregs_ext __user *sregs_ext)
{
__u64 vxrs[__NUM_VXRS_LOW];
int i;
/* Save vector registers to signal stack */
/* Save vector registers to signal stack. */
if (cpu_has_vx()) {
for (i = 0; i < __NUM_VXRS_LOW; i++)
vxrs[i] = current->thread.ufpu.vxrs[i].low;
@ -191,13 +182,12 @@ static int save_sigregs_ext(struct pt_regs *regs,
return 0;
}
static int restore_sigregs_ext(struct pt_regs *regs,
_sigregs_ext __user *sregs_ext)
static int restore_sigregs_ext(struct pt_regs *regs, _sigregs_ext __user *sregs_ext)
{
__u64 vxrs[__NUM_VXRS_LOW];
int i;
/* Restore vector registers from signal stack */
/* Restore vector registers from signal stack. */
if (cpu_has_vx()) {
if (__copy_from_user(vxrs, &sregs_ext->vxrs_low,
sizeof(sregs_ext->vxrs_low)) ||
@ -213,11 +203,12 @@ static int restore_sigregs_ext(struct pt_regs *regs,
SYSCALL_DEFINE0(sigreturn)
{
struct pt_regs *regs = task_pt_regs(current);
struct sigframe __user *frame =
(struct sigframe __user *) regs->gprs[15];
struct sigframe __user *frame;
struct pt_regs *regs;
sigset_t set;
regs = task_pt_regs(current);
frame = (struct sigframe __user *)regs->gprs[15];
if (__copy_from_user(&set.sig, &frame->sc.oldmask, _SIGMASK_COPY_SIZE))
goto badframe;
set_current_blocked(&set);
@ -235,11 +226,12 @@ SYSCALL_DEFINE0(sigreturn)
SYSCALL_DEFINE0(rt_sigreturn)
{
struct pt_regs *regs = task_pt_regs(current);
struct rt_sigframe __user *frame =
(struct rt_sigframe __user *)regs->gprs[15];
struct rt_sigframe __user *frame;
struct pt_regs *regs;
sigset_t set;
regs = task_pt_regs(current);
frame = (struct rt_sigframe __user *)regs->gprs[15];
if (__copy_from_user(&set.sig, &frame->uc.uc_sigmask, sizeof(set)))
goto badframe;
set_current_blocked(&set);
@ -257,94 +249,76 @@ SYSCALL_DEFINE0(rt_sigreturn)
return 0;
}
/*
* Determine which stack to use..
*/
static inline void __user *
get_sigframe(struct k_sigaction *ka, struct pt_regs * regs, size_t frame_size)
/* Determine which stack to use. */
static inline void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size)
{
unsigned long sp;
/* Default to using normal stack */
/* Default to using normal stack. */
sp = regs->gprs[15];
/* Overflow on alternate signal stack gives SIGSEGV. */
if (on_sig_stack(sp) && !on_sig_stack((sp - frame_size) & -8UL))
return (void __user *) -1UL;
/* This is the X/Open sanctioned signal stack switching. */
return (void __user *)-1UL;
/* This is the X/Open sanctioned signal stack switching. */
if (ka->sa.sa_flags & SA_ONSTACK) {
if (! sas_ss_flags(sp))
if (!sas_ss_flags(sp))
sp = current->sas_ss_sp + current->sas_ss_size;
}
return (void __user *)((sp - frame_size) & -8ul);
return (void __user *)((sp - frame_size) & -8UL);
}
static int setup_frame(int sig, struct k_sigaction *ka,
sigset_t *set, struct pt_regs * regs)
static int setup_frame(int sig, struct k_sigaction *ka, sigset_t *set, struct pt_regs *regs)
{
struct sigframe __user *frame;
struct sigcontext sc;
unsigned long restorer;
struct sigcontext sc;
size_t frame_size;
frame_size = sizeof(*frame) - sizeof(frame->sregs_ext);
if (cpu_has_vx())
frame_size += sizeof(frame->sregs_ext);
frame = get_sigframe(ka, regs, frame_size);
if (frame == (void __user *) -1UL)
if (frame == (void __user *)-1UL)
return -EFAULT;
/* Set up backchain. */
if (__put_user(regs->gprs[15], (addr_t __user *) frame))
if (__put_user(regs->gprs[15], (addr_t __user *)frame))
return -EFAULT;
/* Create struct sigcontext on the signal stack */
/* Create struct sigcontext on the signal stack. */
memcpy(&sc.oldmask, &set->sig, _SIGMASK_COPY_SIZE);
sc.sregs = (_sigregs __user __force *) &frame->sregs;
sc.sregs = (_sigregs __user __force *)&frame->sregs;
if (__copy_to_user(&frame->sc, &sc, sizeof(frame->sc)))
return -EFAULT;
/* Store registers needed to create the signal frame */
/* Store registers needed to create the signal frame. */
store_sigregs();
/* Create _sigregs on the signal stack */
/* Create _sigregs on the signal stack. */
if (save_sigregs(regs, &frame->sregs))
return -EFAULT;
/* Place signal number on stack to allow backtrace from handler. */
if (__put_user(regs->gprs[2], (int __user *) &frame->signo))
/* Place signal number on stack to allow backtrace from handler. */
if (__put_user(regs->gprs[2], (int __user *)&frame->signo))
return -EFAULT;
/* Create _sigregs_ext on the signal stack */
/* Create _sigregs_ext on the signal stack. */
if (save_sigregs_ext(regs, &frame->sregs_ext))
return -EFAULT;
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
/*
* Set up to return from userspace. If provided, use a stub
* already in userspace.
*/
if (ka->sa.sa_flags & SA_RESTORER)
restorer = (unsigned long) ka->sa.sa_restorer;
restorer = (unsigned long)ka->sa.sa_restorer;
else
restorer = VDSO_SYMBOL(current, sigreturn);
/* Set up registers for signal handler */
/* Set up registers for signal handler. */
regs->gprs[14] = restorer;
regs->gprs[15] = (unsigned long) frame;
regs->gprs[15] = (unsigned long)frame;
/* Force default amode and default user address space control. */
regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA |
(PSW_USER_BITS & PSW_MASK_ASC) |
(regs->psw.mask & ~PSW_MASK_ASC);
regs->psw.addr = (unsigned long) ka->sa.sa_handler;
regs->psw.mask &= ~PSW_MASK_ASC;
regs->psw.mask |= (PSW_USER_BITS & PSW_MASK_ASC) | PSW_MASK_EA | PSW_MASK_BA;
regs->psw.addr = (unsigned long)ka->sa.sa_handler;
regs->gprs[2] = sig;
regs->gprs[3] = (unsigned long) &frame->sc;
/* We forgot to include these in the sigcontext.
To avoid breaking binary compatibility, they are passed as args. */
if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL ||
sig == SIGTRAP || sig == SIGFPE) {
/* set extra registers only for synchronous signals */
regs->gprs[3] = (unsigned long)&frame->sc;
/* Missing in sigcontext. Keep binary compatibility by passing as args. */
if (sig == SIGSEGV || sig == SIGBUS || sig == SIGILL || sig == SIGTRAP || sig == SIGFPE) {
/* Set extra registers only for synchronous signals. */
regs->gprs[4] = regs->int_code & 127;
regs->gprs[5] = regs->int_parm_long;
regs->gprs[6] = current->thread.last_break;
@ -352,8 +326,7 @@ static int setup_frame(int sig, struct k_sigaction *ka,
return 0;
}
static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
struct pt_regs *regs)
static int setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe __user *frame;
unsigned long uc_flags, restorer;
@ -366,27 +339,24 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
uc_flags |= UC_VXRS;
}
frame = get_sigframe(&ksig->ka, regs, frame_size);
if (frame == (void __user *) -1UL)
if (frame == (void __user *)-1UL)
return -EFAULT;
/* Set up backchain. */
if (__put_user(regs->gprs[15], (addr_t __user *) frame))
if (__put_user(regs->gprs[15], (addr_t __user *)frame))
return -EFAULT;
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
/*
* Set up to return from userspace. If provided, use a stub
* already in userspace.
*/
if (ksig->ka.sa.sa_flags & SA_RESTORER)
restorer = (unsigned long) ksig->ka.sa.sa_restorer;
restorer = (unsigned long)ksig->ka.sa.sa_restorer;
else
restorer = VDSO_SYMBOL(current, rt_sigreturn);
/* Create siginfo on the signal stack */
/* Create siginfo on the signal stack. */
if (copy_siginfo_to_user(&frame->info, &ksig->info))
return -EFAULT;
/* Store registers needed to create the signal frame */
/* Store registers needed to create the signal frame. */
store_sigregs();
/* Create ucontext on the signal stack. */
if (__put_user(uc_flags, &frame->uc.uc_flags) ||
__put_user(NULL, &frame->uc.uc_link) ||
@ -395,34 +365,29 @@ static int setup_rt_frame(struct ksignal *ksig, sigset_t *set,
__copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set)) ||
save_sigregs_ext(regs, &frame->uc.uc_mcontext_ext))
return -EFAULT;
/* Set up registers for signal handler */
/* Set up registers for signal handler. */
regs->gprs[14] = restorer;
regs->gprs[15] = (unsigned long) frame;
regs->gprs[15] = (unsigned long)frame;
/* Force default amode and default user address space control. */
regs->psw.mask = PSW_MASK_EA | PSW_MASK_BA |
(PSW_USER_BITS & PSW_MASK_ASC) |
(regs->psw.mask & ~PSW_MASK_ASC);
regs->psw.addr = (unsigned long) ksig->ka.sa.sa_handler;
regs->psw.mask &= ~PSW_MASK_ASC;
regs->psw.mask |= (PSW_USER_BITS & PSW_MASK_ASC) | PSW_MASK_EA | PSW_MASK_BA;
regs->psw.addr = (unsigned long)ksig->ka.sa.sa_handler;
regs->gprs[2] = ksig->sig;
regs->gprs[3] = (unsigned long) &frame->info;
regs->gprs[4] = (unsigned long) &frame->uc;
regs->gprs[3] = (unsigned long)&frame->info;
regs->gprs[4] = (unsigned long)&frame->uc;
regs->gprs[5] = current->thread.last_break;
return 0;
}
static void handle_signal(struct ksignal *ksig, sigset_t *oldset,
struct pt_regs *regs)
static void handle_signal(struct ksignal *ksig, sigset_t *oldset, struct pt_regs *regs)
{
int ret;
/* Set up the stack frame */
/* Set up the stack frame. */
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
ret = setup_rt_frame(ksig, oldset, regs);
else
ret = setup_frame(ksig->sig, &ksig->ka, oldset, regs);
signal_setup_done(ret, ksig, test_thread_flag(TIF_SINGLE_STEP));
}
@ -435,22 +400,19 @@ static void handle_signal(struct ksignal *ksig, sigset_t *oldset,
* the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that.
*/
void arch_do_signal_or_restart(struct pt_regs *regs)
{
struct ksignal ksig;
sigset_t *oldset = sigmask_to_save();
struct ksignal ksig;
/*
* Get signal to deliver. When running under ptrace, at this point
* the debugger may change all our registers, including the system
* call information.
*/
current->thread.system_call =
test_pt_regs_flag(regs, PIF_SYSCALL) ? regs->int_code : 0;
current->thread.system_call = test_pt_regs_flag(regs, PIF_SYSCALL) ? regs->int_code : 0;
if (get_signal(&ksig)) {
/* Whee! Actually deliver the signal. */
/* Whee! Actually deliver the signal. */
if (current->thread.system_call) {
regs->int_code = current->thread.system_call;
/* Check for system call restarting. */
@ -467,27 +429,28 @@ void arch_do_signal_or_restart(struct pt_regs *regs)
fallthrough;
case -ERESTARTNOINTR:
regs->gprs[2] = regs->orig_gpr2;
regs->psw.addr =
__rewind_psw(regs->psw,
regs->int_code >> 16);
regs->psw.addr = __rewind_psw(regs->psw, regs->int_code >> 16);
break;
}
}
/* No longer in a system call */
/* No longer in a system call. */
clear_pt_regs_flag(regs, PIF_SYSCALL);
if (!(current->restart_block.arch_data & 1)) {
regs->psw.addr = current->restart_block.arch_data;
current->restart_block.arch_data = 1;
regs->gprs[2] = -EINTR;
}
rseq_signal_deliver(&ksig, regs);
handle_signal(&ksig, oldset, regs);
return;
}
/* No handlers present - check for system call restart */
/* No handlers present - check for system call restart. */
clear_pt_regs_flag(regs, PIF_SYSCALL);
if (current->thread.system_call) {
regs->int_code = current->thread.system_call;
switch (regs->gprs[2]) {
case -ERESTART_RESTARTBLOCK:
/* Restart with sys_restart_syscall */
/* Restart with sys_restart_syscall. */
regs->gprs[2] = regs->orig_gpr2;
current->restart_block.arch_data = regs->psw.addr;
regs->psw.addr = VDSO_SYMBOL(current, restart_syscall);
@ -504,9 +467,6 @@ void arch_do_signal_or_restart(struct pt_regs *regs)
break;
}
}
/*
* If there's no signal to deliver, we just put the saved sigmask back.
*/
/* If there's no signal to deliver, put the saved sigmask back. */
restore_saved_sigmask();
}

View File

@ -48,7 +48,7 @@
#include <asm/setup.h>
#include <asm/irq.h>
#include <asm/tlbflush.h>
#include <asm/vtimer.h>
#include <asm/vtime.h>
#include <asm/abs_lowcore.h>
#include <asm/sclp.h>
#include <asm/debug.h>

View File

@ -19,9 +19,9 @@
* affects a few functions that are not performance-relevant.
*/
.macro BR_EX_AMODE31_r14
exrl 0,0f
exrl 0,555f
j .
0: br %r14
555: br %r14
.endm
/*
@ -33,15 +33,13 @@ SYM_TYPED_FUNC_START(_diag14_amode31)
lgr %r3,%r4
lhi %r5,-EIO
sam31
diag %r1,%r2,0x14
.Ldiag14_ex:
0: diag %r1,%r2,0x14
ipm %r5
srl %r5,28
.Ldiag14_fault:
sam64
1: sam64
lgfr %r2,%r5
BR_EX_AMODE31_r14
EX_TABLE_AMODE31(.Ldiag14_ex, .Ldiag14_fault)
EX_TABLE_INSN_AMODE31(0b, 1b)
SYM_FUNC_END(_diag14_amode31)
/*
@ -51,15 +49,13 @@ SYM_TYPED_FUNC_START(_diag210_amode31)
lgr %r1,%r2
lhi %r2,-1
sam31
diag %r1,%r0,0x210
.Ldiag210_ex:
0: diag %r1,%r0,0x210
ipm %r2
srl %r2,28
.Ldiag210_fault:
sam64
1: sam64
lgfr %r2,%r2
BR_EX_AMODE31_r14
EX_TABLE_AMODE31(.Ldiag210_ex, .Ldiag210_fault)
EX_TABLE_INSN_AMODE31(0b, 1b)
SYM_FUNC_END(_diag210_amode31)
/*
@ -68,12 +64,11 @@ SYM_FUNC_END(_diag210_amode31)
SYM_TYPED_FUNC_START(_diag8c_amode31)
llgf %r3,0(%r3)
sam31
diag %r2,%r4,0x8c
.Ldiag8c_ex:
sam64
0: diag %r2,%r4,0x8c
1: sam64
lgfr %r2,%r3
BR_EX_AMODE31_r14
EX_TABLE_AMODE31(.Ldiag8c_ex, .Ldiag8c_ex)
EX_TABLE_INSN_AMODE31(0b, 1b)
SYM_FUNC_END(_diag8c_amode31)
/*
* int _diag26c_amode31(void *req, void *resp, enum diag26c_sc subcode)
@ -81,12 +76,11 @@ SYM_FUNC_END(_diag8c_amode31)
SYM_TYPED_FUNC_START(_diag26c_amode31)
lghi %r5,-EOPNOTSUPP
sam31
diag %r2,%r4,0x26c
.Ldiag26c_ex:
sam64
0: diag %r2,%r4,0x26c
1: sam64
lgfr %r2,%r5
BR_EX_AMODE31_r14
EX_TABLE_AMODE31(.Ldiag26c_ex, .Ldiag26c_ex)
EX_TABLE_INSN_AMODE31(0b, 1b)
SYM_FUNC_END(_diag26c_amode31)
/*

View File

@ -48,9 +48,9 @@
#include <asm/vdso.h>
#include <asm/irq.h>
#include <asm/irq_regs.h>
#include <asm/vtimer.h>
#include <asm/stp.h>
#include <asm/cio.h>
#include <asm/vtime.h>
#include "entry.h"
union tod_clock __bootdata_preserved(tod_clock_base);

View File

@ -26,6 +26,7 @@
#include <linux/entry-common.h>
#include <linux/kmsan.h>
#include <linux/bug.h>
#include <asm/pgmcheck-defs.h>
#include <asm/entry-percpu.h>
#include <asm/asm-extable.h>
#include <asm/irqflags.h>
@ -41,16 +42,21 @@ struct pgm_stat {
static DEFINE_PER_CPU_SHARED_ALIGNED(struct pgm_stat, pgm_stat);
static inline void __user *get_trap_ip(struct pt_regs *regs)
unsigned long get_trap_ip(struct pt_regs *regs)
{
unsigned long address;
static const unsigned long nullified[2] = { PGM_CHECKS_NULLIFIED };
psw_t psw = regs->psw;
if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED))
return psw.addr;
if (regs->int_code & 0x200)
address = current->thread.trap_tdb.data[3];
else
address = regs->psw.addr;
return (void __user *)(address - (regs->int_code >> 16));
psw.addr = current->thread.trap_tdb.data[3];
if (test_bit(regs->int_code & PGM_INT_CODE_MASK, nullified))
return psw.addr;
psw.addr = __rewind_psw(psw, regs->int_code >> 16);
return psw.addr;
}
NOKPROBE_SYMBOL(get_trap_ip);
#ifdef CONFIG_GENERIC_BUG
int is_valid_bugaddr(unsigned long addr)
@ -62,7 +68,7 @@ int is_valid_bugaddr(unsigned long addr)
void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
{
if (user_mode(regs)) {
force_sig_fault(si_signo, si_code, get_trap_ip(regs));
force_sig_fault(si_signo, si_code, (void __user *)get_trap_ip(regs));
report_user_fault(regs, si_signo, 0);
} else {
if (!fixup_exception(regs))
@ -148,12 +154,12 @@ static void translation_specification_exception(struct pt_regs *regs)
static void illegal_op(struct pt_regs *regs)
{
int is_uprobe_insn = 0;
u16 __user *location;
int signal = 0;
u16 opcode;
location = get_trap_ip(regs);
if (user_mode(regs)) {
u16 __user *location = (void __user *)get_trap_ip(regs);
if (get_user(opcode, location))
return;
if (opcode == S390_BREAKPOINT_U16) {
@ -267,6 +273,7 @@ static void monitor_event_exception(struct pt_regs *regs)
return;
if (regs->monitor_code == MONCODE_BUG_ARG) {
regs->psw.addr = regs->gprs[14];
set_pt_regs_flag(regs, PIF_WARN_TRAP_SKIPPED);
btt = report_bug_entry((struct bug_entry *)regs->gprs[2], regs);
} else {
btt = report_bug(regs->psw.addr - (regs->int_code >> 16), regs);
@ -283,6 +290,12 @@ static void monitor_event_exception(struct pt_regs *regs)
}
}
/*
* Content of the faulting CPU's lowcore, copied by stack_invalid().
*/
struct lowcore stack_invalid_lowcore;
int stack_invalid_lowcore_used;
void kernel_stack_invalid(struct pt_regs *regs)
{
/*
@ -305,10 +318,10 @@ static void __init test_monitor_call(void)
if (!IS_ENABLED(CONFIG_BUG))
return;
asm_inline volatile(
" mc %[monc](%%r0),0\n"
"0: lhi %[val],0\n"
"0: mc %[monc](%%r0),0\n"
" lhi %[val],0\n"
"1:\n"
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: [val] "+d" (val)
: [monc] "i" (MONCODE_BUG));
if (!val)
@ -338,7 +351,6 @@ static void (*pgm_check_table[128])(struct pt_regs *regs);
void noinstr __do_pgm_check(struct pt_regs *regs, unsigned long flags)
{
struct lowcore *lc = get_lowcore();
bool percpu_needs_fixup;
irqentry_state_t state;
struct pgm_stat *stat;
unsigned int trapnr;
@ -348,6 +360,7 @@ void noinstr __do_pgm_check(struct pt_regs *regs, unsigned long flags)
regs->int_code = lc->pgm_int_code;
regs->int_parm_long = teid.val;
regs->monitor_code = lc->monitor_code;
set_pt_regs_flag(regs, PIF_PGM_CHECK);
trapnr = regs->int_code & PGM_INT_CODE_MASK;
stat = this_cpu_ptr(&pgm_stat);
@ -400,9 +413,8 @@ void noinstr __do_pgm_check(struct pt_regs *regs, unsigned long flags)
pgm_check_table[trapnr](regs);
out:
local_irq_disable();
percpu_needs_fixup = percpu_code_check(regs);
irqentry_exit(regs, state);
percpu_exit(regs, percpu_needs_fixup);
percpu_exit(regs);
}
static int pgm_check_stat_show(struct seq_file *p, void *v)

View File

@ -241,7 +241,7 @@ static void sim_stor_event(struct pt_regs *regs, void *addr, int len)
return;
if (!(current->thread.per_user.control & PER_EVENT_STORE))
return;
if ((void *)current->thread.per_user.start > (addr + len))
if ((void *)current->thread.per_user.start > (addr + len - 1))
return;
if ((void *)current->thread.per_user.end < addr)
return;

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Virtual cpu timer based timer functions.
* Virtual CPU time accounting
*
* Copyright IBM Corp. 2004, 2012
* Author(s): Jan Glauber <jan.glauber@de.ibm.com>
@ -14,7 +14,6 @@
#include <linux/time.h>
#include <asm/alternative.h>
#include <asm/cputime.h>
#include <asm/vtimer.h>
#include <asm/vtime.h>
#include <asm/cpu_mf.h>
#include <asm/idle.h>
@ -22,19 +21,14 @@
#include "entry.h"
static void virt_timer_expire(void);
static LIST_HEAD(virt_timer_list);
static DEFINE_SPINLOCK(virt_timer_lock);
static atomic64_t virt_timer_current;
static atomic64_t virt_timer_elapsed;
#define CPU_TIMER_MAX 0x7fffffffffffffffUL
DEFINE_PER_CPU(u64, mt_cycles[8]);
static DEFINE_PER_CPU(u64, mt_scaling_mult) = { 1 };
static DEFINE_PER_CPU(u64, mt_scaling_div) = { 1 };
static DEFINE_PER_CPU(unsigned long, mt_scaling_jiffies);
static inline void set_vtimer(u64 expires)
static inline void cpu_timer_init(u64 value)
{
struct lowcore *lc = get_lowcore();
u64 timer;
@ -42,18 +36,9 @@ static inline void set_vtimer(u64 expires)
asm volatile(
" stpt %0\n" /* Store current cpu timer value */
" spt %1" /* Set new value imm. afterwards */
: "=Q" (timer) : "Q" (expires));
: "=Q" (timer) : "Q" (value));
lc->system_timer += lc->last_update_timer - timer;
lc->last_update_timer = expires;
}
static inline int virt_timer_forward(u64 elapsed)
{
lockdep_assert_irqs_disabled();
if (list_empty(&virt_timer_list))
return 0;
elapsed = atomic64_add_return(elapsed, &virt_timer_elapsed);
return elapsed >= atomic64_read(&virt_timer_current);
lc->last_update_timer = value;
}
static void update_mt_scaling(void)
@ -125,7 +110,7 @@ static inline void vtime_reset_last_update(struct lowcore *lc)
* Update process times based on virtual cpu times stored by entry.S
* to the lowcore fields user_timer, system_timer & steal_clock.
*/
static int do_account_vtime(struct task_struct *tsk)
static void do_account_vtime(struct task_struct *tsk)
{
u64 timer, clock, user, guest, system, hardirq, softirq;
struct lowcore *lc = get_lowcore();
@ -172,8 +157,6 @@ static int do_account_vtime(struct task_struct *tsk)
account_system_index_scaled(tsk, hardirq, CPUTIME_IRQ);
if (softirq)
account_system_index_scaled(tsk, softirq, CPUTIME_SOFTIRQ);
return virt_timer_forward(user + guest + system + hardirq + softirq);
}
void vtime_task_switch(struct task_struct *prev)
@ -203,8 +186,7 @@ void vtime_flush(struct task_struct *tsk)
struct lowcore *lc = get_lowcore();
u64 steal, avg_steal;
if (do_account_vtime(tsk))
virt_timer_expire();
do_account_vtime(tsk);
steal = lc->steal_timer;
avg_steal = lc->avg_steal_timer;
@ -247,187 +229,9 @@ void vtime_account_hardirq(struct task_struct *tsk)
get_lowcore()->hardirq_timer += vtime_delta();
}
/*
* Sorted add to a list. List is linear searched until first bigger
* element is found.
*/
static void list_add_sorted(struct vtimer_list *timer, struct list_head *head)
{
struct vtimer_list *tmp;
list_for_each_entry(tmp, head, entry) {
if (tmp->expires > timer->expires) {
list_add_tail(&timer->entry, &tmp->entry);
return;
}
}
list_add_tail(&timer->entry, head);
}
/*
* Handler for expired virtual CPU timer.
*/
static void virt_timer_expire(void)
{
struct vtimer_list *timer, *tmp;
unsigned long elapsed;
LIST_HEAD(cb_list);
/* walk timer list, fire all expired timers */
spin_lock(&virt_timer_lock);
elapsed = atomic64_read(&virt_timer_elapsed);
list_for_each_entry_safe(timer, tmp, &virt_timer_list, entry) {
if (timer->expires < elapsed)
/* move expired timer to the callback queue */
list_move_tail(&timer->entry, &cb_list);
else
timer->expires -= elapsed;
}
if (!list_empty(&virt_timer_list)) {
timer = list_first_entry(&virt_timer_list,
struct vtimer_list, entry);
atomic64_set(&virt_timer_current, timer->expires);
}
atomic64_sub(elapsed, &virt_timer_elapsed);
spin_unlock(&virt_timer_lock);
/* Do callbacks and recharge periodic timers */
list_for_each_entry_safe(timer, tmp, &cb_list, entry) {
list_del_init(&timer->entry);
timer->function(timer->data);
if (timer->interval) {
/* Recharge interval timer */
timer->expires = timer->interval +
atomic64_read(&virt_timer_elapsed);
spin_lock(&virt_timer_lock);
list_add_sorted(timer, &virt_timer_list);
spin_unlock(&virt_timer_lock);
}
}
}
void init_virt_timer(struct vtimer_list *timer)
{
timer->function = NULL;
INIT_LIST_HEAD(&timer->entry);
}
EXPORT_SYMBOL(init_virt_timer);
static inline int vtimer_pending(struct vtimer_list *timer)
{
return !list_empty(&timer->entry);
}
static void internal_add_vtimer(struct vtimer_list *timer)
{
if (list_empty(&virt_timer_list)) {
/* First timer, just program it. */
atomic64_set(&virt_timer_current, timer->expires);
atomic64_set(&virt_timer_elapsed, 0);
list_add(&timer->entry, &virt_timer_list);
} else {
/* Update timer against current base. */
timer->expires += atomic64_read(&virt_timer_elapsed);
if (likely((s64) timer->expires <
(s64) atomic64_read(&virt_timer_current)))
/* The new timer expires before the current timer. */
atomic64_set(&virt_timer_current, timer->expires);
/* Insert new timer into the list. */
list_add_sorted(timer, &virt_timer_list);
}
}
static void __add_vtimer(struct vtimer_list *timer, int periodic)
{
unsigned long flags;
timer->interval = periodic ? timer->expires : 0;
spin_lock_irqsave(&virt_timer_lock, flags);
internal_add_vtimer(timer);
spin_unlock_irqrestore(&virt_timer_lock, flags);
}
/*
* add_virt_timer - add a oneshot virtual CPU timer
*/
void add_virt_timer(struct vtimer_list *timer)
{
__add_vtimer(timer, 0);
}
EXPORT_SYMBOL(add_virt_timer);
/*
* add_virt_timer_int - add an interval virtual CPU timer
*/
void add_virt_timer_periodic(struct vtimer_list *timer)
{
__add_vtimer(timer, 1);
}
EXPORT_SYMBOL(add_virt_timer_periodic);
static int __mod_vtimer(struct vtimer_list *timer, u64 expires, int periodic)
{
unsigned long flags;
int rc;
BUG_ON(!timer->function);
if (timer->expires == expires && vtimer_pending(timer))
return 1;
spin_lock_irqsave(&virt_timer_lock, flags);
rc = vtimer_pending(timer);
if (rc)
list_del_init(&timer->entry);
timer->interval = periodic ? expires : 0;
timer->expires = expires;
internal_add_vtimer(timer);
spin_unlock_irqrestore(&virt_timer_lock, flags);
return rc;
}
/*
* returns whether it has modified a pending timer (1) or not (0)
*/
int mod_virt_timer(struct vtimer_list *timer, u64 expires)
{
return __mod_vtimer(timer, expires, 0);
}
EXPORT_SYMBOL(mod_virt_timer);
/*
* returns whether it has modified a pending timer (1) or not (0)
*/
int mod_virt_timer_periodic(struct vtimer_list *timer, u64 expires)
{
return __mod_vtimer(timer, expires, 1);
}
EXPORT_SYMBOL(mod_virt_timer_periodic);
/*
* Delete a virtual timer.
*
* returns whether the deleted timer was pending (1) or not (0)
*/
int del_virt_timer(struct vtimer_list *timer)
{
unsigned long flags;
if (!vtimer_pending(timer))
return 0;
spin_lock_irqsave(&virt_timer_lock, flags);
list_del_init(&timer->entry);
spin_unlock_irqrestore(&virt_timer_lock, flags);
return 1;
}
EXPORT_SYMBOL(del_virt_timer);
/*
* Start the virtual CPU timer on the current CPU.
*/
void vtime_init(void)
{
/* set initial cpu timer */
set_vtimer(VTIMER_MAX_SLICE);
cpu_timer_init(CPU_TIMER_MAX);
/* Setup initial MT scaling values */
if (smp_cpu_mtid) {
__this_cpu_write(mt_scaling_jiffies, jiffies);

View File

@ -838,12 +838,11 @@ static int mvcos_key(void *to, const void *from, unsigned long size, u8 dst_key,
int exception = PGM_PROTECTION;
asm_inline volatile(
" lr %%r0,%[spec]\n"
"0: mvcos %[to],%[from],%[size]\n"
"1: lhi %[exc],0\n"
"2:\n"
EX_TABLE(0b, 2b)
EX_TABLE(1b, 2b)
" lr %%r0,%[spec]\n"
"0: mvcos %[to],%[from],%[size]\n"
" lhi %[exc],0\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
: [size] "+d" (size), [to] "=Q" (*(char *)to), [exc] "+d" (exception)
: [spec] "d" (spec.val), [from] "Q" (*(const char *)from)
: "memory", "cc", "0");

View File

@ -77,6 +77,10 @@ int probe_get_fixup_type(u16 *insn)
if ((insn[0] & 0xff) == 0xb2)
fixup = FIXUP_NOT_REQUIRED;
break;
case 0xb9: /* bctgr */
if ((insn[0] & 0xff) == 0x46)
fixup = FIXUP_BRANCH_NOT_TAKEN;
break;
case 0xa7: /* bras */
if ((insn[0] & 0x0f) == 0x05)
fixup |= FIXUP_RETURN_REGISTER;
@ -85,6 +89,14 @@ int probe_get_fixup_type(u16 *insn)
if ((insn[0] & 0x0f) == 0x05) /* brasl */
fixup |= FIXUP_RETURN_REGISTER;
break;
case 0xe3:
switch (insn[2] & 0xff) {
case 0x46: /* bctg */
case 0x47: /* bic */
fixup = FIXUP_BRANCH_NOT_TAKEN;
break;
}
break;
case 0xeb:
switch (insn[2] & 0xff) {
case 0x44: /* bxhg */
@ -93,18 +105,14 @@ int probe_get_fixup_type(u16 *insn)
break;
}
break;
case 0xe3: /* bctg */
if ((insn[2] & 0xff) == 0x46)
fixup = FIXUP_BRANCH_NOT_TAKEN;
break;
case 0xec:
switch (insn[2] & 0xff) {
case 0xe4: /* cgrb */
case 0xe5: /* clgrb */
case 0xe6: /* cgrb */
case 0xf6: /* crb */
case 0xf7: /* clrb */
case 0xfc: /* cgib */
case 0xfd: /* cglib */
case 0xfd: /* clgib */
case 0xfe: /* cib */
case 0xff: /* clib */
fixup = FIXUP_BRANCH_NOT_TAKEN;

View File

@ -318,6 +318,7 @@ void arch_read_lock_wait(arch_rwlock_t *rw)
/* Remove this reader again to allow recursive read locking */
__atomic_add_const(-1, &rw->cnts);
trace_contention_begin(rw, LCB_F_SPIN | LCB_F_READ);
/* Put the reader into the wait queue */
arch_spin_lock(&rw->wait);
/* Now add this reader to the count value again */
@ -326,6 +327,7 @@ void arch_read_lock_wait(arch_rwlock_t *rw)
while (READ_ONCE(rw->cnts) & 0x10000)
barrier();
arch_spin_unlock(&rw->wait);
trace_contention_end(rw, 0);
}
EXPORT_SYMBOL(arch_read_lock_wait);
@ -333,6 +335,7 @@ void arch_write_lock_wait(arch_rwlock_t *rw)
{
int old;
trace_contention_begin(rw, LCB_F_SPIN | LCB_F_WRITE);
/* Add this CPU to the write waiters */
__atomic_add(0x20000, &rw->cnts);
@ -349,6 +352,7 @@ void arch_write_lock_wait(arch_rwlock_t *rw)
}
arch_spin_unlock(&rw->wait);
trace_contention_end(rw, 0);
}
EXPORT_SYMBOL(arch_write_lock_wait);

View File

@ -49,24 +49,22 @@ static nokprobe_inline int __cmpxchg_key_small(void *address, unsigned int *uval
"20: spka 0(%[key])\n"
" llill %[count],%[max_loops]\n"
"0: l %[prev],%[address]\n"
"1: nr %[prev],%[mask]\n"
" nr %[prev],%[mask]\n"
" xilf %[mask],0xffffffff\n"
" or %[new],%[prev]\n"
" or %[prev],%[tmp]\n"
"2: lr %[tmp],%[prev]\n"
"3: cs %[prev],%[new],%[address]\n"
"4: jnl 5f\n"
"1: lr %[tmp],%[prev]\n"
"2: cs %[prev],%[new],%[address]\n"
" jnl 3f\n"
" xr %[tmp],%[prev]\n"
" xr %[new],%[tmp]\n"
" nr %[tmp],%[mask]\n"
" jnz 5f\n"
" brct %[count],2b\n"
"5: spka %[default_key]\n"
" jnz 3f\n"
" brct %[count],1b\n"
"3: spka %[default_key]\n"
"21:\n"
EX_TABLE_UA_LOAD_REG(0b, 5b, %[rc], %[prev])
EX_TABLE_UA_LOAD_REG(1b, 5b, %[rc], %[prev])
EX_TABLE_UA_LOAD_REG(3b, 5b, %[rc], %[prev])
EX_TABLE_UA_LOAD_REG(4b, 5b, %[rc], %[prev])
EX_TABLE_INSN_UA_LOAD_REG(0b, 3b, %[rc], %[prev])
EX_TABLE_INSN_UA_LOAD_REG(2b, 3b, %[rc], %[prev])
SKEY_REGION(20b, 21b)
: [rc] "+&d" (rc),
[prev] "=&d" (prev),
@ -133,8 +131,7 @@ int __kprobes __cmpxchg_key4(void *address, unsigned int *uval, unsigned int old
"0: cs %[prev],%[new],%[address]\n"
"1: spka %[default_key]\n"
"21:\n"
EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev])
EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev])
EX_TABLE_INSN_UA_LOAD_REG(0b, 1b, %[rc], %[prev])
SKEY_REGION(20b, 21b)
: [rc] "+&d" (rc),
[prev] "+&d" (prev),
@ -160,8 +157,7 @@ int __kprobes __cmpxchg_key8(void *address, unsigned long *uval, unsigned long o
"0: csg %[prev],%[new],%[address]\n"
"1: spka %[default_key]\n"
"21:\n"
EX_TABLE_UA_LOAD_REG(0b, 1b, %[rc], %[prev])
EX_TABLE_UA_LOAD_REG(1b, 1b, %[rc], %[prev])
EX_TABLE_INSN_UA_LOAD_REG(0b, 1b, %[rc], %[prev])
SKEY_REGION(20b, 21b)
: [rc] "+&d" (rc),
[prev] "+&d" (prev),
@ -187,8 +183,7 @@ int __kprobes __cmpxchg_key16(void *address, __uint128_t *uval, __uint128_t old,
"0: cdsg %[prev],%[new],%[address]\n"
"1: spka %[default_key]\n"
"21:\n"
EX_TABLE_UA_LOAD_REGPAIR(0b, 1b, %[rc], %[prev])
EX_TABLE_UA_LOAD_REGPAIR(1b, 1b, %[rc], %[prev])
EX_TABLE_INSN_UA_LOAD_REGPAIR(0b, 1b, %[rc], %[prev])
SKEY_REGION(20b, 21b)
: [rc] "+&d" (rc),
[prev] "+&d" (prev),

View File

@ -106,9 +106,9 @@ static bool ex_handler_ua_mvcos(const struct exception_table_entry *ex,
regs->psw.addr = extable_fixup(ex);
insn = (struct insn_ssf *)regs->psw.addr;
if (from)
uaddr = regs->gprs[insn->b2] + insn->d2;
uaddr = (insn->b2 ? regs->gprs[insn->b2] : 0) + insn->d2;
else
uaddr = regs->gprs[insn->b1] + insn->d1;
uaddr = (insn->b1 ? regs->gprs[insn->b1] : 0) + insn->d1;
remainder = PAGE_SIZE - (uaddr & (PAGE_SIZE - 1));
if (regs->gprs[insn->r3] <= remainder)
remainder = 0;
@ -120,7 +120,7 @@ bool fixup_exception(struct pt_regs *regs)
{
const struct exception_table_entry *ex;
ex = s390_search_extables(instruction_pointer(regs));
ex = s390_search_extables(get_trap_ip(regs));
if (!ex)
return false;
switch (ex->type) {

View File

@ -58,9 +58,9 @@ int __pfault_init(void)
return rc;
diag_stat_inc(DIAG_STAT_X258);
asm_inline volatile(
" diag %[refbk],%[rc],0x258\n"
"0: nopr %%r7\n"
EX_TABLE(0b, 0b)
"0: diag %[refbk],%[rc],0x258\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
: [rc] "+d" (rc)
: [refbk] "a" (virt_to_phys(&pfault_init_refbk)), "m" (pfault_init_refbk)
: "cc");
@ -80,9 +80,9 @@ void __pfault_fini(void)
return;
diag_stat_inc(DIAG_STAT_X258);
asm_inline volatile(
" diag %[refbk],0,0x258\n"
"0: nopr %%r7\n"
EX_TABLE(0b, 0b)
"0: diag %[refbk],0,0x258\n"
"1:\n"
EX_TABLE_INSN(0b, 1b)
:
: [refbk] "a" (virt_to_phys(&pfault_fini_refbk)), "m" (pfault_fini_refbk)
: "cc");

View File

@ -107,7 +107,7 @@ static inline pte_t ptep_flush_lazy(struct mm_struct *mm,
atomic_inc(&mm->context.flush_count);
if (cpumask_equal(&mm->context.cpu_attach_mask,
cpumask_of(smp_processor_id()))) {
set_pte(ptep, set_pte_bit(*ptep, __pgprot(_PAGE_INVALID)));
set_pte(ptep, set_pte_bit(old, __pgprot(_PAGE_INVALID)));
mm->context.flush_mm = 1;
} else
ptep_ipte_global(mm, addr, ptep, nodat);
@ -227,7 +227,7 @@ static inline pmd_t pmdp_flush_lazy(struct mm_struct *mm,
atomic_inc(&mm->context.flush_count);
if (cpumask_equal(&mm->context.cpu_attach_mask,
cpumask_of(smp_processor_id()))) {
set_pmd(pmdp, set_pmd_bit(*pmdp, __pgprot(_SEGMENT_ENTRY_INVALID)));
set_pmd(pmdp, set_pmd_bit(old, __pgprot(_SEGMENT_ENTRY_INVALID)));
mm->context.flush_mm = 1;
} else {
pmdp_idte_global(mm, addr, pmdp);

View File

@ -723,7 +723,6 @@ bool ex_handler_bpf(const struct exception_table_entry *x, struct pt_regs *regs)
*/
struct bpf_jit_probe {
int prg; /* JITed instruction offset */
int nop_prg; /* JITed nop offset */
int reg; /* Register to clear on exception */
int arena_reg; /* Register to use for arena addressing */
};
@ -731,29 +730,10 @@ struct bpf_jit_probe {
static void bpf_jit_probe_init(struct bpf_jit_probe *probe)
{
probe->prg = -1;
probe->nop_prg = -1;
probe->reg = -1;
probe->arena_reg = REG_0;
}
/*
* Handlers of certain exceptions leave psw.addr pointing to the instruction
* directly after the failing one. Therefore, create two exception table
* entries and also add a nop in case two probing instructions come directly
* after each other.
*/
static void bpf_jit_probe_emit_nop(struct bpf_jit *jit,
struct bpf_jit_probe *probe)
{
if (probe->prg == -1 || probe->nop_prg != -1)
/* The probe is not armed or nop is already emitted. */
return;
probe->nop_prg = jit->prg;
/* bcr 0,%0 */
_EMIT2(0x0700);
}
static void bpf_jit_probe_load_pre(struct bpf_jit *jit, struct bpf_insn *insn,
struct bpf_jit_probe *probe)
{
@ -815,47 +795,36 @@ static int bpf_jit_probe_post(struct bpf_jit *jit, struct bpf_prog *fp,
struct bpf_jit_probe *probe)
{
struct exception_table_entry *ex;
int i, prg;
s64 delta;
u8 *insn;
if (probe->prg == -1)
/* The probe is not armed. */
return 0;
bpf_jit_probe_emit_nop(jit, probe);
if (!fp->aux->extable)
/* Do nothing during early JIT passes. */
return 0;
insn = jit->prg_buf + probe->prg;
if (WARN_ON_ONCE(probe->prg + insn_length(*insn) != probe->nop_prg))
/* JIT bug - gap between probe and nop instructions. */
if (WARN_ON_ONCE(jit->excnt >= fp->aux->num_exentries))
/* Verifier bug - not enough entries. */
return -1;
for (i = 0; i < 2; i++) {
if (WARN_ON_ONCE(jit->excnt >= fp->aux->num_exentries))
/* Verifier bug - not enough entries. */
return -1;
ex = &fp->aux->extable[jit->excnt];
/* Add extable entries for probe and nop instructions. */
prg = i == 0 ? probe->prg : probe->nop_prg;
delta = jit->prg_buf + prg - (u8 *)&ex->insn;
if (WARN_ON_ONCE(delta < INT_MIN || delta > INT_MAX))
/* JIT bug - code and extable must be close. */
return -1;
ex->insn = delta;
/*
* Land on the current instruction. Note that the extable
* infrastructure ignores the fixup field; it is handled by
* ex_handler_bpf().
*/
delta = jit->prg_buf + jit->prg - (u8 *)&ex->fixup;
if (WARN_ON_ONCE(delta < INT_MIN || delta > INT_MAX))
/* JIT bug - landing pad and extable must be close. */
return -1;
ex->fixup = delta;
ex->type = EX_TYPE_BPF;
ex->data = probe->reg;
jit->excnt++;
}
ex = &fp->aux->extable[jit->excnt];
delta = jit->prg_buf + probe->prg - (u8 *)&ex->insn;
if (WARN_ON_ONCE(delta < INT_MIN || delta > INT_MAX))
/* JIT bug - code and extable must be close. */
return -1;
ex->insn = delta;
/*
* Land on the current instruction. Note that the extable
* infrastructure ignores the fixup field; it is handled by
* ex_handler_bpf().
*/
delta = jit->prg_buf + jit->prg - (u8 *)&ex->fixup;
if (WARN_ON_ONCE(delta < INT_MIN || delta > INT_MAX))
/* JIT bug - landing pad and extable must be close. */
return -1;
ex->fixup = delta;
ex->type = EX_TYPE_BPF;
ex->data = probe->reg;
jit->excnt++;
return 0;
}
@ -1705,7 +1674,6 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
EMIT6_DISP_LH(0xe3000000,
is32 ? 0x0058 : 0x0004, REG_W0, REG_0,
load_probe.arena_reg, off);
bpf_jit_probe_emit_nop(jit, &load_probe);
/* Reuse {ly|lg}'s arena_reg for {csy|csg}. */
if (load_probe.prg != -1) {
probe.prg = jit->prg;
@ -1716,7 +1684,6 @@ static noinline int bpf_jit_insn(struct bpf_jit *jit, struct bpf_prog *fp,
/* 0: {csy|csg} %w0,%src,off(%arena) */
EMIT6_DISP_LH(0xeb000000, is32 ? 0x0014 : 0x0030,
REG_W0, src_reg, probe.arena_reg, off);
bpf_jit_probe_emit_nop(jit, &probe);
/* brc 4,0b */
EMIT4_PCREL_RIC(0xa7040000, 4, loop_start);
/* {llgfr|lgr} %src,%w0 */
@ -2364,9 +2331,6 @@ static struct bpf_binary_header *bpf_jit_alloc(struct bpf_jit *jit,
*/
fp->aux->num_exentries += 1;
}
/* We need two entries per insn. */
fp->aux->num_exentries *= 2;
code_size = roundup(jit->size,
__alignof__(struct exception_table_entry));
extable_size = fp->aux->num_exentries *

View File

@ -55,11 +55,11 @@ static inline int clp_get_ilp(unsigned long *ilp)
exception = 1;
asm_inline volatile (
" .insn rrf,0xb9a00000,%[mask],%[cmd],8,0\n"
"0: lhi %[exc],0\n"
"0: .insn rrf,0xb9a00000,%[mask],%[cmd],8,0\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [mask] "=d" (mask), [exc] "+d" (exception)
: [cmd] "a" (1)
: CC_CLOBBER);
@ -78,11 +78,11 @@ static __always_inline int clp_req(void *data, unsigned int lps)
exception = 1;
asm_inline volatile (
" .insn rrf,0xb9a00000,%[ign],%[req],0,%[lps]\n"
"0: lhi %[exc],0\n"
"0: .insn rrf,0xb9a00000,%[ign],%[req],0,%[lps]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [ign] "=d" (ignored), "+m" (*req), [exc] "+d" (exception)
: [req] "a" (req), [lps] "i" (lps)
: CC_CLOBBER);

View File

@ -18,22 +18,49 @@
#include <asm/asm.h>
#define ZPCI_INSN_BUSY_DELAY 1 /* 1 microsecond */
#define ZPCI_ERR_INSN_DATA_SIZE 32
struct zpci_err_insn_data {
u8 insn;
u8 cc;
u8 status;
u8 pad0[5];
union {
struct {
u64 req;
u64 offset;
union {
/* PCILG/PCISTG/MPCIFC */
struct {
u64 offset;
u64 pad1;
};
/* RPCIT */
struct {
u64 iova;
u64 range;
};
};
};
/* PCILGI/PCISTGI */
struct {
u64 addr;
u64 len;
u64 pad2;
};
};
} __packed;
};
static_assert(sizeof(struct zpci_err_insn_data) <= ZPCI_ERR_INSN_DATA_SIZE);
static inline void zpci_err_insn_rpcit(int lvl, u8 insn, u8 cc, u8 status,
u64 req, u64 iova, u64 range)
{
struct zpci_err_insn_data data = {
.insn = insn, .cc = cc, .status = status,
.req = req, .iova = iova, .range = range};
zpci_err_hex_level(lvl, &data, sizeof(data));
}
static inline void zpci_err_insn_req(int lvl, u8 insn, u8 cc, u8 status,
u64 req, u64 offset)
@ -111,26 +138,27 @@ static inline u8 __rpcit(u64 fn, u64 addr, u64 range, u8 *status)
return CC_TRANSFORM(cc);
}
int zpci_refresh_trans(u64 fn, u64 addr, u64 range)
int zpci_refresh_trans(u32 fh, u64 addr, u64 range)
{
u64 req = ZPCI_CREATE_REQ(fh, 0, 0);
bool retried = false;
u8 cc, status;
do {
cc = __rpcit(fn, addr, range, &status);
cc = __rpcit(req, addr, range, &status);
if (cc == 2) {
udelay(ZPCI_INSN_BUSY_DELAY);
if (!retried) {
zpci_err_insn_addr(1, 'R', cc, status, addr, range);
zpci_err_insn_rpcit(1, 'R', cc, status, req, addr, range);
retried = true;
}
}
} while (cc == 2);
if (cc)
zpci_err_insn_addr(0, 'R', cc, status, addr, range);
zpci_err_insn_rpcit(0, 'R', cc, status, req, addr, range);
else if (retried)
zpci_err_insn_addr(1, 'R', cc, status, addr, range);
zpci_err_insn_rpcit(1, 'R', cc, status, req, addr, range);
if (cc == 1 && (status == 4 || status == 16))
return -ENOMEM;
@ -161,11 +189,11 @@ static inline int ____pcilg(u64 *data, u64 req, u64 offset, u8 *status)
exception = 1;
asm_inline volatile (
" .insn rre,0xb9d20000,%[data],%[req_off]\n"
"0: lhi %[exc],0\n"
"0: .insn rre,0xb9d20000,%[data],%[req_off]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [data] "=d" (__data),
[req_off] "+d" (req_off.pair), [exc] "+d" (exception)
:
@ -230,11 +258,11 @@ static inline int __pcilg_mio(u64 *data, u64 ioaddr, u64 len, u8 *status)
exception = 1;
asm_inline volatile (
" .insn rre,0xb9d60000,%[data],%[ioaddr_len]\n"
"0: lhi %[exc],0\n"
"0: .insn rre,0xb9d60000,%[data],%[ioaddr_len]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [data] "=d" (__data),
[ioaddr_len] "+d" (ioaddr_len.pair), [exc] "+d" (exception)
:
@ -268,11 +296,11 @@ static inline int __pcistg(u64 data, u64 req, u64 offset, u8 *status)
exception = 1;
asm_inline volatile (
" .insn rre,0xb9d00000,%[data],%[req_off]\n"
"0: lhi %[exc],0\n"
"0: .insn rre,0xb9d00000,%[data],%[req_off]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [req_off] "+d" (req_off.pair), [exc] "+d" (exception)
: [data] "d" (data)
: CC_CLOBBER);
@ -322,11 +350,11 @@ static inline int __pcistg_mio(u64 data, u64 ioaddr, u64 len, u8 *status)
exception = 1;
asm_inline volatile (
" .insn rre,0xb9d40000,%[data],%[ioaddr_len]\n"
"0: lhi %[exc],0\n"
"0: .insn rre,0xb9d40000,%[data],%[ioaddr_len]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [ioaddr_len] "+d" (ioaddr_len.pair), [exc] "+d" (exception)
: [data] "d" (data)
: CC_CLOBBER_LIST("memory"));
@ -357,11 +385,11 @@ static inline int __pcistb(const u64 *data, u64 req, u64 offset, u8 *status)
exception = 1;
asm_inline volatile (
" .insn rsy,0xeb00000000d0,%[req],%[offset],%[data]\n"
"0: lhi %[exc],0\n"
"0: .insn rsy,0xeb00000000d0,%[req],%[offset],%[data]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [req] "+d" (req), [exc] "+d" (exception)
: [offset] "d" (offset), [data] "Q" (*data)
: CC_CLOBBER);
@ -411,11 +439,11 @@ static inline int __pcistb_mio(const u64 *data, u64 ioaddr, u64 len, u8 *status)
exception = 1;
asm_inline volatile (
" .insn rsy,0xeb00000000d4,%[len],%[ioaddr],%[data]\n"
"0: lhi %[exc],0\n"
"0: .insn rsy,0xeb00000000d4,%[len],%[ioaddr],%[data]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [len] "+d" (len), [exc] "+d" (exception)
: [ioaddr] "d" (ioaddr), [data] "Q" (*data)
: CC_CLOBBER);

View File

@ -22,12 +22,11 @@ static enum {FLOATING, DIRECTED} irq_delivery;
*/
static struct airq_iv *zpci_sbv;
/*
* interrupt bit vectors
* FLOATING - interrupt bit vector per function
* DIRECTED - interrupt bit vector per cpu
*/
static struct airq_iv **zpci_ibv;
/* FLOATING - interrupt bit vector per function */
static struct airq_iv __rcu **zpci_ibv;
/* DIRECTED - interrupt bit vector per cpu */
static struct airq_iv **zpci_dibv;
/* Modify PCI: Register floating adapter interruptions */
static int zpci_set_airq(struct zpci_dev *zdev)
@ -169,7 +168,7 @@ static struct irq_chip zpci_irq_chip = {
static void zpci_handle_cpu_local_irq(bool rescan)
{
struct airq_iv *dibv = zpci_ibv[smp_processor_id()];
struct airq_iv *dibv = zpci_dibv[smp_processor_id()];
union zpci_sic_iib iib = {{0}};
struct irq_domain *msi_domain;
irq_hw_number_t hwirq;
@ -279,7 +278,9 @@ static void zpci_floating_irq_handler(struct airq_struct *airq,
}
/* Scan the adapter interrupt vector for this device. */
aibv = zpci_ibv[si];
aibv = rcu_dereference(zpci_ibv[si]);
if (!aibv)
continue;
for (ai = 0;;) {
ai = airq_iv_scan(aibv, ai, airq_iv_end(aibv));
if (ai == -1UL)
@ -299,7 +300,7 @@ static int __alloc_airq(struct zpci_dev *zdev, int msi_vecs,
{
if (irq_delivery == DIRECTED) {
/* Allocate cpu vector bits */
*bit = airq_iv_alloc(zpci_ibv[0], msi_vecs);
*bit = airq_iv_alloc(zpci_dibv[0], msi_vecs);
if (*bit == -1UL)
return -EIO;
} else {
@ -313,11 +314,14 @@ static int __alloc_airq(struct zpci_dev *zdev, int msi_vecs,
zdev->aibv = airq_iv_create(msi_vecs,
AIRQ_IV_PTR | AIRQ_IV_DATA | AIRQ_IV_BITLOCK,
NULL);
if (!zdev->aibv)
if (!zdev->aibv) {
airq_iv_free_bit(zpci_sbv, *bit);
zdev->aisb = -1UL;
return -ENOMEM;
}
/* Wire up shortcut pointer */
zpci_ibv[*bit] = zdev->aibv;
rcu_assign_pointer(zpci_ibv[*bit], zdev->aibv);
/* Each function has its own interrupt vector */
*bit = 0;
}
@ -339,13 +343,16 @@ static struct airq_struct zpci_airq = {
static void zpci_msi_teardown_directed(struct zpci_dev *zdev)
{
airq_iv_free(zpci_ibv[0], zdev->msi_first_bit, zdev->max_msi);
airq_iv_free(zpci_dibv[0], zdev->msi_first_bit, zdev->msi_nr_irqs);
zdev->msi_first_bit = -1U;
zdev->msi_nr_irqs = 0;
}
static void zpci_msi_teardown_floating(struct zpci_dev *zdev)
{
rcu_assign_pointer(zpci_ibv[zdev->aisb], NULL);
synchronize_rcu();
airq_iv_release(zdev->aibv);
zdev->aibv = NULL;
airq_iv_free_bit(zpci_sbv, zdev->aisb);
@ -425,9 +432,9 @@ static int zpci_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
if (irq_delivery == DIRECTED) {
for_each_possible_cpu(cpu) {
airq_iv_set_ptr(zpci_ibv[cpu], bit + i,
airq_iv_set_ptr(zpci_dibv[cpu], bit + i,
(unsigned long)zbus->msi_parent_domain);
airq_iv_set_data(zpci_ibv[cpu], bit + i, hwirq + i);
airq_iv_set_data(zpci_dibv[cpu], bit + i, hwirq + i);
}
} else {
airq_iv_set_ptr(zdev->aibv, bit + i,
@ -439,7 +446,7 @@ static int zpci_msi_domain_alloc(struct irq_domain *domain, unsigned int virq,
return 0;
}
static void zpci_msi_clear_airq(struct irq_data *d, int i)
static void zpci_msi_clear_airq(struct irq_data *d)
{
struct msi_desc *desc = irq_data_get_msi_desc(d);
struct zpci_dev *zdev = to_zpci_dev(desc->dev);
@ -452,12 +459,12 @@ static void zpci_msi_clear_airq(struct irq_data *d, int i)
if (irq_delivery == DIRECTED) {
for_each_possible_cpu(cpu) {
airq_iv_set_ptr(zpci_ibv[cpu], bit + i, 0);
airq_iv_set_data(zpci_ibv[cpu], bit + i, 0);
airq_iv_set_ptr(zpci_dibv[cpu], bit, 0);
airq_iv_set_data(zpci_dibv[cpu], bit, 0);
}
} else {
airq_iv_set_ptr(zdev->aibv, bit + i, 0);
airq_iv_set_data(zdev->aibv, bit + i, 0);
airq_iv_set_ptr(zdev->aibv, bit, 0);
airq_iv_set_data(zdev->aibv, bit, 0);
}
}
@ -469,7 +476,7 @@ static void zpci_msi_domain_free(struct irq_domain *domain, unsigned int virq,
for (i = 0; i < nr_irqs; i++) {
d = irq_domain_get_irq_data(domain, virq + i);
zpci_msi_clear_airq(d, i);
zpci_msi_clear_airq(d);
irq_domain_reset_irq_data(d);
}
}
@ -516,9 +523,6 @@ int zpci_create_parent_msi_domain(struct zpci_bus *zbus)
return -ENOMEM;
}
if (irq_delivery == FLOATING)
zpci_msi_parent_ops.required_flags |= MSI_FLAG_NO_AFFINITY;
zbus->msi_parent_domain = msi_create_parent_irq_domain(&info, &zpci_msi_parent_ops);
if (!zbus->msi_parent_domain) {
irq_domain_free_fwnode(info.fwnode);
@ -533,9 +537,13 @@ void zpci_remove_parent_msi_domain(struct zpci_bus *zbus)
{
struct fwnode_handle *fn;
if (!zbus->msi_parent_domain)
return;
fn = zbus->msi_parent_domain->fwnode;
irq_domain_remove(zbus->msi_parent_domain);
irq_domain_free_fwnode(fn);
zbus->msi_parent_domain = NULL;
}
static void __init cpu_enable_directed_irq(void *unused)
@ -543,7 +551,7 @@ static void __init cpu_enable_directed_irq(void *unused)
union zpci_sic_iib iib = {{0}};
union zpci_sic_iib ziib = {{0}};
iib.cdiib.dibv_addr = virt_to_phys(zpci_ibv[smp_processor_id()]->vector);
iib.cdiib.dibv_addr = virt_to_phys(zpci_dibv[smp_processor_id()]->vector);
zpci_set_irq_ctrl(SIC_IRQ_MODE_SET_CPU, 0, &iib);
zpci_set_irq_ctrl(SIC_IRQ_MODE_D_SINGLE, PCI_ISC, &ziib);
@ -558,33 +566,45 @@ static int __init zpci_directed_irq_init(void)
if (!zpci_sbv)
return -ENOMEM;
iib.diib.isc = PCI_ISC;
iib.diib.nr_cpus = num_possible_cpus();
iib.diib.disb_addr = virt_to_phys(zpci_sbv->vector);
zpci_set_irq_ctrl(SIC_IRQ_MODE_DIRECT, 0, &iib);
zpci_ibv = kzalloc_objs(*zpci_ibv, num_possible_cpus());
if (!zpci_ibv)
return -ENOMEM;
zpci_dibv = kzalloc_objs(*zpci_dibv, num_possible_cpus());
if (!zpci_dibv)
goto out_free_sbv;
for_each_possible_cpu(cpu) {
/*
* Per CPU IRQ vectors look the same but bit-allocation
* is only done on the first vector.
*/
zpci_ibv[cpu] = airq_iv_create(cache_line_size() * BITS_PER_BYTE,
AIRQ_IV_PTR |
AIRQ_IV_DATA |
AIRQ_IV_CACHELINE |
(!cpu ? AIRQ_IV_ALLOC : 0), NULL);
if (!zpci_ibv[cpu])
return -ENOMEM;
zpci_dibv[cpu] = airq_iv_create(cache_line_size() * BITS_PER_BYTE,
AIRQ_IV_PTR |
AIRQ_IV_DATA |
AIRQ_IV_CACHELINE |
(!cpu ? AIRQ_IV_ALLOC : 0), NULL);
if (!zpci_dibv[cpu])
goto out_free_dibv;
}
iib.diib.isc = PCI_ISC;
iib.diib.nr_cpus = num_possible_cpus();
iib.diib.disb_addr = virt_to_phys(zpci_sbv->vector);
zpci_set_irq_ctrl(SIC_IRQ_MODE_DIRECT, 0, &iib);
on_each_cpu(cpu_enable_directed_irq, NULL, 1);
zpci_irq_chip.irq_set_affinity = zpci_set_irq_affinity;
return 0;
out_free_dibv:
for_each_possible_cpu(cpu) {
if (zpci_dibv[cpu])
airq_iv_release(zpci_dibv[cpu]);
}
kfree(zpci_dibv);
zpci_dibv = NULL;
out_free_sbv:
airq_iv_release(zpci_sbv);
zpci_sbv = NULL;
return -ENOMEM;
}
static int __init zpci_floating_irq_init(void)
@ -613,6 +633,9 @@ int __init zpci_irq_init(void)
if (s390_pci_force_floating)
irq_delivery = FLOATING;
if (irq_delivery == FLOATING)
zpci_msi_parent_ops.required_flags |= MSI_FLAG_NO_AFFINITY;
if (irq_delivery == DIRECTED)
zpci_airq.handler = zpci_directed_irq_handler;
@ -651,13 +674,15 @@ void __init zpci_irq_exit(void)
{
unsigned int cpu;
unregister_adapter_interrupt(&zpci_airq);
if (irq_delivery == DIRECTED) {
for_each_possible_cpu(cpu) {
airq_iv_release(zpci_ibv[cpu]);
airq_iv_release(zpci_dibv[cpu]);
}
kfree(zpci_dibv);
} else {
kfree(zpci_ibv);
}
kfree(zpci_ibv);
if (zpci_sbv)
airq_iv_release(zpci_sbv);
unregister_adapter_interrupt(&zpci_airq);
}

View File

@ -39,10 +39,10 @@ static inline int __pcistb_mio_inuser(
asm_inline volatile (
" sacf 256\n"
"0: .insn rsy,0xeb00000000d4,%[len],%[ioaddr],%[src]\n"
"1: lhi %[exc],0\n"
"2: sacf 768\n"
" lhi %[exc],0\n"
"1: sacf 768\n"
CC_IPM(cc)
EX_TABLE(0b, 2b) EX_TABLE(1b, 2b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [len] "+d" (len), [exc] "+d" (exception)
: [ioaddr] "a" (ioaddr), [src] "Q" (*((u8 __force *)src))
: CC_CLOBBER_LIST("memory"));
@ -72,15 +72,16 @@ static inline int __pcistg_mio_inuser(
asm_inline volatile (
" sacf 256\n"
"0: llgc %[tmp],0(%[src])\n"
"4: sllg %[val],%[val],8\n"
" sllg %[val],%[val],8\n"
" aghi %[src],1\n"
" ogr %[val],%[tmp]\n"
" brctg %[cnt],0b\n"
"1: .insn rre,0xb9d40000,%[val],%[ioaddr_len]\n"
"2: lhi %[exc],0\n"
"3: sacf 768\n"
" lhi %[exc],0\n"
"2: sacf 768\n"
CC_IPM(cc)
EX_TABLE(0b, 3b) EX_TABLE(4b, 3b) EX_TABLE(1b, 3b) EX_TABLE(2b, 3b)
EX_TABLE_INSN(0b, 2b)
EX_TABLE_INSN(1b, 2b)
: [src] "+a" (src), [cnt] "+d" (cnt),
[val] "+d" (val), [tmp] "=d" (tmp), [exc] "+d" (exception),
CC_OUT(cc, cc), [ioaddr_len] "+&d" (ioaddr_len.pair)
@ -227,13 +228,13 @@ static inline int __pcilg_mio_inuser(
asm_inline volatile (
" sacf 256\n"
"0: .insn rre,0xb9d60000,%[val],%[ioaddr_len]\n"
"1: lhi %[exc],0\n"
" lhi %[exc],0\n"
" jne 4f\n"
"2: ahi %[shift],-8\n"
"1: ahi %[shift],-8\n"
" srlg %[tmp],%[val],0(%[shift])\n"
"3: stc %[tmp],0(%[dst])\n"
"5: aghi %[dst],1\n"
" brctg %[cnt],2b\n"
"2: stc %[tmp],0(%[dst])\n"
" aghi %[dst],1\n"
" brctg %[cnt],1b\n"
/*
* Use xr to clear exc and set condition code to zero
* to ensure flag output is correct for this branch.
@ -241,7 +242,8 @@ static inline int __pcilg_mio_inuser(
" xr %[exc],%[exc]\n"
"4: sacf 768\n"
CC_IPM(cc)
EX_TABLE(0b, 4b) EX_TABLE(1b, 4b) EX_TABLE(3b, 4b) EX_TABLE(5b, 4b)
EX_TABLE_INSN(0b, 4b)
EX_TABLE_INSN(2b, 4b)
: [ioaddr_len] "+&d" (ioaddr_len.pair), [exc] "+d" (exception),
CC_OUT(cc, cc), [val] "=d" (val),
[dst] "+a" (dst), [cnt] "+d" (cnt), [tmp] "=d" (tmp),

View File

@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
gen_facilities
gen_bitmaps
gen_opcode_table
relocs

View File

@ -4,25 +4,31 @@
#
kapi := arch/$(ARCH)/include/generated/asm
kapi-hdrs-y := $(kapi)/facility-defs.h $(kapi)/dis-defs.h
kapi-hdrs-y := $(kapi)/dis-defs.h
kapi-hdrs-y += $(kapi)/facility-defs.h
kapi-hdrs-y += $(kapi)/pgmcheck-defs.h
PHONY += kapi
kapi: $(kapi-hdrs-y)
hostprogs += gen_facilities
hostprogs += gen_bitmaps
hostprogs += gen_opcode_table
HOSTCFLAGS_gen_facilities.o += $(LINUXINCLUDE)
HOSTCFLAGS_gen_bitmaps.o += $(LINUXINCLUDE)
filechk_facility-defs.h = $(obj)/gen_facilities
filechk_facility-defs.h = $(obj)/gen_bitmaps --facility
filechk_pgmcheck-defs.h = $(obj)/gen_bitmaps --pgmcheck
filechk_dis-defs.h = \
$(obj)/gen_opcode_table < $(srctree)/arch/$(ARCH)/tools/opcodes.txt
$(kapi)/facility-defs.h: $(obj)/gen_facilities FORCE
$(kapi)/facility-defs.h: $(obj)/gen_bitmaps FORCE
$(call filechk,facility-defs.h)
$(kapi)/pgmcheck-defs.h: $(obj)/gen_bitmaps FORCE
$(call filechk,pgmcheck-defs.h)
$(kapi)/dis-defs.h: $(obj)/gen_opcode_table FORCE
$(call filechk,dis-defs.h)

View File

@ -1,24 +1,27 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Simple program to generate defines out of facility lists that use the bit
* numbering scheme from the Princples of Operations: most significant bit
* has bit number 0.
* Simple program to generate bitmaps, for example facility lists.
*
* Copyright IBM Corp. 2015, 2018
*
*/
#include <getopt.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
struct facility_def {
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
struct bitmap_def {
char *name;
int *bits;
int inverted;
int maxbit;
};
static struct facility_def facility_defs[] = {
static struct bitmap_def facility_defs[] = {
{
/*
* FACILITIES_ALS contains the list of facilities that are
@ -26,6 +29,7 @@ static struct facility_def facility_defs[] = {
* -march=<machine>.
*/
.name = "FACILITIES_ALS",
.inverted = 1,
.bits = (int[]){
0, /* N3 instructions */
1, /* z/Arch mode installed */
@ -70,6 +74,7 @@ static struct facility_def facility_defs[] = {
* guest facilities supported by KVM.
*/
.name = "FACILITIES_KVM",
.inverted = 1,
.bits = (int[]){
0, /* N3 instructions */
1, /* z/Arch mode installed */
@ -115,6 +120,7 @@ static struct facility_def facility_defs[] = {
*/
.name = "FACILITIES_KVM_CPUMODEL",
.inverted = 1,
.bits = (int[]){
12, /* AP Query Configuration Information */
15, /* AP Facilities Test */
@ -124,17 +130,56 @@ static struct facility_def facility_defs[] = {
},
};
static void print_facility_list(struct facility_def *def)
static struct bitmap_def pgm_check_defs[] = {
{
.name = "PGM_CHECKS_NULLIFIED",
.inverted = 0,
.maxbit = 127,
.bits = (int[]){
0x10, /* Segment Translation */
0x11, /* Page Translation */
0x16, /* Trace Table */
0x20, /* AFX Translation */
0x21, /* ASX Translation */
0x22, /* LX Translation */
0x23, /* EX Translation */
0x24, /* Primary Authority */
0x25, /* Secondary Authority */
0x26, /* LFX Translation */
0x27, /* LSX Translation */
0x29, /* ALEN Translation */
0x2a, /* ALE Sequence */
0x2b, /* ASTE Validity */
0x2c, /* ASTE Sequence */
0x2d, /* Extended Authority */
0x2e, /* LSTE Sequence */
0x2f, /* ASTE Instance */
0x30, /* Stack Full */
0x31, /* Stack Empty */
0x32, /* Stack Specification */
0x33, /* Stack Type */
0x34, /* Stack Operation */
0x38, /* ASCE Type */
0x39, /* Region First Translation */
0x3a, /* Region Second Translation */
0x3b, /* Region Third Translation */
0x3d, /* Secure Storage Access */
-1 /* END */
}
},
};
static void print_bitmap(struct bitmap_def *def)
{
unsigned int high, bit, dword, i;
unsigned long long *array;
array = calloc(1, 8);
high = def->maxbit ? def->maxbit / 64 : 0;
array = calloc(high + 1, 8);
if (!array)
exit(EXIT_FAILURE);
high = 0;
for (i = 0; def->bits[i] != -1; i++) {
bit = 63 - (def->bits[i] & 63);
bit = def->inverted ? 63 - (def->bits[i] & 63) : (def->bits[i] & 63);
dword = def->bits[i] / 64;
if (dword > high) {
array = realloc(array, (dword + 1) * 8);
@ -151,25 +196,45 @@ static void print_facility_list(struct facility_def *def)
free(array);
}
static void print_facility_lists(void)
static void print_header(const char *guard, struct bitmap_def *defs, size_t num)
{
unsigned int i;
for (i = 0; i < sizeof(facility_defs) / sizeof(facility_defs[0]); i++)
print_facility_list(&facility_defs[i]);
}
int main(int argc, char **argv)
{
printf("#ifndef __ASM_S390_FACILITY_DEFS__\n");
printf("#define __ASM_S390_FACILITY_DEFS__\n");
printf("#ifndef __ASM_S390_%s_DEFS__\n", guard);
printf("#define __ASM_S390_%s_DEFS__\n", guard);
printf("/*\n");
printf(" * DO NOT MODIFY.\n");
printf(" *\n");
printf(" * This file was generated by %s\n", __FILE__);
printf(" */\n\n");
printf("#include <linux/const.h>\n\n");
print_facility_lists();
for (i = 0; i < num; i++)
print_bitmap(&defs[i]);
printf("\n#endif\n");
}
static const struct option long_options[] = {
{ "facility", no_argument, NULL, 'f' },
{ "pgmcheck", no_argument, NULL, 'p' },
{ NULL, 0, NULL, 0 }
};
int main(int argc, char **argv)
{
int opt;
opt = getopt_long(argc, argv, "", long_options, NULL);
if (opt == -1)
return 1;
switch (opt) {
case 'f':
print_header("FACILITY", facility_defs, ARRAY_SIZE(facility_defs));
break;
case 'p':
print_header("PGMCHECK", pgm_check_defs, ARRAY_SIZE(pgm_check_defs));
break;
default:
return 1;
}
return 0;
}

View File

@ -201,17 +201,6 @@ static int cmp_long_insn(const void *a, const void *b)
return strcmp(((struct insn *)a)->name, ((struct insn *)b)->name);
}
static void print_insn_name(const char *name)
{
size_t i, len;
len = strlen(name);
printf("{");
for (i = 0; i < len; i++)
printf(" \'%c\',", name[i]);
printf(" }");
}
static void print_long_insn(struct gen_opcode *desc)
{
struct insn *insn;
@ -234,9 +223,7 @@ static void print_long_insn(struct gen_opcode *desc)
insn = &desc->insn[i];
if (insn->name_len < 6)
continue;
printf("\t[LONG_INSN_%s] = ", insn->upper);
print_insn_name(insn->name);
printf(", \\\n");
printf("\t[LONG_INSN_%s] = \"%s\", \\\n", insn->upper, insn->name);
}
printf("}\n\n");
}
@ -249,12 +236,10 @@ static void print_opcode(struct insn *insn, int nr)
if (insn->type->byte != 0)
opcode += 2;
printf("\t[%4d] = { .opfrag = 0x%s, .format = INSTR_%s, ", nr, opcode, insn->format);
if (insn->name_len < 6) {
printf(".name = ");
print_insn_name(insn->name);
} else {
if (insn->name_len < 6)
printf(".name = \"%s\"", insn->name);
else
printf(".offset = LONG_INSN_%s", insn->upper);
}
printf(" }, \\\n");
}

View File

@ -788,7 +788,7 @@ static struct iommu_device *s390_iommu_probe_device(struct device *dev)
static int zpci_refresh_all(struct zpci_dev *zdev)
{
return zpci_refresh_trans((u64)zdev->fh << 32, zdev->start_dma,
return zpci_refresh_trans(zdev->fh, zdev->start_dma,
zdev->end_dma - zdev->start_dma + 1);
}
@ -819,8 +819,7 @@ static void s390_iommu_iotlb_sync(struct iommu_domain *domain,
rcu_read_lock();
list_for_each_entry_rcu(zdev, &s390_domain->devices, iommu_list) {
atomic64_inc(&s390_domain->ctrs.sync_rpcits);
zpci_refresh_trans((u64)zdev->fh << 32, gather->start,
size);
zpci_refresh_trans(zdev->fh, gather->start, size);
}
rcu_read_unlock();
}
@ -837,8 +836,7 @@ static int s390_iommu_iotlb_sync_map(struct iommu_domain *domain,
if (!zdev->tlb_refresh)
continue;
atomic64_inc(&s390_domain->ctrs.sync_map_rpcits);
ret = zpci_refresh_trans((u64)zdev->fh << 32,
iova, size);
ret = zpci_refresh_trans(zdev->fh, iova, size);
/*
* let the hypervisor discover invalidated entries
* allowing it to free IOVAs and unpin pages

View File

@ -77,11 +77,11 @@ static inline int __dia250(void *iob, int cmd)
exception = 1;
asm_inline volatile(
" diag %[rx],%[cmd],0x250\n"
"0: lhi %[exc],0\n"
"0: diag %[rx],%[cmd],0x250\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b,1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [rx] "+d" (rx.pair),
"+m" (*(addr_type *)iob), [exc] "+d" (exception)
: [cmd] "d" (cmd)

View File

@ -106,11 +106,11 @@ static int diag_ftp_2c4(struct diag_ftp_ldfpl *fpl,
diag_stat_inc(DIAG_STAT_X2C4);
asm_inline volatile(
" diag %[addr],%[cmd],0x2c4\n"
"0: j 2f\n"
"0: diag %[addr],%[cmd],0x2c4\n"
" j 2f\n"
"1: la %[rc],%[err]\n"
"2:\n"
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: [rc] "=d" (rc), "+m" (*fpl)
: [cmd] "0" (cmd), [addr] "d" (virt_to_phys(fpl)),
[err] "i" (DIAG_FTP_RET_EPERM)

View File

@ -355,6 +355,9 @@ static long fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (copy_to_user(argp, &iocb, sizeof(struct raw3270_iocb)))
rc = -EFAULT;
break;
default:
rc = -ENOTTY;
break;
}
mutex_unlock(&fs3270_mutex);
return rc;

View File

@ -81,7 +81,8 @@ kbd_alloc(void) {
kbd = kzalloc_obj(struct kbd_data);
if (!kbd)
goto out;
kbd->key_maps = kzalloc(sizeof(ebc_key_maps), GFP_KERNEL);
kbd->key_maps = kcalloc(ARRAY_SIZE(ebc_key_maps),
sizeof(*kbd->key_maps), GFP_KERNEL);
if (!kbd->key_maps)
goto out_kbd;
for (i = 0; i < ARRAY_SIZE(ebc_key_maps); i++) {
@ -93,7 +94,8 @@ kbd_alloc(void) {
goto out_maps;
}
}
kbd->func_table = kzalloc(sizeof(ebc_func_table), GFP_KERNEL);
kbd->func_table = kcalloc(ARRAY_SIZE(ebc_func_table),
sizeof(*kbd->func_table), GFP_KERNEL);
if (!kbd->func_table)
goto out_maps;
for (i = 0; i < ARRAY_SIZE(ebc_func_table); i++) {

View File

@ -320,11 +320,10 @@ static inline int sclp_service_call(sclp_cmdw_t command, void *sccb)
exception = 1;
asm_inline volatile(
"0: .insn rre,0xb2200000,%[cmd],%[sccb]\n" /* servc */
"1: lhi %[exc],0\n"
"2:\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 2b)
EX_TABLE(1b, 2b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), [exc] "+d" (exception)
: [cmd] "d" (command), [sccb] "a" (__pa(sccb))
: CC_CLOBBER_LIST("memory"));

View File

@ -138,7 +138,6 @@ int __init sclp_early_get_core_info(struct sclp_core_info *info)
if (!sccb)
return -ENOMEM;
memset(sccb, 0, length);
sccb->header.length = length;
sccb->header.control_mask[2] = 0x80;
if (sclp_early_cmd(SCLP_CMDW_READ_CPU_INFO, sccb)) {

View File

@ -27,6 +27,7 @@
#include <linux/init.h>
#include <linux/reboot.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/uaccess.h>
#include "sclp.h"
@ -52,6 +53,17 @@ struct sclp_vt220_sccb {
struct evbuf_header evbuf;
};
struct sclp_vt220_evbuf {
struct evbuf_header header;
char type;
char data[];
} __packed;
struct sclp_vt220_resize_data_t {
u16 rows;
u16 cols;
} __packed;
#define SCLP_VT220_MAX_CHARS_PER_BUFFER (PAGE_SIZE - \
sizeof(struct sclp_vt220_request) - \
sizeof(struct sclp_vt220_sccb))
@ -89,6 +101,15 @@ static int __initdata sclp_vt220_init_count;
* another buffer */
static int sclp_vt220_flush_later;
/* Work struct required for scheduling resize */
static struct work_struct sclp_vt220_resize_work;
/* Current vt220 terminal winsize */
static struct winsize sclp_vt220_winsize = {
.ws_row = 24,
.ws_col = 80,
};
static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf);
static int __sclp_vt220_emit(struct sclp_vt220_request *request);
static void sclp_vt220_emit_current(void);
@ -468,6 +489,7 @@ sclp_vt220_write(struct tty_struct *tty, const u8 *buf, size_t count)
#define SCLP_VT220_SESSION_ENDED 0x01
#define SCLP_VT220_SESSION_STARTED 0x80
#define SCLP_VT220_SESSION_DATA 0x00
#define SCLP_VT220_SESSION_RESIZE 0x08
#ifdef CONFIG_MAGIC_SYSRQ
@ -516,30 +538,65 @@ static void sclp_vt220_handle_input(const char *buffer, unsigned int count)
#endif
static void sclp_vt220_resize(struct work_struct *work)
{
struct tty_struct *tty;
struct winsize ws;
spin_lock_irq(&sclp_vt220_lock);
ws = sclp_vt220_winsize;
spin_unlock_irq(&sclp_vt220_lock);
tty = tty_port_tty_get(&sclp_vt220_port);
if (!tty)
return;
tty_do_resize(tty, &ws);
tty_kref_put(tty);
}
static void sclp_vt220_resize_sched(void *buffer, unsigned int count)
{
struct sclp_vt220_resize_data_t *data = buffer;
unsigned long flags;
if (count < sizeof(*data))
return;
spin_lock_irqsave(&sclp_vt220_lock, flags);
sclp_vt220_winsize.ws_row = data->rows;
sclp_vt220_winsize.ws_col = data->cols;
schedule_work(&sclp_vt220_resize_work);
spin_unlock_irqrestore(&sclp_vt220_lock, flags);
}
/*
* Called by the SCLP to report incoming event buffers.
*/
static void
sclp_vt220_receiver_fn(struct evbuf_header *evbuf)
static void sclp_vt220_receiver_fn(struct evbuf_header *evbuf)
{
char *buffer;
struct sclp_vt220_evbuf *buffer;
unsigned int count;
buffer = (char *) ((addr_t) evbuf + sizeof(struct evbuf_header));
count = evbuf->length - sizeof(struct evbuf_header);
if (evbuf->length < offsetof(struct sclp_vt220_evbuf, data))
return;
switch (*buffer) {
buffer = (struct sclp_vt220_evbuf *)evbuf;
count = evbuf->length - offsetof(struct sclp_vt220_evbuf, data);
switch (buffer->type) {
case SCLP_VT220_SESSION_ENDED:
case SCLP_VT220_SESSION_STARTED:
sclp_vt220_reset_session();
break;
case SCLP_VT220_SESSION_DATA:
/* Send input to line discipline */
buffer++;
count--;
sclp_vt220_handle_input(buffer, count);
sclp_vt220_handle_input(buffer->data, count);
tty_flip_buffer_push(&sclp_vt220_port);
break;
case SCLP_VT220_SESSION_RESIZE:
sclp_vt220_resize_sched(buffer->data, count);
break;
}
}
@ -551,10 +608,12 @@ sclp_vt220_open(struct tty_struct *tty, struct file *filp)
{
if (tty->count == 1) {
tty_port_tty_set(&sclp_vt220_port, tty);
if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
tty->winsize.ws_row = 24;
tty->winsize.ws_col = 80;
}
mutex_lock(&tty->winsize_mutex);
spin_lock_irq(&sclp_vt220_lock);
tty->winsize = sclp_vt220_winsize;
spin_unlock_irq(&sclp_vt220_lock);
mutex_unlock(&tty->winsize_mutex);
}
return 0;
}
@ -747,6 +806,8 @@ static int __init sclp_vt220_tty_init(void)
rc = tty_register_driver(driver);
if (rc)
goto out_init;
INIT_WORK(&sclp_vt220_resize_work, sclp_vt220_resize);
rc = sclp_register(&sclp_vt220_register_input);
if (rc)
goto out_reg;

View File

@ -104,13 +104,11 @@ static int vmcp_open(struct inode *inode, struct file *file)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
session = kmalloc_obj(*session);
session = kzalloc_obj(*session);
if (!session)
return -ENOMEM;
session->bufsize = PAGE_SIZE;
session->response = NULL;
session->resp_size = 0;
mutex_init(&session->mutex);
file->private_data = session;
return nonseekable_open(inode, file);

View File

@ -931,12 +931,12 @@ static int cub_alloc(struct channel_subsystem *css)
int i;
for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
css->cub[i] = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
css->cub[i] = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!css->cub[i])
return -ENOMEM;
}
for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
css->ecub[i] = (void *)get_zeroed_page(GFP_KERNEL);
css->ecub[i] = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!css->ecub[i])
return -ENOMEM;
}
@ -949,11 +949,11 @@ static void cub_free(struct channel_subsystem *css)
int i;
for (i = 0; i < CSS_NUM_CUB_PAGES; i++) {
free_page((unsigned long)css->cub[i]);
kfree(css->cub[i]);
css->cub[i] = NULL;
}
for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
free_page((unsigned long)css->ecub[i]);
kfree(css->ecub[i]);
css->ecub[i] = NULL;
}
}
@ -1142,8 +1142,8 @@ int __init chsc_init(void)
{
int ret;
sei_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
chsc_page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
sei_page = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
chsc_page = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sei_page || !chsc_page) {
ret = -ENOMEM;
goto out_err;
@ -1153,16 +1153,16 @@ int __init chsc_init(void)
goto out_err;
return ret;
out_err:
free_page((unsigned long)chsc_page);
free_page((unsigned long)sei_page);
kfree(chsc_page);
kfree(sei_page);
return ret;
}
void __init chsc_init_cleanup(void)
{
crw_unregister_handler(CRW_RSC_CSS);
free_page((unsigned long)chsc_page);
free_page((unsigned long)sei_page);
kfree(chsc_page);
kfree(sei_page);
}
int __chsc_enable_facility(struct chsc_sda_area *sda_area, int operation_code)

View File

@ -15,6 +15,7 @@
#include <linux/uaccess.h>
#include <linux/miscdevice.h>
#include <linux/kernel_stat.h>
#include <linux/cleanup.h>
#include <asm/cio.h>
#include <asm/chsc.h>
@ -292,7 +293,7 @@ static int chsc_ioctl_start(void __user *user_area)
if (!css_general_characteristics.dynio)
/* It makes no sense to try. */
return -EOPNOTSUPP;
chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
chsc_area = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
if (!chsc_area)
return -ENOMEM;
request = kzalloc_obj(*request);
@ -321,7 +322,7 @@ static int chsc_ioctl_start(void __user *user_area)
snprintf(dbf, sizeof(dbf), "ret:%d", ret);
CHSC_LOG(0, dbf);
kfree(request);
free_page((unsigned long)chsc_area);
kfree(chsc_area);
return ret;
}
@ -340,7 +341,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
ret = -ENOMEM;
goto out_unlock;
}
on_close_chsc_area = (void *)get_zeroed_page(GFP_DMA | GFP_KERNEL);
on_close_chsc_area = kzalloc(PAGE_SIZE, GFP_DMA | GFP_KERNEL);
if (!on_close_chsc_area) {
ret = -ENOMEM;
goto out_free_request;
@ -353,7 +354,7 @@ static int chsc_ioctl_on_close_set(void __user *user_area)
goto out_unlock;
out_free_chsc:
free_page((unsigned long)on_close_chsc_area);
kfree(on_close_chsc_area);
on_close_chsc_area = NULL;
out_free_request:
kfree(on_close_request);
@ -375,7 +376,7 @@ static int chsc_ioctl_on_close_remove(void)
ret = -ENOENT;
goto out_unlock;
}
free_page((unsigned long)on_close_chsc_area);
kfree(on_close_chsc_area);
on_close_chsc_area = NULL;
kfree(on_close_request);
on_close_request = NULL;
@ -389,39 +390,29 @@ static int chsc_ioctl_on_close_remove(void)
static int chsc_ioctl_start_sync(void __user *user_area)
{
struct chsc_sync_area *chsc_area;
int ret, ccode;
struct chsc_sync_area *chsc_area __free(kfree) = NULL;
int ccode;
chsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
chsc_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!chsc_area)
return -ENOMEM;
if (copy_from_user(chsc_area, user_area, PAGE_SIZE)) {
ret = -EFAULT;
goto out_free;
}
if (chsc_area->header.code & 0x4000) {
ret = -EINVAL;
goto out_free;
}
if (copy_from_user(chsc_area, user_area, PAGE_SIZE))
return -EFAULT;
if (chsc_area->header.code & 0x4000)
return -EINVAL;
chsc_log_command(chsc_area);
ccode = chsc(chsc_area);
if (ccode != 0) {
ret = -EIO;
goto out_free;
}
if (ccode != 0)
return -EIO;
if (copy_to_user(user_area, chsc_area, PAGE_SIZE))
ret = -EFAULT;
else
ret = 0;
out_free:
free_page((unsigned long)chsc_area);
return ret;
return -EFAULT;
return 0;
}
static int chsc_ioctl_info_channel_path(void __user *user_cd)
{
struct chsc_chp_cd *cd;
int ret, ccode;
struct chsc_chp_cd *cd __free(kfree) = NULL;
int ccode;
struct {
struct chsc_header request;
u32 : 2;
@ -436,20 +427,16 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
u32 : 32;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *scpcd_area;
} __attribute__ ((packed)) *scpcd_area __free(kfree) = NULL;
scpcd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
scpcd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scpcd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
if (!cd) {
ret = -ENOMEM;
goto out_free;
}
if (copy_from_user(cd, user_cd, sizeof(*cd))) {
ret = -EFAULT;
goto out_free;
}
if (!cd)
return -ENOMEM;
if (copy_from_user(cd, user_cd, sizeof(*cd)))
return -EFAULT;
scpcd_area->request.length = 0x0010;
scpcd_area->request.code = 0x0028;
scpcd_area->m = cd->m;
@ -459,31 +446,23 @@ static int chsc_ioctl_info_channel_path(void __user *user_cd)
scpcd_area->last_chpid = cd->chpid.id;
ccode = chsc(scpcd_area);
if (ccode != 0) {
ret = -EIO;
goto out_free;
}
if (ccode != 0)
return -EIO;
if (scpcd_area->response.code != 0x0001) {
ret = -EIO;
CHSC_MSG(0, "scpcd: response code=%x\n",
scpcd_area->response.code);
goto out_free;
return -EIO;
}
memcpy(&cd->cpcb, &scpcd_area->response, scpcd_area->response.length);
if (copy_to_user(user_cd, cd, sizeof(*cd)))
ret = -EFAULT;
else
ret = 0;
out_free:
kfree(cd);
free_page((unsigned long)scpcd_area);
return ret;
return -EFAULT;
return 0;
}
static int chsc_ioctl_info_cu(void __user *user_cd)
{
struct chsc_cu_cd *cd;
int ret, ccode;
struct chsc_cu_cd *cd __free(kfree) = NULL;
int ccode;
struct {
struct chsc_header request;
u32 : 2;
@ -498,20 +477,16 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
u32 : 32;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *scucd_area;
} __attribute__ ((packed)) *scucd_area __free(kfree) = NULL;
scucd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
scucd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scucd_area)
return -ENOMEM;
cd = kzalloc_obj(*cd);
if (!cd) {
ret = -ENOMEM;
goto out_free;
}
if (copy_from_user(cd, user_cd, sizeof(*cd))) {
ret = -EFAULT;
goto out_free;
}
if (!cd)
return -ENOMEM;
if (copy_from_user(cd, user_cd, sizeof(*cd)))
return -EFAULT;
scucd_area->request.length = 0x0010;
scucd_area->request.code = 0x0026;
scucd_area->m = cd->m;
@ -521,31 +496,23 @@ static int chsc_ioctl_info_cu(void __user *user_cd)
scucd_area->last_cun = cd->cun;
ccode = chsc(scucd_area);
if (ccode != 0) {
ret = -EIO;
goto out_free;
}
if (ccode != 0)
return -EIO;
if (scucd_area->response.code != 0x0001) {
ret = -EIO;
CHSC_MSG(0, "scucd: response code=%x\n",
scucd_area->response.code);
goto out_free;
return -EIO;
}
memcpy(&cd->cucb, &scucd_area->response, scucd_area->response.length);
if (copy_to_user(user_cd, cd, sizeof(*cd)))
ret = -EFAULT;
else
ret = 0;
out_free:
kfree(cd);
free_page((unsigned long)scucd_area);
return ret;
return -EFAULT;
return 0;
}
static int chsc_ioctl_info_sch_cu(void __user *user_cud)
{
struct chsc_sch_cud *cud;
int ret, ccode;
struct chsc_sch_cud *cud __free(kfree) = NULL;
int ccode;
struct {
struct chsc_header request;
u32 : 2;
@ -561,20 +528,16 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
u32 : 32;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *sscud_area;
} __attribute__ ((packed)) *sscud_area __free(kfree) = NULL;
sscud_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
sscud_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sscud_area)
return -ENOMEM;
cud = kzalloc_obj(*cud);
if (!cud) {
ret = -ENOMEM;
goto out_free;
}
if (copy_from_user(cud, user_cud, sizeof(*cud))) {
ret = -EFAULT;
goto out_free;
}
if (!cud)
return -ENOMEM;
if (copy_from_user(cud, user_cud, sizeof(*cud)))
return -EFAULT;
sscud_area->request.length = 0x0010;
sscud_area->request.code = 0x0006;
sscud_area->m = cud->schid.m;
@ -585,31 +548,23 @@ static int chsc_ioctl_info_sch_cu(void __user *user_cud)
sscud_area->last_sch = cud->schid.sch_no;
ccode = chsc(sscud_area);
if (ccode != 0) {
ret = -EIO;
goto out_free;
}
if (ccode != 0)
return -EIO;
if (sscud_area->response.code != 0x0001) {
ret = -EIO;
CHSC_MSG(0, "sscud: response code=%x\n",
sscud_area->response.code);
goto out_free;
return -EIO;
}
memcpy(&cud->scub, &sscud_area->response, sscud_area->response.length);
if (copy_to_user(user_cud, cud, sizeof(*cud)))
ret = -EFAULT;
else
ret = 0;
out_free:
kfree(cud);
free_page((unsigned long)sscud_area);
return ret;
return -EFAULT;
return 0;
}
static int chsc_ioctl_conf_info(void __user *user_ci)
{
struct chsc_conf_info *ci;
int ret, ccode;
struct chsc_conf_info *ci __free(kfree) = NULL;
int ccode;
struct {
struct chsc_header request;
u32 : 2;
@ -623,20 +578,16 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
u64 : 64;
struct chsc_header response;
u8 data[PAGE_SIZE - 20];
} __attribute__ ((packed)) *sci_area;
} __attribute__ ((packed)) *sci_area __free(kfree) = NULL;
sci_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
sci_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sci_area)
return -ENOMEM;
ci = kzalloc_obj(*ci);
if (!ci) {
ret = -ENOMEM;
goto out_free;
}
if (copy_from_user(ci, user_ci, sizeof(*ci))) {
ret = -EFAULT;
goto out_free;
}
if (!ci)
return -ENOMEM;
if (copy_from_user(ci, user_ci, sizeof(*ci)))
return -EFAULT;
sci_area->request.length = 0x0010;
sci_area->request.code = 0x0012;
sci_area->m = ci->id.m;
@ -645,31 +596,23 @@ static int chsc_ioctl_conf_info(void __user *user_ci)
sci_area->ssid = ci->id.ssid;
ccode = chsc(sci_area);
if (ccode != 0) {
ret = -EIO;
goto out_free;
}
if (ccode != 0)
return -EIO;
if (sci_area->response.code != 0x0001) {
ret = -EIO;
CHSC_MSG(0, "sci: response code=%x\n",
sci_area->response.code);
goto out_free;
return -EIO;
}
memcpy(&ci->scid, &sci_area->response, sci_area->response.length);
if (copy_to_user(user_ci, ci, sizeof(*ci)))
ret = -EFAULT;
else
ret = 0;
out_free:
kfree(ci);
free_page((unsigned long)sci_area);
return ret;
return -EFAULT;
return 0;
}
static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
{
struct chsc_comp_list *ccl;
int ret, ccode;
struct chsc_comp_list *ccl __free(kfree) = NULL;
int ccode;
struct {
struct chsc_header request;
u32 ctype : 8;
@ -681,7 +624,7 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
u64 : 64;
struct chsc_header response;
u8 data[PAGE_SIZE - 36];
} __attribute__ ((packed)) *sccl_area;
} __attribute__ ((packed)) *sccl_area __free(kfree) = NULL;
struct {
u32 m : 1;
u32 : 31;
@ -696,18 +639,14 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
u32 res;
} __attribute__ ((packed)) *cssids_parm;
sccl_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
sccl_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sccl_area)
return -ENOMEM;
ccl = kzalloc_obj(*ccl);
if (!ccl) {
ret = -ENOMEM;
goto out_free;
}
if (copy_from_user(ccl, user_ccl, sizeof(*ccl))) {
ret = -EFAULT;
goto out_free;
}
if (!ccl)
return -ENOMEM;
if (copy_from_user(ccl, user_ccl, sizeof(*ccl)))
return -EFAULT;
sccl_area->request.length = 0x0020;
sccl_area->request.code = 0x0030;
sccl_area->fmt = ccl->req.fmt;
@ -728,61 +667,46 @@ static int chsc_ioctl_conf_comp_list(void __user *user_ccl)
break;
}
ccode = chsc(sccl_area);
if (ccode != 0) {
ret = -EIO;
goto out_free;
}
if (ccode != 0)
return -EIO;
if (sccl_area->response.code != 0x0001) {
ret = -EIO;
CHSC_MSG(0, "sccl: response code=%x\n",
sccl_area->response.code);
goto out_free;
return -EIO;
}
memcpy(&ccl->sccl, &sccl_area->response, sccl_area->response.length);
if (copy_to_user(user_ccl, ccl, sizeof(*ccl)))
ret = -EFAULT;
else
ret = 0;
out_free:
kfree(ccl);
free_page((unsigned long)sccl_area);
return ret;
return -EFAULT;
return 0;
}
static int chsc_ioctl_chpd(void __user *user_chpd)
{
struct chsc_scpd *scpd_area;
struct chsc_cpd_info *chpd;
struct chsc_scpd *scpd_area __free(kfree) = NULL;
struct chsc_cpd_info *chpd __free(kfree) = NULL;
int ret;
chpd = kzalloc_obj(*chpd);
scpd_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scpd_area || !chpd) {
ret = -ENOMEM;
goto out_free;
}
if (copy_from_user(chpd, user_chpd, sizeof(*chpd))) {
ret = -EFAULT;
goto out_free;
}
scpd_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scpd_area || !chpd)
return -ENOMEM;
if (copy_from_user(chpd, user_chpd, sizeof(*chpd)))
return -EFAULT;
ret = chsc_determine_channel_path_desc(chpd->chpid, chpd->fmt,
chpd->rfmt, chpd->c, chpd->m,
scpd_area);
if (ret)
goto out_free;
return ret;
memcpy(&chpd->chpdb, &scpd_area->response, scpd_area->response.length);
if (copy_to_user(user_chpd, chpd, sizeof(*chpd)))
ret = -EFAULT;
out_free:
kfree(chpd);
free_page((unsigned long)scpd_area);
return ret;
return -EFAULT;
return 0;
}
static int chsc_ioctl_dcal(void __user *user_dcal)
{
struct chsc_dcal *dcal;
int ret, ccode;
struct chsc_dcal *dcal __free(kfree) = NULL;
int ccode;
struct {
struct chsc_header request;
u32 atype : 8;
@ -794,20 +718,16 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
u32 res1[2];
struct chsc_header response;
u8 data[PAGE_SIZE - 36];
} __attribute__ ((packed)) *sdcal_area;
} __attribute__ ((packed)) *sdcal_area __free(kfree) = NULL;
sdcal_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
sdcal_area = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!sdcal_area)
return -ENOMEM;
dcal = kzalloc_obj(*dcal);
if (!dcal) {
ret = -ENOMEM;
goto out_free;
}
if (copy_from_user(dcal, user_dcal, sizeof(*dcal))) {
ret = -EFAULT;
goto out_free;
}
if (!dcal)
return -ENOMEM;
if (copy_from_user(dcal, user_dcal, sizeof(*dcal)))
return -EFAULT;
sdcal_area->request.length = 0x0020;
sdcal_area->request.code = 0x0034;
sdcal_area->atype = dcal->req.atype;
@ -816,26 +736,18 @@ static int chsc_ioctl_dcal(void __user *user_dcal)
sizeof(sdcal_area->list_parm));
ccode = chsc(sdcal_area);
if (ccode != 0) {
ret = -EIO;
goto out_free;
}
if (ccode != 0)
return -EIO;
if (sdcal_area->response.code != 0x0001) {
ret = -EIO;
CHSC_MSG(0, "sdcal: response code=%x\n",
sdcal_area->response.code);
goto out_free;
return -EIO;
}
memcpy(&dcal->sdcal, &sdcal_area->response,
sdcal_area->response.length);
if (copy_to_user(user_dcal, dcal, sizeof(*dcal)))
ret = -EFAULT;
else
ret = 0;
out_free:
kfree(dcal);
free_page((unsigned long)sdcal_area);
return ret;
return -EFAULT;
return 0;
}
static long chsc_ioctl(struct file *filp, unsigned int cmd,
@ -904,7 +816,7 @@ static int chsc_release(struct inode *inode, struct file *filp)
}
snprintf(dbf, sizeof(dbf), "relret:%d", ret);
CHSC_LOG(0, dbf);
free_page((unsigned long)on_close_chsc_area);
kfree(on_close_chsc_area);
on_close_chsc_area = NULL;
kfree(on_close_request);
on_close_request = NULL;

View File

@ -482,6 +482,9 @@ static int alloc_cmb(struct ccw_device *cdev)
ssize_t size;
struct cmb_data *cmb_data;
if (cmb_area.num_channels <= 0)
return -ENOMEM;
/* Allocate private cmb_data. */
cmb_data = kzalloc_obj(struct cmb_data);
if (!cmb_data)
@ -501,12 +504,12 @@ static int alloc_cmb(struct ccw_device *cdev)
WARN_ON(!list_empty(&cmb_area.list));
spin_unlock(&cmb_area.lock);
mem = (void *)__get_free_pages(GFP_KERNEL, get_order(size));
mem = kmalloc(size, GFP_KERNEL);
spin_lock(&cmb_area.lock);
if (cmb_area.mem) {
/* ok, another thread was faster */
free_pages((unsigned long)mem, get_order(size));
kfree(mem);
} else if (!mem) {
/* no luck */
ret = -ENOMEM;
@ -547,10 +550,8 @@ static void free_cmb(struct ccw_device *cdev)
list_del_init(&priv->cmb_list);
if (list_empty(&cmb_area.list)) {
ssize_t size;
size = sizeof(struct cmb) * cmb_area.num_channels;
cmf_activate(NULL, CMF_OFF);
free_pages((unsigned long)cmb_area.mem, get_order(size));
kfree(cmb_area.mem);
cmb_area.mem = NULL;
}
spin_unlock_irq(cdev->ccwlock);

View File

@ -24,11 +24,11 @@ static inline int __stsch(struct subchannel_id schid, struct schib *addr)
exception = 1;
asm_inline volatile(
" lgr 1,%[r1]\n"
" stsch %[addr]\n"
"0: lhi %[exc],0\n"
"0: stsch %[addr]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, ccode), [addr] "=Q" (*addr), [exc] "+d" (exception)
: [r1] "d" (r1)
: CC_CLOBBER_LIST("1"));
@ -54,11 +54,11 @@ static inline int __msch(struct subchannel_id schid, struct schib *addr)
exception = 1;
asm_inline volatile(
" lgr 1,%[r1]\n"
" msch %[addr]\n"
"0: lhi %[exc],0\n"
"0: msch %[addr]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, ccode), [exc] "+d" (exception)
: [r1] "d" (r1), [addr] "Q" (*addr)
: CC_CLOBBER_LIST("1"));
@ -108,11 +108,11 @@ static inline int __ssch(struct subchannel_id schid, union orb *addr)
exception = 1;
asm_inline volatile(
" lgr 1,%[r1]\n"
" ssch %[addr]\n"
"0: lhi %[exc],0\n"
"0: ssch %[addr]\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, ccode), [exc] "+d" (exception)
: [r1] "d" (r1), [addr] "Q" (*addr)
: CC_CLOBBER_LIST("memory", "1"));
@ -179,11 +179,11 @@ int chsc(void *chsc_area)
exception = 1;
asm_inline volatile(
" .insn rre,0xb25f0000,%[chsc_area],0\n"
"0: lhi %[exc],0\n"
"0: .insn rre,0xb25f0000,%[chsc_area],0\n"
" lhi %[exc],0\n"
"1:\n"
CC_IPM(cc)
EX_TABLE(0b, 1b)
EX_TABLE_INSN(0b, 1b)
: CC_OUT(cc, cc), "+m" (*(addr_type *)chsc_area), [exc] "+d" (exception)
: [chsc_area] "d" (chsc_area)
: CC_CLOBBER);

View File

@ -244,7 +244,7 @@ struct qdio_irq {
int perf_stat_enabled;
struct qdr *qdr;
unsigned long chsc_page;
void *chsc_page;
struct qdio_q *input_qs[QDIO_MAX_QUEUES_PER_IRQ];
struct qdio_q *output_qs[QDIO_MAX_QUEUES_PER_IRQ];

View File

@ -12,11 +12,11 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/kmemleak.h>
#include <linux/delay.h>
#include <linux/gfp.h>
#include <linux/io.h>
#include <linux/atomic.h>
#include <linux/slab.h>
#include <asm/debug.h>
#include <asm/qdio.h>
#include <asm/asm.h>
@ -933,10 +933,10 @@ int qdio_free(struct ccw_device *cdev)
mutex_unlock(&irq_ptr->setup_mutex);
qdio_free_queues(irq_ptr);
free_page((unsigned long) irq_ptr->qdr);
free_page(irq_ptr->chsc_page);
kfree(irq_ptr->qdr);
kfree(irq_ptr->chsc_page);
kfree(irq_ptr->ccw);
free_page((unsigned long) irq_ptr);
kfree(irq_ptr);
return 0;
}
EXPORT_SYMBOL_GPL(qdio_free);
@ -961,7 +961,7 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
no_output_qs > QDIO_MAX_QUEUES_PER_IRQ)
return -EINVAL;
irq_ptr = (void *) get_zeroed_page(GFP_KERNEL);
irq_ptr = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!irq_ptr)
return -ENOMEM;
@ -969,9 +969,6 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
if (!irq_ptr->ccw)
goto err_ccw;
/* kmemleak doesn't scan the page-allocated irq_ptr: */
kmemleak_not_leak(irq_ptr->ccw);
irq_ptr->cdev = cdev;
mutex_init(&irq_ptr->setup_mutex);
if (qdio_allocate_dbf(irq_ptr))
@ -986,12 +983,12 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
* qdio_establish. In case of low memory and swap on a zfcp disk
* we may not be able to allocate memory otherwise.
*/
irq_ptr->chsc_page = get_zeroed_page(GFP_KERNEL);
irq_ptr->chsc_page = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!irq_ptr->chsc_page)
goto err_chsc;
/* qdr is used in ccw1.cda which is u32 */
irq_ptr->qdr = (struct qdr *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
irq_ptr->qdr = kzalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!irq_ptr->qdr)
goto err_qdr;
@ -1004,14 +1001,14 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
return 0;
err_queues:
free_page((unsigned long) irq_ptr->qdr);
kfree(irq_ptr->qdr);
err_qdr:
free_page(irq_ptr->chsc_page);
kfree(irq_ptr->chsc_page);
err_chsc:
err_dbf:
kfree(irq_ptr->ccw);
err_ccw:
free_page((unsigned long) irq_ptr);
kfree(irq_ptr);
return rc;
}
EXPORT_SYMBOL_GPL(qdio_allocate);

View File

@ -35,7 +35,7 @@ void qdio_free_buffers(struct qdio_buffer **buf, unsigned int count)
int pos;
for (pos = 0; pos < count; pos += QBUFF_PER_PAGE)
free_page((unsigned long) buf[pos]);
kfree(buf[pos]);
}
EXPORT_SYMBOL_GPL(qdio_free_buffers);
@ -49,7 +49,7 @@ int qdio_alloc_buffers(struct qdio_buffer **buf, unsigned int count)
int pos;
for (pos = 0; pos < count; pos += QBUFF_PER_PAGE) {
buf[pos] = (void *) get_zeroed_page(GFP_KERNEL);
buf[pos] = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (!buf[pos]) {
qdio_free_buffers(buf, count);
return -ENOMEM;
@ -83,7 +83,7 @@ static void __qdio_free_queues(struct qdio_q **queues, unsigned int count)
for (i = 0; i < count; i++) {
q = queues[i];
free_page((unsigned long)q->sl_page);
kfree(q->sl_page);
kmem_cache_free(qdio_q_cache, q);
}
}
@ -109,7 +109,7 @@ static int __qdio_allocate_qs(struct qdio_q **irq_ptr_qs, int nr_queues)
return -ENOMEM;
}
q->sl_page = (void *)__get_free_page(GFP_KERNEL);
q->sl_page = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!q->sl_page) {
kmem_cache_free(qdio_q_cache, q);
__qdio_free_queues(irq_ptr_qs, i);
@ -249,11 +249,11 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
DBF_EVENT("getssqd:%4x", schid->sch_no);
if (!irq_ptr) {
ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL);
ssqd = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!ssqd)
return -ENOMEM;
} else {
ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page;
ssqd = irq_ptr->chsc_page;
}
rc = chsc_ssqd(*schid, ssqd);
@ -270,7 +270,7 @@ int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr,
out:
if (!irq_ptr)
free_page((unsigned long)ssqd);
kfree(ssqd);
return rc;
}

View File

@ -136,7 +136,7 @@ static struct airq_struct tiqdio_airq = {
static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset)
{
struct chsc_scssc_area *scssc = (void *)irq_ptr->chsc_page;
struct chsc_scssc_area *scssc = irq_ptr->chsc_page;
dma64_t summary_indicator_addr, subchannel_indicator_addr;
int rc;

View File

@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/cleanup.h>
#include <asm/eadm.h>
#include "chsc.h"
@ -224,12 +225,12 @@ static int scm_add(struct chsc_scm_info *scm_info, size_t num)
int scm_update_information(void)
{
struct chsc_scm_info *scm_info;
struct chsc_scm_info *scm_info __free(kfree) = NULL;
u64 token = 0;
size_t num;
int ret;
scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
scm_info = kmalloc(PAGE_SIZE, GFP_KERNEL | GFP_DMA);
if (!scm_info)
return -ENOMEM;
@ -250,8 +251,6 @@ int scm_update_information(void)
token = scm_info->restok;
} while (token);
free_page((unsigned long)scm_info);
return ret;
}

View File

@ -854,7 +854,7 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms,
struct ica_xcRB *xcrb)
{
bool userspace = xflags & ZCRYPT_XFLAG_USERSPACE;
unsigned int card, domain, func_code = 0;
unsigned int card, domain, _dom, func_code = 0;
unsigned int wgt = 0, pref_wgt = 0;
struct zcrypt_queue *zq, *pref_zq;
struct zcrypt_card *zc, *pref_zc;
@ -877,10 +877,17 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms,
print_hex_dump_debug("ccareq: ", DUMP_PREFIX_ADDRESS, 16, 1,
ap_msg.msg, ap_msg.len, false);
/* Check domain for either AUTOSEL_DOM or in range 0...AP_DOMAINS-1 */
if (domain != AUTOSEL_DOM && domain >= AP_DOMAINS) {
rc = -EINVAL;
goto out;
}
/* Spectre-v1: sanitize domain unconditionally for later use */
_dom = array_index_nospec(domain, AP_DOMAINS);
if (perms != &ap_perms && domain < AP_DOMAINS) {
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
domain = array_index_nospec(domain, AP_DOMAINS);
if (!test_bit_inv(domain, perms->adm)) {
if (!test_bit_inv(_dom, perms->adm)) {
rc = -ENODEV;
goto out;
}
@ -893,10 +900,11 @@ static long _zcrypt_send_cprb(u32 xflags, struct ap_perms *perms,
* If a valid target domain is set and this domain is NOT a usage
* domain but a control only domain, autoselect target domain.
*/
if (domain < AP_DOMAINS &&
!ap_test_config_usage_domain(domain) &&
ap_test_config_ctrl_domain(domain))
domain = AUTOSEL_DOM;
if (domain < AP_DOMAINS) {
if (!ap_test_config_usage_domain(_dom) &&
ap_test_config_ctrl_domain(_dom))
domain = AUTOSEL_DOM;
}
pref_zc = NULL;
pref_zq = NULL;
@ -1041,7 +1049,7 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms,
struct ep11_target_dev *targets = NULL;
unsigned short target_num;
unsigned int wgt = 0, pref_wgt = 0;
unsigned int func_code = 0, domain;
unsigned int func_code = 0, domain, _dom;
struct ap_message ap_msg;
int cpen, qpen, qid = 0, rc;
struct module *mod;
@ -1078,10 +1086,17 @@ static long _zcrypt_send_ep11_cprb(u32 xflags, struct ap_perms *perms,
print_hex_dump_debug("ep11req: ", DUMP_PREFIX_ADDRESS, 16, 1,
ap_msg.msg, ap_msg.len, false);
/* Check domain for either AUTOSEL_DOM or in range 0...AP_DOMAINS-1 */
if (domain != AUTOSEL_DOM && domain >= AP_DOMAINS) {
rc = -EINVAL;
goto out;
}
/* Spectre-v1: sanitize domain unconditionally for later use */
_dom = array_index_nospec(domain, AP_DOMAINS);
if (perms != &ap_perms && domain < AP_DOMAINS) {
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
domain = array_index_nospec(domain, AP_DOMAINS);
if (!test_bit_inv(domain, perms->adm)) {
if (!test_bit_inv(_dom, perms->adm)) {
rc = -ENODEV;
goto out;
}

View File

@ -1627,6 +1627,7 @@ int cca_query_crypto_facility(u16 cardnr, u16 domain,
u8 subfunc_code[2];
u8 lvdata[];
} __packed * prepparm;
size_t datalen;
/* get already prepared memory for 2 cprbs with param block each */
rc = alloc_and_prep_cprbmem(parmbsize, &mem,
@ -1673,27 +1674,44 @@ int cca_query_crypto_facility(u16 cardnr, u16 domain,
prepcblk->rpl_parmb = (u8 __user *)ptr;
prepparm = (struct fqrepparm *)ptr;
ptr = prepparm->lvdata;
datalen = parmbsize - 2 * sizeof(u8);
/* check and possibly copy reply rule array */
len = *((u16 *)ptr);
if (len > datalen) {
ZCRYPT_DBF_ERR("%s reply rule array len %u exceeds datalen %zu\n",
__func__, len, datalen);
rc = -EIO;
goto out;
}
datalen -= sizeof(u16);
ptr += sizeof(u16);
if (len > sizeof(u16)) {
ptr += sizeof(u16);
len -= sizeof(u16);
if (rarray && rarraylen && *rarraylen > 0) {
*rarraylen = (len > *rarraylen ? *rarraylen : len);
memcpy(rarray, ptr, *rarraylen);
}
datalen -= len;
ptr += len;
}
/* check and possible copy reply var array */
len = *((u16 *)ptr);
if (len > datalen) {
ZCRYPT_DBF_ERR("%s reply var array len %u exceeds datalen %zu\n",
__func__, len, datalen);
rc = -EIO;
goto out;
}
datalen -= sizeof(u16);
ptr += sizeof(u16);
if (len > sizeof(u16)) {
ptr += sizeof(u16);
len -= sizeof(u16);
if (varray && varraylen && *varraylen > 0) {
*varraylen = (len > *varraylen ? *varraylen : len);
memcpy(varray, ptr, *varraylen);
}
datalen -= len;
ptr += len;
}

View File

@ -152,6 +152,11 @@ struct type80_hdr {
unsigned char reserved3[8];
} __packed;
struct type80_reply {
struct type80_hdr hdr;
char data[];
} __packed;
int get_rsa_modex_fc(struct ica_rsa_modexpo *mex, int *fcode)
{
if (!mex->inputdatalength)
@ -340,32 +345,43 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_queue *zq,
* @data: pointer to user output data
* @length: size of user output data
*
* Returns 0 on success or -EFAULT.
* Returns 0 on success or neg. errno value on failure.
*/
static int convert_type80(struct zcrypt_queue *zq,
struct ap_message *reply,
char __user *outputdata,
unsigned int outputdatalength)
{
struct type80_hdr *t80h = reply->msg;
unsigned char *data;
struct type80_reply *msg = reply->msg;
size_t payload_len;
if (t80h->len < sizeof(*t80h) + outputdatalength) {
/* The result is too short, the CEXxA card may not do that.. */
/*
* reply->len is always >= sizeof(struct type80_reply) here and
* outputdatalength (the modulus size) is guaranteed to be equal to
* inputdatalength and 0 < outputdatalength <= CEX3A_MAX_MOD_SIZE,
* also payload size may be >= outputdatalength.
*/
payload_len = reply->len - sizeof(msg->hdr);
if (outputdatalength > payload_len) {
/* We expect at least outputdatalength bytes, broken card ? */
zq->online = 0;
pr_err("Crypto dev=%02x.%04x code=0x%02x => online=0 rc=EAGAIN\n",
AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid), t80h->code);
AP_QID_QUEUE(zq->queue->qid), msg->hdr.code);
ZCRYPT_DBF_ERR("%s dev=%02x.%04x code=0x%02x => online=0 rc=EAGAIN\n",
__func__, AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid), t80h->code);
AP_QID_QUEUE(zq->queue->qid), msg->hdr.code);
ap_send_online_uevent(&zq->queue->ap_dev, zq->online);
return -EAGAIN;
}
BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
data = reply->msg + t80h->len - outputdatalength;
if (copy_to_user(outputdata, data, outputdatalength))
if (copy_to_user(outputdata,
msg->data + payload_len - outputdatalength,
outputdatalength))
return -EFAULT;
return 0;
}
@ -374,14 +390,19 @@ static int convert_response(struct zcrypt_queue *zq,
char __user *outputdata,
unsigned int outputdatalength)
{
/* Response type byte is the second byte in the response. */
unsigned char rtype = ((unsigned char *)reply->msg)[1];
struct type80_reply *msg = reply->msg;
switch (rtype) {
/* reply->len is always >= sizeof(struct error_hdr) here */
switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
return convert_error(zq, reply);
case TYPE80_RSP_CODE:
if (msg->hdr.code)
return convert_error(zq, reply);
if (reply->len < sizeof(struct type80_reply))
return -EINVAL;
return convert_type80(zq, reply,
outputdata, outputdatalength);
default: /* Unknown response type, this should NEVER EVER happen */
@ -389,11 +410,11 @@ static int convert_response(struct zcrypt_queue *zq,
pr_err("Crypto dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n",
AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid),
(int)rtype);
(int)msg->hdr.type);
ZCRYPT_DBF_ERR(
"%s dev=%02x.%04x unknown response type 0x%02x => online=0 rc=EAGAIN\n",
__func__, AP_QID_CARD(zq->queue->qid),
AP_QID_QUEUE(zq->queue->qid), (int)rtype);
AP_QID_QUEUE(zq->queue->qid), (int)msg->hdr.type);
ap_send_online_uevent(&zq->queue->ap_dev, zq->online);
return -EAGAIN;
}
@ -416,26 +437,46 @@ static void zcrypt_msgtype50_receive(struct ap_queue *aq,
.reply_code = REP82_ERROR_MACHINE_FAILURE,
};
struct type80_hdr *t80h;
int len;
size_t len;
/* Copy the reply message to the request message buffer. */
if (!reply)
goto out; /* ap_msg->rc indicates the error */
t80h = reply->msg;
if (t80h->type == TYPE80_RSP_CODE) {
len = t80h->len;
if (len > reply->bufsize || len > msg->bufsize ||
len != reply->len) {
pr_debug("len mismatch => EMSGSIZE\n");
msg->rc = -EMSGSIZE;
if (reply->len < sizeof(*t80h) ||
t80h->type != TYPE80_RSP_CODE) {
if (reply->len < sizeof(error_reply)) {
/* total broken reply, use static error reply instead */
memcpy(msg->msg, &error_reply, sizeof(error_reply));
msg->len = sizeof(error_reply);
goto out;
} else {
/* malformed reply, convert function will handle this */
len = reply->len;
goto copy_len_and_out;
}
memcpy(msg->msg, reply->msg, len);
msg->len = len;
} else {
memcpy(msg->msg, reply->msg, sizeof(error_reply));
msg->len = sizeof(error_reply);
}
len = t80h->len;
copy_len_and_out:
if (len != reply->len) {
msg->rc = -EMSGSIZE;
pr_debug("len %zu rpl.len %zu mismatch, msg.rc=%d\n",
len, reply->len, msg->rc);
goto out;
}
if (len > reply->bufsize || len > msg->bufsize) {
msg->rc = -EMSGSIZE;
pr_debug("len %zu exceeds buf %zu/%zu, msg.rc=%d\n",
len, reply->bufsize, msg->bufsize, msg->rc);
goto out;
}
memcpy(msg->msg, reply->msg, len);
msg->len = len;
out:
complete(&msg->response.work);
}

View File

@ -766,6 +766,15 @@ static int convert_type86_rng(struct zcrypt_queue *zq,
if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
return -EINVAL;
/*
* Note that offset2 and count2 have already been checked in
* zcrypt_msgtype6_receive(). So only check for valid count2
* and for not exceeding the hard coded rng buffer size.
*/
if (!msg->fmt2.count2)
return -EINVAL;
if (msg->fmt2.count2 > ZCRYPT_RNG_BUFFER_SIZE)
return -EMSGSIZE;
memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
return msg->fmt2.count2;
}
@ -777,11 +786,17 @@ static int convert_response_ica(struct zcrypt_queue *zq,
{
struct type86x_reply *msg = reply->msg;
/* reply->len is always >= sizeof(struct error_hdr) here */
switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
return convert_error(zq, reply);
case TYPE86_RSP_CODE:
if (msg->hdr.reply_code)
return convert_error(zq, reply);
if (reply->len < sizeof(struct type86x_reply))
return -EINVAL;
if (msg->cprbx.ccp_rtcode &&
msg->cprbx.ccp_rscode == 0x14f &&
outputdatalength > 256) {
@ -792,8 +807,6 @@ static int convert_response_ica(struct zcrypt_queue *zq,
return -EINVAL;
}
}
if (msg->hdr.reply_code)
return convert_error(zq, reply);
if (msg->cprbx.cprb_ver_id == 0x02)
return convert_type86_ica(zq, reply,
outputdata, outputdatalength);
@ -820,6 +833,8 @@ static int convert_response_xcrb(bool userspace, struct zcrypt_queue *zq,
{
struct type86x_reply *msg = reply->msg;
/* reply->len is always >= sizeof(struct error_hdr) here */
switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
@ -827,9 +842,16 @@ static int convert_response_xcrb(bool userspace, struct zcrypt_queue *zq,
return convert_error(zq, reply);
case TYPE86_RSP_CODE:
if (msg->hdr.reply_code) {
xcrb->status = msg->fmt2.apfs;
if (reply->len < sizeof(struct type86_fmt2_msg))
xcrb->status = 0x0008044DL;
else
xcrb->status = msg->fmt2.apfs;
return convert_error(zq, reply);
}
if (reply->len < sizeof(struct type86_fmt2_msg) +
offsetof(struct CPRBX, cprb_ver_id) +
sizeof(msg->cprbx.cprb_ver_id))
return -EINVAL;
if (msg->cprbx.cprb_ver_id == 0x02)
return convert_type86_xcrb(userspace, zq, reply, xcrb);
fallthrough; /* wrong cprb version is an unknown response */
@ -854,6 +876,8 @@ static int convert_response_ep11_xcrb(bool userspace, struct zcrypt_queue *zq,
{
struct type86_ep11_reply *msg = reply->msg;
/* reply->len is always >= sizeof(struct error_hdr) here */
switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE87_RSP_CODE:
@ -861,6 +885,8 @@ static int convert_response_ep11_xcrb(bool userspace, struct zcrypt_queue *zq,
case TYPE86_RSP_CODE:
if (msg->hdr.reply_code)
return convert_error(zq, reply);
if (reply->len < sizeof(struct type86_ep11_reply))
return -EINVAL;
if (msg->cprbx.cprb_ver_id == 0x04)
return convert_type86_ep11_xcrb(userspace, zq, reply, xcrb);
fallthrough; /* wrong cprb version is an unknown resp */
@ -885,6 +911,8 @@ static int convert_response_rng(struct zcrypt_queue *zq,
{
struct type86x_reply *msg = reply->msg;
/* reply->len is always >= sizeof(struct error_hdr) here */
switch (msg->hdr.type) {
case TYPE82_RSP_CODE:
case TYPE88_RSP_CODE:
@ -892,6 +920,8 @@ static int convert_response_rng(struct zcrypt_queue *zq,
case TYPE86_RSP_CODE:
if (msg->hdr.reply_code)
return -EINVAL;
if (reply->len < sizeof(struct type86x_reply))
return -EINVAL;
if (msg->cprbx.cprb_ver_id == 0x02)
return convert_type86_rng(zq, reply, data);
fallthrough; /* wrong cprb version is an unknown response */
@ -928,48 +958,85 @@ static void zcrypt_msgtype6_receive(struct ap_queue *aq,
};
struct ap_response_type *resp_type = &msg->response;
struct type86x_reply *t86r;
int len;
size_t minlen, len;
/* Copy the reply message to the request message buffer. */
if (!reply)
goto out; /* ap_msg->rc indicates the error */
t86r = reply->msg;
if (t86r->hdr.type == TYPE86_RSP_CODE &&
t86r->cprbx.cprb_ver_id == 0x02) {
switch (resp_type->type) {
case CEXXC_RESPONSE_TYPE_ICA:
len = sizeof(struct type86x_reply) + t86r->length;
if (len > reply->bufsize || len > msg->bufsize ||
len != reply->len) {
pr_debug("len mismatch => EMSGSIZE\n");
msg->rc = -EMSGSIZE;
goto out;
}
memcpy(msg->msg, reply->msg, len);
msg->len = len;
break;
case CEXXC_RESPONSE_TYPE_XCRB:
if (t86r->fmt2.count2)
len = t86r->fmt2.offset2 + t86r->fmt2.count2;
else
len = t86r->fmt2.offset1 + t86r->fmt2.count1;
if (len > reply->bufsize || len > msg->bufsize ||
len != reply->len) {
pr_debug("len mismatch => EMSGSIZE\n");
msg->rc = -EMSGSIZE;
goto out;
}
memcpy(msg->msg, reply->msg, len);
msg->len = len;
break;
default:
minlen = sizeof(t86r->hdr) + sizeof(t86r->fmt2) +
offsetof(struct CPRBX, cprb_ver_id) +
sizeof(t86r->cprbx.cprb_ver_id);
if (reply->len < minlen ||
t86r->hdr.type != TYPE86_RSP_CODE ||
t86r->cprbx.cprb_ver_id != 0x02) {
if (reply->len < sizeof(error_reply)) {
/* total broken reply, use static error reply instead */
memcpy(msg->msg, &error_reply, sizeof(error_reply));
msg->len = sizeof(error_reply);
goto out;
} else {
/* malformed reply, convert function will handle this */
len = reply->len;
goto copy_len_and_out;
}
} else {
memcpy(msg->msg, reply->msg, sizeof(error_reply));
msg->len = sizeof(error_reply);
}
switch (resp_type->type) {
case CEXXC_RESPONSE_TYPE_ICA:
if (reply->len < sizeof(struct type86x_reply)) {
msg->rc = -EMSGSIZE;
pr_debug("rpl.len %zu < struct type86_reply, msg.rc=%d\n",
reply->len, msg->rc);
goto out;
}
len = sizeof(struct type86x_reply) + (size_t)t86r->length;
break;
case CEXXC_RESPONSE_TYPE_XCRB:
len = (size_t)t86r->fmt2.offset1 + (size_t)t86r->fmt2.count1;
if (len > reply->len) {
msg->rc = -EMSGSIZE;
pr_debug("offset1 %u count1 %u rpl.len %zu mismatch, msg.rc=%d\n",
t86r->fmt2.offset1, t86r->fmt2.count1,
reply->len, msg->rc);
goto out;
}
if (t86r->fmt2.count2) {
len = (size_t)t86r->fmt2.offset2 +
(size_t)t86r->fmt2.count2;
if (len > reply->len) {
msg->rc = -EMSGSIZE;
pr_debug("offset2 %u count2 %u rpl.len %zu mismatch, msg.rc=%d\n",
t86r->fmt2.offset2, t86r->fmt2.count2,
reply->len, msg->rc);
goto out;
}
}
break;
default:
memcpy(msg->msg, &error_reply, sizeof(error_reply));
msg->len = sizeof(error_reply);
goto out;
}
copy_len_and_out:
if (len != reply->len) {
msg->rc = -EMSGSIZE;
pr_debug("len %zu rpl.len %zu mismatch, msg.rc=%d\n",
len, reply->len, msg->rc);
goto out;
}
if (len > reply->bufsize || len > msg->bufsize) {
msg->rc = -EMSGSIZE;
pr_debug("len %zu exceeds buf %zu/%zu, msg.rc=%d\n",
len, reply->bufsize, msg->bufsize, msg->rc);
goto out;
}
memcpy(msg->msg, reply->msg, len);
msg->len = len;
out:
complete(&resp_type->work);
}
@ -992,34 +1059,65 @@ static void zcrypt_msgtype6_receive_ep11(struct ap_queue *aq,
};
struct ap_response_type *resp_type = &msg->response;
struct type86_ep11_reply *t86r;
int len;
size_t minlen, len;
/* Copy the reply message to the request message buffer. */
if (!reply)
goto out; /* ap_msg->rc indicates the error */
t86r = reply->msg;
if (t86r->hdr.type == TYPE86_RSP_CODE &&
t86r->cprbx.cprb_ver_id == 0x04) {
switch (resp_type->type) {
case CEXXC_RESPONSE_TYPE_EP11:
len = t86r->fmt2.offset1 + t86r->fmt2.count1;
if (len > reply->bufsize || len > msg->bufsize ||
len != reply->len) {
pr_debug("len mismatch => EMSGSIZE\n");
msg->rc = -EMSGSIZE;
goto out;
}
memcpy(msg->msg, reply->msg, len);
msg->len = len;
break;
default:
minlen = sizeof(t86r->hdr) + sizeof(t86r->fmt2) +
offsetof(struct ep11_cprb, cprb_ver_id) +
sizeof(t86r->cprbx.cprb_ver_id);
if (reply->len < minlen ||
t86r->hdr.type != TYPE86_RSP_CODE ||
t86r->cprbx.cprb_ver_id != 0x04) {
if (reply->len < sizeof(error_reply)) {
/* total broken reply, use static error reply instead */
memcpy(msg->msg, &error_reply, sizeof(error_reply));
msg->len = sizeof(error_reply);
goto out;
} else {
/* malformed reply, convert function will handle this */
len = reply->len;
goto copy_len_and_out;
}
} else {
memcpy(msg->msg, reply->msg, sizeof(error_reply));
msg->len = sizeof(error_reply);
}
switch (resp_type->type) {
case CEXXC_RESPONSE_TYPE_EP11:
len = (size_t)t86r->fmt2.offset1 + (size_t)t86r->fmt2.count1;
if (len > reply->len) {
msg->rc = -EMSGSIZE;
pr_debug("offset1 %u count1 %u rpl.len %zu mismatch, msg.rc=%d\n",
t86r->fmt2.offset1, t86r->fmt2.count1,
reply->len, msg->rc);
goto out;
}
break;
default:
memcpy(msg->msg, &error_reply, sizeof(error_reply));
msg->len = sizeof(error_reply);
goto out;
}
copy_len_and_out:
if (len != reply->len) {
msg->rc = -EMSGSIZE;
pr_debug("len %zu rpl.len %zu mismatch, msg.rc=%d\n",
len, reply->len, msg->rc);
goto out;
}
if (len > reply->bufsize || len > msg->bufsize) {
msg->rc = -EMSGSIZE;
pr_debug("len %zu exceeds buf %zu/%zu, msg.rc=%d\n",
len, reply->bufsize, msg->bufsize, msg->rc);
goto out;
}
memcpy(msg->msg, reply->msg, len);
msg->len = len;
out:
complete(&resp_type->work);
}