+ goto bail;
+ }
+
+ if (!(user_header || fd)) {
+ ret = EINVAL;
+ goto bail;
+ }
+
+ // Initialize the cpu map
+ ret = kdbg_cpumap_init_internal(kd_ctrl_page.kdebug_iops, kd_ctrl_page.kdebug_cpus, &cpumap, &cpumap_size);
+ if (ret != KERN_SUCCESS) {
+ goto bail;
+ }
+
+ // Check if a thread map is initialized
+ if (!kd_mapptr) {
+ ret = EINVAL;
+ goto bail;
+ }
+ thrmap_size = kd_mapcount * sizeof(kd_threadmap);
+
+ mach_timebase_info_data_t timebase = {0, 0};
+ clock_timebase_info(&timebase);
+
+ // Setup the header.
+ // See v3 header description in sys/kdebug.h for more inforamtion.
+ kd_header_v3 header = {
+ .tag = RAW_VERSION3,
+ .sub_tag = V3_HEADER_VERSION,
+ .length = (sizeof(kd_header_v3) + cpumap_size - sizeof(kd_cpumap_header)),
+ .timebase_numer = timebase.numer,
+ .timebase_denom = timebase.denom,
+ .timestamp = 0, /* FIXME rdar://problem/22053009 */
+ .walltime_secs = 0,
+ .walltime_usecs = 0,
+ .timezone_minuteswest = 0,
+ .timezone_dst = 0,
+#if defined(__LP64__)
+ .flags = 1,
+#else
+ .flags = 0,
+#endif
+ };
+
+ // If its a buffer, check if we have enough space to copy the header and the maps.
+ if (user_header) {
+ bytes_needed = header.length + thrmap_size + (2 * sizeof(kd_chunk_header_v3));
+ if (*user_header_size < bytes_needed) {
+ ret = EINVAL;
+ goto bail;
+ }
+ }
+
+ // Start writing the header
+ if (fd) {
+ void *hdr_ptr = (void *)(((uintptr_t) &header) + sizeof(kd_chunk_header_v3));
+ size_t payload_size = (sizeof(kd_header_v3) - sizeof(kd_chunk_header_v3));
+
+ ret = kdbg_write_v3_chunk_to_fd(RAW_VERSION3, V3_HEADER_VERSION, header.length, hdr_ptr, payload_size, fd);
+ if (ret) {
+ goto bail;
+ }
+ } else {
+ if (copyout(&header, user_header, sizeof(kd_header_v3))) {
+ ret = EFAULT;
+ goto bail;
+ }
+ // Update the user pointer
+ user_header += sizeof(kd_header_v3);
+ }
+
+ // Write a cpu map. This is a sub chunk of the header
+ cpumap = (uint8_t*)((uintptr_t) cpumap + sizeof(kd_cpumap_header));
+ size_t payload_size = (size_t)(cpumap_size - sizeof(kd_cpumap_header));
+ if (fd) {
+ ret = kdbg_write_v3_chunk_to_fd(V3_CPU_MAP, V3_CPUMAP_VERSION, payload_size, (void *)cpumap, payload_size, fd);
+ if (ret) {
+ goto bail;
+ }
+ } else {
+ ret = kdbg_write_v3_chunk_header(user_header, V3_CPU_MAP, V3_CPUMAP_VERSION, payload_size, NULL, NULL);
+ if (ret) {
+ goto bail;
+ }
+ user_header += sizeof(kd_chunk_header_v3);
+ if (copyout(cpumap, user_header, payload_size)) {
+ ret = EFAULT;
+ goto bail;
+ }
+ // Update the user pointer
+ user_header += payload_size;
+ }
+
+ // Write a thread map
+ if (fd) {
+ ret = kdbg_write_v3_chunk_to_fd(V3_THREAD_MAP, V3_THRMAP_VERSION, thrmap_size, (void *)kd_mapptr, thrmap_size, fd);
+ if (ret) {
+ goto bail;
+ }
+ } else {
+ ret = kdbg_write_v3_chunk_header(user_header, V3_THREAD_MAP, V3_THRMAP_VERSION, thrmap_size, NULL, NULL);
+ if (ret) {
+ goto bail;
+ }
+ user_header += sizeof(kd_chunk_header_v3);
+ if (copyout(kd_mapptr, user_header, thrmap_size)) {
+ ret = EFAULT;
+ goto bail;
+ }
+ user_header += thrmap_size;
+ }
+
+ if (fd) {
+ RAW_file_written += bytes_needed;
+ }
+
+ *user_header_size = bytes_needed;
+bail:
+ if (cpumap) {
+ kmem_free(kernel_map, (vm_offset_t)cpumap, cpumap_size);
+ }
+ return ret;
+}
+
+int
+kdbg_readcpumap(user_addr_t user_cpumap, size_t *user_cpumap_size)
+{
+ uint8_t* cpumap = NULL;
+ uint32_t cpumap_size = 0;
+ int ret = KERN_SUCCESS;
+
+ if (kd_ctrl_page.kdebug_flags & KDBG_BUFINIT) {
+ if (kdbg_cpumap_init_internal(kd_ctrl_page.kdebug_iops, kd_ctrl_page.kdebug_cpus, &cpumap, &cpumap_size) == KERN_SUCCESS) {
+ if (user_cpumap) {
+ size_t bytes_to_copy = (*user_cpumap_size >= cpumap_size) ? cpumap_size : *user_cpumap_size;
+ if (copyout(cpumap, user_cpumap, (size_t)bytes_to_copy)) {
+ ret = EFAULT;
+ }
+ }
+ *user_cpumap_size = cpumap_size;
+ kmem_free(kernel_map, (vm_offset_t)cpumap, cpumap_size);
+ } else {
+ ret = EINVAL;
+ }
+ } else {
+ ret = EINVAL;
+ }
+
+ return ret;
+}
+
+int
+kdbg_readcurthrmap(user_addr_t buffer, size_t *bufsize)
+{
+ kd_threadmap *mapptr;
+ unsigned int mapsize;
+ unsigned int mapcount;
+ unsigned int count = 0;
+ int ret = 0;
+
+ count = *bufsize / sizeof(kd_threadmap);
+ *bufsize = 0;
+
+ if ((mapptr = kdbg_thrmap_init_internal(count, &mapsize, &mapcount))) {
+ if (copyout(mapptr, buffer, mapcount * sizeof(kd_threadmap))) {
+ ret = EFAULT;
+ } else {
+ *bufsize = (mapcount * sizeof(kd_threadmap));
+ }
+
+ kmem_free(kernel_map, (vm_offset_t)mapptr, mapsize);
+ } else {
+ ret = EINVAL;
+ }
+
+ return ret;
+}
+
+static int
+kdbg_write_v1_header(bool write_thread_map, vnode_t vp, vfs_context_t ctx)
+{
+ int ret = 0;
+ RAW_header header;
+ clock_sec_t secs;
+ clock_usec_t usecs;
+ char *pad_buf;
+ uint32_t pad_size;
+ uint32_t extra_thread_count = 0;
+ uint32_t cpumap_size;
+ size_t map_size = 0;
+ size_t map_count = 0;
+
+ if (write_thread_map) {
+ assert(kd_ctrl_page.kdebug_flags & KDBG_MAPINIT);
+ map_count = kd_mapcount;
+ map_size = map_count * sizeof(kd_threadmap);
+ }
+
+ /*
+ * Without the buffers initialized, we cannot construct a CPU map or a
+ * thread map, and cannot write a header.
+ */
+ if (!(kd_ctrl_page.kdebug_flags & KDBG_BUFINIT)) {
+ return EINVAL;
+ }
+
+ /*
+ * To write a RAW_VERSION1+ file, we must embed a cpumap in the
+ * "padding" used to page align the events following the threadmap. If
+ * the threadmap happens to not require enough padding, we artificially
+ * increase its footprint until it needs enough padding.
+ */
+
+ assert(vp);
+ assert(ctx);
+
+ pad_size = PAGE_16KB - ((sizeof(RAW_header) + map_size) & PAGE_MASK_64);
+ cpumap_size = sizeof(kd_cpumap_header) + kd_ctrl_page.kdebug_cpus * sizeof(kd_cpumap);
+
+ if (cpumap_size > pad_size) {
+ /* If the cpu map doesn't fit in the current available pad_size,
+ * we increase the pad_size by 16K. We do this so that the event
+ * data is always available on a page aligned boundary for both
+ * 4k and 16k systems. We enforce this alignment for the event
+ * data so that we can take advantage of optimized file/disk writes.
+ */
+ pad_size += PAGE_16KB;
+ }
+
+ /* The way we are silently embedding a cpumap in the "padding" is by artificially
+ * increasing the number of thread entries. However, we'll also need to ensure that
+ * the cpumap is embedded in the last 4K page before when the event data is expected.
+ * This way the tools can read the data starting the next page boundary on both
+ * 4K and 16K systems preserving compatibility with older versions of the tools
+ */
+ if (pad_size > PAGE_4KB) {
+ pad_size -= PAGE_4KB;
+ extra_thread_count = (pad_size / sizeof(kd_threadmap)) + 1;
+ }
+
+ memset(&header, 0, sizeof(header));
+ header.version_no = RAW_VERSION1;
+ header.thread_count = map_count + extra_thread_count;
+
+ clock_get_calendar_microtime(&secs, &usecs);
+ header.TOD_secs = secs;
+ header.TOD_usecs = usecs;
+
+ ret = vn_rdwr(UIO_WRITE, vp, (caddr_t)&header, sizeof(RAW_header), RAW_file_offset,
+ UIO_SYSSPACE, IO_NODELOCKED | IO_UNIT, vfs_context_ucred(ctx), (int *) 0, vfs_context_proc(ctx));
+ if (ret) {
+ goto write_error;
+ }
+ RAW_file_offset += sizeof(RAW_header);
+ RAW_file_written += sizeof(RAW_header);
+
+ if (write_thread_map) {
+ ret = vn_rdwr(UIO_WRITE, vp, (caddr_t)kd_mapptr, map_size, RAW_file_offset,
+ UIO_SYSSPACE, IO_NODELOCKED | IO_UNIT, vfs_context_ucred(ctx), (int *) 0, vfs_context_proc(ctx));
+ if (ret) {
+ goto write_error;
+ }
+
+ RAW_file_offset += map_size;
+ RAW_file_written += map_size;
+ }
+
+ if (extra_thread_count) {
+ pad_size = extra_thread_count * sizeof(kd_threadmap);
+ pad_buf = kalloc(pad_size);
+ if (!pad_buf) {
+ ret = ENOMEM;
+ goto write_error;
+ }
+ memset(pad_buf, 0, pad_size);
+
+ ret = vn_rdwr(UIO_WRITE, vp, (caddr_t)pad_buf, pad_size, RAW_file_offset,
+ UIO_SYSSPACE, IO_NODELOCKED | IO_UNIT, vfs_context_ucred(ctx), (int *) 0, vfs_context_proc(ctx));
+ kfree(pad_buf, pad_size);
+ if (ret) {
+ goto write_error;
+ }
+
+ RAW_file_offset += pad_size;
+ RAW_file_written += pad_size;
+ }
+
+ pad_size = PAGE_SIZE - (RAW_file_offset & PAGE_MASK_64);
+ if (pad_size) {
+ pad_buf = (char *)kalloc(pad_size);
+ if (!pad_buf) {
+ ret = ENOMEM;
+ goto write_error;
+ }
+ memset(pad_buf, 0, pad_size);
+
+ /*
+ * embed a cpumap in the padding bytes.
+ * older code will skip this.
+ * newer code will know how to read it.
+ */
+ uint32_t temp = pad_size;
+ if (kdbg_cpumap_init_internal(kd_ctrl_page.kdebug_iops, kd_ctrl_page.kdebug_cpus, (uint8_t**)&pad_buf, &temp) != KERN_SUCCESS) {
+ memset(pad_buf, 0, pad_size);
+ }
+
+ ret = vn_rdwr(UIO_WRITE, vp, (caddr_t)pad_buf, pad_size, RAW_file_offset,
+ UIO_SYSSPACE, IO_NODELOCKED | IO_UNIT, vfs_context_ucred(ctx), (int *) 0, vfs_context_proc(ctx));
+ kfree(pad_buf, pad_size);
+ if (ret) {
+ goto write_error;
+ }
+
+ RAW_file_offset += pad_size;
+ RAW_file_written += pad_size;