1 | /* |
2 | * Copyright (c) 2015 Apple Inc. All rights reserved. |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ |
5 | * |
6 | * This file contains Original Code and/or Modifications of Original Code |
7 | * as defined in and that are subject to the Apple Public Source License |
8 | * Version 2.0 (the 'License'). You may not use this file except in |
9 | * compliance with the License. The rights granted to you under the License |
10 | * may not be used to create, or enable the creation or redistribution of, |
11 | * unlawful or unlicensed copies of an Apple operating system, or to |
12 | * circumvent, violate, or enable the circumvention or violation of, any |
13 | * terms of an Apple operating system software license agreement. |
14 | * |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. |
17 | * |
18 | * The Original Code and all software distributed under the License are |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. |
23 | * Please see the License for the specific language governing rights and |
24 | * limitations under the License. |
25 | * |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ |
28 | #include <sys/param.h> |
29 | #include <sys/kernel.h> |
30 | #include <sys/kernel_types.h> |
31 | #include <sys/sysproto.h> |
32 | #include <sys/priv.h> |
33 | #include <sys/work_interval.h> |
34 | #include <kern/sched_prim.h> |
35 | #include <kern/thread.h> |
36 | #include <kern/task.h> |
37 | #include <kern/work_interval.h> |
38 | |
39 | #include <libkern/libkern.h> |
40 | |
41 | int |
42 | work_interval_ctl(__unused proc_t p, struct work_interval_ctl_args *uap, |
43 | __unused int32_t *retval) |
44 | { |
45 | uint32_t operation = uap->operation; |
46 | int error = 0; |
47 | kern_return_t kret = KERN_SUCCESS; |
48 | struct work_interval_notification notification; |
49 | |
50 | struct work_interval_create_params create_params; |
51 | struct kern_work_interval_create_args create_args; |
52 | struct work_interval_workload_id_params workload_id_params; |
53 | struct kern_work_interval_workload_id_args workload_id_args; |
54 | mach_port_name_t port_name; |
55 | |
56 | switch (operation) { |
57 | case WORK_INTERVAL_OPERATION_CREATE: |
58 | return ENOTSUP; |
59 | case WORK_INTERVAL_OPERATION_CREATE2: |
60 | if (uap->arg == USER_ADDR_NULL || uap->work_interval_id != 0) { |
61 | return EINVAL; |
62 | } |
63 | if (uap->len < sizeof(create_params)) { |
64 | return EINVAL; |
65 | } |
66 | |
67 | if ((error = copyin(uap->arg, &create_params, sizeof(create_params)))) { |
68 | return error; |
69 | } |
70 | |
71 | if ((error = priv_check_cred(cred: kauth_cred_get(), PRIV_WORK_INTERVAL, flags: 0)) != 0) { |
72 | return error; |
73 | } |
74 | |
75 | create_args = (struct kern_work_interval_create_args) { |
76 | .wica_id = create_params.wicp_id, |
77 | .wica_port = create_params.wicp_port, |
78 | .wica_create_flags = create_params.wicp_create_flags, |
79 | }; |
80 | |
81 | kret = kern_work_interval_create(thread: current_thread(), create_params: &create_args); |
82 | |
83 | /* thread already has a work interval */ |
84 | if (kret == KERN_FAILURE) { |
85 | return EALREADY; |
86 | } |
87 | |
88 | /* port copyout failed */ |
89 | if (kret == KERN_RESOURCE_SHORTAGE) { |
90 | return ENOMEM; |
91 | } |
92 | |
93 | /* some other failure */ |
94 | if (kret != KERN_SUCCESS) { |
95 | return EINVAL; |
96 | } |
97 | |
98 | create_params = (struct work_interval_create_params) { |
99 | .wicp_id = create_args.wica_id, |
100 | .wicp_port = create_args.wica_port, |
101 | .wicp_create_flags = create_args.wica_create_flags, |
102 | }; |
103 | |
104 | if ((error = copyout(&create_params, uap->arg, sizeof(create_params)))) { |
105 | kern_work_interval_destroy(thread: current_thread(), work_interval_id: create_args.wica_id); |
106 | return error; |
107 | } |
108 | break; |
109 | case WORK_INTERVAL_OPERATION_GET_FLAGS: |
110 | if (uap->arg == USER_ADDR_NULL || uap->len < sizeof(create_params)) { |
111 | return EINVAL; |
112 | } |
113 | |
114 | port_name = (mach_port_name_t) uap->work_interval_id; |
115 | if (!MACH_PORT_VALID(port_name)) { |
116 | return EINVAL; |
117 | } |
118 | |
119 | create_params = (struct work_interval_create_params) { |
120 | .wicp_port = port_name |
121 | }; |
122 | |
123 | kret = kern_work_interval_get_flags_from_port(port_name, flags: &create_params.wicp_create_flags); |
124 | if (kret != KERN_SUCCESS) { |
125 | return EINVAL; |
126 | } |
127 | |
128 | if ((error = copyout(&create_params, uap->arg, sizeof(create_params)))) { |
129 | return error; |
130 | } |
131 | break; |
132 | case WORK_INTERVAL_OPERATION_SET_NAME: |
133 | if (uap->arg == USER_ADDR_NULL || uap->len < WORK_INTERVAL_NAME_MAX) { |
134 | return EINVAL; |
135 | } |
136 | port_name = (mach_port_name_t) uap->work_interval_id; |
137 | if (!MACH_PORT_VALID(port_name)) { |
138 | return EINVAL; |
139 | } |
140 | size_t wi_name_len = 0; |
141 | char wi_name[WORK_INTERVAL_NAME_MAX]; |
142 | if ((error = copyinstr(uaddr: uap->arg, kaddr: wi_name, len: sizeof(wi_name), done: &wi_name_len)) != 0) { |
143 | return error; |
144 | } |
145 | |
146 | kret = kern_work_interval_set_name(port_name, name: wi_name, len: wi_name_len); |
147 | if (kret != KERN_SUCCESS) { |
148 | return EINVAL; |
149 | } |
150 | break; |
151 | case WORK_INTERVAL_OPERATION_SET_WORKLOAD_ID: |
152 | if (uap->arg == USER_ADDR_NULL || |
153 | uap->len < sizeof(struct work_interval_workload_id_params)) { |
154 | return EINVAL; |
155 | } |
156 | port_name = (mach_port_name_t) uap->work_interval_id; |
157 | if (!MACH_PORT_VALID(port_name)) { |
158 | return EINVAL; |
159 | } |
160 | if ((error = copyin(uap->arg, &workload_id_params, |
161 | sizeof(workload_id_params)))) { |
162 | return error; |
163 | } |
164 | |
165 | size_t wlid_name_len = 0; |
166 | char wlid_name[WORK_INTERVAL_WORKLOAD_ID_NAME_MAX] = {}; |
167 | user_addr_t wlidp_name = CAST_USER_ADDR_T(workload_id_params.wlidp_name); |
168 | if (wlidp_name != USER_ADDR_NULL) { |
169 | if ((error = copyinstr(uaddr: wlidp_name, kaddr: wlid_name, len: sizeof(wlid_name), |
170 | done: &wlid_name_len)) != 0) { |
171 | return error; |
172 | } |
173 | } |
174 | |
175 | workload_id_args = (struct kern_work_interval_workload_id_args) { |
176 | .wlida_flags = workload_id_params.wlidp_flags, |
177 | .wlida_wicreate_flags = workload_id_params.wlidp_wicreate_flags, |
178 | .wlida_name = wlid_name, |
179 | }; |
180 | |
181 | kret = kern_work_interval_set_workload_id(port_name, workload_id_args: &workload_id_args); |
182 | if (kret != KERN_SUCCESS) { |
183 | return EINVAL; |
184 | } |
185 | |
186 | workload_id_params = (struct work_interval_workload_id_params) { |
187 | .wlidp_flags = workload_id_args.wlida_flags, |
188 | .wlidp_wicreate_flags = workload_id_args.wlida_wicreate_flags, |
189 | .wlidp_syscall_mask = { |
190 | [0] = workload_id_args.wlida_syscall_mask[0], |
191 | [1] = workload_id_args.wlida_syscall_mask[1], |
192 | }, |
193 | }; |
194 | |
195 | if ((error = copyout(&workload_id_params, uap->arg, |
196 | sizeof(workload_id_params)))) { |
197 | return error; |
198 | } |
199 | break; |
200 | case WORK_INTERVAL_OPERATION_DESTROY: |
201 | if (uap->arg != USER_ADDR_NULL || uap->work_interval_id == 0) { |
202 | return EINVAL; |
203 | } |
204 | |
205 | /* |
206 | * No privilege check, we assume a previous WORK_INTERVAL_OPERATION_CREATE |
207 | * operation would have allocated a work interval ID for the current |
208 | * thread, which the scheduler will validate. |
209 | */ |
210 | kret = kern_work_interval_destroy(thread: current_thread(), work_interval_id: uap->work_interval_id); |
211 | if (kret != KERN_SUCCESS) { |
212 | return EINVAL; |
213 | } |
214 | |
215 | break; |
216 | case WORK_INTERVAL_OPERATION_NOTIFY: |
217 | if (uap->arg == USER_ADDR_NULL || uap->work_interval_id == 0) { |
218 | return EINVAL; |
219 | } |
220 | |
221 | if (uap->len < sizeof(notification)) { |
222 | return EINVAL; |
223 | } |
224 | |
225 | /* |
226 | * No privilege check, we assume a previous WORK_INTERVAL_OPERATION_CREATE |
227 | * operation would have allocated a work interval ID for the current |
228 | * thread, which the scheduler will validate. |
229 | */ |
230 | if ((error = copyin(uap->arg, ¬ification, sizeof(notification)))) { |
231 | return error; |
232 | } |
233 | |
234 | |
235 | struct kern_work_interval_args kwi_args = { |
236 | .work_interval_id = uap->work_interval_id, |
237 | .start = notification.start, |
238 | .finish = notification.finish, |
239 | .deadline = notification.deadline, |
240 | .next_start = notification.next_start, |
241 | .notify_flags = notification.notify_flags, |
242 | .create_flags = notification.create_flags, |
243 | }; |
244 | |
245 | kret = kern_work_interval_notify(thread: current_thread(), kwi_args: &kwi_args); |
246 | if (kret != KERN_SUCCESS) { |
247 | return EINVAL; |
248 | } |
249 | |
250 | break; |
251 | case WORK_INTERVAL_OPERATION_JOIN: |
252 | if (uap->arg != USER_ADDR_NULL) { |
253 | return EINVAL; |
254 | } |
255 | |
256 | /* |
257 | * No privilege check, because the work interval port |
258 | * is a capability. |
259 | */ |
260 | kret = kern_work_interval_join(thread: current_thread(), |
261 | port_name: (mach_port_name_t)uap->work_interval_id); |
262 | if (kret != KERN_SUCCESS) { |
263 | return EINVAL; |
264 | } |
265 | |
266 | break; |
267 | |
268 | default: |
269 | return ENOTSUP; |
270 | } |
271 | |
272 | return error; |
273 | } |
274 | |