diff --git a/kmod/patch/kpatch-patch-hook.c b/kmod/patch/kpatch-patch-hook.c index ca8d5d6..a70f340 100644 --- a/kmod/patch/kpatch-patch-hook.c +++ b/kmod/patch/kpatch-patch-hook.c @@ -22,6 +22,7 @@ #include #include #include +#include #include "kpatch.h" static bool replace; @@ -33,6 +34,15 @@ extern char __kpatch_dynrelas, __kpatch_dynrelas_end; static struct kpatch_module kpmod; static struct kobject *patch_kobj; +static struct kobject *functions_kobj; + +struct kpatch_func_obj { + struct kobject kobj; + struct kpatch_patch *patch; + char name[KSYM_NAME_LEN]; +}; + +static struct kpatch_func_obj **funcs = NULL; static ssize_t patch_enabled_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -67,12 +77,83 @@ static ssize_t patch_enabled_store(struct kobject *kobj, return count; } + static struct kobj_attribute patch_enabled_attr = __ATTR(enabled, 0644, patch_enabled_show, patch_enabled_store); +static ssize_t func_old_addr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct kpatch_func_obj *func = + container_of(kobj, struct kpatch_func_obj, kobj); + + return sprintf(buf, "0x%lx\n", func->patch->old_addr); +} + +static ssize_t func_new_addr_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct kpatch_func_obj *func = + container_of(kobj, struct kpatch_func_obj, kobj); + + return sprintf(buf, "0x%lx\n", func->patch->new_addr); +} + +static struct kobj_attribute old_addr_attr = + __ATTR(old_addr, S_IRUGO, func_old_addr_show, NULL); + +static struct kobj_attribute new_addr_attr = + __ATTR(new_addr, S_IRUGO, func_new_addr_show, NULL); + +static void func_kobj_free(struct kobject *kobj) +{ + struct kpatch_func_obj *func = + container_of(kobj, struct kpatch_func_obj, kobj); + kfree(func); +} + +static struct attribute *func_kobj_attrs[] = { + &old_addr_attr.attr, + &new_addr_attr.attr, + NULL, +}; + +static ssize_t func_kobj_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct kobj_attribute *func_attr = + container_of(attr, struct kobj_attribute, attr); + + return func_attr->show(kobj, func_attr, buf); +} + +static const struct sysfs_ops func_sysfs_ops = { + .show = func_kobj_show, +}; + +static struct kobj_type func_ktype = { + .release = func_kobj_free, + .sysfs_ops = &func_sysfs_ops, + .default_attrs = func_kobj_attrs, +}; + +static struct kpatch_func_obj *func_kobj_alloc(void) +{ + struct kpatch_func_obj *func; + func = kzalloc(sizeof(*func), GFP_KERNEL); + if (!func) + return NULL; + + kobject_init(&func->kobj, &func_ktype); + + return func; +} + static int __init patch_init(void) { int ret; + int i = 0; + struct kpatch_func_obj *func = NULL; kpmod.mod = THIS_MODULE; kpmod.patches = (struct kpatch_patch *)&__kpatch_patches; @@ -82,6 +163,13 @@ static int __init patch_init(void) kpmod.dynrelas_nr = (&__kpatch_dynrelas_end - &__kpatch_dynrelas) / sizeof(*kpmod.dynrelas); + funcs = kzalloc(kpmod.patches_nr * sizeof(struct kpatch_func_obj*), + GFP_KERNEL); + if (!funcs) { + ret = -ENOMEM; + goto err_ret; + } + patch_kobj = kobject_create_and_add(THIS_MODULE->name, kpatch_patches_kobj); if (!patch_kobj) { @@ -93,23 +181,62 @@ static int __init patch_init(void) if (ret) goto err_put; + functions_kobj = kobject_create_and_add("functions", patch_kobj); + if (!functions_kobj) { + ret = -ENOMEM; + goto err_sysfs; + } + + for (i = 0; i < kpmod.patches_nr; i++) { + func = func_kobj_alloc(); + if (!func) { + ret = -ENOMEM; + goto err_sysfs2; + } + funcs[i] = func; + + sprint_symbol_no_offset(func->name, kpmod.patches[i].old_addr); + + ret = kobject_add(&func->kobj, functions_kobj, + "%s", func->name); + if (ret) + goto err_sysfs2; + + func->patch = &kpmod.patches[i]; + } + ret = kpatch_register(&kpmod, replace); if (ret) - goto err_sysfs; + goto err_sysfs2; return 0; +err_sysfs2: + for (i = 0; i < kpmod.patches_nr; i++) { + if (funcs[i] != NULL) + kobject_put(&funcs[i]->kobj); + } + kobject_put(functions_kobj); err_sysfs: sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr); err_put: kobject_put(patch_kobj); err_free: + kfree(funcs); +err_ret: return ret; } static void __exit patch_exit(void) { + int i; WARN_ON(kpmod.enabled); + + for (i = 0; i < kpmod.patches_nr; i++) { + kobject_put(&funcs[i]->kobj); + } + kfree(funcs); + kobject_put(functions_kobj); sysfs_remove_file(patch_kobj, &patch_enabled_attr.attr); kobject_put(patch_kobj); }