/ duct-tape / xnu / osfmk / kern / mk_timer.c
mk_timer.c
  1  /*
  2   * Copyright (c) 2000-2020 Apple Computer, 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  /*
 29   * Copyright (c) 2000 Apple Computer, Inc.  All rights reserved.
 30   *
 31   * HISTORY
 32   *
 33   * 29 June 2000 (debo)
 34   *  Created.
 35   */
 36  
 37  #include <mach/mach_types.h>
 38  #include <mach/mach_traps.h>
 39  #include <mach/mach_port_server.h>
 40  
 41  #include <mach/mk_timer.h>
 42  
 43  #include <ipc/ipc_space.h>
 44  
 45  #include <kern/lock_group.h>
 46  #include <kern/mk_timer.h>
 47  #include <kern/thread_call.h>
 48  #include <ipc/ipc_kmsg.h>
 49  
 50  struct mk_timer {
 51  	decl_simple_lock_data(, lock);
 52  	thread_call_data_t      mkt_thread_call;
 53  	uint32_t                is_dead:1,
 54  	    is_armed:1;
 55  	int                     active;
 56  	ipc_port_t              port;
 57  };
 58  
 59  #ifdef __DARLING__
 60  zone_t mk_timer_zone;
 61  
 62  void dtape_mk_timer_init(void) {
 63  	mk_timer_zone = zone_create("mk_timer", sizeof(struct mk_timer), ZC_NOENCRYPT);
 64  };
 65  #else
 66  static ZONE_DECLARE(mk_timer_zone, "mk_timer",
 67      sizeof(struct mk_timer), ZC_NOENCRYPT);
 68  #endif
 69  
 70  static mach_port_qos_t mk_timer_qos = {
 71  	.name       = FALSE,
 72  	.prealloc   = TRUE,
 73  	.len        = sizeof(mk_timer_expire_msg_t),
 74  };
 75  
 76  static void     mk_timer_expire(
 77  	void                    *p0,
 78  	void                    *p1);
 79  
 80  mach_port_name_t
 81  mk_timer_create_trap(
 82  	__unused struct mk_timer_create_trap_args *args)
 83  {
 84  	struct mk_timer*      timer;
 85  	ipc_space_t           myspace = current_space();
 86  	mach_port_name_t      name = MACH_PORT_NULL;
 87  	ipc_port_init_flags_t init_flags;
 88  	ipc_port_t            port;
 89  	kern_return_t         result;
 90  
 91  	/* Allocate and initialize local state of a timer object */
 92  	timer = (struct mk_timer*)zalloc(mk_timer_zone);
 93  	if (timer == NULL) {
 94  		return MACH_PORT_NULL;
 95  	}
 96  	simple_lock_init(&timer->lock, 0);
 97  	thread_call_setup(&timer->mkt_thread_call, mk_timer_expire, timer);
 98  	timer->is_armed = timer->is_dead = FALSE;
 99  	timer->active = 0;
100  
101  	/* Pre-allocate a kmsg for the timer messages */
102  	ipc_kmsg_t kmsg;
103  	kmsg = ipc_kmsg_prealloc(mk_timer_qos.len + MAX_TRAILER_SIZE);
104  	if (kmsg == IKM_NULL) {
105  		zfree(mk_timer_zone, timer);
106  		return MACH_PORT_NULL;
107  	}
108  
109  	init_flags = IPC_PORT_INIT_MESSAGE_QUEUE;
110  	result = ipc_port_alloc(myspace, init_flags, &name, &port);
111  	if (result != KERN_SUCCESS) {
112  		zfree(mk_timer_zone, timer);
113  		ipc_kmsg_free(kmsg);
114  		return MACH_PORT_NULL;
115  	}
116  
117  	/* Associate the pre-allocated kmsg with the port */
118  	ipc_kmsg_set_prealloc(kmsg, port);
119  
120  	/* port locked, receive right at user-space */
121  	ipc_kobject_set_atomically(port, (ipc_kobject_t)timer, IKOT_TIMER);
122  
123  	/* make a (naked) send right for the timer to keep */
124  	timer->port = ipc_port_make_send_locked(port);
125  
126  	ip_unlock(port);
127  
128  	return name;
129  }
130  
131  void
132  mk_timer_port_destroy(
133  	ipc_port_t                      port)
134  {
135  	struct mk_timer* timer = NULL;
136  
137  	ip_lock(port);
138  	if (ip_kotype(port) == IKOT_TIMER) {
139  		timer = (struct mk_timer*) ip_get_kobject(port);
140  		assert(timer != NULL);
141  		ipc_kobject_set_atomically(port, IKO_NULL, IKOT_NONE);
142  		simple_lock(&timer->lock, LCK_GRP_NULL);
143  		assert(timer->port == port);
144  	}
145  	ip_unlock(port);
146  
147  	if (timer != NULL) {
148  		if (thread_call_cancel(&timer->mkt_thread_call)) {
149  			timer->active--;
150  		}
151  		timer->is_armed = FALSE;
152  
153  		timer->is_dead = TRUE;
154  		if (timer->active == 0) {
155  			simple_unlock(&timer->lock);
156  			zfree(mk_timer_zone, timer);
157  
158  			ipc_port_release_send(port);
159  			return;
160  		}
161  
162  		simple_unlock(&timer->lock);
163  	}
164  }
165  
166  static void
167  mk_timer_expire(
168  	void                    *p0,
169  	__unused void           *p1)
170  {
171  	struct mk_timer* timer = p0;
172  
173  	simple_lock(&timer->lock, LCK_GRP_NULL);
174  
175  	if (timer->active > 1) {
176  		timer->active--;
177  		simple_unlock(&timer->lock);
178  		return;
179  	}
180  
181  	ipc_port_t port = timer->port;
182  	assert(port != IP_NULL);
183  	assert(timer->active == 1);
184  
185  	while (timer->is_armed && timer->active == 1) {
186  		mk_timer_expire_msg_t           msg;
187  
188  		timer->is_armed = FALSE;
189  		simple_unlock(&timer->lock);
190  
191  		msg.header.msgh_bits =
192  		    MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0);
193  		msg.header.msgh_remote_port = port;
194  		msg.header.msgh_local_port = MACH_PORT_NULL;
195  		msg.header.msgh_voucher_port = MACH_PORT_NULL;
196  		msg.header.msgh_id = 0;
197  
198  		msg.unused[0] = msg.unused[1] = msg.unused[2] = 0;
199  
200  		(void) mach_msg_send_from_kernel_proper(&msg.header, sizeof(msg));
201  
202  		simple_lock(&timer->lock, LCK_GRP_NULL);
203  	}
204  
205  	if (--timer->active == 0 && timer->is_dead) {
206  		simple_unlock(&timer->lock);
207  		zfree(mk_timer_zone, timer);
208  
209  		ipc_port_release_send(port);
210  		return;
211  	}
212  
213  	simple_unlock(&timer->lock);
214  }
215  
216  /*
217   * mk_timer_destroy_trap: Destroy the Mach port associated with a timer
218   *
219   * Parameters:  args                     User argument descriptor (see below)
220   *
221   * Indirect:     args->name               Mach port name
222   *
223   *
224   * Returns:        0                      Success
225   *                !0                      Not success
226   *
227   */
228  kern_return_t
229  mk_timer_destroy_trap(
230  	struct mk_timer_destroy_trap_args *args)
231  {
232  	mach_port_name_t        name = args->name;
233  	ipc_space_t                     myspace = current_space();
234  	ipc_port_t                      port;
235  	kern_return_t           result;
236  
237  	result = ipc_port_translate_receive(myspace, name, &port);
238  	if (result != KERN_SUCCESS) {
239  		return result;
240  	}
241  
242  	if (ip_kotype(port) == IKOT_TIMER) {
243  		ip_unlock(port);
244  		/* TODO: this should be mach_port_mod_refs */
245  		result = mach_port_destroy(myspace, name);
246  	} else {
247  		ip_unlock(port);
248  		result = KERN_INVALID_ARGUMENT;
249  	}
250  
251  	return result;
252  }
253  
254  /*
255   * mk_timer_arm_trap: Start (arm) a timer
256   *
257   * Parameters:  args                     User argument descriptor (see below)
258   *
259   * Indirect:     args->name               Mach port name
260   *               args->expire_time        Time when timer expires
261   *
262   *
263   * Returns:        0                      Success
264   *                !0                      Not success
265   *
266   */
267  
268  static kern_return_t
269  mk_timer_arm_trap_internal(mach_port_name_t name, uint64_t expire_time, uint64_t mk_leeway, uint64_t mk_timer_flags)
270  {
271  	struct mk_timer*                timer;
272  	ipc_space_t                     myspace = current_space();
273  	ipc_port_t                      port;
274  	kern_return_t                   result;
275  
276  	result = ipc_port_translate_receive(myspace, name, &port);
277  	if (result != KERN_SUCCESS) {
278  		return result;
279  	}
280  
281  	if (ip_kotype(port) == IKOT_TIMER) {
282  
283  		timer = (struct mk_timer*) ip_get_kobject(port);
284  		assert(timer != NULL);
285  
286  		simple_lock(&timer->lock, LCK_GRP_NULL);
287  		assert(timer->port == port);
288  		ip_unlock(port);
289  
290  		if (!timer->is_dead) {
291  			timer->is_armed = TRUE;
292  
293  			if (expire_time > mach_absolute_time()) {
294  				uint32_t tcflags = THREAD_CALL_DELAY_USER_NORMAL;
295  
296  				if (mk_timer_flags & MK_TIMER_CRITICAL) {
297  					tcflags = THREAD_CALL_DELAY_USER_CRITICAL;
298  				}
299  
300  				if (mk_leeway != 0) {
301  					tcflags |= THREAD_CALL_DELAY_LEEWAY;
302  				}
303  
304  				if (!thread_call_enter_delayed_with_leeway(
305  					    &timer->mkt_thread_call, NULL,
306  					    expire_time, mk_leeway, tcflags)) {
307  					timer->active++;
308  				}
309  			} else {
310  				if (!thread_call_enter1(&timer->mkt_thread_call, NULL)) {
311  					timer->active++;
312  				}
313  			}
314  		}
315  
316  		simple_unlock(&timer->lock);
317  	} else {
318  		ip_unlock(port);
319  		result = KERN_INVALID_ARGUMENT;
320  	}
321  	return result;
322  }
323  
324  kern_return_t
325  mk_timer_arm_trap(struct mk_timer_arm_trap_args *args)
326  {
327  	return mk_timer_arm_trap_internal(args->name, args->expire_time, 0, MK_TIMER_NORMAL);
328  }
329  
330  kern_return_t
331  mk_timer_arm_leeway_trap(struct mk_timer_arm_leeway_trap_args *args)
332  {
333  	return mk_timer_arm_trap_internal(args->name, args->expire_time, args->mk_leeway, args->mk_timer_flags);
334  }
335  
336  /*
337   * mk_timer_cancel_trap: Cancel a timer
338   *
339   * Parameters:  args                     User argument descriptor (see below)
340   *
341   * Indirect:     args->name               Mach port name
342   *               args->result_time        The armed time of the cancelled timer (return value)
343   *
344   *
345   * Returns:        0                      Success
346   *                !0                      Not success
347   *
348   */
349  kern_return_t
350  mk_timer_cancel_trap(
351  	struct mk_timer_cancel_trap_args *args)
352  {
353  	mach_port_name_t        name = args->name;
354  	mach_vm_address_t       result_time_addr = args->result_time;
355  	uint64_t                        armed_time = 0;
356  	struct mk_timer*                timer;
357  	ipc_space_t                     myspace = current_space();
358  	ipc_port_t                      port;
359  	kern_return_t           result;
360  
361  	result = ipc_port_translate_receive(myspace, name, &port);
362  	if (result != KERN_SUCCESS) {
363  		return result;
364  	}
365  
366  	if (ip_kotype(port) == IKOT_TIMER) {
367  		timer = (struct mk_timer*) ip_get_kobject(port);
368  		assert(timer != NULL);
369  		simple_lock(&timer->lock, LCK_GRP_NULL);
370  		assert(timer->port == port);
371  		ip_unlock(port);
372  
373  		if (timer->is_armed) {
374  			armed_time = thread_call_get_armed_deadline(&timer->mkt_thread_call);
375  			if (thread_call_cancel(&timer->mkt_thread_call)) {
376  				timer->active--;
377  			}
378  			timer->is_armed = FALSE;
379  		}
380  
381  		simple_unlock(&timer->lock);
382  	} else {
383  		ip_unlock(port);
384  		result = KERN_INVALID_ARGUMENT;
385  	}
386  
387  	if (result == KERN_SUCCESS && result_time_addr != 0) {
388  		if (copyout((void *)&armed_time, result_time_addr, sizeof(armed_time)) != 0) {
389  			result = KERN_FAILURE;
390  		}
391  	}
392  
393  	return result;
394  }