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 = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 171 msg.header.msgh_remote_port = port; 172 msg.header.msgh_local_port = MACH_PORT_NULL; 173 msg.header.msgh_reserved = msg.header.msgh_id = 0; 174 175 msg.unused[0] = msg.unused[1] = msg.unused[2] = 0; 176 177 (void) mach_msg_send_from_kernel_proper(&msg.header, sizeof (msg)); 178 179 simple_lock(&timer->lock); 180 } 181 182 if (--timer->active == 0 && timer->is_dead) { 183 simple_unlock(&timer->lock); 184 zfree(mk_timer_zone, timer); 185 186 ipc_port_release_send(port); 187 return; 188 } 189 190 simple_unlock(&timer->lock); 191} 192 193/* 194 * mk_timer_destroy_trap: Destroy the Mach port associated with a timer 195 * 196 * Parameters: args User argument descriptor (see below) 197 * 198 * Indirect: args->name Mach port name 199 * 200 * 201 * Returns: 0 Success 202 * !0 Not success 203 * 204 */ 205kern_return_t 206mk_timer_destroy_trap( 207 struct mk_timer_destroy_trap_args *args) 208{ 209 mach_port_name_t name = args->name; 210 ipc_space_t myspace = current_space(); 211 ipc_port_t port; 212 kern_return_t result; 213 214 result = ipc_port_translate_receive(myspace, name, &port); 215 if (result != KERN_SUCCESS) 216 return (result); 217 218 if (ip_kotype(port) == IKOT_TIMER) { 219 ip_unlock(port); 220 result = mach_port_destroy(myspace, name); 221 } 222 else { 223 ip_unlock(port); 224 result = KERN_INVALID_ARGUMENT; 225 } 226 227 return (result); 228} 229 230/* 231 * mk_timer_arm_trap: Start (arm) a timer 232 * 233 * Parameters: args User argument descriptor (see below) 234 * 235 * Indirect: args->name Mach port name 236 * args->expire_time Time when timer expires 237 * 238 * 239 * Returns: 0 Success 240 * !0 Not success 241 * 242 */ 243kern_return_t 244mk_timer_arm_trap( 245 struct mk_timer_arm_trap_args *args) 246{ 247 mach_port_name_t name = args->name; 248 uint64_t expire_time = args->expire_time; 249 mk_timer_t timer; 250 ipc_space_t myspace = current_space(); 251 ipc_port_t port; 252 kern_return_t result; 253 254 result = ipc_port_translate_receive(myspace, name, &port); 255 if (result != KERN_SUCCESS) 256 return (result); 257 258 if (ip_kotype(port) == IKOT_TIMER) { 259 timer = (mk_timer_t)port->ip_kobject; 260 assert(timer != NULL); 261 simple_lock(&timer->lock); 262 assert(timer->port == port); 263 ip_unlock(port); 264 265 if (!timer->is_dead) { 266 timer->is_armed = TRUE; 267 268 if (!thread_call_enter_delayed(&timer->call_entry, expire_time)) 269 timer->active++; 270 } 271 272 simple_unlock(&timer->lock); 273 } 274 else { 275 ip_unlock(port); 276 result = KERN_INVALID_ARGUMENT; 277 } 278 279 return (result); 280} 281 282/* 283 * mk_timer_cancel_trap: Cancel a timer 284 * 285 * Parameters: args User argument descriptor (see below) 286 * 287 * Indirect: args->name Mach port name 288 * args->result_time The armed time of the cancelled timer (return value) 289 * 290 * 291 * Returns: 0 Success 292 * !0 Not success 293 * 294 */ 295kern_return_t 296mk_timer_cancel_trap( 297 struct mk_timer_cancel_trap_args *args) 298{ 299 mach_port_name_t name = args->name; 300 mach_vm_address_t result_time_addr = args->result_time; 301 uint64_t armed_time = 0; 302 mk_timer_t timer; 303 ipc_space_t myspace = current_space(); 304 ipc_port_t port; 305 kern_return_t result; 306 307 result = ipc_port_translate_receive(myspace, name, &port); 308 if (result != KERN_SUCCESS) 309 return (result); 310 311 if (ip_kotype(port) == IKOT_TIMER) { 312 timer = (mk_timer_t)port->ip_kobject; 313 assert(timer != NULL); 314 simple_lock(&timer->lock); 315 assert(timer->port == port); 316 ip_unlock(port); 317 318 if (timer->is_armed) { 319 armed_time = timer->call_entry.tc_call.deadline; 320 if (thread_call_cancel(&timer->call_entry)) 321 timer->active--; 322 timer->is_armed = FALSE; 323 } 324 325 simple_unlock(&timer->lock); 326 } 327 else { 328 ip_unlock(port); 329 result = KERN_INVALID_ARGUMENT; 330 } 331 332 if (result == KERN_SUCCESS) 333 if ( result_time_addr != 0 && 334 copyout((void *)&armed_time, result_time_addr, 335 sizeof (armed_time)) != 0 ) 336 result = KERN_FAILURE; 337 338 return (result); 339} 340