1/* 2 * Copyright (c) 2000-2004 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/mk_timer.h> 46#include <kern/thread_call.h> 47 48static zone_t mk_timer_zone; 49 50static mach_port_qos_t mk_timer_qos = { 51 FALSE, TRUE, 0, sizeof (mk_timer_expire_msg_t) 52}; 53 54static void mk_timer_expire( 55 void *p0, 56 void *p1); 57 58mach_port_name_t 59mk_timer_create_trap( 60 __unused struct mk_timer_create_trap_args *args) 61{ 62 mk_timer_t timer; 63 ipc_space_t myspace = current_space(); 64 mach_port_name_t name = MACH_PORT_NULL; 65 ipc_port_t port; 66 kern_return_t result; 67 68 timer = (mk_timer_t)zalloc(mk_timer_zone); 69 if (timer == NULL) 70 return (MACH_PORT_NULL); 71 72 result = mach_port_allocate_qos(myspace, MACH_PORT_RIGHT_RECEIVE, 73 &mk_timer_qos, &name); 74 if (result == KERN_SUCCESS) 75 result = ipc_port_translate_receive(myspace, name, &port); 76 77 if (result != KERN_SUCCESS) { 78 zfree(mk_timer_zone, timer); 79 80 return (MACH_PORT_NULL); 81 } 82 83 simple_lock_init(&timer->lock, 0); 84 thread_call_setup(&timer->call_entry, mk_timer_expire, timer); 85 timer->is_armed = timer->is_dead = FALSE; 86 timer->active = 0; 87 88 timer->port = port; 89 ipc_kobject_set_atomically(port, (ipc_kobject_t)timer, IKOT_TIMER); 90 91 port->ip_srights++; 92 ip_reference(port); 93 ip_unlock(port); 94 95 return (name); 96} 97 98void 99mk_timer_port_destroy( 100 ipc_port_t port) 101{ 102 mk_timer_t timer = NULL; 103 104 ip_lock(port); 105 if (ip_kotype(port) == IKOT_TIMER) { 106 timer = (mk_timer_t)port->ip_kobject; 107 assert(timer != NULL); 108 ipc_kobject_set_atomically(port, IKO_NULL, IKOT_NONE); 109 simple_lock(&timer->lock); 110 assert(timer->port == port); 111 } 112 ip_unlock(port); 113 114 if (timer != NULL) { 115 if (thread_call_cancel(&timer->call_entry)) 116 timer->active--; 117 timer->is_armed = FALSE; 118 119 timer->is_dead = TRUE; 120 if (timer->active == 0) { 121 simple_unlock(&timer->lock); 122 zfree(mk_timer_zone, timer); 123 124 ipc_port_release_send(port); 125 return; 126 } 127 128 simple_unlock(&timer->lock); 129 } 130} 131 132void 133mk_timer_init(void) 134{ 135 int s = sizeof (mk_timer_data_t); 136 137 assert(!(mk_timer_zone != NULL)); 138 139 mk_timer_zone = zinit(s, (4096 * s), (16 * s), "mk_timer"); 140 141 zone_change(mk_timer_zone, Z_NOENCRYPT, TRUE); 142} 143 144static void 145mk_timer_expire( 146 void *p0, 147 __unused void *p1) 148{ 149 mk_timer_t timer = p0; 150 ipc_port_t port; 151 152 simple_lock(&timer->lock); 153 154 if (timer->active > 1) { 155 timer->active--; 156 simple_unlock(&timer->lock); 157 return; 158 } 159 160 port = timer->port; 161 assert(port != IP_NULL); 162 assert(timer->active == 1); 163 164 while (timer->is_armed && timer->active == 1) { 165 mk_timer_expire_msg_t msg; 166 167 timer->is_armed = FALSE; 168 simple_unlock(&timer->lock); 169 170 msg.header.msgh_bits = 171 MACH_MSGH_BITS_SET(MACH_MSG_TYPE_COPY_SEND, 0, 0, 0); 172 msg.header.msgh_remote_port = port; 173 msg.header.msgh_local_port = MACH_PORT_NULL; 174 msg.header.msgh_voucher_port = MACH_PORT_NULL; 175 msg.header.msgh_id = 0; 176 177 msg.unused[0] = msg.unused[1] = msg.unused[2] = 0; 178 179 (void) mach_msg_send_from_kernel_proper(&msg.header, sizeof (msg)); 180 181 simple_lock(&timer->lock); 182 } 183 184 if (--timer->active == 0 && timer->is_dead) { 185 simple_unlock(&timer->lock); 186 zfree(mk_timer_zone, timer); 187 188 ipc_port_release_send(port); 189 return; 190 } 191 192 simple_unlock(&timer->lock); 193} 194 195/* 196 * mk_timer_destroy_trap: Destroy the Mach port associated with a timer 197 * 198 * Parameters: args User argument descriptor (see below) 199 * 200 * Indirect: args->name Mach port name 201 * 202 * 203 * Returns: 0 Success 204 * !0 Not success 205 * 206 */ 207kern_return_t 208mk_timer_destroy_trap( 209 struct mk_timer_destroy_trap_args *args) 210{ 211 mach_port_name_t name = args->name; 212 ipc_space_t myspace = current_space(); 213 ipc_port_t port; 214 kern_return_t result; 215 216 result = ipc_port_translate_receive(myspace, name, &port); 217 if (result != KERN_SUCCESS) 218 return (result); 219 220 if (ip_kotype(port) == IKOT_TIMER) { 221 ip_unlock(port); 222 result = mach_port_destroy(myspace, name); 223 } 224 else { 225 ip_unlock(port); 226 result = KERN_INVALID_ARGUMENT; 227 } 228 229 return (result); 230} 231 232/* 233 * mk_timer_arm_trap: Start (arm) a timer 234 * 235 * Parameters: args User argument descriptor (see below) 236 * 237 * Indirect: args->name Mach port name 238 * args->expire_time Time when timer expires 239 * 240 * 241 * Returns: 0 Success 242 * !0 Not success 243 * 244 */ 245kern_return_t 246mk_timer_arm_trap( 247 struct mk_timer_arm_trap_args *args) 248{ 249 mach_port_name_t name = args->name; 250 uint64_t expire_time = args->expire_time; 251 mk_timer_t timer; 252 ipc_space_t myspace = current_space(); 253 ipc_port_t port; 254 kern_return_t result; 255 256 result = ipc_port_translate_receive(myspace, name, &port); 257 if (result != KERN_SUCCESS) 258 return (result); 259 260 if (ip_kotype(port) == IKOT_TIMER) { 261 timer = (mk_timer_t)port->ip_kobject; 262 assert(timer != NULL); 263 264 simple_lock(&timer->lock); 265 assert(timer->port == port); 266 ip_unlock(port); 267 268 if (!timer->is_dead) { 269 timer->is_armed = TRUE; 270 271 if (expire_time > mach_absolute_time()) { 272 if (!thread_call_enter_delayed_with_leeway(&timer->call_entry, NULL, 273 expire_time, 0, THREAD_CALL_DELAY_USER_NORMAL)) 274 timer->active++; 275 } 276 else { 277 if (!thread_call_enter1(&timer->call_entry, NULL)) 278 timer->active++; 279 } 280 } 281 282 simple_unlock(&timer->lock); 283 } 284 else { 285 ip_unlock(port); 286 result = KERN_INVALID_ARGUMENT; 287 } 288 289 return (result); 290} 291 292/* 293 * mk_timer_cancel_trap: Cancel a timer 294 * 295 * Parameters: args User argument descriptor (see below) 296 * 297 * Indirect: args->name Mach port name 298 * args->result_time The armed time of the cancelled timer (return value) 299 * 300 * 301 * Returns: 0 Success 302 * !0 Not success 303 * 304 */ 305kern_return_t 306mk_timer_cancel_trap( 307 struct mk_timer_cancel_trap_args *args) 308{ 309 mach_port_name_t name = args->name; 310 mach_vm_address_t result_time_addr = args->result_time; 311 uint64_t armed_time = 0; 312 mk_timer_t timer; 313 ipc_space_t myspace = current_space(); 314 ipc_port_t port; 315 kern_return_t result; 316 317 result = ipc_port_translate_receive(myspace, name, &port); 318 if (result != KERN_SUCCESS) 319 return (result); 320 321 if (ip_kotype(port) == IKOT_TIMER) { 322 timer = (mk_timer_t)port->ip_kobject; 323 assert(timer != NULL); 324 simple_lock(&timer->lock); 325 assert(timer->port == port); 326 ip_unlock(port); 327 328 if (timer->is_armed) { 329 armed_time = timer->call_entry.tc_call.deadline; 330 if (thread_call_cancel(&timer->call_entry)) 331 timer->active--; 332 timer->is_armed = FALSE; 333 } 334 335 simple_unlock(&timer->lock); 336 } 337 else { 338 ip_unlock(port); 339 result = KERN_INVALID_ARGUMENT; 340 } 341 342 if (result == KERN_SUCCESS) 343 if ( result_time_addr != 0 && 344 copyout((void *)&armed_time, result_time_addr, 345 sizeof (armed_time)) != 0 ) 346 result = KERN_FAILURE; 347 348 return (result); 349} 350