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