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 call_entry_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 142static void 143mk_timer_expire( 144 void *p0, 145 __unused void *p1) 146{ 147 mk_timer_t timer = p0; 148 ipc_port_t port; 149 150 simple_lock(&timer->lock); 151 152 if (timer->active > 1) { 153 timer->active--; 154 simple_unlock(&timer->lock); 155 return; 156 } 157 158 port = timer->port; 159 assert(port != IP_NULL); 160 assert(timer->active == 1); 161 162 while (timer->is_armed && timer->active == 1) { 163 mk_timer_expire_msg_t msg; 164 165 timer->is_armed = FALSE; 166 simple_unlock(&timer->lock); 167 168 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 169 msg.header.msgh_remote_port = port; 170 msg.header.msgh_local_port = MACH_PORT_NULL; 171 msg.header.msgh_reserved = msg.header.msgh_id = 0; 172 173 msg.unused[0] = msg.unused[1] = msg.unused[2] = 0; 174 175 (void) mach_msg_send_from_kernel(&msg.header, sizeof (msg)); 176 177 simple_lock(&timer->lock); 178 } 179 180 if (--timer->active == 0 && timer->is_dead) { 181 simple_unlock(&timer->lock); 182 zfree(mk_timer_zone, timer); 183 184 ipc_port_release_send(port); 185 return; 186 } 187 188 simple_unlock(&timer->lock); 189} 190 191kern_return_t 192mk_timer_destroy_trap( 193 struct mk_timer_destroy_trap_args *args) 194{ 195 mach_port_name_t name = args->name; 196 ipc_space_t myspace = current_space(); 197 ipc_port_t port; 198 kern_return_t result; 199 200 result = ipc_port_translate_receive(myspace, name, &port); 201 if (result != KERN_SUCCESS) 202 return (result); 203 204 if (ip_kotype(port) == IKOT_TIMER) { 205 ip_unlock(port); 206 result = mach_port_destroy(myspace, name); 207 } 208 else { 209 ip_unlock(port); 210 result = KERN_INVALID_ARGUMENT; 211 } 212 213 return (result); 214} 215 216kern_return_t 217mk_timer_arm_trap( 218 struct mk_timer_arm_trap_args *args) 219{ 220 mach_port_name_t name = args->name; 221 uint64_t expire_time = args->expire_time; 222 mk_timer_t timer; 223 ipc_space_t myspace = current_space(); 224 ipc_port_t port; 225 kern_return_t result; 226 227 result = ipc_port_translate_receive(myspace, name, &port); 228 if (result != KERN_SUCCESS) 229 return (result); 230 231 if (ip_kotype(port) == IKOT_TIMER) { 232 timer = (mk_timer_t)port->ip_kobject; 233 assert(timer != NULL); 234 simple_lock(&timer->lock); 235 assert(timer->port == port); 236 ip_unlock(port); 237 238 if (!timer->is_dead) { 239 timer->is_armed = TRUE; 240 241 if (!thread_call_enter_delayed(&timer->call_entry, expire_time)) 242 timer->active++; 243 } 244 245 simple_unlock(&timer->lock); 246 } 247 else { 248 ip_unlock(port); 249 result = KERN_INVALID_ARGUMENT; 250 } 251 252 return (result); 253} 254 255kern_return_t 256mk_timer_cancel_trap( 257 struct mk_timer_cancel_trap_args *args) 258{ 259 mach_port_name_t name = args->name; 260 mach_vm_address_t result_time_addr = args->result_time; 261 uint64_t armed_time = 0; 262 mk_timer_t timer; 263 ipc_space_t myspace = current_space(); 264 ipc_port_t port; 265 kern_return_t result; 266 267 result = ipc_port_translate_receive(myspace, name, &port); 268 if (result != KERN_SUCCESS) 269 return (result); 270 271 if (ip_kotype(port) == IKOT_TIMER) { 272 timer = (mk_timer_t)port->ip_kobject; 273 assert(timer != NULL); 274 simple_lock(&timer->lock); 275 assert(timer->port == port); 276 ip_unlock(port); 277 278 if (timer->is_armed) { 279 armed_time = timer->call_entry.deadline; 280 if (thread_call_cancel(&timer->call_entry)) 281 timer->active--; 282 timer->is_armed = FALSE; 283 } 284 285 simple_unlock(&timer->lock); 286 } 287 else { 288 ip_unlock(port); 289 result = KERN_INVALID_ARGUMENT; 290 } 291 292 if (result == KERN_SUCCESS) 293 if ( result_time_addr != 0 && 294 copyout((void *)&armed_time, result_time_addr, 295 sizeof (armed_time)) != 0 ) 296 result = KERN_FAILURE; 297 298 return (result); 299} 300