4e0502b8b3
Specify HAVE_JUMP_LABEL_BATCH in header file. This allows to make use of the arch_jump_label_transform_queue()/arch_jump_label_transform_apply() mechanism. However unlike on x86, which currently is the only user of this mechanism, the to be patched instructions are still directly modified. The only difference to before is that serialization is only done after all instructions have been modified. This way the number of serialization/synchronization events is reduced. Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
100 lines
2.4 KiB
C
100 lines
2.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Jump label s390 support
|
|
*
|
|
* Copyright IBM Corp. 2011
|
|
* Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
|
|
*/
|
|
#include <linux/uaccess.h>
|
|
#include <linux/jump_label.h>
|
|
#include <asm/text-patching.h>
|
|
#include <asm/ipl.h>
|
|
|
|
struct insn {
|
|
u16 opcode;
|
|
s32 offset;
|
|
} __packed;
|
|
|
|
static void jump_label_make_nop(struct jump_entry *entry, struct insn *insn)
|
|
{
|
|
/* brcl 0,offset */
|
|
insn->opcode = 0xc004;
|
|
insn->offset = (jump_entry_target(entry) - jump_entry_code(entry)) >> 1;
|
|
}
|
|
|
|
static void jump_label_make_branch(struct jump_entry *entry, struct insn *insn)
|
|
{
|
|
/* brcl 15,offset */
|
|
insn->opcode = 0xc0f4;
|
|
insn->offset = (jump_entry_target(entry) - jump_entry_code(entry)) >> 1;
|
|
}
|
|
|
|
static void jump_label_bug(struct jump_entry *entry, struct insn *expected,
|
|
struct insn *new)
|
|
{
|
|
unsigned char *ipc = (unsigned char *)jump_entry_code(entry);
|
|
unsigned char *ipe = (unsigned char *)expected;
|
|
unsigned char *ipn = (unsigned char *)new;
|
|
|
|
pr_emerg("Jump label code mismatch at %pS [%px]\n", ipc, ipc);
|
|
pr_emerg("Found: %6ph\n", ipc);
|
|
pr_emerg("Expected: %6ph\n", ipe);
|
|
pr_emerg("New: %6ph\n", ipn);
|
|
panic("Corrupted kernel text");
|
|
}
|
|
|
|
static struct insn orignop = {
|
|
.opcode = 0xc004,
|
|
.offset = JUMP_LABEL_NOP_OFFSET >> 1,
|
|
};
|
|
|
|
static void __jump_label_transform(struct jump_entry *entry,
|
|
enum jump_label_type type,
|
|
int init)
|
|
{
|
|
void *code = (void *)jump_entry_code(entry);
|
|
struct insn old, new;
|
|
|
|
if (type == JUMP_LABEL_JMP) {
|
|
jump_label_make_nop(entry, &old);
|
|
jump_label_make_branch(entry, &new);
|
|
} else {
|
|
jump_label_make_branch(entry, &old);
|
|
jump_label_make_nop(entry, &new);
|
|
}
|
|
if (init) {
|
|
if (memcmp(code, &orignop, sizeof(orignop)))
|
|
jump_label_bug(entry, &orignop, &new);
|
|
} else {
|
|
if (memcmp(code, &old, sizeof(old)))
|
|
jump_label_bug(entry, &old, &new);
|
|
}
|
|
s390_kernel_write(code, &new, sizeof(new));
|
|
}
|
|
|
|
void arch_jump_label_transform(struct jump_entry *entry,
|
|
enum jump_label_type type)
|
|
{
|
|
__jump_label_transform(entry, type, 0);
|
|
text_poke_sync();
|
|
}
|
|
|
|
bool arch_jump_label_transform_queue(struct jump_entry *entry,
|
|
enum jump_label_type type)
|
|
{
|
|
__jump_label_transform(entry, type, 0);
|
|
return true;
|
|
}
|
|
|
|
void arch_jump_label_transform_apply(void)
|
|
{
|
|
text_poke_sync();
|
|
}
|
|
|
|
void arch_jump_label_transform_static(struct jump_entry *entry,
|
|
enum jump_label_type type)
|
|
{
|
|
__jump_label_transform(entry, type, 1);
|
|
text_poke_sync();
|
|
}
|