#include <kern/assert.h>
#include <mach/mach_types.h>
#include <mach/boolean.h>
#include <mach/vm_param.h>
#include <kern/kern_types.h>
#include <kern/mach_param.h>
#include <kern/thread.h>
#include <kern/task.h>
#include <kern/kern_cdata.h>
#include <kern/kalloc.h>
#include <mach/mach_vm.h>
static kern_return_t kcdata_get_memory_addr_with_flavor(kcdata_descriptor_t data, uint32_t type, uint32_t size, uint64_t flags, mach_vm_address_t *user_addr);
uint32_t kcdata_estimate_required_buffer_size(uint32_t num_items, uint32_t payload_size)
{
uint32_t max_padding_bytes = num_items * (KCDATA_ALIGNMENT_SIZE - 1);
uint32_t item_description_bytes = num_items * sizeof(struct kcdata_item);
uint32_t begin_and_end_marker_bytes = 2 * sizeof(struct kcdata_item);
return max_padding_bytes + item_description_bytes + begin_and_end_marker_bytes + payload_size;
}
kcdata_descriptor_t kcdata_memory_alloc_init(mach_vm_address_t buffer_addr_p, unsigned data_type, unsigned size, unsigned flags)
{
kcdata_descriptor_t data = NULL;
mach_vm_address_t user_addr = 0;
data = kalloc(sizeof(struct kcdata_descriptor));
if (data == NULL) {
return NULL;
}
bzero(data, sizeof(struct kcdata_descriptor));
data->kcd_addr_begin = buffer_addr_p;
data->kcd_addr_end = buffer_addr_p;
data->kcd_flags = (flags & KCFLAG_USE_COPYOUT)? KCFLAG_USE_COPYOUT : KCFLAG_USE_MEMCOPY;
data->kcd_length = size;
if (KERN_SUCCESS != kcdata_get_memory_addr(data, data_type, 0, &user_addr)){
kcdata_memory_destroy(data);
return NULL;
}
return data;
}
kern_return_t kcdata_memory_static_init(kcdata_descriptor_t data, mach_vm_address_t buffer_addr_p, unsigned data_type, unsigned size, unsigned flags)
{
mach_vm_address_t user_addr = 0;
if (data == NULL) {
return KERN_INVALID_ARGUMENT;
}
bzero(data, sizeof(struct kcdata_descriptor));
data->kcd_addr_begin = buffer_addr_p;
data->kcd_addr_end = buffer_addr_p;
data->kcd_flags = (flags & KCFLAG_USE_COPYOUT)? KCFLAG_USE_COPYOUT : KCFLAG_USE_MEMCOPY;
data->kcd_length = size;
return kcdata_get_memory_addr(data, data_type, 0, &user_addr);
}
void *kcdata_memory_get_begin_addr(kcdata_descriptor_t data)
{
if (data == NULL) {
return NULL;
}
return (void *)data->kcd_addr_begin;
}
uint64_t kcdata_memory_get_used_bytes(kcdata_descriptor_t kcd)
{
assert(kcd != NULL);
return ((uint64_t)kcd->kcd_addr_end - (uint64_t)kcd->kcd_addr_begin) + sizeof(struct kcdata_item);
}
kern_return_t kcdata_memory_destroy(kcdata_descriptor_t data)
{
if (!data) {
return KERN_INVALID_ARGUMENT;
}
kfree(data, sizeof(struct kcdata_descriptor));
return KERN_SUCCESS;
}
kern_return_t
kcdata_get_memory_addr(kcdata_descriptor_t data, uint32_t type, uint32_t size, mach_vm_address_t * user_addr)
{
uint64_t flags = (KCDATA_FLAGS_STRUCT_PADDING_MASK & kcdata_calc_padding(size)) | KCDATA_FLAGS_STRUCT_HAS_PADDING;
return kcdata_get_memory_addr_with_flavor(data, type, size, flags, user_addr);
}
kern_return_t
kcdata_write_buffer_end(kcdata_descriptor_t data)
{
struct kcdata_item info;
bzero(&info, sizeof(info));
info.type = KCDATA_TYPE_BUFFER_END;
info.size = 0;
return kcdata_memcpy(data, data->kcd_addr_end, &info, sizeof(info));
}
static kern_return_t kcdata_get_memory_addr_with_flavor(
kcdata_descriptor_t data,
uint32_t type,
uint32_t size,
uint64_t flags,
mach_vm_address_t *user_addr)
{
struct kcdata_item info;
uint32_t orig_size = size;
size += kcdata_calc_padding(size);
uint32_t total_size = size + sizeof(info);
if (user_addr == NULL || data == NULL || total_size + sizeof(info) < orig_size) {
return KERN_INVALID_ARGUMENT;
}
bzero(&info, sizeof(info));
info.type = type;
info.size = size;
info.flags = flags;
if (total_size + sizeof(info) > data->kcd_length ||
data->kcd_length - (total_size + sizeof(info)) < data->kcd_addr_end - data->kcd_addr_begin) {
return KERN_RESOURCE_SHORTAGE;
}
if (data->kcd_flags & KCFLAG_USE_COPYOUT) {
if (copyout(&info, data->kcd_addr_end, sizeof(info)))
return KERN_NO_ACCESS;
} else {
memcpy((void *)data->kcd_addr_end, &info, sizeof(info));
}
data->kcd_addr_end += sizeof(info);
*user_addr = data->kcd_addr_end;
data->kcd_addr_end += size;
if (!(data->kcd_flags & KCFLAG_NO_AUTO_ENDBUFFER)) {
return kcdata_write_buffer_end(data);
} else {
return KERN_SUCCESS;
}
}
kern_return_t kcdata_get_memory_addr_for_array(
kcdata_descriptor_t data,
uint32_t type_of_element,
uint32_t size_of_element,
uint32_t count,
mach_vm_address_t *user_addr)
{
uint64_t flags = type_of_element;
flags = (flags << 32) | count;
uint32_t total_size = count * size_of_element;
uint32_t pad = kcdata_calc_padding(total_size);
return kcdata_get_memory_addr_with_flavor(data, KCDATA_TYPE_ARRAY_PAD0 | pad, total_size, flags, user_addr);
}
kern_return_t kcdata_add_container_marker(
kcdata_descriptor_t data,
uint32_t header_type,
uint32_t container_type,
uint64_t identifier)
{
mach_vm_address_t user_addr;
kern_return_t kr;
assert(header_type == KCDATA_TYPE_CONTAINER_END || header_type == KCDATA_TYPE_CONTAINER_BEGIN);
uint32_t data_size = (header_type == KCDATA_TYPE_CONTAINER_BEGIN)? sizeof(uint32_t): 0;
kr = kcdata_get_memory_addr_with_flavor(data, header_type, data_size, identifier, &user_addr);
if (kr != KERN_SUCCESS)
return kr;
if (data_size)
kr = kcdata_memcpy(data, user_addr, &container_type, data_size);
return kr;
}
kern_return_t
kcdata_undo_add_container_begin(kcdata_descriptor_t data)
{
const mach_vm_address_t padded_payload_size = 16;
data->kcd_addr_end -= sizeof(struct kcdata_item) + padded_payload_size;
if (!(data->kcd_flags & KCFLAG_NO_AUTO_ENDBUFFER)) {
return kcdata_write_buffer_end(data);
} else {
return KERN_SUCCESS;
}
}
kern_return_t kcdata_memcpy(kcdata_descriptor_t data, mach_vm_address_t dst_addr, void *src_addr, uint32_t size)
{
if (data->kcd_flags & KCFLAG_USE_COPYOUT) {
if (copyout(src_addr, dst_addr, size))
return KERN_NO_ACCESS;
} else {
memcpy((void *)dst_addr, src_addr, size);
}
return KERN_SUCCESS;
}
kern_return_t kcdata_add_type_definition(
kcdata_descriptor_t data,
uint32_t type_id,
char *type_name,
struct kcdata_subtype_descriptor *elements_array_addr,
uint32_t elements_count)
{
kern_return_t kr = KERN_SUCCESS;
struct kcdata_type_definition kc_type_definition;
mach_vm_address_t user_addr;
uint32_t total_size = sizeof(struct kcdata_type_definition);
bzero(&kc_type_definition, sizeof(kc_type_definition));
if (strlen(type_name) >= KCDATA_DESC_MAXLEN)
return KERN_INVALID_ARGUMENT;
strlcpy(&kc_type_definition.kct_name[0], type_name, KCDATA_DESC_MAXLEN);
kc_type_definition.kct_num_elements = elements_count;
kc_type_definition.kct_type_identifier = type_id;
total_size += elements_count * sizeof(struct kcdata_subtype_descriptor);
if (KERN_SUCCESS != (kr = kcdata_get_memory_addr_with_flavor(data, KCDATA_TYPE_TYPEDEFINTION, total_size,
kcdata_calc_padding(total_size), &user_addr)))
return kr;
if (KERN_SUCCESS != (kr = kcdata_memcpy(data, user_addr, (void *)&kc_type_definition, sizeof(struct kcdata_type_definition))))
return kr;
user_addr += sizeof(struct kcdata_type_definition);
if (KERN_SUCCESS != (kr = kcdata_memcpy(data, user_addr, (void *)elements_array_addr, elements_count * sizeof(struct kcdata_subtype_descriptor))))
return kr;
return kr;
}
#pragma pack(4)
struct _uint64_with_description_data {
char desc[KCDATA_DESC_MAXLEN];
uint64_t data;
};
struct _uint32_with_description_data {
char desc[KCDATA_DESC_MAXLEN];
uint32_t data;
};
#pragma pack()
kern_return_t
kcdata_add_uint64_with_description(kcdata_descriptor_t data_desc, uint64_t data, const char * description)
{
if (strlen(description) >= KCDATA_DESC_MAXLEN)
return KERN_INVALID_ARGUMENT;
kern_return_t kr = 0;
mach_vm_address_t user_addr;
struct _uint64_with_description_data save_data;
const uint64_t size_req = sizeof(save_data);
bzero(&save_data, size_req);
strlcpy(&(save_data.desc[0]), description, sizeof(save_data.desc));
save_data.data = data;
kr = kcdata_get_memory_addr(data_desc, KCDATA_TYPE_UINT64_DESC, size_req, &user_addr);
if (kr != KERN_SUCCESS)
return kr;
if (data_desc->kcd_flags & KCFLAG_USE_COPYOUT) {
if (copyout(&save_data, user_addr, size_req))
return KERN_NO_ACCESS;
} else {
memcpy((void *)user_addr, &save_data, size_req);
}
return KERN_SUCCESS;
}
kern_return_t kcdata_add_uint32_with_description(
kcdata_descriptor_t data_desc,
uint32_t data,
const char *description)
{
assert(strlen(description) < KCDATA_DESC_MAXLEN);
if (strlen(description) >= KCDATA_DESC_MAXLEN)
return KERN_INVALID_ARGUMENT;
kern_return_t kr = 0;
mach_vm_address_t user_addr;
struct _uint32_with_description_data save_data;
const uint64_t size_req = sizeof(save_data);
bzero(&save_data, size_req);
strlcpy(&(save_data.desc[0]), description, sizeof(save_data.desc));
save_data.data = data;
kr = kcdata_get_memory_addr(data_desc, KCDATA_TYPE_UINT32_DESC, size_req, &user_addr);
if (kr != KERN_SUCCESS)
return kr;
if (data_desc->kcd_flags & KCFLAG_USE_COPYOUT) {
if (copyout(&save_data, user_addr, size_req))
return KERN_NO_ACCESS;
} else {
memcpy((void *)user_addr, &save_data, size_req);
}
return KERN_SUCCESS;
}