Linux Audio

Check our new training course

Loading...
v5.9
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2020 SiFive
  4 */
  5
  6#include <linux/spinlock.h>
  7#include <linux/mm.h>
  8#include <linux/memory.h>
  9#include <linux/uaccess.h>
 10#include <linux/stop_machine.h>
 11#include <asm/kprobes.h>
 12#include <asm/cacheflush.h>
 13#include <asm/fixmap.h>
 14#include <asm/patch.h>
 15
 16struct patch_insn {
 17	void *addr;
 18	u32 insn;
 19	atomic_t cpu_count;
 20};
 21
 22#ifdef CONFIG_MMU
 23static void *patch_map(void *addr, int fixmap)
 
 
 
 
 
 24{
 25	uintptr_t uintaddr = (uintptr_t) addr;
 26	struct page *page;
 27
 28	if (core_kernel_text(uintaddr))
 29		page = phys_to_page(__pa_symbol(addr));
 30	else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
 31		page = vmalloc_to_page(addr);
 32	else
 33		return addr;
 34
 35	BUG_ON(!page);
 36
 37	return (void *)set_fixmap_offset(fixmap, page_to_phys(page) +
 38					 (uintaddr & ~PAGE_MASK));
 39}
 40NOKPROBE_SYMBOL(patch_map);
 41
 42static void patch_unmap(int fixmap)
 43{
 44	clear_fixmap(fixmap);
 45}
 46NOKPROBE_SYMBOL(patch_unmap);
 47
 48static int patch_insn_write(void *addr, const void *insn, size_t len)
 49{
 50	void *waddr = addr;
 51	bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE;
 52	int ret;
 53
 54	/*
 55	 * Before reaching here, it was expected to lock the text_mutex
 56	 * already, so we don't need to give another lock here and could
 57	 * ensure that it was safe between each cores.
 58	 */
 59	lockdep_assert_held(&text_mutex);
 60
 61	if (across_pages)
 62		patch_map(addr + len, FIX_TEXT_POKE1);
 63
 64	waddr = patch_map(addr, FIX_TEXT_POKE0);
 65
 66	ret = copy_to_kernel_nofault(waddr, insn, len);
 67
 68	patch_unmap(FIX_TEXT_POKE0);
 69
 70	if (across_pages)
 71		patch_unmap(FIX_TEXT_POKE1);
 72
 73	return ret;
 74}
 75NOKPROBE_SYMBOL(patch_insn_write);
 76#else
 77static int patch_insn_write(void *addr, const void *insn, size_t len)
 78{
 79	return copy_to_kernel_nofault(addr, insn, len);
 80}
 81NOKPROBE_SYMBOL(patch_insn_write);
 82#endif /* CONFIG_MMU */
 83
 84int patch_text_nosync(void *addr, const void *insns, size_t len)
 85{
 86	u32 *tp = addr;
 87	int ret;
 88
 89	ret = patch_insn_write(tp, insns, len);
 90
 91	if (!ret)
 92		flush_icache_range((uintptr_t) tp, (uintptr_t) tp + len);
 93
 94	return ret;
 95}
 96NOKPROBE_SYMBOL(patch_text_nosync);
 97
 98static int patch_text_cb(void *data)
 99{
100	struct patch_insn *patch = data;
101	int ret = 0;
102
103	if (atomic_inc_return(&patch->cpu_count) == 1) {
104		ret =
105		    patch_text_nosync(patch->addr, &patch->insn,
106					    GET_INSN_LENGTH(patch->insn));
107		atomic_inc(&patch->cpu_count);
108	} else {
109		while (atomic_read(&patch->cpu_count) <= num_online_cpus())
110			cpu_relax();
111		smp_mb();
112	}
113
114	return ret;
115}
116NOKPROBE_SYMBOL(patch_text_cb);
117
118int patch_text(void *addr, u32 insn)
119{
120	struct patch_insn patch = {
121		.addr = addr,
122		.insn = insn,
123		.cpu_count = ATOMIC_INIT(0),
124	};
125
126	return stop_machine_cpuslocked(patch_text_cb,
127				       &patch, cpu_online_mask);
128}
129NOKPROBE_SYMBOL(patch_text);
v6.2
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2020 SiFive
  4 */
  5
  6#include <linux/spinlock.h>
  7#include <linux/mm.h>
  8#include <linux/memory.h>
  9#include <linux/uaccess.h>
 10#include <linux/stop_machine.h>
 11#include <asm/kprobes.h>
 12#include <asm/cacheflush.h>
 13#include <asm/fixmap.h>
 14#include <asm/patch.h>
 15
 16struct patch_insn {
 17	void *addr;
 18	u32 insn;
 19	atomic_t cpu_count;
 20};
 21
 22#ifdef CONFIG_MMU
 23/*
 24 * The fix_to_virt(, idx) needs a const value (not a dynamic variable of
 25 * reg-a0) or BUILD_BUG_ON failed with "idx >= __end_of_fixed_addresses".
 26 * So use '__always_inline' and 'const unsigned int fixmap' here.
 27 */
 28static __always_inline void *patch_map(void *addr, const unsigned int fixmap)
 29{
 30	uintptr_t uintaddr = (uintptr_t) addr;
 31	struct page *page;
 32
 33	if (core_kernel_text(uintaddr))
 34		page = phys_to_page(__pa_symbol(addr));
 35	else if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
 36		page = vmalloc_to_page(addr);
 37	else
 38		return addr;
 39
 40	BUG_ON(!page);
 41
 42	return (void *)set_fixmap_offset(fixmap, page_to_phys(page) +
 43					 (uintaddr & ~PAGE_MASK));
 44}
 
 45
 46static void patch_unmap(int fixmap)
 47{
 48	clear_fixmap(fixmap);
 49}
 50NOKPROBE_SYMBOL(patch_unmap);
 51
 52static int patch_insn_write(void *addr, const void *insn, size_t len)
 53{
 54	void *waddr = addr;
 55	bool across_pages = (((uintptr_t) addr & ~PAGE_MASK) + len) > PAGE_SIZE;
 56	int ret;
 57
 58	/*
 59	 * Before reaching here, it was expected to lock the text_mutex
 60	 * already, so we don't need to give another lock here and could
 61	 * ensure that it was safe between each cores.
 62	 */
 63	lockdep_assert_held(&text_mutex);
 64
 65	if (across_pages)
 66		patch_map(addr + len, FIX_TEXT_POKE1);
 67
 68	waddr = patch_map(addr, FIX_TEXT_POKE0);
 69
 70	ret = copy_to_kernel_nofault(waddr, insn, len);
 71
 72	patch_unmap(FIX_TEXT_POKE0);
 73
 74	if (across_pages)
 75		patch_unmap(FIX_TEXT_POKE1);
 76
 77	return ret;
 78}
 79NOKPROBE_SYMBOL(patch_insn_write);
 80#else
 81static int patch_insn_write(void *addr, const void *insn, size_t len)
 82{
 83	return copy_to_kernel_nofault(addr, insn, len);
 84}
 85NOKPROBE_SYMBOL(patch_insn_write);
 86#endif /* CONFIG_MMU */
 87
 88int patch_text_nosync(void *addr, const void *insns, size_t len)
 89{
 90	u32 *tp = addr;
 91	int ret;
 92
 93	ret = patch_insn_write(tp, insns, len);
 94
 95	if (!ret)
 96		flush_icache_range((uintptr_t) tp, (uintptr_t) tp + len);
 97
 98	return ret;
 99}
100NOKPROBE_SYMBOL(patch_text_nosync);
101
102static int patch_text_cb(void *data)
103{
104	struct patch_insn *patch = data;
105	int ret = 0;
106
107	if (atomic_inc_return(&patch->cpu_count) == num_online_cpus()) {
108		ret =
109		    patch_text_nosync(patch->addr, &patch->insn,
110					    GET_INSN_LENGTH(patch->insn));
111		atomic_inc(&patch->cpu_count);
112	} else {
113		while (atomic_read(&patch->cpu_count) <= num_online_cpus())
114			cpu_relax();
115		smp_mb();
116	}
117
118	return ret;
119}
120NOKPROBE_SYMBOL(patch_text_cb);
121
122int patch_text(void *addr, u32 insn)
123{
124	struct patch_insn patch = {
125		.addr = addr,
126		.insn = insn,
127		.cpu_count = ATOMIC_INIT(0),
128	};
129
130	return stop_machine_cpuslocked(patch_text_cb,
131				       &patch, cpu_online_mask);
132}
133NOKPROBE_SYMBOL(patch_text);