e73550e946d6aa194479fc51b868fb2f61fc55b1
[linux-2.6-microblaze.git] / kernel / usermode_driver.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * umd - User mode driver support
4  */
5 #include <linux/shmem_fs.h>
6 #include <linux/pipe_fs_i.h>
7 #include <linux/usermode_driver.h>
8
9 static LIST_HEAD(umh_list);
10 static DEFINE_MUTEX(umh_list_lock);
11
12 static int umd_setup(struct subprocess_info *info, struct cred *new)
13 {
14         struct umd_info *umd_info = info->data;
15         struct file *from_umh[2];
16         struct file *to_umh[2];
17         int err;
18
19         /* create pipe to send data to umh */
20         err = create_pipe_files(to_umh, 0);
21         if (err)
22                 return err;
23         err = replace_fd(0, to_umh[0], 0);
24         fput(to_umh[0]);
25         if (err < 0) {
26                 fput(to_umh[1]);
27                 return err;
28         }
29
30         /* create pipe to receive data from umh */
31         err = create_pipe_files(from_umh, 0);
32         if (err) {
33                 fput(to_umh[1]);
34                 replace_fd(0, NULL, 0);
35                 return err;
36         }
37         err = replace_fd(1, from_umh[1], 0);
38         fput(from_umh[1]);
39         if (err < 0) {
40                 fput(to_umh[1]);
41                 replace_fd(0, NULL, 0);
42                 fput(from_umh[0]);
43                 return err;
44         }
45
46         umd_info->pipe_to_umh = to_umh[1];
47         umd_info->pipe_from_umh = from_umh[0];
48         umd_info->pid = task_pid_nr(current);
49         current->flags |= PF_UMH;
50         return 0;
51 }
52
53 static void umd_cleanup(struct subprocess_info *info)
54 {
55         struct umd_info *umd_info = info->data;
56
57         /* cleanup if umh_setup() was successful but exec failed */
58         if (info->retval) {
59                 fput(umd_info->pipe_to_umh);
60                 fput(umd_info->pipe_from_umh);
61         }
62 }
63
64 /**
65  * fork_usermode_blob - fork a blob of bytes as a usermode process
66  * @data: a blob of bytes that can be do_execv-ed as a file
67  * @len: length of the blob
68  * @info: information about usermode process (shouldn't be NULL)
69  *
70  * If info->cmdline is set it will be used as command line for the
71  * user process, else "usermodehelper" is used.
72  *
73  * Returns either negative error or zero which indicates success
74  * in executing a blob of bytes as a usermode process. In such
75  * case 'struct umd_info *info' is populated with two pipes
76  * and a pid of the process. The caller is responsible for health
77  * check of the user process, killing it via pid, and closing the
78  * pipes when user process is no longer needed.
79  */
80 int fork_usermode_blob(void *data, size_t len, struct umd_info *info)
81 {
82         const char *cmdline = (info->cmdline) ? info->cmdline : "usermodehelper";
83         struct subprocess_info *sub_info;
84         char **argv = NULL;
85         struct file *file;
86         ssize_t written;
87         loff_t pos = 0;
88         int err;
89
90         file = shmem_kernel_file_setup("", len, 0);
91         if (IS_ERR(file))
92                 return PTR_ERR(file);
93
94         written = kernel_write(file, data, len, &pos);
95         if (written != len) {
96                 err = written;
97                 if (err >= 0)
98                         err = -ENOMEM;
99                 goto out;
100         }
101
102         err = -ENOMEM;
103         argv = argv_split(GFP_KERNEL, cmdline, NULL);
104         if (!argv)
105                 goto out;
106
107         sub_info = call_usermodehelper_setup("none", argv, NULL, GFP_KERNEL,
108                                              umd_setup, umd_cleanup, info);
109         if (!sub_info)
110                 goto out;
111
112         sub_info->file = file;
113         err = call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
114         if (!err) {
115                 mutex_lock(&umh_list_lock);
116                 list_add(&info->list, &umh_list);
117                 mutex_unlock(&umh_list_lock);
118         }
119 out:
120         if (argv)
121                 argv_free(argv);
122         fput(file);
123         return err;
124 }
125 EXPORT_SYMBOL_GPL(fork_usermode_blob);
126
127 void __exit_umh(struct task_struct *tsk)
128 {
129         struct umd_info *info;
130         pid_t pid = tsk->pid;
131
132         mutex_lock(&umh_list_lock);
133         list_for_each_entry(info, &umh_list, list) {
134                 if (info->pid == pid) {
135                         list_del(&info->list);
136                         mutex_unlock(&umh_list_lock);
137                         goto out;
138                 }
139         }
140         mutex_unlock(&umh_list_lock);
141         return;
142 out:
143         if (info->cleanup)
144                 info->cleanup(info);
145 }
146