s390 updates for the 5.18 merge window #2
- Add kretprobes framepointer verification and return address recovery in stacktrace. - Support control domain masks on custom zcrypt devices and filter admin requests. - Cleanup timer API usage. - Rework absolute lowcore access helpers. - Other various small improvements and fixes. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEE3QHqV+H2a8xAv27vjYWKoQLXFBgFAmJG+10ACgkQjYWKoQLX FBjvOggAhBOcGniD4LDvyPw6Mwp6x3+bnSL0Hv+UdjyTXEsj2/XpSQeAsG807mq0 hKicEY44suVYrk6ywLCuG9tA6oaxr+t6g13Z/cuNqvdljMDFdeZw9ptIeAXfoUQb 19fxDpVyAV9iWc9VfcjwhgOXep9fk6dPDpr5clE81zuAgn94hV31tHpCbMIYDxHa mceza1rdW5DnByCz2afYcgF72pW2hnLadOPWddbuqyVZV6jjJeZDDShAzf5MCJ6p e91yJh6RYDT1f1Yn+htz2Bw8URb9FKRnJRMHf35h98kHT3r0x6N6VUjYiQ66CAjE k02XBdJIwgXwgGtErj6lZsQZFjzT8g== =5yyr -----END PGP SIGNATURE----- Merge tag 's390-5.18-2' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux Pull more s390 updates from Vasily Gorbik: - Add kretprobes framepointer verification and return address recovery in stacktrace. - Support control domain masks on custom zcrypt devices and filter admin requests. - Cleanup timer API usage. - Rework absolute lowcore access helpers. - Other various small improvements and fixes. * tag 's390-5.18-2' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (26 commits) s390/alternatives: avoid using jgnop mnemonic s390/pci: rename get_zdev_by_bus() to zdev_from_bus() s390/pci: improve zpci_dev reference counting s390/smp: use physical address for SIGP_SET_PREFIX command s390: cleanup timer API use s390/zcrypt: fix using the correct variable for sizeof() s390/vfio-ap: fix kernel doc and signature of group notifier functions s390/maccess: rework absolute lowcore accessors s390/smp: cleanup control register update routines s390/smp: cleanup target CPU callback starting s390/test_unwind: verify __kretprobe_trampoline is replaced s390/unwind: avoid duplicated unwinding entries for kretprobes s390/unwind: recover kretprobe modified return address in stacktrace s390/kprobes: enable kretprobes framepointer verification s390/test_unwind: extend kretprobe test s390/ap: adjust whitespace s390/ap: use insn format for new instructions s390/alternatives: use insn format for new instructions s390/alternatives: use instructions instead of byte patterns s390/traps: improve panic message for translation-specification exception ...
This commit is contained in:
commit
9ae24d5aa0
@ -58,6 +58,7 @@ config S390
|
||||
select ALTERNATE_USER_ADDRESS_SPACE
|
||||
select ARCH_32BIT_USTAT_F_TINODE
|
||||
select ARCH_BINFMT_ELF_STATE
|
||||
select ARCH_CORRECT_STACKTRACE_ON_KRETPROBE
|
||||
select ARCH_ENABLE_MEMORY_HOTPLUG if SPARSEMEM
|
||||
select ARCH_ENABLE_MEMORY_HOTREMOVE
|
||||
select ARCH_ENABLE_SPLIT_PMD_PTLOCK if PGTABLE_LEVELS > 2
|
||||
|
@ -37,9 +37,15 @@
|
||||
* a 2-byte nop if the size of the area is not divisible by 6.
|
||||
*/
|
||||
.macro alt_pad_fill bytes
|
||||
.fill ( \bytes ) / 6, 6, 0xc0040000
|
||||
.fill ( \bytes ) % 6 / 4, 4, 0x47000000
|
||||
.fill ( \bytes ) % 6 % 4 / 2, 2, 0x0700
|
||||
.rept ( \bytes ) / 6
|
||||
brcl 0,0
|
||||
.endr
|
||||
.rept ( \bytes ) % 6 / 4
|
||||
nop
|
||||
.endr
|
||||
.rept ( \bytes ) % 6 % 4 / 2
|
||||
nopr
|
||||
.endr
|
||||
.endm
|
||||
|
||||
/*
|
||||
|
@ -71,11 +71,18 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
|
||||
".if " oldinstr_pad_len(num) " > 6\n" \
|
||||
"\tjg " e_oldinstr_pad_end "f\n" \
|
||||
"6620:\n" \
|
||||
"\t.fill (" oldinstr_pad_len(num) " - (6620b-662b)) / 2, 2, 0x0700\n" \
|
||||
"\t.rept (" oldinstr_pad_len(num) " - (6620b-662b)) / 2\n" \
|
||||
"\tnopr\n" \
|
||||
".else\n" \
|
||||
"\t.fill " oldinstr_pad_len(num) " / 6, 6, 0xc0040000\n" \
|
||||
"\t.fill " oldinstr_pad_len(num) " %% 6 / 4, 4, 0x47000000\n" \
|
||||
"\t.fill " oldinstr_pad_len(num) " %% 6 %% 4 / 2, 2, 0x0700\n" \
|
||||
"\t.rept " oldinstr_pad_len(num) " / 6\n" \
|
||||
"\t.brcl 0,0\n" \
|
||||
"\t.endr\n" \
|
||||
"\t.rept " oldinstr_pad_len(num) " %% 6 / 4\n" \
|
||||
"\tnop\n" \
|
||||
"\t.endr\n" \
|
||||
"\t.rept " oldinstr_pad_len(num) " %% 6 %% 4 / 2\n" \
|
||||
"\tnopr\n" \
|
||||
".endr\n" \
|
||||
".endif\n"
|
||||
|
||||
#define OLDINSTR(oldinstr, num) \
|
||||
|
@ -60,11 +60,11 @@ static inline bool ap_instructions_available(void)
|
||||
unsigned long reg1 = 0;
|
||||
|
||||
asm volatile(
|
||||
" lgr 0,%[reg0]\n" /* qid into gr0 */
|
||||
" lghi 1,0\n" /* 0 into gr1 */
|
||||
" lghi 2,0\n" /* 0 into gr2 */
|
||||
" .long 0xb2af0000\n" /* PQAP(TAPQ) */
|
||||
"0: la %[reg1],1\n" /* 1 into reg1 */
|
||||
" 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 */
|
||||
"1:\n"
|
||||
EX_TABLE(0b, 1b)
|
||||
: [reg1] "+&d" (reg1)
|
||||
@ -86,11 +86,11 @@ static inline struct ap_queue_status ap_tapq(ap_qid_t qid, unsigned long *info)
|
||||
unsigned long reg2;
|
||||
|
||||
asm volatile(
|
||||
" lgr 0,%[qid]\n" /* qid into gr0 */
|
||||
" lghi 2,0\n" /* 0 into gr2 */
|
||||
" .long 0xb2af0000\n" /* PQAP(TAPQ) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
" lgr %[reg2],2\n" /* gr2 into reg2 */
|
||||
" lgr 0,%[qid]\n" /* qid into gr0 */
|
||||
" lghi 2,0\n" /* 0 into gr2 */
|
||||
" .insn rre,0xb2af0000,0,0\n" /* PQAP(TAPQ) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
" lgr %[reg2],2\n" /* gr2 into reg2 */
|
||||
: [reg1] "=&d" (reg1), [reg2] "=&d" (reg2)
|
||||
: [qid] "d" (qid)
|
||||
: "cc", "0", "1", "2");
|
||||
@ -128,9 +128,9 @@ static inline struct ap_queue_status ap_rapq(ap_qid_t qid)
|
||||
struct ap_queue_status reg1;
|
||||
|
||||
asm volatile(
|
||||
" lgr 0,%[reg0]\n" /* qid arg into gr0 */
|
||||
" .long 0xb2af0000\n" /* PQAP(RAPQ) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
" lgr 0,%[reg0]\n" /* qid arg into gr0 */
|
||||
" .insn rre,0xb2af0000,0,0\n" /* PQAP(RAPQ) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
: [reg1] "=&d" (reg1)
|
||||
: [reg0] "d" (reg0)
|
||||
: "cc", "0", "1");
|
||||
@ -149,9 +149,9 @@ static inline struct ap_queue_status ap_zapq(ap_qid_t qid)
|
||||
struct ap_queue_status reg1;
|
||||
|
||||
asm volatile(
|
||||
" lgr 0,%[reg0]\n" /* qid arg into gr0 */
|
||||
" .long 0xb2af0000\n" /* PQAP(ZAPQ) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
" lgr 0,%[reg0]\n" /* qid arg into gr0 */
|
||||
" .insn rre,0xb2af0000,0,0\n" /* PQAP(ZAPQ) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
: [reg1] "=&d" (reg1)
|
||||
: [reg0] "d" (reg0)
|
||||
: "cc", "0", "1");
|
||||
@ -190,10 +190,10 @@ static inline int ap_qci(struct ap_config_info *config)
|
||||
struct ap_config_info *reg2 = config;
|
||||
|
||||
asm volatile(
|
||||
" lgr 0,%[reg0]\n" /* QCI fc into gr0 */
|
||||
" lgr 2,%[reg2]\n" /* ptr to config into gr2 */
|
||||
" .long 0xb2af0000\n" /* PQAP(QCI) */
|
||||
"0: la %[reg1],0\n" /* good case, QCI fc available */
|
||||
" 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 */
|
||||
"1:\n"
|
||||
EX_TABLE(0b, 1b)
|
||||
: [reg1] "+&d" (reg1)
|
||||
@ -246,11 +246,11 @@ static inline struct ap_queue_status ap_aqic(ap_qid_t qid,
|
||||
reg1.qirqctrl = qirqctrl;
|
||||
|
||||
asm volatile(
|
||||
" lgr 0,%[reg0]\n" /* qid param into gr0 */
|
||||
" lgr 1,%[reg1]\n" /* irq ctrl into gr1 */
|
||||
" lgr 2,%[reg2]\n" /* ni addr into gr2 */
|
||||
" .long 0xb2af0000\n" /* PQAP(AQIC) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
" lgr 0,%[reg0]\n" /* qid param into gr0 */
|
||||
" lgr 1,%[reg1]\n" /* irq ctrl into gr1 */
|
||||
" lgr 2,%[reg2]\n" /* ni addr into gr2 */
|
||||
" .insn rre,0xb2af0000,0,0\n" /* PQAP(AQIC) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
: [reg1] "+&d" (reg1)
|
||||
: [reg0] "d" (reg0), [reg2] "d" (reg2)
|
||||
: "cc", "0", "1", "2");
|
||||
@ -297,11 +297,11 @@ static inline struct ap_queue_status ap_qact(ap_qid_t qid, int ifbit,
|
||||
reg1.value = apinfo->val;
|
||||
|
||||
asm volatile(
|
||||
" lgr 0,%[reg0]\n" /* qid param into gr0 */
|
||||
" lgr 1,%[reg1]\n" /* qact in info into gr1 */
|
||||
" .long 0xb2af0000\n" /* PQAP(QACT) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
" lgr %[reg2],2\n" /* qact out info into reg2 */
|
||||
" lgr 0,%[reg0]\n" /* qid param into gr0 */
|
||||
" lgr 1,%[reg1]\n" /* qact in info into gr1 */
|
||||
" .insn rre,0xb2af0000,0,0\n" /* PQAP(QACT) */
|
||||
" lgr %[reg1],1\n" /* gr1 (status) into reg1 */
|
||||
" lgr %[reg2],2\n" /* qact out info into reg2 */
|
||||
: [reg1] "+&d" (reg1), [reg2] "=&d" (reg2)
|
||||
: [reg0] "d" (reg0)
|
||||
: "cc", "0", "1", "2");
|
||||
|
@ -74,8 +74,17 @@ static __always_inline void __ctl_clear_bit(unsigned int cr, unsigned int bit)
|
||||
__ctl_load(reg, cr, cr);
|
||||
}
|
||||
|
||||
void smp_ctl_set_bit(int cr, int bit);
|
||||
void smp_ctl_clear_bit(int cr, int bit);
|
||||
void smp_ctl_set_clear_bit(int cr, int bit, bool set);
|
||||
|
||||
static inline void ctl_set_bit(int cr, int bit)
|
||||
{
|
||||
smp_ctl_set_clear_bit(cr, bit, true);
|
||||
}
|
||||
|
||||
static inline void ctl_clear_bit(int cr, int bit)
|
||||
{
|
||||
smp_ctl_set_clear_bit(cr, bit, false);
|
||||
}
|
||||
|
||||
union ctlreg0 {
|
||||
unsigned long val;
|
||||
@ -130,8 +139,5 @@ union ctlreg15 {
|
||||
};
|
||||
};
|
||||
|
||||
#define ctl_set_bit(cr, bit) smp_ctl_set_bit(cr, bit)
|
||||
#define ctl_clear_bit(cr, bit) smp_ctl_clear_bit(cr, bit)
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASM_CTL_REG_H */
|
||||
|
@ -319,11 +319,18 @@ extern void (*s390_base_pgm_handler_fn)(struct pt_regs *regs);
|
||||
extern int memcpy_real(void *, unsigned long, size_t);
|
||||
extern void memcpy_absolute(void *, void *, size_t);
|
||||
|
||||
#define mem_assign_absolute(dest, val) do { \
|
||||
__typeof__(dest) __tmp = (val); \
|
||||
\
|
||||
BUILD_BUG_ON(sizeof(__tmp) != sizeof(val)); \
|
||||
memcpy_absolute(&(dest), &__tmp, sizeof(__tmp)); \
|
||||
#define put_abs_lowcore(member, x) do { \
|
||||
unsigned long __abs_address = offsetof(struct lowcore, member); \
|
||||
__typeof__(((struct lowcore *)0)->member) __tmp = (x); \
|
||||
\
|
||||
memcpy_absolute(__va(__abs_address), &__tmp, sizeof(__tmp)); \
|
||||
} while (0)
|
||||
|
||||
#define get_abs_lowcore(x, member) do { \
|
||||
unsigned long __abs_address = offsetof(struct lowcore, member); \
|
||||
__typeof__(((struct lowcore *)0)->member) *__ptr = &(x); \
|
||||
\
|
||||
memcpy_absolute(__ptr, __va(__abs_address), sizeof(*__ptr)); \
|
||||
} while (0)
|
||||
|
||||
extern int s390_isolate_bp(void);
|
||||
|
@ -78,7 +78,7 @@ static inline void arch_spin_unlock(arch_spinlock_t *lp)
|
||||
{
|
||||
typecheck(int, lp->lock);
|
||||
asm_inline volatile(
|
||||
ALTERNATIVE("", ".long 0xb2fa0070", 49) /* NIAI 7 */
|
||||
ALTERNATIVE("", ".insn rre,0xb2fa0000,7,0", 49) /* NIAI 7 */
|
||||
" sth %1,%0\n"
|
||||
: "=R" (((unsigned short *) &lp->lock)[1])
|
||||
: "d" (0) : "cc", "memory");
|
||||
|
@ -162,4 +162,4 @@
|
||||
__diag_pop(); \
|
||||
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
|
||||
|
||||
#endif /* _ASM_X86_SYSCALL_WRAPPER_H */
|
||||
#endif /* _ASM_S390_SYSCALL_WRAPPER_H */
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/kprobes.h>
|
||||
#include <linux/llist.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/stacktrace.h>
|
||||
|
||||
@ -36,10 +38,21 @@ struct unwind_state {
|
||||
struct pt_regs *regs;
|
||||
unsigned long sp, ip;
|
||||
int graph_idx;
|
||||
struct llist_node *kr_cur;
|
||||
bool reliable;
|
||||
bool error;
|
||||
};
|
||||
|
||||
/* Recover the return address modified by kretprobe and ftrace_graph. */
|
||||
static inline unsigned long unwind_recover_ret_addr(struct unwind_state *state,
|
||||
unsigned long ip)
|
||||
{
|
||||
ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL);
|
||||
if (is_kretprobe_trampoline(ip))
|
||||
ip = kretprobe_find_ret_addr(state->task, (void *)state->sp, &state->kr_cur);
|
||||
return ip;
|
||||
}
|
||||
|
||||
void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
||||
struct pt_regs *regs, unsigned long first_frame);
|
||||
bool unwind_next_frame(struct unwind_state *state);
|
||||
|
@ -121,22 +121,22 @@ _LPP_OFFSET = __LC_LPP
|
||||
.endm
|
||||
|
||||
.macro BPOFF
|
||||
ALTERNATIVE "", ".long 0xb2e8c000", 82
|
||||
ALTERNATIVE "", ".insn rrf,0xb2e80000,0,0,12,0", 82
|
||||
.endm
|
||||
|
||||
.macro BPON
|
||||
ALTERNATIVE "", ".long 0xb2e8d000", 82
|
||||
ALTERNATIVE "", ".insn rrf,0xb2e80000,0,0,13,0", 82
|
||||
.endm
|
||||
|
||||
.macro BPENTER tif_ptr,tif_mask
|
||||
ALTERNATIVE "TSTMSK \tif_ptr,\tif_mask; jz .+8; .long 0xb2e8d000", \
|
||||
ALTERNATIVE "TSTMSK \tif_ptr,\tif_mask; jz .+8; .insn rrf,0xb2e80000,0,0,13,0", \
|
||||
"", 82
|
||||
.endm
|
||||
|
||||
.macro BPEXIT tif_ptr,tif_mask
|
||||
TSTMSK \tif_ptr,\tif_mask
|
||||
ALTERNATIVE "jz .+8; .long 0xb2e8c000", \
|
||||
"jnz .+8; .long 0xb2e8d000", 82
|
||||
ALTERNATIVE "jz .+8; .insn rrf,0xb2e80000,0,0,12,0", \
|
||||
"jnz .+8; .insn rrf,0xb2e80000,0,0,13,0", 82
|
||||
.endm
|
||||
|
||||
/*
|
||||
|
@ -1646,8 +1646,8 @@ static void dump_reipl_run(struct shutdown_trigger *trigger)
|
||||
|
||||
csum = (__force unsigned int)
|
||||
csum_partial(reipl_block_actual, reipl_block_actual->hdr.len, 0);
|
||||
mem_assign_absolute(S390_lowcore.ipib, ipib);
|
||||
mem_assign_absolute(S390_lowcore.ipib_checksum, csum);
|
||||
put_abs_lowcore(ipib, ipib);
|
||||
put_abs_lowcore(ipib_checksum, csum);
|
||||
dump_run(trigger);
|
||||
}
|
||||
|
||||
|
@ -284,11 +284,11 @@ NOKPROBE_SYMBOL(pop_kprobe);
|
||||
|
||||
void arch_prepare_kretprobe(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
{
|
||||
ri->ret_addr = (kprobe_opcode_t *) regs->gprs[14];
|
||||
ri->fp = NULL;
|
||||
ri->ret_addr = (kprobe_opcode_t *)regs->gprs[14];
|
||||
ri->fp = (void *)regs->gprs[15];
|
||||
|
||||
/* Replace the return addr with trampoline addr */
|
||||
regs->gprs[14] = (unsigned long) &__kretprobe_trampoline;
|
||||
regs->gprs[14] = (unsigned long)&__kretprobe_trampoline;
|
||||
}
|
||||
NOKPROBE_SYMBOL(arch_prepare_kretprobe);
|
||||
|
||||
@ -385,7 +385,7 @@ NOKPROBE_SYMBOL(arch_kretprobe_fixup_return);
|
||||
*/
|
||||
void trampoline_probe_handler(struct pt_regs *regs)
|
||||
{
|
||||
kretprobe_trampoline_handler(regs, NULL);
|
||||
kretprobe_trampoline_handler(regs, (void *)regs->gprs[15]);
|
||||
}
|
||||
NOKPROBE_SYMBOL(trampoline_probe_handler);
|
||||
|
||||
|
@ -226,7 +226,7 @@ void arch_crash_save_vmcoreinfo(void)
|
||||
vmcoreinfo_append_str("SAMODE31=%lx\n", __samode31);
|
||||
vmcoreinfo_append_str("EAMODE31=%lx\n", __eamode31);
|
||||
vmcoreinfo_append_str("KERNELOFFSET=%lx\n", kaslr_offset());
|
||||
mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note());
|
||||
put_abs_lowcore(vmcore_info, paddr_vmcoreinfo_note());
|
||||
}
|
||||
|
||||
void machine_shutdown(void)
|
||||
|
@ -63,7 +63,7 @@ void __init os_info_init(void)
|
||||
os_info.version_minor = OS_INFO_VERSION_MINOR;
|
||||
os_info.magic = OS_INFO_MAGIC;
|
||||
os_info.csum = os_info_csum(&os_info);
|
||||
mem_assign_absolute(S390_lowcore.os_info, __pa(ptr));
|
||||
put_abs_lowcore(os_info, __pa(ptr));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
|
@ -481,11 +481,11 @@ static void __init setup_lowcore_dat_off(void)
|
||||
lc->mcck_stack = mcck_stack + STACK_INIT_OFFSET;
|
||||
|
||||
/* Setup absolute zero lowcore */
|
||||
mem_assign_absolute(S390_lowcore.restart_stack, lc->restart_stack);
|
||||
mem_assign_absolute(S390_lowcore.restart_fn, lc->restart_fn);
|
||||
mem_assign_absolute(S390_lowcore.restart_data, lc->restart_data);
|
||||
mem_assign_absolute(S390_lowcore.restart_source, lc->restart_source);
|
||||
mem_assign_absolute(S390_lowcore.restart_psw, lc->restart_psw);
|
||||
put_abs_lowcore(restart_stack, lc->restart_stack);
|
||||
put_abs_lowcore(restart_fn, lc->restart_fn);
|
||||
put_abs_lowcore(restart_data, lc->restart_data);
|
||||
put_abs_lowcore(restart_source, lc->restart_source);
|
||||
put_abs_lowcore(restart_psw, lc->restart_psw);
|
||||
|
||||
lc->spinlock_lockval = arch_spin_lockval(0);
|
||||
lc->spinlock_index = 0;
|
||||
@ -501,6 +501,7 @@ static void __init setup_lowcore_dat_off(void)
|
||||
static void __init setup_lowcore_dat_on(void)
|
||||
{
|
||||
struct lowcore *lc = lowcore_ptr[0];
|
||||
int cr;
|
||||
|
||||
__ctl_clear_bit(0, 28);
|
||||
S390_lowcore.external_new_psw.mask |= PSW_MASK_DAT;
|
||||
@ -509,10 +510,10 @@ static void __init setup_lowcore_dat_on(void)
|
||||
S390_lowcore.io_new_psw.mask |= PSW_MASK_DAT;
|
||||
__ctl_store(S390_lowcore.cregs_save_area, 0, 15);
|
||||
__ctl_set_bit(0, 28);
|
||||
mem_assign_absolute(S390_lowcore.restart_flags, RESTART_FLAG_CTLREGS);
|
||||
mem_assign_absolute(S390_lowcore.program_new_psw, lc->program_new_psw);
|
||||
memcpy_absolute(&S390_lowcore.cregs_save_area, lc->cregs_save_area,
|
||||
sizeof(S390_lowcore.cregs_save_area));
|
||||
put_abs_lowcore(restart_flags, RESTART_FLAG_CTLREGS);
|
||||
put_abs_lowcore(program_new_psw, lc->program_new_psw);
|
||||
for (cr = 0; cr < ARRAY_SIZE(lc->cregs_save_area); cr++)
|
||||
put_abs_lowcore(cregs_save_area[cr], lc->cregs_save_area[cr]);
|
||||
}
|
||||
|
||||
static struct resource code_resource = {
|
||||
|
@ -213,7 +213,7 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
|
||||
if (nmi_alloc_mcesa(&lc->mcesad))
|
||||
goto out;
|
||||
lowcore_ptr[cpu] = lc;
|
||||
pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, (u32)(unsigned long) lc);
|
||||
pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, __pa(lc));
|
||||
return 0;
|
||||
|
||||
out:
|
||||
@ -326,10 +326,17 @@ static void pcpu_delegate(struct pcpu *pcpu,
|
||||
/* Stop target cpu (if func returns this stops the current cpu). */
|
||||
pcpu_sigp_retry(pcpu, SIGP_STOP, 0);
|
||||
/* Restart func on the target cpu and stop the current cpu. */
|
||||
mem_assign_absolute(lc->restart_stack, stack);
|
||||
mem_assign_absolute(lc->restart_fn, (unsigned long) func);
|
||||
mem_assign_absolute(lc->restart_data, (unsigned long) data);
|
||||
mem_assign_absolute(lc->restart_source, source_cpu);
|
||||
if (lc) {
|
||||
lc->restart_stack = stack;
|
||||
lc->restart_fn = (unsigned long)func;
|
||||
lc->restart_data = (unsigned long)data;
|
||||
lc->restart_source = source_cpu;
|
||||
} else {
|
||||
put_abs_lowcore(restart_stack, stack);
|
||||
put_abs_lowcore(restart_fn, (unsigned long)func);
|
||||
put_abs_lowcore(restart_data, (unsigned long)data);
|
||||
put_abs_lowcore(restart_source, source_cpu);
|
||||
}
|
||||
__bpon();
|
||||
asm volatile(
|
||||
"0: sigp 0,%0,%2 # sigp restart to target cpu\n"
|
||||
@ -570,39 +577,27 @@ static void smp_ctl_bit_callback(void *info)
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(ctl_lock);
|
||||
static unsigned long ctlreg;
|
||||
|
||||
/*
|
||||
* Set a bit in a control register of all cpus
|
||||
*/
|
||||
void smp_ctl_set_bit(int cr, int bit)
|
||||
void smp_ctl_set_clear_bit(int cr, int bit, bool set)
|
||||
{
|
||||
struct ec_creg_mask_parms parms = { 1UL << bit, -1UL, cr };
|
||||
struct ec_creg_mask_parms parms = { .cr = cr, };
|
||||
u64 ctlreg;
|
||||
|
||||
if (set) {
|
||||
parms.orval = 1UL << bit;
|
||||
parms.andval = -1UL;
|
||||
} else {
|
||||
parms.orval = 0;
|
||||
parms.andval = ~(1UL << bit);
|
||||
}
|
||||
spin_lock(&ctl_lock);
|
||||
memcpy_absolute(&ctlreg, &S390_lowcore.cregs_save_area[cr], sizeof(ctlreg));
|
||||
__set_bit(bit, &ctlreg);
|
||||
memcpy_absolute(&S390_lowcore.cregs_save_area[cr], &ctlreg, sizeof(ctlreg));
|
||||
get_abs_lowcore(ctlreg, cregs_save_area[cr]);
|
||||
ctlreg = (ctlreg & parms.andval) | parms.orval;
|
||||
put_abs_lowcore(cregs_save_area[cr], ctlreg);
|
||||
spin_unlock(&ctl_lock);
|
||||
on_each_cpu(smp_ctl_bit_callback, &parms, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(smp_ctl_set_bit);
|
||||
|
||||
/*
|
||||
* Clear a bit in a control register of all cpus
|
||||
*/
|
||||
void smp_ctl_clear_bit(int cr, int bit)
|
||||
{
|
||||
struct ec_creg_mask_parms parms = { 0, ~(1UL << bit), cr };
|
||||
|
||||
spin_lock(&ctl_lock);
|
||||
memcpy_absolute(&ctlreg, &S390_lowcore.cregs_save_area[cr], sizeof(ctlreg));
|
||||
__clear_bit(bit, &ctlreg);
|
||||
memcpy_absolute(&S390_lowcore.cregs_save_area[cr], &ctlreg, sizeof(ctlreg));
|
||||
spin_unlock(&ctl_lock);
|
||||
on_each_cpu(smp_ctl_bit_callback, &parms, 1);
|
||||
}
|
||||
EXPORT_SYMBOL(smp_ctl_clear_bit);
|
||||
EXPORT_SYMBOL(smp_ctl_set_clear_bit);
|
||||
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
|
||||
|
@ -141,10 +141,10 @@ static inline void do_fp_trap(struct pt_regs *regs, __u32 fpc)
|
||||
do_trap(regs, SIGFPE, si_code, "floating point exception");
|
||||
}
|
||||
|
||||
static void translation_exception(struct pt_regs *regs)
|
||||
static void translation_specification_exception(struct pt_regs *regs)
|
||||
{
|
||||
/* May never happen. */
|
||||
panic("Translation exception");
|
||||
panic("Translation-Specification Exception");
|
||||
}
|
||||
|
||||
static void illegal_op(struct pt_regs *regs)
|
||||
@ -368,7 +368,7 @@ static void (*pgm_check_table[128])(struct pt_regs *regs) = {
|
||||
[0x0f] = hfp_divide_exception,
|
||||
[0x10] = do_dat_exception,
|
||||
[0x11] = do_dat_exception,
|
||||
[0x12] = translation_exception,
|
||||
[0x12] = translation_specification_exception,
|
||||
[0x13] = special_op_exception,
|
||||
[0x14] = default_trap_handler,
|
||||
[0x15] = operand_exception,
|
||||
|
@ -64,8 +64,8 @@ bool unwind_next_frame(struct unwind_state *state)
|
||||
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
||||
reliable = false;
|
||||
regs = NULL;
|
||||
if (!__kernel_text_address(ip)) {
|
||||
/* skip bogus %r14 */
|
||||
/* skip bogus %r14 or if is the same as regs->psw.addr */
|
||||
if (!__kernel_text_address(ip) || state->ip == unwind_recover_ret_addr(state, ip)) {
|
||||
state->regs = NULL;
|
||||
return unwind_next_frame(state);
|
||||
}
|
||||
@ -103,13 +103,11 @@ bool unwind_next_frame(struct unwind_state *state)
|
||||
if (sp & 0x7)
|
||||
goto out_err;
|
||||
|
||||
ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, (void *) sp);
|
||||
|
||||
/* Update unwind state */
|
||||
state->sp = sp;
|
||||
state->ip = ip;
|
||||
state->regs = regs;
|
||||
state->reliable = reliable;
|
||||
state->ip = unwind_recover_ret_addr(state, ip);
|
||||
return true;
|
||||
|
||||
out_err:
|
||||
@ -161,12 +159,10 @@ void __unwind_start(struct unwind_state *state, struct task_struct *task,
|
||||
ip = READ_ONCE_NOCHECK(sf->gprs[8]);
|
||||
}
|
||||
|
||||
ip = ftrace_graph_ret_addr(state->task, &state->graph_idx, ip, NULL);
|
||||
|
||||
/* Update unwind state */
|
||||
state->sp = sp;
|
||||
state->ip = ip;
|
||||
state->reliable = true;
|
||||
state->ip = unwind_recover_ret_addr(state, ip);
|
||||
|
||||
if (!first_frame)
|
||||
return;
|
||||
|
@ -75,7 +75,7 @@ static inline int arch_load_niai4(int *lock)
|
||||
int owner;
|
||||
|
||||
asm_inline volatile(
|
||||
ALTERNATIVE("", ".long 0xb2fa0040", 49) /* NIAI 4 */
|
||||
ALTERNATIVE("", ".insn rre,0xb2fa0000,4,0", 49) /* NIAI 4 */
|
||||
" l %0,%1\n"
|
||||
: "=d" (owner) : "Q" (*lock) : "memory");
|
||||
return owner;
|
||||
@ -86,7 +86,7 @@ static inline int arch_cmpxchg_niai8(int *lock, int old, int new)
|
||||
int expected = old;
|
||||
|
||||
asm_inline volatile(
|
||||
ALTERNATIVE("", ".long 0xb2fa0080", 49) /* NIAI 8 */
|
||||
ALTERNATIVE("", ".insn rre,0xb2fa0000,8,0", 49) /* NIAI 8 */
|
||||
" cs %0,%3,%1\n"
|
||||
: "=d" (old), "=Q" (*lock)
|
||||
: "0" (old), "d" (new), "Q" (*lock)
|
||||
|
@ -47,7 +47,7 @@ static void print_backtrace(char *bt)
|
||||
static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
|
||||
unsigned long sp)
|
||||
{
|
||||
int frame_count, prev_is_func2, seen_func2_func1;
|
||||
int frame_count, prev_is_func2, seen_func2_func1, seen_kretprobe_trampoline;
|
||||
const int max_frames = 128;
|
||||
struct unwind_state state;
|
||||
size_t bt_pos = 0;
|
||||
@ -63,6 +63,7 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
|
||||
frame_count = 0;
|
||||
prev_is_func2 = 0;
|
||||
seen_func2_func1 = 0;
|
||||
seen_kretprobe_trampoline = 0;
|
||||
unwind_for_each_frame(&state, task, regs, sp) {
|
||||
unsigned long addr = unwind_get_return_address(&state);
|
||||
char sym[KSYM_SYMBOL_LEN];
|
||||
@ -88,6 +89,8 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
|
||||
if (prev_is_func2 && str_has_prefix(sym, "unwindme_func1"))
|
||||
seen_func2_func1 = 1;
|
||||
prev_is_func2 = str_has_prefix(sym, "unwindme_func2");
|
||||
if (str_has_prefix(sym, "__kretprobe_trampoline+0x0/"))
|
||||
seen_kretprobe_trampoline = 1;
|
||||
}
|
||||
|
||||
/* Check the results. */
|
||||
@ -103,6 +106,10 @@ static noinline int test_unwind(struct task_struct *task, struct pt_regs *regs,
|
||||
kunit_err(current_test, "Maximum number of frames exceeded\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (seen_kretprobe_trampoline) {
|
||||
kunit_err(current_test, "__kretprobe_trampoline+0x0 in unwinding results\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
if (ret || force_bt)
|
||||
print_backtrace(bt);
|
||||
kfree(bt);
|
||||
@ -132,36 +139,50 @@ static struct unwindme *unwindme;
|
||||
#define UWM_PGM 0x40 /* Unwind from program check handler */
|
||||
#define UWM_KPROBE_ON_FTRACE 0x80 /* Unwind from kprobe handler called via ftrace. */
|
||||
#define UWM_FTRACE 0x100 /* Unwind from ftrace handler. */
|
||||
#define UWM_KRETPROBE 0x200 /* Unwind kretprobe handlers. */
|
||||
#define UWM_KRETPROBE 0x200 /* Unwind through kretprobed function. */
|
||||
#define UWM_KRETPROBE_HANDLER 0x400 /* Unwind from kretprobe handler. */
|
||||
|
||||
static __always_inline unsigned long get_psw_addr(void)
|
||||
static __always_inline struct pt_regs fake_pt_regs(void)
|
||||
{
|
||||
unsigned long psw_addr;
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
regs.gprs[15] = current_stack_pointer();
|
||||
|
||||
asm volatile(
|
||||
"basr %[psw_addr],0\n"
|
||||
: [psw_addr] "=d" (psw_addr));
|
||||
return psw_addr;
|
||||
: [psw_addr] "=d" (regs.psw.addr));
|
||||
return regs;
|
||||
}
|
||||
|
||||
static int kretprobe_ret_handler(struct kretprobe_instance *ri, struct pt_regs *regs)
|
||||
{
|
||||
struct unwindme *u = unwindme;
|
||||
|
||||
if (!(u->flags & UWM_KRETPROBE_HANDLER))
|
||||
return 0;
|
||||
|
||||
u->ret = test_unwind(NULL, (u->flags & UWM_REGS) ? regs : NULL,
|
||||
(u->flags & UWM_SP) ? u->sp : 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static noinline notrace void test_unwind_kretprobed_func(void)
|
||||
static noinline notrace int test_unwind_kretprobed_func(struct unwindme *u)
|
||||
{
|
||||
asm volatile(" nop\n");
|
||||
struct pt_regs regs;
|
||||
|
||||
if (!(u->flags & UWM_KRETPROBE))
|
||||
return 0;
|
||||
|
||||
regs = fake_pt_regs();
|
||||
return test_unwind(NULL, (u->flags & UWM_REGS) ? ®s : NULL,
|
||||
(u->flags & UWM_SP) ? u->sp : 0);
|
||||
}
|
||||
|
||||
static noinline void test_unwind_kretprobed_func_caller(void)
|
||||
static noinline int test_unwind_kretprobed_func_caller(struct unwindme *u)
|
||||
{
|
||||
test_unwind_kretprobed_func();
|
||||
return test_unwind_kretprobed_func(u);
|
||||
}
|
||||
|
||||
static int test_unwind_kretprobe(struct unwindme *u)
|
||||
@ -187,10 +208,12 @@ static int test_unwind_kretprobe(struct unwindme *u)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
test_unwind_kretprobed_func_caller();
|
||||
ret = test_unwind_kretprobed_func_caller(u);
|
||||
unregister_kretprobe(&my_kretprobe);
|
||||
unwindme = NULL;
|
||||
return u->ret;
|
||||
if (u->flags & UWM_KRETPROBE_HANDLER)
|
||||
ret = u->ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kprobe_pre_handler(struct kprobe *p, struct pt_regs *regs)
|
||||
@ -304,16 +327,13 @@ static noinline int unwindme_func4(struct unwindme *u)
|
||||
return 0;
|
||||
} else if (u->flags & (UWM_PGM | UWM_KPROBE_ON_FTRACE)) {
|
||||
return test_unwind_kprobe(u);
|
||||
} else if (u->flags & (UWM_KRETPROBE)) {
|
||||
} else if (u->flags & (UWM_KRETPROBE | UWM_KRETPROBE_HANDLER)) {
|
||||
return test_unwind_kretprobe(u);
|
||||
} else if (u->flags & UWM_FTRACE) {
|
||||
return test_unwind_ftrace(u);
|
||||
} else {
|
||||
struct pt_regs regs;
|
||||
struct pt_regs regs = fake_pt_regs();
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
regs.psw.addr = get_psw_addr();
|
||||
regs.gprs[15] = current_stack_pointer();
|
||||
return test_unwind(NULL,
|
||||
(u->flags & UWM_REGS) ? ®s : NULL,
|
||||
(u->flags & UWM_SP) ? u->sp : 0);
|
||||
@ -452,6 +472,10 @@ static const struct test_params param_list[] = {
|
||||
TEST_WITH_FLAGS(UWM_KRETPROBE | UWM_SP),
|
||||
TEST_WITH_FLAGS(UWM_KRETPROBE | UWM_REGS),
|
||||
TEST_WITH_FLAGS(UWM_KRETPROBE | UWM_SP | UWM_REGS),
|
||||
TEST_WITH_FLAGS(UWM_KRETPROBE_HANDLER),
|
||||
TEST_WITH_FLAGS(UWM_KRETPROBE_HANDLER | UWM_SP),
|
||||
TEST_WITH_FLAGS(UWM_KRETPROBE_HANDLER | UWM_REGS),
|
||||
TEST_WITH_FLAGS(UWM_KRETPROBE_HANDLER | UWM_SP | UWM_REGS),
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -69,6 +69,7 @@ struct zpci_dev *get_zdev_by_fid(u32 fid)
|
||||
list_for_each_entry(tmp, &zpci_list, entry) {
|
||||
if (tmp->fid == fid) {
|
||||
zdev = tmp;
|
||||
zpci_zdev_get(zdev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -399,7 +400,7 @@ EXPORT_SYMBOL(pci_iounmap);
|
||||
static int pci_read(struct pci_bus *bus, unsigned int devfn, int where,
|
||||
int size, u32 *val)
|
||||
{
|
||||
struct zpci_dev *zdev = get_zdev_by_bus(bus, devfn);
|
||||
struct zpci_dev *zdev = zdev_from_bus(bus, devfn);
|
||||
|
||||
return (zdev) ? zpci_cfg_load(zdev, where, val, size) : -ENODEV;
|
||||
}
|
||||
@ -407,7 +408,7 @@ static int pci_read(struct pci_bus *bus, unsigned int devfn, int where,
|
||||
static int pci_write(struct pci_bus *bus, unsigned int devfn, int where,
|
||||
int size, u32 val)
|
||||
{
|
||||
struct zpci_dev *zdev = get_zdev_by_bus(bus, devfn);
|
||||
struct zpci_dev *zdev = zdev_from_bus(bus, devfn);
|
||||
|
||||
return (zdev) ? zpci_cfg_store(zdev, where, val, size) : -ENODEV;
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error);
|
||||
void zpci_release_device(struct kref *kref);
|
||||
static inline void zpci_zdev_put(struct zpci_dev *zdev)
|
||||
{
|
||||
kref_put(&zdev->kref, zpci_release_device);
|
||||
if (zdev)
|
||||
kref_put(&zdev->kref, zpci_release_device);
|
||||
}
|
||||
|
||||
static inline void zpci_zdev_get(struct zpci_dev *zdev)
|
||||
@ -32,8 +33,8 @@ void zpci_free_domain(int domain);
|
||||
int zpci_setup_bus_resources(struct zpci_dev *zdev,
|
||||
struct list_head *resources);
|
||||
|
||||
static inline struct zpci_dev *get_zdev_by_bus(struct pci_bus *bus,
|
||||
unsigned int devfn)
|
||||
static inline struct zpci_dev *zdev_from_bus(struct pci_bus *bus,
|
||||
unsigned int devfn)
|
||||
{
|
||||
struct zpci_bus *zbus = bus->sysdata;
|
||||
|
||||
|
@ -23,6 +23,8 @@
|
||||
#include <asm/clp.h>
|
||||
#include <uapi/asm/clp.h>
|
||||
|
||||
#include "pci_bus.h"
|
||||
|
||||
bool zpci_unique_uid;
|
||||
|
||||
void update_uid_checking(bool new)
|
||||
@ -404,8 +406,11 @@ static void __clp_add(struct clp_fh_list_entry *entry, void *data)
|
||||
return;
|
||||
|
||||
zdev = get_zdev_by_fid(entry->fid);
|
||||
if (!zdev)
|
||||
zpci_create_device(entry->fid, entry->fh, entry->config_state);
|
||||
if (zdev) {
|
||||
zpci_zdev_put(zdev);
|
||||
return;
|
||||
}
|
||||
zpci_create_device(entry->fid, entry->fh, entry->config_state);
|
||||
}
|
||||
|
||||
int clp_scan_pci_devices(void)
|
||||
|
@ -269,7 +269,7 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)
|
||||
pdev ? pci_name(pdev) : "n/a", ccdf->pec, ccdf->fid);
|
||||
|
||||
if (!pdev)
|
||||
return;
|
||||
goto no_pdev;
|
||||
|
||||
switch (ccdf->pec) {
|
||||
case 0x003a: /* Service Action or Error Recovery Successful */
|
||||
@ -286,6 +286,8 @@ static void __zpci_event_error(struct zpci_ccdf_err *ccdf)
|
||||
break;
|
||||
}
|
||||
pci_dev_put(pdev);
|
||||
no_pdev:
|
||||
zpci_zdev_put(zdev);
|
||||
}
|
||||
|
||||
void zpci_event_error(void *data)
|
||||
@ -314,6 +316,7 @@ static void zpci_event_hard_deconfigured(struct zpci_dev *zdev, u32 fh)
|
||||
static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
|
||||
{
|
||||
struct zpci_dev *zdev = get_zdev_by_fid(ccdf->fid);
|
||||
bool existing_zdev = !!zdev;
|
||||
enum zpci_state state;
|
||||
|
||||
zpci_dbg(3, "avl fid:%x, fh:%x, pec:%x\n",
|
||||
@ -378,6 +381,8 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (existing_zdev)
|
||||
zpci_zdev_put(zdev);
|
||||
}
|
||||
|
||||
void zpci_event_availability(void *data)
|
||||
|
@ -745,9 +745,7 @@ sclp_sync_wait(void)
|
||||
/* Loop until driver state indicates finished request */
|
||||
while (sclp_running_state != sclp_running_state_idle) {
|
||||
/* Check for expired request timer */
|
||||
if (timer_pending(&sclp_request_timer) &&
|
||||
get_tod_clock_fast() > timeout &&
|
||||
del_timer(&sclp_request_timer))
|
||||
if (get_tod_clock_fast() > timeout && del_timer(&sclp_request_timer))
|
||||
sclp_request_timer.function(&sclp_request_timer);
|
||||
cpu_relax();
|
||||
}
|
||||
|
@ -109,8 +109,7 @@ static void sclp_console_sync_queue(void)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&sclp_con_lock, flags);
|
||||
if (timer_pending(&sclp_con_timer))
|
||||
del_timer(&sclp_con_timer);
|
||||
del_timer(&sclp_con_timer);
|
||||
while (sclp_con_queue_running) {
|
||||
spin_unlock_irqrestore(&sclp_con_lock, flags);
|
||||
sclp_sync_wait();
|
||||
|
@ -231,8 +231,7 @@ sclp_vt220_emit_current(void)
|
||||
list_add_tail(&sclp_vt220_current_request->list,
|
||||
&sclp_vt220_outqueue);
|
||||
sclp_vt220_current_request = NULL;
|
||||
if (timer_pending(&sclp_vt220_timer))
|
||||
del_timer(&sclp_vt220_timer);
|
||||
del_timer(&sclp_vt220_timer);
|
||||
}
|
||||
sclp_vt220_flush_later = 0;
|
||||
}
|
||||
@ -776,8 +775,7 @@ static void __sclp_vt220_flush_buffer(void)
|
||||
|
||||
sclp_vt220_emit_current();
|
||||
spin_lock_irqsave(&sclp_vt220_lock, flags);
|
||||
if (timer_pending(&sclp_vt220_timer))
|
||||
del_timer(&sclp_vt220_timer);
|
||||
del_timer(&sclp_vt220_timer);
|
||||
while (sclp_vt220_queue_running) {
|
||||
spin_unlock_irqrestore(&sclp_vt220_lock, flags);
|
||||
sclp_sync_wait();
|
||||
|
@ -354,10 +354,10 @@ tape_34xx_unit_check(struct tape_device *device, struct tape_request *request,
|
||||
if ((
|
||||
sense[0] == SENSE_DATA_CHECK ||
|
||||
sense[0] == SENSE_EQUIPMENT_CHECK ||
|
||||
sense[0] == SENSE_EQUIPMENT_CHECK + SENSE_DEFERRED_UNIT_CHECK
|
||||
sense[0] == (SENSE_EQUIPMENT_CHECK | SENSE_DEFERRED_UNIT_CHECK)
|
||||
) && (
|
||||
sense[1] == SENSE_DRIVE_ONLINE ||
|
||||
sense[1] == SENSE_BEGINNING_OF_TAPE + SENSE_WRITE_MODE
|
||||
sense[1] == (SENSE_BEGINNING_OF_TAPE | SENSE_WRITE_MODE)
|
||||
)) {
|
||||
switch (request->op) {
|
||||
/*
|
||||
|
@ -113,16 +113,10 @@ ccw_device_timeout(struct timer_list *t)
|
||||
void
|
||||
ccw_device_set_timeout(struct ccw_device *cdev, int expires)
|
||||
{
|
||||
if (expires == 0) {
|
||||
if (expires == 0)
|
||||
del_timer(&cdev->private->timer);
|
||||
return;
|
||||
}
|
||||
if (timer_pending(&cdev->private->timer)) {
|
||||
if (mod_timer(&cdev->private->timer, jiffies + expires))
|
||||
return;
|
||||
}
|
||||
cdev->private->timer.expires = jiffies + expires;
|
||||
add_timer(&cdev->private->timer);
|
||||
else
|
||||
mod_timer(&cdev->private->timer, jiffies + expires);
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -112,16 +112,10 @@ static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires)
|
||||
{
|
||||
struct eadm_private *private = get_eadm_private(sch);
|
||||
|
||||
if (expires == 0) {
|
||||
if (expires == 0)
|
||||
del_timer(&private->timer);
|
||||
return;
|
||||
}
|
||||
if (timer_pending(&private->timer)) {
|
||||
if (mod_timer(&private->timer, jiffies + expires))
|
||||
return;
|
||||
}
|
||||
private->timer.expires = jiffies + expires;
|
||||
add_timer(&private->timer);
|
||||
else
|
||||
mod_timer(&private->timer, jiffies + expires);
|
||||
}
|
||||
|
||||
static void eadm_subchannel_irq(struct subchannel *sch)
|
||||
|
@ -315,6 +315,7 @@ struct ap_perms {
|
||||
unsigned long ioctlm[BITS_TO_LONGS(AP_IOCTLS)];
|
||||
unsigned long apm[BITS_TO_LONGS(AP_DEVICES)];
|
||||
unsigned long aqm[BITS_TO_LONGS(AP_DOMAINS)];
|
||||
unsigned long adm[BITS_TO_LONGS(AP_DOMAINS)];
|
||||
};
|
||||
extern struct ap_perms ap_perms;
|
||||
extern struct mutex ap_perms_mutex;
|
||||
|
@ -155,7 +155,7 @@ static int pkey_skey2pkey(const u8 *key, struct pkey_protkey *pkey)
|
||||
/*
|
||||
* The cca_xxx2protkey call may fail when a card has been
|
||||
* addressed where the master key was changed after last fetch
|
||||
* of the mkvp into the cache. Try 3 times: First witout verify
|
||||
* of the mkvp into the cache. Try 3 times: First without verify
|
||||
* then with verify and last round with verify and old master
|
||||
* key verification pattern match not ignored.
|
||||
*/
|
||||
|
@ -1189,13 +1189,6 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = {
|
||||
* @matrix_mdev: a mediated matrix device
|
||||
* @kvm: reference to KVM instance
|
||||
*
|
||||
* Note: The matrix_dev->lock must be taken prior to calling
|
||||
* this function; however, the lock will be temporarily released while the
|
||||
* guest's AP configuration is set to avoid a potential lockdep splat.
|
||||
* The kvm->lock is taken to set the guest's AP configuration which, under
|
||||
* certain circumstances, will result in a circular lock dependency if this is
|
||||
* done under the @matrix_mdev->lock.
|
||||
*
|
||||
* Return: 0 if no other mediated matrix device has a reference to @kvm;
|
||||
* otherwise, returns an -EPERM.
|
||||
*/
|
||||
@ -1269,18 +1262,11 @@ static int vfio_ap_mdev_iommu_notifier(struct notifier_block *nb,
|
||||
* by @matrix_mdev.
|
||||
*
|
||||
* @matrix_mdev: a matrix mediated device
|
||||
* @kvm: the pointer to the kvm structure being unset.
|
||||
*
|
||||
* Note: The matrix_dev->lock must be taken prior to calling
|
||||
* this function; however, the lock will be temporarily released while the
|
||||
* guest's AP configuration is cleared to avoid a potential lockdep splat.
|
||||
* The kvm->lock is taken to clear the guest's AP configuration which, under
|
||||
* certain circumstances, will result in a circular lock dependency if this is
|
||||
* done under the @matrix_mdev->lock.
|
||||
*/
|
||||
static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev,
|
||||
struct kvm *kvm)
|
||||
static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
|
||||
{
|
||||
struct kvm *kvm = matrix_mdev->kvm;
|
||||
|
||||
if (kvm && kvm->arch.crypto.crycbd) {
|
||||
down_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
kvm->arch.crypto.pqap_hook = NULL;
|
||||
@ -1311,7 +1297,7 @@ static int vfio_ap_mdev_group_notifier(struct notifier_block *nb,
|
||||
matrix_mdev = container_of(nb, struct ap_matrix_mdev, group_notifier);
|
||||
|
||||
if (!data)
|
||||
vfio_ap_mdev_unset_kvm(matrix_mdev, matrix_mdev->kvm);
|
||||
vfio_ap_mdev_unset_kvm(matrix_mdev);
|
||||
else if (vfio_ap_mdev_set_kvm(matrix_mdev, data))
|
||||
notify_rc = NOTIFY_DONE;
|
||||
|
||||
@ -1448,7 +1434,7 @@ static void vfio_ap_mdev_close_device(struct vfio_device *vdev)
|
||||
&matrix_mdev->iommu_notifier);
|
||||
vfio_unregister_notifier(vdev->dev, VFIO_GROUP_NOTIFY,
|
||||
&matrix_mdev->group_notifier);
|
||||
vfio_ap_mdev_unset_kvm(matrix_mdev, matrix_mdev->kvm);
|
||||
vfio_ap_mdev_unset_kvm(matrix_mdev);
|
||||
}
|
||||
|
||||
static int vfio_ap_mdev_get_device_info(unsigned long arg)
|
||||
|
@ -285,10 +285,53 @@ static ssize_t aqmask_store(struct device *dev,
|
||||
|
||||
static DEVICE_ATTR_RW(aqmask);
|
||||
|
||||
static ssize_t admask_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
int i, rc;
|
||||
struct zcdn_device *zcdndev = to_zcdn_dev(dev);
|
||||
|
||||
if (mutex_lock_interruptible(&ap_perms_mutex))
|
||||
return -ERESTARTSYS;
|
||||
|
||||
buf[0] = '0';
|
||||
buf[1] = 'x';
|
||||
for (i = 0; i < sizeof(zcdndev->perms.adm) / sizeof(long); i++)
|
||||
snprintf(buf + 2 + 2 * i * sizeof(long),
|
||||
PAGE_SIZE - 2 - 2 * i * sizeof(long),
|
||||
"%016lx", zcdndev->perms.adm[i]);
|
||||
buf[2 + 2 * i * sizeof(long)] = '\n';
|
||||
buf[2 + 2 * i * sizeof(long) + 1] = '\0';
|
||||
rc = 2 + 2 * i * sizeof(long) + 1;
|
||||
|
||||
mutex_unlock(&ap_perms_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static ssize_t admask_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int rc;
|
||||
struct zcdn_device *zcdndev = to_zcdn_dev(dev);
|
||||
|
||||
rc = ap_parse_mask_str(buf, zcdndev->perms.adm,
|
||||
AP_DOMAINS, &ap_perms_mutex);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(admask);
|
||||
|
||||
static struct attribute *zcdn_dev_attrs[] = {
|
||||
&dev_attr_ioctlmask.attr,
|
||||
&dev_attr_apmask.attr,
|
||||
&dev_attr_aqmask.attr,
|
||||
&dev_attr_admask.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
@ -880,11 +923,22 @@ static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms,
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
tdom = *domain;
|
||||
if (perms != &ap_perms && tdom < AP_DOMAINS) {
|
||||
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
|
||||
if (!test_bit_inv(tdom, perms->adm)) {
|
||||
rc = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
} else if ((ap_msg.flags & AP_MSG_FLAG_USAGE) == 0) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If a valid target domain is set and this domain is NOT a usage
|
||||
* domain but a control only domain, autoselect target domain.
|
||||
*/
|
||||
tdom = *domain;
|
||||
if (tdom < AP_DOMAINS &&
|
||||
!ap_test_config_usage_domain(tdom) &&
|
||||
ap_test_config_ctrl_domain(tdom))
|
||||
@ -1062,6 +1116,18 @@ static long _zcrypt_send_ep11_cprb(bool userspace, struct ap_perms *perms,
|
||||
if (rc)
|
||||
goto out_free;
|
||||
|
||||
if (perms != &ap_perms && domain < AUTOSEL_DOM) {
|
||||
if (ap_msg.flags & AP_MSG_FLAG_ADMIN) {
|
||||
if (!test_bit_inv(domain, perms->adm)) {
|
||||
rc = -ENODEV;
|
||||
goto out_free;
|
||||
}
|
||||
} else if ((ap_msg.flags & AP_MSG_FLAG_USAGE) == 0) {
|
||||
rc = -EOPNOTSUPP;
|
||||
goto out_free;
|
||||
}
|
||||
}
|
||||
|
||||
pref_zc = NULL;
|
||||
pref_zq = NULL;
|
||||
spin_lock(&zcrypt_list_lock);
|
||||
|
@ -90,7 +90,7 @@ static ssize_t online_store(struct device *dev,
|
||||
list_for_each_entry(zq, &zc->zqueues, list)
|
||||
maxzqs++;
|
||||
if (maxzqs > 0)
|
||||
zq_uelist = kcalloc(maxzqs + 1, sizeof(zq), GFP_ATOMIC);
|
||||
zq_uelist = kcalloc(maxzqs + 1, sizeof(*zq_uelist), GFP_ATOMIC);
|
||||
list_for_each_entry(zq, &zc->zqueues, list)
|
||||
if (zcrypt_queue_force_online(zq, online))
|
||||
if (zq_uelist) {
|
||||
|
@ -1109,7 +1109,7 @@ static int ep11_wrapkey(u16 card, u16 domain,
|
||||
if (kb->head.type == TOKTYPE_NON_CCA &&
|
||||
kb->head.version == TOKVER_EP11_AES) {
|
||||
has_header = true;
|
||||
keysize = kb->head.len < keysize ? kb->head.len : keysize;
|
||||
keysize = min_t(size_t, kb->head.len, keysize);
|
||||
}
|
||||
|
||||
/* request cprb and payload */
|
||||
|
Loading…
Reference in New Issue
Block a user