Commit de855d39 authored by Nikita Panov's avatar Nikita Panov Committed by Denis Darvish
Browse files

arm64: make kernel text patching aware about replicas

kunpeng inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/IBOJU2



-------------------------------------------------

Acked-by: default avatarArtem Kuzin <artem.kuzin@huawei.com>
Acked-by: default avatarAlexander Grubnikov <alexander.grubnikov@huawei.com>
Acked-by: default avatarIlya Hanov <ilya.hanov@huawei-partners.com>
Acked-by: default avatarDenis Darvish <darvish.denis@huawei.com>
Signed-off-by: default avatarNikita Panov <panov.nikita@huawei.com>
parent 912efda3
Loading
Loading
Loading
Loading
+26 −1
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@

#include <linux/init.h>
#include <linux/cpu.h>
#include <linux/numa_kernel_replication.h>
#include <linux/elf.h>
#include <asm/cacheflush.h>
#include <asm/alternative.h>
@@ -139,6 +140,30 @@ static noinstr void clean_dcache_range_nopatch(u64 start, u64 end)
	} while (cur += d_size, cur < end);
}

static void __nocfi __write_alternatives(struct alt_instr *alt,
					 alternative_cb_t alt_cb,
					 __le32 *origptr, __le32 *updptr,
					 int nr_inst)
{
#ifdef CONFIG_KERNEL_REPLICATION
	if (is_text_replicated() && is_kernel_text((unsigned long)origptr)) {
		int nid;

		for_each_memory_node(nid) {
			__le32 *ptr = numa_get_replica(origptr, nid);

			alt_cb(alt, origptr, ptr, nr_inst);
			clean_dcache_range_nopatch((u64)ptr,
						   (u64)(ptr + nr_inst));
		}

		return;
	}
#endif /* CONFIG_KERNEL_REPLICATION */
	alt_cb(alt, origptr, updptr, nr_inst);
}


static void __apply_alternatives(const struct alt_region *region,
				 bool is_module,
				 unsigned long *cpucap_mask)
@@ -171,7 +196,7 @@ static void __apply_alternatives(const struct alt_region *region,
		else
			alt_cb = patch_alternative;

		alt_cb(alt, origptr, updptr, nr_inst);
		__write_alternatives(alt, alt_cb, origptr, updptr, nr_inst);

		if (!is_module) {
			clean_dcache_range_nopatch((u64)origptr,
+59 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
#include <linux/spinlock.h>
#include <linux/stop_machine.h>
#include <linux/uaccess.h>
#include <linux/numa_kernel_replication.h>

#include <asm/cacheflush.h>
#include <asm/fixmap.h>
@@ -15,6 +16,7 @@

static DEFINE_RAW_SPINLOCK(patch_lock);

#ifndef CONFIG_KERNEL_REPLICATION
static bool is_exit_text(unsigned long addr)
{
	/* discarded with init text/data */
@@ -41,10 +43,22 @@ static void __kprobes *patch_map(void *addr, int fixmap)
	else
		return addr;

	return (void *)set_fixmap_offset(fixmap, page_to_phys(page) +
			(uintaddr & ~PAGE_MASK));
}
#else
static void __kprobes *patch_map(void *addr, int fixmap, int nid)
{
	unsigned long uintaddr = (uintptr_t) addr;
	struct page *page;

	page = walk_to_page_node(nid, addr);
	BUG_ON(!page);

	return (void *)set_fixmap_offset(fixmap, page_to_phys(page) +
			(uintaddr & ~PAGE_MASK));
}
#endif /* CONFIG_KERNEL_REPLICATION */

static void __kprobes patch_unmap(int fixmap)
{
@@ -66,6 +80,28 @@ int __kprobes aarch64_insn_read(void *addr, u32 *insnp)
	return ret;
}

#ifdef CONFIG_KERNEL_REPLICATION
static int __kprobes __aarch64_insn_write(void *addr, __le32 insn)
{
	int nid;
	void *waddr = addr;
	unsigned long flags = 0;
	int ret;

	raw_spin_lock_irqsave(&patch_lock, flags);
	for_each_memory_node(nid) {
		waddr = patch_map(addr, FIX_TEXT_POKE0, nid);
		ret = copy_to_kernel_nofault(waddr, &insn, AARCH64_INSN_SIZE);
		patch_unmap(FIX_TEXT_POKE0);

		if (ret || !is_text_replicated())
			break;
	}
	raw_spin_unlock_irqrestore(&patch_lock, flags);

	return ret;
}
#else
static int __kprobes __aarch64_insn_write(void *addr, __le32 insn)
{
	void *waddr = addr;
@@ -82,12 +118,34 @@ static int __kprobes __aarch64_insn_write(void *addr, __le32 insn)

	return ret;
}
#endif /* CONFIG_KERNEL_REPLICATION */

int __kprobes aarch64_insn_write(void *addr, u32 insn)
{
	return __aarch64_insn_write(addr, cpu_to_le32(insn));
}

#ifdef CONFIG_KERNEL_REPLICATION
noinstr int aarch64_insn_write_literal_u64(void *addr, u64 val)
{
	int nid;
	u64 *waddr;
	unsigned long flags;
	int ret;

	raw_spin_lock_irqsave(&patch_lock, flags);
	for_each_memory_node(nid) {
		waddr = patch_map(addr, FIX_TEXT_POKE0, nid);

		ret = copy_to_kernel_nofault(waddr, &val, sizeof(val));

		patch_unmap(FIX_TEXT_POKE0);
	}
	raw_spin_unlock_irqrestore(&patch_lock, flags);

	return ret;
}
#else
noinstr int aarch64_insn_write_literal_u64(void *addr, u64 val)
{
	u64 *waddr;
@@ -104,6 +162,7 @@ noinstr int aarch64_insn_write_literal_u64(void *addr, u64 val)

	return ret;
}
#endif /* CONFIG_KERNEL_REPLICATION */

int __kprobes aarch64_insn_patch_text_nosync(void *addr, u32 insn)
{