// 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; 
 | 
    if (tgid) { 
 | 
        wait_event(tgid->wait_pidfd, thread_group_exited(tgid)); 
 | 
        umd_cleanup_helper(&umd_ops.info); 
 | 
    } 
 | 
    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) 
 | 
{ 
 | 
    struct pid *tgid; 
 | 
  
 | 
    bpf_preload_ops = NULL; 
 | 
  
 | 
    /* kill UMD in case it's still there due to earlier error */ 
 | 
    tgid = umd_ops.info.tgid; 
 | 
    if (tgid) { 
 | 
        kill_pid(tgid, SIGKILL, 1); 
 | 
  
 | 
        wait_event(tgid->wait_pidfd, thread_group_exited(tgid)); 
 | 
        umd_cleanup_helper(&umd_ops.info); 
 | 
    } 
 | 
    umd_unload_blob(&umd_ops.info); 
 | 
} 
 | 
late_initcall(load_umd); 
 | 
module_exit(fini_umd); 
 | 
MODULE_LICENSE("GPL"); 
 |