X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/21362eb3e66fd2c787aee132bce100a44d71a99c..4452a7af2eac33dbad800bcc91f2399d62c18f53:/osfmk/kern/clock_oldops.c diff --git a/osfmk/kern/clock_oldops.c b/osfmk/kern/clock_oldops.c new file mode 100644 index 000000000..987036954 --- /dev/null +++ b/osfmk/kern/clock_oldops.c @@ -0,0 +1,808 @@ +/* + * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * @OSF_COPYRIGHT@ + */ +/* + * DEPRECATED INTERFACES - Should be removed + * + * Purpose: Routines for the creation and use of kernel + * alarm clock services. This file and the ipc + * routines in kern/ipc_clock.c constitute the + * machine-independent clock service layer. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +/* + * Actual clock alarm structure. Used for user clock_sleep() and + * clock_alarm() calls. Alarms are allocated from the alarm free + * list and entered in time priority order into the active alarm + * chain of the target clock. + */ +struct alarm { + struct alarm *al_next; /* next alarm in chain */ + struct alarm *al_prev; /* previous alarm in chain */ + int al_status; /* alarm status */ + mach_timespec_t al_time; /* alarm time */ + struct { /* message alarm data */ + int type; /* alarm type */ + ipc_port_t port; /* alarm port */ + mach_msg_type_name_t + port_type; /* alarm port type */ + struct clock *clock; /* alarm clock */ + void *data; /* alarm data */ + } al_alrm; +#define al_type al_alrm.type +#define al_port al_alrm.port +#define al_port_type al_alrm.port_type +#define al_clock al_alrm.clock +#define al_data al_alrm.data + long al_seqno; /* alarm sequence number */ +}; +typedef struct alarm alarm_data_t; + +/* alarm status */ +#define ALARM_FREE 0 /* alarm is on free list */ +#define ALARM_SLEEP 1 /* active clock_sleep() */ +#define ALARM_CLOCK 2 /* active clock_alarm() */ +#define ALARM_DONE 4 /* alarm has expired */ + +/* local data declarations */ +decl_simple_lock_data(static,alarm_lock) /* alarm synchronization */ +static struct zone *alarm_zone; /* zone for user alarms */ +static struct alarm *alrmfree; /* alarm free list pointer */ +static struct alarm *alrmdone; /* alarm done list pointer */ +static struct alarm *alrmlist; +static long alrm_seqno; /* uniquely identifies alarms */ +static thread_call_data_t alarm_done_call; +static timer_call_data_t alarm_expire_timer; + +extern struct clock clock_list[]; +extern int clock_count; + +static void post_alarm( + alarm_t alarm); + +static void set_alarm( + mach_timespec_t *alarm_time); + +static int check_time( + alarm_type_t alarm_type, + mach_timespec_t *alarm_time, + mach_timespec_t *clock_time); + +static void alarm_done(void); + +static void alarm_expire(void); + +static kern_return_t clock_sleep_internal( + clock_t clock, + sleep_type_t sleep_type, + mach_timespec_t *sleep_time); + +int rtclock_config(void); + +int rtclock_init(void); + +kern_return_t rtclock_gettime( + mach_timespec_t *cur_time); + +kern_return_t rtclock_getattr( + clock_flavor_t flavor, + clock_attr_t attr, + mach_msg_type_number_t *count); + +struct clock_ops sysclk_ops = { + rtclock_config, rtclock_init, + rtclock_gettime, + rtclock_getattr, +}; + +kern_return_t calend_gettime( + mach_timespec_t *cur_time); + +kern_return_t calend_getattr( + clock_flavor_t flavor, + clock_attr_t attr, + mach_msg_type_number_t *count); + +struct clock_ops calend_ops = { + 0, 0, + calend_gettime, + calend_getattr, +}; + +/* + * Macros to lock/unlock clock system. + */ +#define LOCK_ALARM(s) \ + s = splclock(); \ + simple_lock(&alarm_lock); + +#define UNLOCK_ALARM(s) \ + simple_unlock(&alarm_lock); \ + splx(s); + +void +clock_oldconfig(void) +{ + clock_t clock; + register int i; + + simple_lock_init(&alarm_lock, 0); + thread_call_setup(&alarm_done_call, (thread_call_func_t)alarm_done, NULL); + timer_call_setup(&alarm_expire_timer, (timer_call_func_t)alarm_expire, NULL); + + /* + * Configure clock devices. + */ + for (i = 0; i < clock_count; i++) { + clock = &clock_list[i]; + if (clock->cl_ops && clock->cl_ops->c_config) { + if ((*clock->cl_ops->c_config)() == 0) + clock->cl_ops = 0; + } + } + + /* start alarm sequence numbers at 0 */ + alrm_seqno = 0; +} + +void +clock_oldinit(void) +{ + clock_t clock; + register int i; + + /* + * Initialize basic clock structures. + */ + for (i = 0; i < clock_count; i++) { + clock = &clock_list[i]; + if (clock->cl_ops && clock->cl_ops->c_init) + (*clock->cl_ops->c_init)(); + } +} + +/* + * Initialize the clock ipc service facility. + */ +void +clock_service_create(void) +{ + clock_t clock; + register int i; + + /* + * Initialize ipc clock services. + */ + for (i = 0; i < clock_count; i++) { + clock = &clock_list[i]; + if (clock->cl_ops) { + ipc_clock_init(clock); + ipc_clock_enable(clock); + } + } + + /* + * Perform miscellaneous late + * initialization. + */ + i = sizeof(struct alarm); + alarm_zone = zinit(i, (4096/i)*i, 10*i, "alarms"); +} + +/* + * Get the service port on a clock. + */ +kern_return_t +host_get_clock_service( + host_t host, + clock_id_t clock_id, + clock_t *clock) /* OUT */ +{ + if (host == HOST_NULL || clock_id < 0 || clock_id >= clock_count) { + *clock = CLOCK_NULL; + return (KERN_INVALID_ARGUMENT); + } + + *clock = &clock_list[clock_id]; + if ((*clock)->cl_ops == 0) + return (KERN_FAILURE); + return (KERN_SUCCESS); +} + +/* + * Get the control port on a clock. + */ +kern_return_t +host_get_clock_control( + host_priv_t host_priv, + clock_id_t clock_id, + clock_t *clock) /* OUT */ +{ + if (host_priv == HOST_PRIV_NULL || + clock_id < 0 || clock_id >= clock_count) { + *clock = CLOCK_NULL; + return (KERN_INVALID_ARGUMENT); + } + + *clock = &clock_list[clock_id]; + if ((*clock)->cl_ops == 0) + return (KERN_FAILURE); + return (KERN_SUCCESS); +} + +/* + * Get the current clock time. + */ +kern_return_t +clock_get_time( + clock_t clock, + mach_timespec_t *cur_time) /* OUT */ +{ + if (clock == CLOCK_NULL) + return (KERN_INVALID_ARGUMENT); + return ((*clock->cl_ops->c_gettime)(cur_time)); +} + +kern_return_t +rtclock_gettime( + mach_timespec_t *time) /* OUT */ +{ + clock_get_system_nanotime(&time->tv_sec, (uint32_t *)&time->tv_nsec); + + return (KERN_SUCCESS); +} + +kern_return_t +calend_gettime( + mach_timespec_t *time) /* OUT */ +{ + clock_get_calendar_nanotime(&time->tv_sec, (uint32_t *)&time->tv_nsec); + + return (KERN_SUCCESS); +} + +/* + * Get clock attributes. + */ +kern_return_t +clock_get_attributes( + clock_t clock, + clock_flavor_t flavor, + clock_attr_t attr, /* OUT */ + mach_msg_type_number_t *count) /* IN/OUT */ +{ + if (clock == CLOCK_NULL) + return (KERN_INVALID_ARGUMENT); + if (clock->cl_ops->c_getattr) + return (clock->cl_ops->c_getattr(flavor, attr, count)); + return (KERN_FAILURE); +} + +kern_return_t +rtclock_getattr( + clock_flavor_t flavor, + clock_attr_t attr, /* OUT */ + mach_msg_type_number_t *count) /* IN/OUT */ +{ + if (*count != 1) + return (KERN_FAILURE); + + switch (flavor) { + + case CLOCK_GET_TIME_RES: /* >0 res */ + case CLOCK_ALARM_CURRES: /* =0 no alarm */ + case CLOCK_ALARM_MINRES: + case CLOCK_ALARM_MAXRES: + *(clock_res_t *) attr = NSEC_PER_SEC / 100; + break; + + default: + return (KERN_INVALID_VALUE); + } + + return (KERN_SUCCESS); +} + +kern_return_t +calend_getattr( + clock_flavor_t flavor, + clock_attr_t attr, /* OUT */ + mach_msg_type_number_t *count) /* IN/OUT */ +{ + if (*count != 1) + return (KERN_FAILURE); + + switch (flavor) { + + case CLOCK_GET_TIME_RES: /* >0 res */ + *(clock_res_t *) attr = NSEC_PER_SEC / 100; + break; + + case CLOCK_ALARM_CURRES: /* =0 no alarm */ + case CLOCK_ALARM_MINRES: + case CLOCK_ALARM_MAXRES: + *(clock_res_t *) attr = 0; + break; + + default: + return (KERN_INVALID_VALUE); + } + + return (KERN_SUCCESS); +} + +/* + * Set the current clock time. + */ +kern_return_t +clock_set_time( + clock_t clock, +__unused mach_timespec_t new_time) +{ + if (clock == CLOCK_NULL) + return (KERN_INVALID_ARGUMENT); + return (KERN_FAILURE); +} + +/* + * Set the clock alarm resolution. + */ +kern_return_t +clock_set_attributes( + clock_t clock, +__unused clock_flavor_t flavor, +__unused clock_attr_t attr, +__unused mach_msg_type_number_t count) +{ + if (clock == CLOCK_NULL) + return (KERN_INVALID_ARGUMENT); + return (KERN_FAILURE); +} + +/* + * Setup a clock alarm. + */ +kern_return_t +clock_alarm( + clock_t clock, + alarm_type_t alarm_type, + mach_timespec_t alarm_time, + ipc_port_t alarm_port, + mach_msg_type_name_t alarm_port_type) +{ + alarm_t alarm; + mach_timespec_t clock_time; + int chkstat; + kern_return_t reply_code; + spl_t s; + + if (clock == CLOCK_NULL) + return (KERN_INVALID_ARGUMENT); + if (clock != &clock_list[SYSTEM_CLOCK]) + return (KERN_FAILURE); + if (IP_VALID(alarm_port) == 0) + return (KERN_INVALID_CAPABILITY); + + /* + * Check alarm parameters. If parameters are invalid, + * send alarm message immediately. + */ + (*clock->cl_ops->c_gettime)(&clock_time); + chkstat = check_time(alarm_type, &alarm_time, &clock_time); + if (chkstat <= 0) { + reply_code = (chkstat < 0 ? KERN_INVALID_VALUE : KERN_SUCCESS); + clock_alarm_reply(alarm_port, alarm_port_type, + reply_code, alarm_type, clock_time); + return (KERN_SUCCESS); + } + + /* + * Get alarm and add to clock alarm list. + */ + + LOCK_ALARM(s); + if ((alarm = alrmfree) == 0) { + UNLOCK_ALARM(s); + alarm = (alarm_t) zalloc(alarm_zone); + if (alarm == 0) + return (KERN_RESOURCE_SHORTAGE); + LOCK_ALARM(s); + } + else + alrmfree = alarm->al_next; + + alarm->al_status = ALARM_CLOCK; + alarm->al_time = alarm_time; + alarm->al_type = alarm_type; + alarm->al_port = alarm_port; + alarm->al_port_type = alarm_port_type; + alarm->al_clock = clock; + alarm->al_seqno = alrm_seqno++; + post_alarm(alarm); + UNLOCK_ALARM(s); + + return (KERN_SUCCESS); +} + +/* + * Sleep on a clock. System trap. User-level libmach clock_sleep + * interface call takes a mach_timespec_t sleep_time argument which it + * converts to sleep_sec and sleep_nsec arguments which are then + * passed to clock_sleep_trap. + */ +kern_return_t +clock_sleep_trap( + struct clock_sleep_trap_args *args) +{ + mach_port_name_t clock_name = args->clock_name; + sleep_type_t sleep_type = args->sleep_type; + int sleep_sec = args->sleep_sec; + int sleep_nsec = args->sleep_nsec; + mach_vm_address_t wakeup_time_addr = args->wakeup_time; + clock_t clock; + mach_timespec_t swtime; + kern_return_t rvalue; + + /* + * Convert the trap parameters. + */ + if (clock_name == MACH_PORT_NULL) + clock = &clock_list[SYSTEM_CLOCK]; + else + clock = port_name_to_clock(clock_name); + + swtime.tv_sec = sleep_sec; + swtime.tv_nsec = sleep_nsec; + + /* + * Call the actual clock_sleep routine. + */ + rvalue = clock_sleep_internal(clock, sleep_type, &swtime); + + /* + * Return current time as wakeup time. + */ + if (rvalue != KERN_INVALID_ARGUMENT && rvalue != KERN_FAILURE) { + copyout((char *)&swtime, wakeup_time_addr, sizeof(mach_timespec_t)); + } + return (rvalue); +} + +static kern_return_t +clock_sleep_internal( + clock_t clock, + sleep_type_t sleep_type, + mach_timespec_t *sleep_time) +{ + alarm_t alarm; + mach_timespec_t clock_time; + kern_return_t rvalue; + int chkstat; + spl_t s; + + if (clock == CLOCK_NULL) + return (KERN_INVALID_ARGUMENT); + + if (clock != &clock_list[SYSTEM_CLOCK]) + return (KERN_FAILURE); + + /* + * Check sleep parameters. If parameters are invalid + * return an error, otherwise post alarm request. + */ + (*clock->cl_ops->c_gettime)(&clock_time); + + chkstat = check_time(sleep_type, sleep_time, &clock_time); + if (chkstat < 0) + return (KERN_INVALID_VALUE); + rvalue = KERN_SUCCESS; + if (chkstat > 0) { + wait_result_t wait_result; + + /* + * Get alarm and add to clock alarm list. + */ + + LOCK_ALARM(s); + if ((alarm = alrmfree) == 0) { + UNLOCK_ALARM(s); + alarm = (alarm_t) zalloc(alarm_zone); + if (alarm == 0) + return (KERN_RESOURCE_SHORTAGE); + LOCK_ALARM(s); + } + else + alrmfree = alarm->al_next; + + /* + * Wait for alarm to occur. + */ + wait_result = assert_wait((event_t)alarm, THREAD_ABORTSAFE); + if (wait_result == THREAD_WAITING) { + alarm->al_time = *sleep_time; + alarm->al_status = ALARM_SLEEP; + post_alarm(alarm); + UNLOCK_ALARM(s); + + wait_result = thread_block(THREAD_CONTINUE_NULL); + + /* + * Note if alarm expired normally or whether it + * was aborted. If aborted, delete alarm from + * clock alarm list. Return alarm to free list. + */ + LOCK_ALARM(s); + if (alarm->al_status != ALARM_DONE) { + assert(wait_result != THREAD_AWAKENED); + if (((alarm->al_prev)->al_next = alarm->al_next) != NULL) + (alarm->al_next)->al_prev = alarm->al_prev; + rvalue = KERN_ABORTED; + } + *sleep_time = alarm->al_time; + alarm->al_status = ALARM_FREE; + } else { + assert(wait_result == THREAD_INTERRUPTED); + assert(alarm->al_status == ALARM_FREE); + rvalue = KERN_ABORTED; + } + alarm->al_next = alrmfree; + alrmfree = alarm; + UNLOCK_ALARM(s); + } + else + *sleep_time = clock_time; + + return (rvalue); +} + +/* + * Service clock alarm expirations. + */ +static void +alarm_expire(void) +{ + clock_t clock; + register alarm_t alrm1; + register alarm_t alrm2; + mach_timespec_t clock_time; + mach_timespec_t *alarm_time; + spl_t s; + + clock = &clock_list[SYSTEM_CLOCK]; + (*clock->cl_ops->c_gettime)(&clock_time); + + /* + * Update clock alarm list. Alarms that are due are moved + * to the alarmdone list to be serviced by a thread callout. + */ + LOCK_ALARM(s); + alrm1 = (alarm_t)&alrmlist; + while ((alrm2 = alrm1->al_next) != NULL) { + alarm_time = &alrm2->al_time; + if (CMP_MACH_TIMESPEC(alarm_time, &clock_time) > 0) + break; + + /* + * Alarm has expired, so remove it from the + * clock alarm list. + */ + if ((alrm1->al_next = alrm2->al_next) != NULL) + (alrm1->al_next)->al_prev = alrm1; + + /* + * If a clock_sleep() alarm, wakeup the thread + * which issued the clock_sleep() call. + */ + if (alrm2->al_status == ALARM_SLEEP) { + alrm2->al_next = 0; + alrm2->al_status = ALARM_DONE; + alrm2->al_time = clock_time; + thread_wakeup((event_t)alrm2); + } + + /* + * If a clock_alarm() alarm, place the alarm on + * the alarm done list and schedule the alarm + * delivery mechanism. + */ + else { + assert(alrm2->al_status == ALARM_CLOCK); + if ((alrm2->al_next = alrmdone) != NULL) + alrmdone->al_prev = alrm2; + else + thread_call_enter(&alarm_done_call); + alrm2->al_prev = (alarm_t)&alrmdone; + alrmdone = alrm2; + alrm2->al_status = ALARM_DONE; + alrm2->al_time = clock_time; + } + } + + /* + * Setup to expire for the next pending alarm. + */ + if (alrm2) + set_alarm(alarm_time); + UNLOCK_ALARM(s); +} + +static void +alarm_done(void) +{ + register alarm_t alrm; + kern_return_t code; + spl_t s; + + LOCK_ALARM(s); + while ((alrm = alrmdone) != NULL) { + if ((alrmdone = alrm->al_next) != NULL) + alrmdone->al_prev = (alarm_t)&alrmdone; + UNLOCK_ALARM(s); + + code = (alrm->al_status == ALARM_DONE? KERN_SUCCESS: KERN_ABORTED); + if (alrm->al_port != IP_NULL) { + /* Deliver message to designated port */ + if (IP_VALID(alrm->al_port)) { + clock_alarm_reply(alrm->al_port, alrm->al_port_type, code, + alrm->al_type, alrm->al_time); + } + + LOCK_ALARM(s); + alrm->al_status = ALARM_FREE; + alrm->al_next = alrmfree; + alrmfree = alrm; + } + else + panic("clock_alarm_deliver"); + } + + UNLOCK_ALARM(s); +} + +/* + * Post an alarm on the active alarm list. + * + * Always called from within a LOCK_ALARM() code section. + */ +static void +post_alarm( + alarm_t alarm) +{ + register alarm_t alrm1, alrm2; + mach_timespec_t *alarm_time; + mach_timespec_t *queue_time; + + /* + * Traverse alarm list until queue time is greater + * than alarm time, then insert alarm. + */ + alarm_time = &alarm->al_time; + alrm1 = (alarm_t)&alrmlist; + while ((alrm2 = alrm1->al_next) != NULL) { + queue_time = &alrm2->al_time; + if (CMP_MACH_TIMESPEC(queue_time, alarm_time) > 0) + break; + alrm1 = alrm2; + } + alrm1->al_next = alarm; + alarm->al_next = alrm2; + alarm->al_prev = alrm1; + if (alrm2) + alrm2->al_prev = alarm; + + /* + * If the inserted alarm is the 'earliest' alarm, + * reset the device layer alarm time accordingly. + */ + if (alrmlist == alarm) + set_alarm(alarm_time); +} + +static void +set_alarm( + mach_timespec_t *alarm_time) +{ + uint64_t abstime; + + nanotime_to_absolutetime(alarm_time->tv_sec, alarm_time->tv_nsec, &abstime); + timer_call_enter(&alarm_expire_timer, abstime); +} + +/* + * Check the validity of 'alarm_time' and 'alarm_type'. If either + * argument is invalid, return a negative value. If the 'alarm_time' + * is now, return a 0 value. If the 'alarm_time' is in the future, + * return a positive value. + */ +static int +check_time( + alarm_type_t alarm_type, + mach_timespec_t *alarm_time, + mach_timespec_t *clock_time) +{ + int result; + + if (BAD_ALRMTYPE(alarm_type)) + return (-1); + if (BAD_MACH_TIMESPEC(alarm_time)) + return (-1); + if ((alarm_type & ALRMTYPE) == TIME_RELATIVE) + ADD_MACH_TIMESPEC(alarm_time, clock_time); + + result = CMP_MACH_TIMESPEC(alarm_time, clock_time); + + return ((result >= 0)? result: 0); +} + +mach_timespec_t +clock_get_system_value(void) +{ + clock_t clock = &clock_list[SYSTEM_CLOCK]; + mach_timespec_t value; + + (void) (*clock->cl_ops->c_gettime)(&value); + + return value; +} + +mach_timespec_t +clock_get_calendar_value(void) +{ + clock_t clock = &clock_list[CALENDAR_CLOCK]; + mach_timespec_t value = MACH_TIMESPEC_ZERO; + + (void) (*clock->cl_ops->c_gettime)(&value); + + return value; +}