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