]> git.saurik.com Git - apple/xnu.git/blame - libkern/os/log.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / libkern / os / log.c
CommitLineData
cb323159
A
1/* * Copyright (c) 2019 Apple Inc. All rights reserved. */
2
39037602
A
3#include <stddef.h>
4#undef offset
5
6#include <kern/cpu_data.h>
7#include <os/base.h>
8#include <os/object.h>
9#include <os/log.h>
10#include <stdbool.h>
11#include <stdint.h>
12
13#include <vm/vm_kern.h>
14#include <mach/vm_statistics.h>
15#include <kern/debug.h>
16#include <libkern/libkern.h>
17#include <libkern/kernel_mach_header.h>
18#include <pexpert/pexpert.h>
19#include <uuid/uuid.h>
20#include <sys/msgbuf.h>
21
22#include <mach/mach_time.h>
23#include <kern/thread.h>
24#include <kern/simple_lock.h>
25#include <kern/kalloc.h>
26#include <kern/clock.h>
27#include <kern/assert.h>
f427ee49 28#include <kern/startup.h>
cb323159 29#include <kern/task.h>
39037602
A
30
31#include <firehose/tracepoint_private.h>
813fb2f6 32#include <firehose/chunk_private.h>
39037602
A
33#include <os/firehose_buffer_private.h>
34#include <os/firehose.h>
35
36#include <os/log_private.h>
37#include "trace_internal.h"
38
39#include "log_encode.h"
c3c9b80d 40#include "log_mem.h"
39037602
A
41
42struct os_log_s {
43 int a;
44};
45
46struct os_log_s _os_log_default;
47struct os_log_s _os_log_replay;
c3c9b80d
A
48
49LOGMEM_STATIC_INIT(os_log_mem, 14, 9, 10);
50
39037602 51extern vm_offset_t kernel_firehose_addr;
813fb2f6 52extern firehose_chunk_t firehose_boot_chunk;
39037602 53
f427ee49 54extern bool bsd_log_lock(bool);
39037602 55extern void bsd_log_unlock(void);
cc8bc92a 56extern void logwakeup(struct msgbuf *);
39037602 57
cb323159 58decl_lck_spin_data(extern, oslog_stream_lock);
0a7de745
A
59#define stream_lock() lck_spin_lock(&oslog_stream_lock)
60#define stream_unlock() lck_spin_unlock(&oslog_stream_lock)
61
39037602
A
62extern void oslog_streamwakeup(void);
63void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,
0a7de745 64 uint64_t stamp, const void *pubdata, size_t publen);
39037602
A
65extern void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry);
66
67extern int oslog_stream_open;
68
69extern void *OSKextKextForAddress(const void *);
70
71/* Counters for persistence mode */
c3c9b80d
A
72SCALABLE_COUNTER_DEFINE(oslog_p_total_msgcount);
73SCALABLE_COUNTER_DEFINE(oslog_p_metadata_saved_msgcount);
74SCALABLE_COUNTER_DEFINE(oslog_p_metadata_dropped_msgcount);
75SCALABLE_COUNTER_DEFINE(oslog_p_error_count);
76SCALABLE_COUNTER_DEFINE(oslog_p_saved_msgcount);
77SCALABLE_COUNTER_DEFINE(oslog_p_dropped_msgcount);
78SCALABLE_COUNTER_DEFINE(oslog_p_boot_dropped_msgcount);
79SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_total_msgcount);
80SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_dropped_msgcount);
81SCALABLE_COUNTER_DEFINE(oslog_p_unresolved_kc_msgcount);
39037602
A
82
83/* Counters for streaming mode */
c3c9b80d
A
84SCALABLE_COUNTER_DEFINE(oslog_s_error_count);
85/* Protected by the stream lock */
86uint32_t oslog_s_total_msgcount;
87uint32_t oslog_s_metadata_msgcount;
39037602 88
f427ee49 89/* Counters for msgbuf logging */
c3c9b80d
A
90SCALABLE_COUNTER_DEFINE(oslog_msgbuf_msgcount)
91SCALABLE_COUNTER_DEFINE(oslog_msgbuf_dropped_msgcount)
f427ee49 92
39037602 93static bool oslog_boot_done = false;
5ba3f43e
A
94
95#ifdef XNU_KERNEL_PRIVATE
96bool startup_serial_logging_active = true;
97uint64_t startup_serial_num_procs = 300;
98#endif /* XNU_KERNEL_PRIVATE */
39037602
A
99
100// XXX
101firehose_tracepoint_id_t
102firehose_debug_trace(firehose_stream_t stream, firehose_tracepoint_id_t trace_id,
0a7de745 103 uint64_t timestamp, const char *format, const void *pubdata, size_t publen);
39037602
A
104
105static inline firehose_tracepoint_id_t
106_firehose_trace(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
f427ee49 107 uint64_t stamp, const void *pubdata, size_t publen, bool use_streaming);
39037602
A
108
109static oslog_stream_buf_entry_t
110oslog_stream_create_buf_entry(oslog_stream_link_type_t type, firehose_tracepoint_id_u ftid,
0a7de745 111 uint64_t stamp, const void* pubdata, size_t publen);
39037602
A
112
113static void
114_os_log_with_args_internal(os_log_t oslog __unused, os_log_type_t type __unused,
cb323159 115 const char *format, va_list args, void *addr, void *dso, bool driverKit, bool addcr);
39037602
A
116
117static void
cb323159 118_os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr);
39037602
A
119
120static void
c3c9b80d 121_os_log_to_log_internal(os_log_type_t type, const char *format, va_list args, void *addr, void *dso, bool driverKit);
39037602 122
c3c9b80d
A
123static bool
124os_log_turned_off(void)
125{
126 return atm_get_diagnostic_config() & (ATM_TRACE_DISABLE | ATM_TRACE_OFF);
127}
39037602
A
128
129bool
130os_log_info_enabled(os_log_t log __unused)
131{
c3c9b80d 132 return !os_log_turned_off();
39037602
A
133}
134
135bool
136os_log_debug_enabled(os_log_t log __unused)
137{
c3c9b80d 138 return !os_log_turned_off();
39037602
A
139}
140
c3c9b80d
A
141static bool
142os_log_disabled(void)
39037602 143{
c3c9b80d 144 return atm_get_diagnostic_config() & ATM_TRACE_DISABLE;
39037602
A
145}
146
c3c9b80d
A
147os_log_t
148os_log_create(const char *subsystem __unused, const char *category __unused)
39037602 149{
c3c9b80d 150 return &_os_log_default;
39037602
A
151}
152
0a7de745 153__attribute__((noinline, not_tail_called)) void
39037602
A
154_os_log_internal(void *dso, os_log_t log, uint8_t type, const char *message, ...)
155{
0a7de745
A
156 va_list args;
157 void *addr = __builtin_return_address(0);
39037602 158
0a7de745 159 va_start(args, message);
39037602 160
cb323159 161 _os_log_with_args_internal(log, type, message, args, addr, dso, FALSE, FALSE);
39037602 162
0a7de745 163 va_end(args);
39037602 164
0a7de745 165 return;
39037602
A
166}
167
cb323159
A
168__attribute__((noinline, not_tail_called)) int
169_os_log_internal_driverKit(void *dso, os_log_t log, uint8_t type, const char *message, ...)
170{
171 va_list args;
172 void *addr = __builtin_return_address(0);
173 bool driverKitLog = FALSE;
174
175 /*
176 * We want to be able to identify dexts from the logs.
177 *
178 * Usually the addr is used to understand if the log line
179 * was generated by a kext or the kernel main executable.
180 * Logd uses copyKextUUIDForAddress with the addr specified
181 * in the log line to retrieve the kext UUID of the sender.
182 *
183 * Dext however are not loaded in kernel space so they do not
184 * have a kernel range of addresses.
185 *
186 * To make the same mechanism work, OSKext fakes a kernel
187 * address range for dexts using the loadTag,
188 * so we just need to use the loadTag as addr here
189 * to allow logd to retrieve the correct UUID.
190 *
191 * NOTE: loadTag is populated in the task when the dext is matching,
192 * so if log lines are generated before the matching they will be
193 * identified as kernel main executable.
194 */
195 task_t self_task = current_task();
196
197 /*
198 * Only dextis are supposed to use this log path.
199 */
200 if (!task_is_driver(self_task)) {
201 return EPERM;
202 }
203
204 uint64_t loadTag = get_task_loadTag(self_task);
205 if (loadTag != 0) {
206 driverKitLog = TRUE;
207 addr = (void*) loadTag;
208 }
209 va_start(args, message);
210
211 _os_log_with_args_internal(log, type, message, args, addr, dso, driverKitLog, true);
212
213 va_end(args);
214
215 return 0;
216}
217
39037602
A
218#pragma mark - shim functions
219
0a7de745 220__attribute__((noinline, not_tail_called)) void
39037602
A
221os_log_with_args(os_log_t oslog, os_log_type_t type, const char *format, va_list args, void *addr)
222{
0a7de745
A
223 // if no address passed, look it up
224 if (addr == NULL) {
225 addr = __builtin_return_address(0);
226 }
39037602 227
cb323159 228 _os_log_with_args_internal(oslog, type, format, args, addr, NULL, FALSE, FALSE);
39037602
A
229}
230
231static void
232_os_log_with_args_internal(os_log_t oslog, os_log_type_t type,
cb323159 233 const char *format, va_list args, void *addr, void *dso, bool driverKit, bool addcr)
39037602 234{
0a7de745
A
235 if (format[0] == '\0') {
236 return;
237 }
5ba3f43e 238
0a7de745 239 /* early boot can log to dmesg for later replay (27307943) */
c3c9b80d
A
240 bool safe = (startup_phase < STARTUP_SUB_EARLY_BOOT || oslog_is_safe());
241 bool logging = !os_log_turned_off();
39037602 242
0a7de745 243 if (oslog != &_os_log_replay) {
cb323159 244 _os_log_to_msgbuf_internal(format, args, safe, logging, addcr);
0a7de745 245 }
39037602 246
0a7de745 247 if (safe && logging) {
c3c9b80d 248 _os_log_to_log_internal(type, format, args, addr, dso, driverKit);
0a7de745 249 }
39037602
A
250}
251
252static void
cb323159 253_os_log_to_msgbuf_internal(const char *format, va_list args, bool safe, bool logging, bool addcr)
39037602 254{
f427ee49
A
255 /*
256 * The following threshold was determined empirically as the point where
257 * it would be more advantageous to be able to fit in more log lines than
258 * to know exactly when a log line was printed out. We don't want to use up
259 * a large percentage of the log buffer on timestamps in a memory-constricted
260 * environment.
261 */
262 const int MSGBUF_TIMESTAMP_THRESHOLD = 4096;
0a7de745 263 static int msgbufreplay = -1;
f427ee49 264 static bool newlogline = true;
0a7de745 265 va_list args_copy;
39037602 266
f427ee49 267 if (!bsd_log_lock(safe)) {
c3c9b80d 268 counter_inc(&oslog_msgbuf_dropped_msgcount);
f427ee49 269 return;
0a7de745 270 }
39037602 271
0a7de745
A
272 if (!safe) {
273 if (-1 == msgbufreplay) {
274 msgbufreplay = msgbufp->msg_bufx;
275 }
276 } else if (logging && (-1 != msgbufreplay)) {
277 uint32_t i;
278 uint32_t localbuff_size;
279 int newl, position;
280 char *localbuff, *p, *s, *next, ch;
281
282 position = msgbufreplay;
283 msgbufreplay = -1;
284 localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */
285 /* Size for non-blocking */
286 if (localbuff_size > 4096) {
287 localbuff_size = 4096;
288 }
289 bsd_log_unlock();
290 /* Allocate a temporary non-circular buffer */
f427ee49
A
291 localbuff = kheap_alloc(KHEAP_TEMP, localbuff_size, Z_NOWAIT);
292 if (localbuff != NULL) {
0a7de745 293 /* in between here, the log could become bigger, but that's fine */
f427ee49 294 bsd_log_lock(true);
0a7de745
A
295 /*
296 * The message buffer is circular; start at the replay pointer, and
297 * make one loop up to write pointer - 1.
298 */
299 p = msgbufp->msg_bufc + position;
300 for (i = newl = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) {
301 if (p >= msgbufp->msg_bufc + msgbufp->msg_size) {
302 p = msgbufp->msg_bufc;
303 }
304 ch = *p;
305 if (ch == '\0') {
306 continue;
307 }
308 newl = (ch == '\n');
309 localbuff[i++] = ch;
310 if (i >= (localbuff_size - 2)) {
311 break;
312 }
313 }
314 bsd_log_unlock();
315
316 if (!newl) {
317 localbuff[i++] = '\n';
318 }
319 localbuff[i++] = 0;
320
321 s = localbuff;
322 while ((next = strchr(s, '\n'))) {
323 next++;
324 ch = next[0];
325 next[0] = 0;
326 os_log(&_os_log_replay, "%s", s);
327 next[0] = ch;
328 s = next;
329 }
f427ee49 330 kheap_free(KHEAP_TEMP, localbuff, localbuff_size);
0a7de745 331 }
f427ee49
A
332 bsd_log_lock(true);
333 }
334
335 /* Do not prepend timestamps when we are memory-constricted */
336 if (newlogline && (msgbufp->msg_size > MSGBUF_TIMESTAMP_THRESHOLD)) {
337 clock_sec_t secs;
338 clock_usec_t microsecs;
339 const uint64_t timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
340 absolutetime_to_microtime(timestamp, &secs, &microsecs);
341 printf_log_locked(FALSE, "[%5lu.%06u]: ", (unsigned long)secs, microsecs);
0a7de745 342 }
39037602 343
0a7de745 344 va_copy(args_copy, args);
f427ee49 345 newlogline = vprintf_log_locked(format, args_copy, addcr);
0a7de745 346 va_end(args_copy);
39037602 347
0a7de745 348 bsd_log_unlock();
f427ee49 349 logwakeup(msgbufp);
c3c9b80d 350 counter_inc(&oslog_msgbuf_msgcount);
39037602
A
351}
352
c3c9b80d
A
353static firehose_stream_t
354firehose_stream(os_log_type_t type)
39037602 355{
c3c9b80d
A
356 return (type == OS_LOG_TYPE_INFO || type == OS_LOG_TYPE_DEBUG) ?
357 firehose_stream_memory : firehose_stream_persist;
358}
39037602 359
c3c9b80d
A
360static void
361_os_log_actual(os_log_type_t type, const char *format, void *dso, void *addr, uint8_t *logdata, size_t logdata_sz,
362 firehose_tracepoint_flags_t flags, bool driverKit)
363{
364 firehose_tracepoint_id_u trace_id;
39037602 365
c3c9b80d
A
366 firehose_stream_t stream = firehose_stream(type);
367 uint64_t timestamp = firehose_tracepoint_time(firehose_activity_flags_default);
39037602 368
c3c9b80d
A
369 if (driverKit) {
370 // set FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT so logd will not try to find the format string in
371 // the executable text
372 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
373 type, flags, (uint32_t)((uintptr_t)addr | FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT));
0a7de745 374 } else {
c3c9b80d
A
375 // create trace_id after we've set additional flags
376 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
377 type, flags, _os_trace_offset(dso, format, (_firehose_tracepoint_flags_activity_t)flags));
0a7de745 378 }
39037602 379
c3c9b80d
A
380
381 _firehose_trace(stream, trace_id, timestamp, logdata, logdata_sz, true);
39037602
A
382}
383
c3c9b80d
A
384static void *
385resolve_dso(const char *fmt, void *dso, void *addr, bool driverKit)
39037602 386{
f427ee49 387 kc_format_t kcformat = KCFormatUnknown;
39037602 388
c3c9b80d
A
389 if (!PE_get_primary_kc_format(&kcformat)) {
390 return NULL;
39037602 391 }
39037602 392
c3c9b80d
A
393 switch (kcformat) {
394 case KCFormatStatic:
395 case KCFormatKCGEN:
396 dso = PE_get_kc_baseaddress(KCKindPrimary);
397 break;
398 case KCFormatDynamic:
399 case KCFormatFileset:
400 if (!dso && (dso = (void *)OSKextKextForAddress(fmt)) == NULL) {
401 return NULL;
402 }
403 if (!_os_trace_addr_in_text_segment(dso, fmt)) {
404 return NULL;
405 }
406 if (!driverKit && (dso != (void *)OSKextKextForAddress(addr))) {
407 return NULL;
408 }
409 break;
410 default:
411 panic("unknown KC format type");
813fb2f6 412 }
39037602 413
c3c9b80d 414 return dso;
39037602
A
415}
416
417static void
c3c9b80d 418_os_log_to_log_internal(os_log_type_t type, const char *fmt, va_list args, void *addr, void *dso, bool driverKit)
39037602 419{
c3c9b80d
A
420 counter_inc(&oslog_p_total_msgcount);
421
422 if (addr == NULL) {
423 counter_inc(&oslog_p_unresolved_kc_msgcount);
813fb2f6
A
424 return;
425 }
39037602 426
c3c9b80d
A
427 if ((dso = resolve_dso(fmt, dso, addr, driverKit)) == NULL) {
428 counter_inc(&oslog_p_unresolved_kc_msgcount);
429 return;
cb323159 430 }
39037602 431
c3c9b80d
A
432 uint8_t buffer[OS_LOG_BUFFER_MAX_SIZE] __attribute__((aligned(8))) = { 0 };
433 struct os_log_context_s ctx;
434
435 os_log_context_init(&ctx, &os_log_mem, buffer, sizeof(buffer));
436
437 if (os_log_context_encode(&ctx, fmt, args, addr, dso, driverKit)) {
438 _os_log_actual(type, fmt, dso, addr, ctx.ctx_buffer, ctx.ctx_content_sz,
439 ctx.ctx_ft_flags, driverKit);
f427ee49 440 } else {
c3c9b80d 441 counter_inc(&oslog_p_error_count);
39037602 442 }
c3c9b80d
A
443
444 os_log_context_free(&ctx);
f427ee49
A
445}
446
447bool
448os_log_coprocessor(void *buff, uint64_t buff_len, os_log_type_t type,
449 const char *uuid, uint64_t timestamp, uint32_t offset, bool stream_log)
450{
451 firehose_tracepoint_id_u trace_id;
452 firehose_tracepoint_id_t return_id = 0;
f427ee49
A
453 uint8_t pubdata[OS_LOG_BUFFER_MAX_SIZE];
454 size_t wr_pos = 0;
455
c3c9b80d
A
456 if (os_log_turned_off()) {
457 return false;
458 }
459
f427ee49
A
460 if (buff_len + 16 + sizeof(uint32_t) > OS_LOG_BUFFER_MAX_SIZE) {
461 return false;
462 }
463
c3c9b80d 464 firehose_stream_t stream = firehose_stream(type);
f427ee49
A
465 // unlike kext, where pc is used to find uuid, in coprocessor logs the uuid is passed as part of the tracepoint
466 firehose_tracepoint_flags_t flags = _firehose_tracepoint_flags_pc_style_uuid_relative;
467
468 memcpy(pubdata, &offset, sizeof(uint32_t));
469 wr_pos += sizeof(uint32_t);
470 memcpy(pubdata + wr_pos, uuid, 16);
471 wr_pos += 16;
472
473 memcpy(pubdata + wr_pos, buff, buff_len);
474
475 // create firehose trace id
476 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log,
477 type, flags, offset);
478
c3c9b80d 479 counter_inc(&oslog_p_coprocessor_total_msgcount);
f427ee49
A
480
481 // send firehose tracepoint containing os log to firehose buffer
482 return_id = _firehose_trace(stream, trace_id, timestamp, pubdata,
483 buff_len + wr_pos, stream_log);
484
485 if (return_id == 0) {
c3c9b80d 486 counter_inc(&oslog_p_coprocessor_dropped_msgcount);
f427ee49
A
487 return false;
488 }
489 return true;
39037602
A
490}
491
492static inline firehose_tracepoint_id_t
493_firehose_trace(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
f427ee49 494 uint64_t stamp, const void *pubdata, size_t publen, bool use_streaming)
39037602
A
495{
496 const uint16_t ft_size = offsetof(struct firehose_tracepoint_s, ft_data);
497 const size_t _firehose_chunk_payload_size =
0a7de745 498 sizeof(((struct firehose_chunk_s *)0)->fc_data);
39037602
A
499
500 firehose_tracepoint_t ft;
501
502 if (slowpath(ft_size + publen > _firehose_chunk_payload_size)) {
503 // We'll need to have some handling here. For now - return 0
c3c9b80d 504 counter_inc(&oslog_p_error_count);
39037602
A
505 return 0;
506 }
507
f427ee49 508 if (oslog_stream_open && (stream != firehose_stream_metadata) && use_streaming) {
0a7de745 509 stream_lock();
39037602 510 if (!oslog_stream_open) {
0a7de745 511 stream_unlock();
39037602
A
512 goto out;
513 }
514
515 oslog_s_total_msgcount++;
516 oslog_streamwrite_locked(ftid, stamp, pubdata, publen);
0a7de745 517 stream_unlock();
39037602
A
518 oslog_streamwakeup();
519 }
520
521out:
522 ft = __firehose_buffer_tracepoint_reserve(stamp, stream, (uint16_t)publen, 0, NULL);
523 if (!fastpath(ft)) {
524 if (oslog_boot_done) {
525 if (stream == firehose_stream_metadata) {
c3c9b80d 526 counter_inc(&oslog_p_metadata_dropped_msgcount);
0a7de745 527 } else {
39037602
A
528 // If we run out of space in the persistence buffer we're
529 // dropping the message.
c3c9b80d 530 counter_inc(&oslog_p_dropped_msgcount);
39037602
A
531 }
532 return 0;
533 }
813fb2f6
A
534 firehose_chunk_t fbc = firehose_boot_chunk;
535 long offset;
39037602
A
536
537 //only stream available during boot is persist
813fb2f6 538 offset = firehose_chunk_tracepoint_try_reserve(fbc, stamp,
f427ee49 539 firehose_stream_persist, 0, (uint16_t)publen, 0, NULL);
813fb2f6 540 if (offset <= 0) {
c3c9b80d 541 counter_inc(&oslog_p_boot_dropped_msgcount);
39037602
A
542 return 0;
543 }
813fb2f6 544
f427ee49 545 ft = firehose_chunk_tracepoint_begin(fbc, stamp, (uint16_t)publen,
0a7de745 546 thread_tid(current_thread()), offset);
813fb2f6
A
547 memcpy(ft->ft_data, pubdata, publen);
548 firehose_chunk_tracepoint_end(fbc, ft, ftid);
c3c9b80d 549 counter_inc(&oslog_p_saved_msgcount);
813fb2f6 550 return ftid.ftid_value;
39037602
A
551 }
552 if (!oslog_boot_done) {
553 oslog_boot_done = true;
554 }
555 memcpy(ft->ft_data, pubdata, publen);
556
557 __firehose_buffer_tracepoint_flush(ft, ftid);
558 if (stream == firehose_stream_metadata) {
c3c9b80d 559 counter_inc(&oslog_p_metadata_saved_msgcount);
0a7de745 560 } else {
c3c9b80d 561 counter_inc(&oslog_p_saved_msgcount);
39037602
A
562 }
563 return ftid.ftid_value;
564}
565
566static oslog_stream_buf_entry_t
f427ee49
A
567oslog_stream_create_buf_entry(oslog_stream_link_type_t type,
568 firehose_tracepoint_id_u ftid, uint64_t stamp,
569 const void* pubdata, size_t publen)
39037602 570{
f427ee49
A
571 const size_t ft_size = sizeof(struct firehose_tracepoint_s) + publen;
572 const size_t m_entry_len = sizeof(struct oslog_stream_buf_entry_s) + ft_size;
573 oslog_stream_buf_entry_t m_entry;
39037602 574
f427ee49 575 if (!pubdata || publen > UINT16_MAX || ft_size > UINT16_MAX) {
39037602
A
576 return NULL;
577 }
578
f427ee49 579 m_entry = kheap_alloc(KHEAP_DEFAULT, m_entry_len, Z_WAITOK);
39037602
A
580 if (!m_entry) {
581 return NULL;
582 }
39037602
A
583 m_entry->type = type;
584 m_entry->timestamp = stamp;
f427ee49 585 m_entry->size = (uint16_t)ft_size;
39037602 586
f427ee49 587 firehose_tracepoint_t ft = m_entry->metadata;
39037602
A
588 ft->ft_thread = thread_tid(current_thread());
589 ft->ft_id.ftid_value = ftid.ftid_value;
f427ee49 590 ft->ft_length = (uint16_t)publen;
39037602
A
591 memcpy(ft->ft_data, pubdata, publen);
592
593 return m_entry;
594}
595
f427ee49
A
596void
597os_log_coprocessor_register(const char *uuid, const char *file_path, bool copy)
598{
599 uint64_t stamp;
600 size_t path_size = strlen(file_path) + 1;
601 firehose_tracepoint_id_u trace_id;
602 size_t uuid_info_len = sizeof(struct firehose_trace_uuid_info_s) + path_size;
603 union {
604 struct firehose_trace_uuid_info_s uuid_info;
605 char path[PATH_MAX + sizeof(struct firehose_trace_uuid_info_s)];
606 } buf;
607
c3c9b80d
A
608 if (os_log_disabled()) {
609 return;
610 }
611
f427ee49
A
612 if (path_size > PATH_MAX) {
613 return;
614 }
615
616 // write metadata to uuid_info
617 memcpy(buf.uuid_info.ftui_uuid, uuid, sizeof(uuid_t));
618 buf.uuid_info.ftui_size = 1;
619 buf.uuid_info.ftui_address = 1;
620
621 stamp = firehose_tracepoint_time(firehose_activity_flags_default);
622
623 // create tracepoint id
624 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_metadata, _firehose_tracepoint_type_metadata_coprocessor,
625 (firehose_tracepoint_flags_t)0, copy ? firehose_tracepoint_code_load_memory : firehose_tracepoint_code_load_filesystem);
626
627 // write path to buffer
628 memcpy(buf.uuid_info.ftui_path, file_path, path_size);
629
630 // send metadata tracepoint to firehose for coprocessor registration in logd
631 firehose_trace_metadata(firehose_stream_metadata, trace_id, stamp, (void *)&buf, uuid_info_len);
632 return;
633}
634
39037602
A
635#ifdef KERNEL
636void
637firehose_trace_metadata(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
0a7de745 638 uint64_t stamp, const void *pubdata, size_t publen)
39037602
A
639{
640 oslog_stream_buf_entry_t m_entry = NULL;
641
c3c9b80d
A
642 if (os_log_disabled()) {
643 return;
644 }
645
39037602
A
646 // If streaming mode is not on, only log the metadata
647 // in the persistence buffer
648
0a7de745 649 stream_lock();
39037602 650 if (!oslog_stream_open) {
0a7de745 651 stream_unlock();
39037602
A
652 goto finish;
653 }
0a7de745 654 stream_unlock();
39037602
A
655
656 // Setup and write the stream metadata entry
657 m_entry = oslog_stream_create_buf_entry(oslog_stream_link_type_metadata, ftid,
0a7de745 658 stamp, pubdata, publen);
39037602 659 if (!m_entry) {
c3c9b80d 660 counter_inc(&oslog_s_error_count);
39037602
A
661 goto finish;
662 }
663
0a7de745 664 stream_lock();
39037602 665 if (!oslog_stream_open) {
0a7de745 666 stream_unlock();
f427ee49
A
667 kheap_free(KHEAP_DEFAULT, m_entry,
668 sizeof(struct oslog_stream_buf_entry_s) +
0a7de745 669 sizeof(struct firehose_tracepoint_s) + publen);
39037602
A
670 goto finish;
671 }
672 oslog_s_metadata_msgcount++;
673 oslog_streamwrite_metadata_locked(m_entry);
0a7de745 674 stream_unlock();
39037602
A
675
676finish:
f427ee49 677 _firehose_trace(stream, ftid, stamp, pubdata, publen, true);
39037602
A
678}
679#endif
680
39037602 681void
0a7de745
A
682__firehose_buffer_push_to_logd(firehose_buffer_t fb __unused, bool for_io __unused)
683{
684 oslogwakeup();
685 return;
39037602
A
686}
687
688void
813fb2f6
A
689__firehose_allocate(vm_offset_t *addr, vm_size_t size __unused)
690{
0a7de745
A
691 firehose_chunk_t kernel_buffer = (firehose_chunk_t)kernel_firehose_addr;
692
693 if (kernel_firehose_addr) {
694 *addr = kernel_firehose_addr;
695 } else {
696 *addr = 0;
697 return;
698 }
699 // Now that we are done adding logs to this chunk, set the number of writers to 0
700 // Without this, logd won't flush when the page is full
701 firehose_boot_chunk->fc_pos.fcp_refcnt = 0;
702 memcpy(&kernel_buffer[FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT - 1], (const void *)firehose_boot_chunk, FIREHOSE_CHUNK_SIZE);
703 return;
39037602
A
704}
705// There isnt a lock held in this case.
706void
0a7de745
A
707__firehose_critical_region_enter(void)
708{
709 disable_preemption();
710 return;
39037602
A
711}
712
713void
0a7de745
A
714__firehose_critical_region_leave(void)
715{
716 enable_preemption();
717 return;
39037602
A
718}
719
d9a64523
A
720#ifdef CONFIG_XNUPOST
721
722#include <tests/xnupost.h>
723#define TESTOSLOGFMT(fn_name) "%u^%llu/%llu^kernel^0^test^" fn_name
724#define TESTOSLOGPFX "TESTLOG:%u#"
725#define TESTOSLOG(fn_name) TESTOSLOGPFX TESTOSLOGFMT(fn_name "#")
726
727extern u_int32_t RandomULong(void);
f427ee49 728extern size_t find_pattern_in_buffer(const char *pattern, size_t len, size_t expected_count);
d9a64523
A
729void test_oslog_default_helper(uint32_t uniqid, uint64_t count);
730void test_oslog_info_helper(uint32_t uniqid, uint64_t count);
731void test_oslog_debug_helper(uint32_t uniqid, uint64_t count);
732void test_oslog_error_helper(uint32_t uniqid, uint64_t count);
733void test_oslog_fault_helper(uint32_t uniqid, uint64_t count);
734void _test_log_loop(void * arg __unused, wait_result_t wres __unused);
735void test_oslog_handleOSLogCtl(int32_t * in, int32_t * out, int32_t len);
736kern_return_t test_stresslog_dropmsg(uint32_t uniqid);
737
738kern_return_t test_os_log(void);
739kern_return_t test_os_log_parallel(void);
740
741#define GENOSLOGHELPER(fname, ident, callout_f) \
742 void fname(uint32_t uniqid, uint64_t count) \
743 { \
0a7de745
A
744 int32_t datalen = 0; \
745 uint32_t checksum = 0; \
746 char databuffer[256]; \
747 T_LOG("Doing os_log of %llu TESTLOG msgs for fn " ident, count); \
748 for (uint64_t i = 0; i < count; i++) \
749 { \
4ba76501 750 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT(ident), uniqid, i + 1, count); \
0a7de745
A
751 checksum = crc32(0, databuffer, datalen); \
752 callout_f(OS_LOG_DEFAULT, TESTOSLOG(ident), checksum, uniqid, i + 1, count); \
753 /*T_LOG(TESTOSLOG(ident), checksum, uniqid, i + 1, count);*/ \
754 } \
d9a64523
A
755 }
756
757GENOSLOGHELPER(test_oslog_info_helper, "oslog_info_helper", os_log_info);
758GENOSLOGHELPER(test_oslog_fault_helper, "oslog_fault_helper", os_log_fault);
759GENOSLOGHELPER(test_oslog_debug_helper, "oslog_debug_helper", os_log_debug);
760GENOSLOGHELPER(test_oslog_error_helper, "oslog_error_helper", os_log_error);
761GENOSLOGHELPER(test_oslog_default_helper, "oslog_default_helper", os_log);
762
0a7de745
A
763kern_return_t
764test_os_log()
d9a64523 765{
0a7de745
A
766 char databuffer[256];
767 uint32_t uniqid = RandomULong();
f427ee49 768 size_t match_count = 0;
0a7de745
A
769 uint32_t checksum = 0;
770 uint32_t total_msg = 0;
771 uint32_t saved_msg = 0;
772 uint32_t dropped_msg = 0;
f427ee49 773 size_t datalen = 0;
0a7de745
A
774 uint64_t a = mach_absolute_time();
775 uint64_t seqno = 1;
776 uint64_t total_seqno = 2;
777
778 os_log_t log_handle = os_log_create("com.apple.xnu.test.t1", "kpost");
779
780 T_ASSERT_EQ_PTR(&_os_log_default, log_handle, "os_log_create returns valid value.");
781 T_ASSERT_EQ_INT(TRUE, os_log_info_enabled(log_handle), "os_log_info is enabled");
782 T_ASSERT_EQ_INT(TRUE, os_log_debug_enabled(log_handle), "os_log_debug is enabled");
783 T_ASSERT_EQ_PTR(&_os_log_default, OS_LOG_DEFAULT, "ensure OS_LOG_DEFAULT is _os_log_default");
784
c3c9b80d
A
785 total_msg = counter_load(&oslog_p_total_msgcount);
786 saved_msg = counter_load(&oslog_p_saved_msgcount);
787 dropped_msg = counter_load(&oslog_p_dropped_msgcount);
0a7de745
A
788 T_LOG("oslog internal counters total %u , saved %u, dropped %u", total_msg, saved_msg, dropped_msg);
789
790 T_LOG("Validating with uniqid %u u64 %llu", uniqid, a);
791 T_ASSERT_NE_UINT(0, uniqid, "random number should not be zero");
792 T_ASSERT_NE_ULLONG(0, a, "absolute time should not be zero");
793
4ba76501 794 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("printf_only"), uniqid, seqno, total_seqno);
0a7de745
A
795 checksum = crc32(0, databuffer, datalen);
796 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, seqno, total_seqno, a);
797
798 seqno += 1;
4ba76501 799 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("printf_only"), uniqid, seqno, total_seqno);
0a7de745
A
800 checksum = crc32(0, databuffer, datalen);
801 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, seqno, total_seqno, a);
802
4ba76501 803 datalen = scnprintf(databuffer, sizeof(databuffer), "kernel^0^test^printf_only#mat%llu", a);
0a7de745 804 match_count = find_pattern_in_buffer(databuffer, datalen, total_seqno);
f427ee49 805 T_EXPECT_EQ_ULONG(match_count, total_seqno, "verify printf_only goes to systemlog buffer");
0a7de745
A
806
807 uint32_t logging_config = atm_get_diagnostic_config();
808 T_LOG("checking atm_diagnostic_config 0x%X", logging_config);
809
810 if ((logging_config & ATM_TRACE_OFF) || (logging_config & ATM_TRACE_DISABLE)) {
811 T_LOG("ATM_TRACE_OFF / ATM_TRACE_DISABLE is set. Would not see oslog messages. skipping the rest of test.");
812 return KERN_SUCCESS;
813 }
d9a64523 814
0a7de745 815 /* for enabled logging printfs should be saved in oslog as well */
c3c9b80d 816 T_EXPECT_GE_UINT((counter_load(&oslog_p_total_msgcount) - total_msg), 2, "atleast 2 msgs should be seen by oslog system");
d9a64523 817
0a7de745
A
818 a = mach_absolute_time();
819 total_seqno = 1;
820 seqno = 1;
c3c9b80d
A
821 total_msg = counter_load(&oslog_p_total_msgcount);
822 saved_msg = counter_load(&oslog_p_saved_msgcount);
823 dropped_msg = counter_load(&oslog_p_dropped_msgcount);
4ba76501 824 datalen = scnprintf(databuffer, sizeof(databuffer), TESTOSLOGFMT("oslog_info"), uniqid, seqno, total_seqno);
0a7de745
A
825 checksum = crc32(0, databuffer, datalen);
826 os_log_info(log_handle, TESTOSLOG("oslog_info") "mat%llu", checksum, uniqid, seqno, total_seqno, a);
c3c9b80d 827 T_EXPECT_GE_UINT((counter_load(&oslog_p_total_msgcount) - total_msg), 1, "total message count in buffer");
d9a64523 828
4ba76501 829 datalen = scnprintf(databuffer, sizeof(databuffer), "kernel^0^test^oslog_info#mat%llu", a);
0a7de745 830 match_count = find_pattern_in_buffer(databuffer, datalen, total_seqno);
f427ee49 831 T_EXPECT_EQ_ULONG(match_count, total_seqno, "verify oslog_info does not go to systemlog buffer");
d9a64523 832
c3c9b80d 833 total_msg = counter_load(&oslog_p_total_msgcount);
0a7de745 834 test_oslog_info_helper(uniqid, 10);
c3c9b80d 835 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_info_helper: Should have seen 10 msgs");
d9a64523 836
c3c9b80d 837 total_msg = counter_load(&oslog_p_total_msgcount);
0a7de745 838 test_oslog_debug_helper(uniqid, 10);
c3c9b80d 839 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_debug_helper:Should have seen 10 msgs");
d9a64523 840
c3c9b80d 841 total_msg = counter_load(&oslog_p_total_msgcount);
0a7de745 842 test_oslog_error_helper(uniqid, 10);
c3c9b80d 843 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_error_helper:Should have seen 10 msgs");
d9a64523 844
c3c9b80d 845 total_msg = counter_load(&oslog_p_total_msgcount);
0a7de745 846 test_oslog_default_helper(uniqid, 10);
c3c9b80d 847 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_default_helper:Should have seen 10 msgs");
d9a64523 848
c3c9b80d 849 total_msg = counter_load(&oslog_p_total_msgcount);
0a7de745 850 test_oslog_fault_helper(uniqid, 10);
c3c9b80d 851 T_EXPECT_GE_UINT(counter_load(&oslog_p_total_msgcount) - total_msg, 10, "test_oslog_fault_helper:Should have seen 10 msgs");
d9a64523 852
c3c9b80d
A
853 T_LOG("oslog internal counters total %u , saved %u, dropped %u", counter_load(&oslog_p_total_msgcount), counter_load(&oslog_p_saved_msgcount),
854 counter_load(&oslog_p_dropped_msgcount));
d9a64523 855
0a7de745 856 return KERN_SUCCESS;
d9a64523
A
857}
858
859static uint32_t _test_log_loop_count = 0;
0a7de745
A
860void
861_test_log_loop(void * arg __unused, wait_result_t wres __unused)
d9a64523 862{
0a7de745
A
863 uint32_t uniqid = RandomULong();
864 test_oslog_debug_helper(uniqid, 100);
cb323159 865 os_atomic_add(&_test_log_loop_count, 100, relaxed);
d9a64523
A
866}
867
0a7de745
A
868kern_return_t
869test_os_log_parallel(void)
d9a64523 870{
0a7de745
A
871 thread_t thread[2];
872 kern_return_t kr;
873 uint32_t uniqid = RandomULong();
d9a64523 874
c3c9b80d
A
875 printf("oslog internal counters total %lld , saved %lld, dropped %lld", counter_load(&oslog_p_total_msgcount), counter_load(&oslog_p_saved_msgcount),
876 counter_load(&oslog_p_dropped_msgcount));
d9a64523 877
0a7de745
A
878 kr = kernel_thread_start(_test_log_loop, NULL, &thread[0]);
879 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
d9a64523 880
0a7de745
A
881 kr = kernel_thread_start(_test_log_loop, NULL, &thread[1]);
882 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
d9a64523 883
0a7de745 884 test_oslog_info_helper(uniqid, 100);
d9a64523 885
0a7de745
A
886 /* wait until other thread has also finished */
887 while (_test_log_loop_count < 200) {
888 delay(1000);
889 }
d9a64523 890
0a7de745
A
891 thread_deallocate(thread[0]);
892 thread_deallocate(thread[1]);
d9a64523 893
c3c9b80d
A
894 T_LOG("oslog internal counters total %lld , saved %lld, dropped %lld", counter_load(&oslog_p_total_msgcount), counter_load(&oslog_p_saved_msgcount),
895 counter_load(&oslog_p_dropped_msgcount));
0a7de745 896 T_PASS("parallel_logging tests is now complete");
d9a64523 897
0a7de745 898 return KERN_SUCCESS;
d9a64523
A
899}
900
0a7de745
A
901void
902test_oslog_handleOSLogCtl(int32_t * in, int32_t * out, int32_t len)
d9a64523 903{
0a7de745
A
904 if (!in || !out || len != 4) {
905 return;
906 }
907 switch (in[0]) {
908 case 1:
909 {
910 /* send out counters */
c3c9b80d
A
911 out[1] = counter_load(&oslog_p_total_msgcount);
912 out[2] = counter_load(&oslog_p_saved_msgcount);
913 out[3] = counter_load(&oslog_p_dropped_msgcount);
0a7de745
A
914 out[0] = KERN_SUCCESS;
915 break;
916 }
917 case 2:
918 {
919 /* mini stress run */
920 out[0] = test_os_log_parallel();
921 break;
922 }
923 case 3:
924 {
925 /* drop msg tests */
926 out[1] = RandomULong();
927 out[0] = test_stresslog_dropmsg(out[1]);
928 break;
929 }
930 case 4:
931 {
932 /* invoke log helpers */
933 uint32_t uniqid = in[3];
934 int32_t msgcount = in[2];
935 if (uniqid == 0 || msgcount == 0) {
936 out[0] = KERN_INVALID_VALUE;
937 return;
938 }
939
940 switch (in[1]) {
941 case OS_LOG_TYPE_INFO: test_oslog_info_helper(uniqid, msgcount); break;
942 case OS_LOG_TYPE_DEBUG: test_oslog_debug_helper(uniqid, msgcount); break;
943 case OS_LOG_TYPE_ERROR: test_oslog_error_helper(uniqid, msgcount); break;
944 case OS_LOG_TYPE_FAULT: test_oslog_fault_helper(uniqid, msgcount); break;
945 case OS_LOG_TYPE_DEFAULT:
946 default: test_oslog_default_helper(uniqid, msgcount); break;
947 }
948 out[0] = KERN_SUCCESS;
949 break;
950 /* end of case 4 */
951 }
952 default:
953 {
954 out[0] = KERN_INVALID_VALUE;
955 break;
956 }
957 }
958 return;
d9a64523
A
959}
960
0a7de745
A
961kern_return_t
962test_stresslog_dropmsg(uint32_t uniqid)
d9a64523 963{
0a7de745 964 uint32_t total, saved, dropped;
c3c9b80d
A
965 total = counter_load(&oslog_p_total_msgcount);
966 saved = counter_load(&oslog_p_saved_msgcount);
967 dropped = counter_load(&oslog_p_dropped_msgcount);
0a7de745
A
968 uniqid = RandomULong();
969 test_oslog_debug_helper(uniqid, 100);
c3c9b80d 970 while ((counter_load(&oslog_p_dropped_msgcount) - dropped) == 0) {
0a7de745
A
971 test_oslog_debug_helper(uniqid, 100);
972 }
c3c9b80d
A
973 printf("test_stresslog_dropmsg: logged %lld msgs, saved %lld and caused a drop of %lld msgs. \n", counter_load(&oslog_p_total_msgcount) - total,
974 counter_load(&oslog_p_saved_msgcount) - saved, counter_load(&oslog_p_dropped_msgcount) - dropped);
0a7de745 975 return KERN_SUCCESS;
d9a64523
A
976}
977
978#endif