X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/3e170ce000f1506b7b5d2c5c7faec85ceabb573d..527f99514973766e9c0382a4d8550dfb00f54939:/osfmk/kern/host.c diff --git a/osfmk/kern/host.c b/osfmk/kern/host.c index 81ba0aa97..47bab64b1 100644 --- a/osfmk/kern/host.c +++ b/osfmk/kern/host.c @@ -77,6 +77,9 @@ #include #include +#include +#include + #include #include #include @@ -86,11 +89,13 @@ #include #include #include +#include // mach_node_port_changed() #include #include #include + #if CONFIG_ATM #include #endif @@ -99,6 +104,8 @@ #include #endif +#include + host_data_t realhost; vm_extmod_statistics_data_t host_extmod_statistics; @@ -106,7 +113,7 @@ vm_extmod_statistics_data_t host_extmod_statistics; kern_return_t host_processors(host_priv_t host_priv, processor_array_t * out_array, mach_msg_type_number_t * countp) { - register processor_t processor, *tp; + processor_t processor, *tp; void * addr; unsigned int count, i; @@ -153,8 +160,8 @@ host_info(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_num switch (flavor) { case HOST_BASIC_INFO: { - register host_basic_info_t basic_info; - register int master_id; + host_basic_info_t basic_info; + int master_id; /* * Basic information about this host. @@ -188,7 +195,7 @@ host_info(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_num } case HOST_SCHED_INFO: { - register host_sched_info_t sched_info; + host_sched_info_t sched_info; uint32_t quantum_time; uint64_t quantum_ns; @@ -222,7 +229,7 @@ host_info(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_num } case HOST_PRIORITY_INFO: { - register host_priority_info_t priority_info; + host_priority_info_t priority_info; if (*count < HOST_PRIORITY_INFO_COUNT) return (KERN_FAILURE); @@ -252,6 +259,19 @@ host_info(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_num return (KERN_SUCCESS); } + case HOST_CAN_HAS_DEBUGGER: { + host_can_has_debugger_info_t can_has_debugger_info; + + if (*count < HOST_CAN_HAS_DEBUGGER_COUNT) + return (KERN_FAILURE); + + can_has_debugger_info = (host_can_has_debugger_info_t)info; + can_has_debugger_info->can_has_debugger = PE_i_can_has_debugger(NULL); + *count = HOST_CAN_HAS_DEBUGGER_COUNT; + + return KERN_SUCCESS; + } + case HOST_VM_PURGABLE: { if (*count < HOST_VM_PURGABLE_COUNT) return (KERN_FAILURE); @@ -274,9 +294,7 @@ host_info(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_num #if CONFIG_COALITIONS debug_info->config_coalitions = 1; #endif -#if CONFIG_BANK debug_info->config_bank = 1; -#endif #if CONFIG_ATM debug_info->config_atm = 1; #endif @@ -293,6 +311,8 @@ host_info(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_num } } +kern_return_t host_statistics(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_number_t * count); + kern_return_t host_statistics(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_number_t * count) { @@ -318,8 +338,8 @@ host_statistics(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_ty } case HOST_VM_INFO: { - register processor_t processor; - register vm_statistics64_t stat; + processor_t processor; + vm_statistics64_t stat; vm_statistics64_data_t host_vm_stat; vm_statistics_t stat32; mach_msg_type_number_t original_count; @@ -365,7 +385,11 @@ host_statistics(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_ty } } stat32->inactive_count = VM_STATISTICS_TRUNCATE_TO_32_BIT(vm_page_inactive_count); +#if CONFIG_EMBEDDED + stat32->wire_count = VM_STATISTICS_TRUNCATE_TO_32_BIT(vm_page_wire_count); +#else stat32->wire_count = VM_STATISTICS_TRUNCATE_TO_32_BIT(vm_page_wire_count + vm_page_throttled_count + vm_lopage_free_count); +#endif stat32->zero_fill_count = VM_STATISTICS_TRUNCATE_TO_32_BIT(host_vm_stat.zero_fill_count); stat32->reactivations = VM_STATISTICS_TRUNCATE_TO_32_BIT(host_vm_stat.reactivations); stat32->pageins = VM_STATISTICS_TRUNCATE_TO_32_BIT(host_vm_stat.pageins); @@ -401,7 +425,7 @@ host_statistics(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_ty } case HOST_CPU_LOAD_INFO: { - register processor_t processor; + processor_t processor; host_cpu_load_info_t cpu_load_info; if (*count < HOST_CPU_LOAD_INFO_COUNT) @@ -470,17 +494,30 @@ host_statistics(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_ty return (KERN_FAILURE); } - task_power_info_t tinfo = (task_power_info_t)info; + task_power_info_t tinfo1 = (task_power_info_t)info; + task_power_info_v2_t tinfo2 = (task_power_info_v2_t)info; - tinfo->task_interrupt_wakeups = dead_task_statistics.task_interrupt_wakeups; - tinfo->task_platform_idle_wakeups = dead_task_statistics.task_platform_idle_wakeups; + tinfo1->task_interrupt_wakeups = dead_task_statistics.task_interrupt_wakeups; + tinfo1->task_platform_idle_wakeups = dead_task_statistics.task_platform_idle_wakeups; - tinfo->task_timer_wakeups_bin_1 = dead_task_statistics.task_timer_wakeups_bin_1; + tinfo1->task_timer_wakeups_bin_1 = dead_task_statistics.task_timer_wakeups_bin_1; - tinfo->task_timer_wakeups_bin_2 = dead_task_statistics.task_timer_wakeups_bin_2; + tinfo1->task_timer_wakeups_bin_2 = dead_task_statistics.task_timer_wakeups_bin_2; - tinfo->total_user = dead_task_statistics.total_user_time; - tinfo->total_system = dead_task_statistics.total_system_time; + tinfo1->total_user = dead_task_statistics.total_user_time; + tinfo1->total_system = dead_task_statistics.total_system_time; + if (*count < TASK_POWER_INFO_V2_COUNT) { + *count = TASK_POWER_INFO_COUNT; + } + else if (*count >= TASK_POWER_INFO_V2_COUNT) { + tinfo2->gpu_energy.task_gpu_utilisation = dead_task_statistics.task_gpu_ns; +#if defined(__arm__) || defined(__arm64__) + tinfo2->task_energy = dead_task_statistics.task_energy; + tinfo2->task_ptime = dead_task_statistics.total_ptime; + tinfo2->task_pset_switches = dead_task_statistics.total_pset_switches; +#endif + *count = TASK_POWER_INFO_V2_COUNT; + } return (KERN_SUCCESS); } @@ -490,6 +527,219 @@ host_statistics(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_ty extern uint32_t c_segment_pages_compressed; +#define HOST_STATISTICS_TIME_WINDOW 1 /* seconds */ +#define HOST_STATISTICS_MAX_REQUESTS 10 /* maximum number of requests per window */ +#define HOST_STATISTICS_MIN_REQUESTS 2 /* minimum number of requests per window */ + +uint64_t host_statistics_time_window; + +static lck_mtx_t host_statistics_lck; +static lck_grp_t* host_statistics_lck_grp; + +#define HOST_VM_INFO64_REV0 0 +#define HOST_VM_INFO64_REV1 1 +#define HOST_EXTMOD_INFO64_REV0 2 +#define HOST_LOAD_INFO_REV0 3 +#define HOST_VM_INFO_REV0 4 +#define HOST_VM_INFO_REV1 5 +#define HOST_VM_INFO_REV2 6 +#define HOST_CPU_LOAD_INFO_REV0 7 +#define HOST_EXPIRED_TASK_INFO_REV0 8 +#define HOST_EXPIRED_TASK_INFO_REV1 9 +#define NUM_HOST_INFO_DATA_TYPES 10 + +static vm_statistics64_data_t host_vm_info64_rev0 = {}; +static vm_statistics64_data_t host_vm_info64_rev1 = {}; +static vm_extmod_statistics_data_t host_extmod_info64 = {}; +static host_load_info_data_t host_load_info = {}; +static vm_statistics_data_t host_vm_info_rev0 = {}; +static vm_statistics_data_t host_vm_info_rev1 = {}; +static vm_statistics_data_t host_vm_info_rev2 = {}; +static host_cpu_load_info_data_t host_cpu_load_info = {}; +static task_power_info_data_t host_expired_task_info = {}; +static task_power_info_v2_data_t host_expired_task_info2 = {}; + +struct host_stats_cache { + uint64_t last_access; + uint64_t current_requests; + uint64_t max_requests; + uintptr_t data; + mach_msg_type_number_t count; //NOTE count is in sizeof(integer_t) +}; + +static struct host_stats_cache g_host_stats_cache[NUM_HOST_INFO_DATA_TYPES] = { + [HOST_VM_INFO64_REV0] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_vm_info64_rev0, .count = HOST_VM_INFO64_REV0_COUNT }, + [HOST_VM_INFO64_REV1] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_vm_info64_rev1, .count = HOST_VM_INFO64_REV1_COUNT }, + [HOST_EXTMOD_INFO64_REV0] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_extmod_info64, .count = HOST_EXTMOD_INFO64_COUNT }, + [HOST_LOAD_INFO_REV0] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_load_info, .count = HOST_LOAD_INFO_COUNT }, + [HOST_VM_INFO_REV0] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_vm_info_rev0, .count = HOST_VM_INFO_REV0_COUNT }, + [HOST_VM_INFO_REV1] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_vm_info_rev1, .count = HOST_VM_INFO_REV1_COUNT }, + [HOST_VM_INFO_REV2] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_vm_info_rev2, .count = HOST_VM_INFO_REV2_COUNT }, + [HOST_CPU_LOAD_INFO_REV0] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_cpu_load_info, .count = HOST_CPU_LOAD_INFO_COUNT }, + [HOST_EXPIRED_TASK_INFO_REV0] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_expired_task_info, .count = TASK_POWER_INFO_COUNT }, + [HOST_EXPIRED_TASK_INFO_REV1] = { .last_access = 0, .current_requests = 0, .max_requests = 0, .data = (uintptr_t)&host_expired_task_info2, .count = TASK_POWER_INFO_V2_COUNT}, +}; + + +void +host_statistics_init(void) +{ + host_statistics_lck_grp = lck_grp_alloc_init("host_statistics", LCK_GRP_ATTR_NULL); + lck_mtx_init(&host_statistics_lck, host_statistics_lck_grp, LCK_ATTR_NULL); + nanoseconds_to_absolutetime((HOST_STATISTICS_TIME_WINDOW * NSEC_PER_SEC), &host_statistics_time_window); +} + +static void +cache_host_statistics(int index, host_info64_t info) +{ + if (index < 0 || index >= NUM_HOST_INFO_DATA_TYPES) + return; + + task_t task = current_task(); + if (task->t_flags & TF_PLATFORM) + return; + + memcpy((void *)g_host_stats_cache[index].data, info, g_host_stats_cache[index].count * sizeof(integer_t)); + return; +} + +static void +get_cached_info(int index, host_info64_t info, mach_msg_type_number_t* count) +{ + if (index < 0 || index >= NUM_HOST_INFO_DATA_TYPES) { + *count = 0; + return; + } + + *count = g_host_stats_cache[index].count; + memcpy(info, (void *)g_host_stats_cache[index].data, g_host_stats_cache[index].count * sizeof(integer_t)); +} + +static int +get_host_info_data_index(bool is_stat64, host_flavor_t flavor, mach_msg_type_number_t* count, kern_return_t* ret) +{ + switch (flavor) { + + case HOST_VM_INFO64: + if (!is_stat64){ + *ret = KERN_INVALID_ARGUMENT; + return -1; + } + if (*count < HOST_VM_INFO64_REV0_COUNT) { + *ret = KERN_FAILURE; + return -1; + } + if (*count >= HOST_VM_INFO64_REV1_COUNT) { + return HOST_VM_INFO64_REV1; + } + return HOST_VM_INFO64_REV0; + + case HOST_EXTMOD_INFO64: + if (!is_stat64){ + *ret = KERN_INVALID_ARGUMENT; + return -1; + } + if (*count < HOST_EXTMOD_INFO64_COUNT) { + *ret = KERN_FAILURE; + return -1; + } + return HOST_EXTMOD_INFO64_REV0; + + case HOST_LOAD_INFO: + if (*count < HOST_LOAD_INFO_COUNT) { + *ret = KERN_FAILURE; + return -1; + } + return HOST_LOAD_INFO_REV0; + + case HOST_VM_INFO: + if (*count < HOST_VM_INFO_REV0_COUNT) { + *ret = KERN_FAILURE; + return -1; + } + if (*count >= HOST_VM_INFO_REV2_COUNT) { + return HOST_VM_INFO_REV2; + } + if (*count >= HOST_VM_INFO_REV1_COUNT) { + return HOST_VM_INFO_REV1; + } + return HOST_VM_INFO_REV0; + + case HOST_CPU_LOAD_INFO: + if (*count < HOST_CPU_LOAD_INFO_COUNT) { + *ret = KERN_FAILURE; + return -1; + } + return HOST_CPU_LOAD_INFO_REV0; + + case HOST_EXPIRED_TASK_INFO: + if (*count < TASK_POWER_INFO_COUNT){ + *ret = KERN_FAILURE; + return -1; + } + if (*count >= TASK_POWER_INFO_V2_COUNT){ + return HOST_EXPIRED_TASK_INFO_REV1; + } + return HOST_EXPIRED_TASK_INFO_REV0; + + default: + *ret = KERN_INVALID_ARGUMENT; + return -1; + + } + +} + +static bool +rate_limit_host_statistics(bool is_stat64, host_flavor_t flavor, host_info64_t info, mach_msg_type_number_t* count, kern_return_t* ret, int *pindex) +{ + task_t task = current_task(); + + assert(task != kernel_task); + + *ret = KERN_SUCCESS; + + /* Access control only for third party applications */ + if (task->t_flags & TF_PLATFORM) { + return FALSE; + } + + /* Rate limit to HOST_STATISTICS_MAX_REQUESTS queries for each HOST_STATISTICS_TIME_WINDOW window of time */ + bool rate_limited = FALSE; + bool set_last_access = TRUE; + + /* there is a cache for every flavor */ + int index = get_host_info_data_index(is_stat64, flavor, count, ret); + if (index == -1) + goto out; + + *pindex = index; + lck_mtx_lock(&host_statistics_lck); + if (g_host_stats_cache[index].last_access > mach_continuous_time() - host_statistics_time_window) { + set_last_access = FALSE; + if (g_host_stats_cache[index].current_requests++ >= g_host_stats_cache[index].max_requests) { + rate_limited = TRUE; + get_cached_info(index, info, count); + } + } + if (set_last_access) { + g_host_stats_cache[index].current_requests = 1; + /* + * select a random number of requests (included between HOST_STATISTICS_MIN_REQUESTS and HOST_STATISTICS_MAX_REQUESTS) + * to let query host_statistics. + * In this way it is not possible to infer looking at when the a cached copy changes if host_statistics was called on + * the provious window. + */ + g_host_stats_cache[index].max_requests = (mach_absolute_time() % (HOST_STATISTICS_MAX_REQUESTS - HOST_STATISTICS_MIN_REQUESTS + 1)) + HOST_STATISTICS_MIN_REQUESTS; + g_host_stats_cache[index].last_access = mach_continuous_time(); + } + lck_mtx_unlock(&host_statistics_lck); +out: + return rate_limited; +} + +kern_return_t host_statistics64(host_t host, host_flavor_t flavor, host_info_t info, mach_msg_type_number_t * count); + kern_return_t host_statistics64(host_t host, host_flavor_t flavor, host_info64_t info, mach_msg_type_number_t * count) { @@ -501,8 +751,8 @@ host_statistics64(host_t host, host_flavor_t flavor, host_info64_t info, mach_ms switch (flavor) { case HOST_VM_INFO64: /* We were asked to get vm_statistics64 */ { - register processor_t processor; - register vm_statistics64_t stat; + processor_t processor; + vm_statistics64_t stat; vm_statistics64_data_t host_vm_stat; mach_msg_type_number_t original_count; unsigned int local_q_internal_count; @@ -557,7 +807,11 @@ host_statistics64(host_t host, host_flavor_t flavor, host_info64_t info, mach_ms } } stat->inactive_count = vm_page_inactive_count; +#if CONFIG_EMBEDDED + stat->wire_count = vm_page_wire_count; +#else stat->wire_count = vm_page_wire_count + vm_page_throttled_count + vm_lopage_free_count; +#endif stat->zero_fill_count = host_vm_stat.zero_fill_count; stat->reactivations = host_vm_stat.reactivations; stat->pageins = host_vm_stat.pageins; @@ -622,6 +876,52 @@ host_statistics64(host_t host, host_flavor_t flavor, host_info64_t info, mach_ms } } +kern_return_t +host_statistics64_from_user(host_t host, host_flavor_t flavor, host_info64_t info, mach_msg_type_number_t * count) +{ + kern_return_t ret = KERN_SUCCESS; + int index; + + if (host == HOST_NULL) + return (KERN_INVALID_HOST); + + if (rate_limit_host_statistics(TRUE, flavor, info, count, &ret, &index)) + return ret; + + if (ret != KERN_SUCCESS) + return ret; + + ret = host_statistics64(host, flavor, info, count); + + if (ret == KERN_SUCCESS) + cache_host_statistics(index, info); + + return ret; +} + +kern_return_t +host_statistics_from_user(host_t host, host_flavor_t flavor, host_info64_t info, mach_msg_type_number_t * count) +{ + kern_return_t ret = KERN_SUCCESS; + int index; + + if (host == HOST_NULL) + return (KERN_INVALID_HOST); + + if (rate_limit_host_statistics(FALSE, flavor, info, count, &ret, &index)) + return ret; + + if (ret != KERN_SUCCESS) + return ret; + + ret = host_statistics(host, flavor, info, count); + + if (ret == KERN_SUCCESS) + cache_host_statistics(index, info); + + return ret; +} + /* * Get host statistics that require privilege. * None for now, just call the un-privileged version. @@ -683,7 +983,7 @@ get_sched_statistics(struct _processor_statistics_np * out, uint32_t * count) /* And include RT Queue information */ bzero(out, sizeof(*out)); out->ps_cpuid = (-1); - out->ps_runq_count_sum = rt_runq.runq_stats.count_sum; + out->ps_runq_count_sum = SCHED(rt_runq_count_sum)(); out++; *count += (uint32_t)sizeof(struct _processor_statistics_np); @@ -840,7 +1140,7 @@ host_processor_info(host_t host, result = vm_map_unwire(ipc_kernel_map, vm_map_trunc_page(addr, VM_MAP_PAGE_MASK(ipc_kernel_map)), vm_map_round_page(addr + size, VM_MAP_PAGE_MASK(ipc_kernel_map)), FALSE); assert(result == KERN_SUCCESS); - result = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr, (vm_map_size_t)size, TRUE, ©); + result = vm_map_copyin(ipc_kernel_map, (vm_map_address_t)addr, (vm_map_size_t)needed, TRUE, ©); assert(result == KERN_SUCCESS); *out_pcount = pcount; @@ -858,15 +1158,38 @@ kernel_set_special_port(host_priv_t host_priv, int id, ipc_port_t port) { ipc_port_t old_port; +#if !MACH_FLIPC + if (id == HOST_NODE_PORT) + return (KERN_NOT_SUPPORTED); +#endif + host_lock(host_priv); old_port = host_priv->special[id]; host_priv->special[id] = port; host_unlock(host_priv); + +#if MACH_FLIPC + if (id == HOST_NODE_PORT) + mach_node_port_changed(); +#endif + if (IP_VALID(old_port)) ipc_port_release_send(old_port); return (KERN_SUCCESS); } +/* + * Kernel interface for retrieving a special port. + */ +kern_return_t +kernel_get_special_port(host_priv_t host_priv, int id, ipc_port_t * portp) +{ + host_lock(host_priv); + *portp = host_priv->special[id]; + host_unlock(host_priv); + return (KERN_SUCCESS); +} + /* * User interface for setting a special port. * @@ -962,3 +1285,25 @@ host_set_atm_diagnostic_flag(host_priv_t host_priv, uint32_t diagnostic_flag) return (KERN_NOT_SUPPORTED); #endif } + +kern_return_t +host_set_multiuser_config_flags(host_priv_t host_priv, uint32_t multiuser_config) +{ +#if CONFIG_EMBEDDED + if (host_priv == HOST_PRIV_NULL) + return (KERN_INVALID_ARGUMENT); + + assert(host_priv == &realhost); + + /* + * Always enforce that the multiuser bit is set + * if a value is written to the commpage word. + */ + commpage_update_multiuser_config(multiuser_config | kIsMultiUserDevice); + return (KERN_SUCCESS); +#else + (void)host_priv; + (void)multiuser_config; + return (KERN_NOT_SUPPORTED); +#endif +}