summaryrefslogtreecommitdiff
path: root/scripts/livepatch/init.c
blob: 2274d8f5a4826cf16840eb145eb7432039760133 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// SPDX-License-Identifier: GPL-2.0
/*
 * Init code for a livepatch kernel module
 */

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/livepatch.h>

extern struct klp_object_ext __start_klp_objects[];
extern struct klp_object_ext __stop_klp_objects[];

static struct klp_patch *patch;

static int __init livepatch_mod_init(void)
{
	struct klp_object *objs;
	unsigned int nr_objs;
	int ret;

	nr_objs = __stop_klp_objects - __start_klp_objects;

	if (!nr_objs) {
		pr_err("nothing to patch!\n");
		ret = -EINVAL;
		goto err;
	}

	patch = kzalloc(sizeof(*patch), GFP_KERNEL);
	if (!patch) {
		ret = -ENOMEM;
		goto err;
	}

	objs = kzalloc(sizeof(struct klp_object) * (nr_objs + 1),  GFP_KERNEL);
	if (!objs) {
		ret = -ENOMEM;
		goto err_free_patch;
	}

	for (int i = 0; i < nr_objs; i++) {
		struct klp_object_ext *obj_ext = __start_klp_objects + i;
		struct klp_func_ext *funcs_ext = obj_ext->funcs;
		unsigned int nr_funcs = obj_ext->nr_funcs;
		struct klp_func *funcs = objs[i].funcs;
		struct klp_object *obj = objs + i;

		funcs = kzalloc(sizeof(struct klp_func) * (nr_funcs + 1), GFP_KERNEL);
		if (!funcs) {
			ret = -ENOMEM;
			for (int j = 0; j < i; j++)
				kfree(objs[i].funcs);
			goto err_free_objs;
		}

		for (int j = 0; j < nr_funcs; j++) {
			funcs[j].old_name   = funcs_ext[j].old_name;
			funcs[j].new_func   = funcs_ext[j].new_func;
			funcs[j].old_sympos = funcs_ext[j].sympos;
		}

		obj->name = obj_ext->name;
		obj->funcs = funcs;

		memcpy(&obj->callbacks, &obj_ext->callbacks, sizeof(struct klp_callbacks));
	}

	patch->mod = THIS_MODULE;
	patch->objs = objs;

	/* TODO patch->states */

#ifdef KLP_NO_REPLACE
	patch->replace = false;
#else
	patch->replace = true;
#endif

	return klp_enable_patch(patch);

err_free_objs:
	kfree(objs);
err_free_patch:
	kfree(patch);
err:
	return ret;
}

static void __exit livepatch_mod_exit(void)
{
	unsigned int nr_objs;

	nr_objs = __stop_klp_objects - __start_klp_objects;

	for (int i = 0; i < nr_objs; i++)
		kfree(patch->objs[i].funcs);

	kfree(patch->objs);
	kfree(patch);
}

module_init(livepatch_mod_init);
module_exit(livepatch_mod_exit);
MODULE_LICENSE("GPL");
MODULE_INFO(livepatch, "Y");
MODULE_DESCRIPTION("Livepatch module");