| // SPDX-License-Identifier: GPL-2.0 | |
| #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
| #include <linux/init.h> | |
| #include <linux/module.h> | |
| #include <linux/pid.h> | |
| #include <linux/fs.h> | |
| #include <linux/sched/signal.h> | |
| #include "bpf_preload.h" | |
| extern char bpf_preload_umd_start; | |
| extern char bpf_preload_umd_end; | |
| static int preload(struct bpf_preload_info *obj); | |
| static int finish(void); | |
| static struct bpf_preload_ops umd_ops = { | |
| .info.driver_name = "bpf_preload", | |
| .preload = preload, | |
| .finish = finish, | |
| .owner = THIS_MODULE, | |
| }; | |
| static int preload(struct bpf_preload_info *obj) | |
| { | |
| int magic = BPF_PRELOAD_START; | |
| loff_t pos = 0; | |
| int i, err; | |
| ssize_t n; | |
| err = fork_usermode_driver(&umd_ops.info); | |
| if (err) | |
| return err; | |
| /* send the start magic to let UMD proceed with loading BPF progs */ | |
| n = kernel_write(umd_ops.info.pipe_to_umh, | |
| &magic, sizeof(magic), &pos); | |
| if (n != sizeof(magic)) | |
| return -EPIPE; | |
| /* receive bpf_link IDs and names from UMD */ | |
| pos = 0; | |
| for (i = 0; i < BPF_PRELOAD_LINKS; i++) { | |
| n = kernel_read(umd_ops.info.pipe_from_umh, | |
| &obj[i], sizeof(*obj), &pos); | |
| if (n != sizeof(*obj)) | |
| return -EPIPE; | |
| } | |
| return 0; | |
| } | |
| static int finish(void) | |
| { | |
| int magic = BPF_PRELOAD_END; | |
| struct pid *tgid; | |
| loff_t pos = 0; | |
| ssize_t n; | |
| /* send the last magic to UMD. It will do a normal exit. */ | |
| n = kernel_write(umd_ops.info.pipe_to_umh, | |
| &magic, sizeof(magic), &pos); | |
| if (n != sizeof(magic)) | |
| return -EPIPE; | |
| tgid = umd_ops.info.tgid; | |
| wait_event(tgid->wait_pidfd, thread_group_exited(tgid)); | |
| umd_ops.info.tgid = NULL; | |
| return 0; | |
| } | |
| static int __init load_umd(void) | |
| { | |
| int err; | |
| err = umd_load_blob(&umd_ops.info, &bpf_preload_umd_start, | |
| &bpf_preload_umd_end - &bpf_preload_umd_start); | |
| if (err) | |
| return err; | |
| bpf_preload_ops = &umd_ops; | |
| return err; | |
| } | |
| static void __exit fini_umd(void) | |
| { | |
| bpf_preload_ops = NULL; | |
| /* kill UMD in case it's still there due to earlier error */ | |
| kill_pid(umd_ops.info.tgid, SIGKILL, 1); | |
| umd_ops.info.tgid = NULL; | |
| umd_unload_blob(&umd_ops.info); | |
| } | |
| late_initcall(load_umd); | |
| module_exit(fini_umd); | |
| MODULE_LICENSE("GPL"); |