1 /*
2 * Copyright (c) 2019-2024 Apple Inc. All rights reserved.
3 */
4
5 #undef offset
6
7 #include <kern/cpu_data.h>
8 #include <os/base.h>
9 #include <os/hash.h>
10 #include <os/object.h>
11 #include <os/log.h>
12 #include <stdbool.h>
13 #include <stddef.h>
14 #include <stdint.h>
15
16 #include <vm/vm_kern_xnu.h>
17 #include <mach/vm_statistics.h>
18 #include <kern/debug.h>
19 #include <libkern/libkern.h>
20 #include <libkern/kernel_mach_header.h>
21 #include <pexpert/pexpert.h>
22 #include <uuid/uuid.h>
23 #include <sys/msgbuf.h>
24
25 #include <mach/mach_time.h>
26 #include <kern/thread.h>
27 #include <kern/simple_lock.h>
28 #include <kern/kalloc.h>
29 #include <kern/clock.h>
30 #include <kern/assert.h>
31 #include <kern/smr_hash.h>
32 #include <kern/startup.h>
33 #include <kern/task.h>
34
35 #include <firehose/firehose_types_private.h>
36 #include <firehose/tracepoint_private.h>
37 #include <firehose/chunk_private.h>
38 #include <os/firehose_buffer_private.h>
39 #include <os/firehose.h>
40 #include <os/log_private.h>
41
42 #include "log_encode.h"
43 #include "log_internal.h"
44 #include "log_mem.h"
45 #include "log_queue.h"
46 #include "trace_internal.h"
47
48 #define OS_LOGMEM_BUF_ORDER 14
49 #define OS_LOGMEM_MIN_LOG_ORDER 9
50 #define OS_LOGMEM_MAX_LOG_ORDER (OS_LOG_MAX_SIZE_ORDER)
51
52 #define OS_LOG_SUBSYSTEM_MAX_CNT 1024
53 #define OS_LOG_SUBSYSTEM_NONE 0xffff
54 #define OS_LOG_SUBSYSTEM_BASE 0x0001 // OS_LOG_DEFAULT takes 0 by definition
55 #define OS_LOG_SUBSYSTEM_LAST (OS_LOG_SUBSYSTEM_BASE + OS_LOG_SUBSYSTEM_MAX_CNT)
56 #define OS_LOG_SUBSYSTEM_NAME_MAX_LEN 128
57
58 /*
59 * OSLog subsystem type. The struct layout matches its libtrace counterpart
60 * and also matches what libtrace (logd) expects from subsystem registration
61 * metadata payload.
62 */
63 struct os_log_subsystem_s {
64 uint16_t ols_id;
65 union {
66 struct {
67 uint8_t ols_sub_size;
68 uint8_t ols_cat_size;
69 };
70 uint16_t ols_sizes;
71 };
72 char ols_name[2 * OS_LOG_SUBSYSTEM_NAME_MAX_LEN];
73 };
74
75 struct os_log_s {
76 struct os_log_subsystem_s ol_subsystem;
77 struct smrq_slink ol_hash_link;
78 };
79
80 struct os_log_s _os_log_default;
81 struct os_log_s _os_log_replay;
82
83 /* Counters for persistence mode */
84 SCALABLE_COUNTER_DEFINE(oslog_p_total_msgcount);
85 SCALABLE_COUNTER_DEFINE(oslog_p_metadata_saved_msgcount);
86 SCALABLE_COUNTER_DEFINE(oslog_p_metadata_dropped_msgcount);
87 SCALABLE_COUNTER_DEFINE(oslog_p_signpost_saved_msgcount);
88 SCALABLE_COUNTER_DEFINE(oslog_p_signpost_dropped_msgcount);
89 SCALABLE_COUNTER_DEFINE(oslog_p_error_count);
90 SCALABLE_COUNTER_DEFINE(oslog_p_saved_msgcount);
91 SCALABLE_COUNTER_DEFINE(oslog_p_dropped_msgcount);
92 SCALABLE_COUNTER_DEFINE(oslog_p_boot_dropped_msgcount);
93 SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_total_msgcount);
94 SCALABLE_COUNTER_DEFINE(oslog_p_coprocessor_dropped_msgcount);
95 SCALABLE_COUNTER_DEFINE(oslog_p_unresolved_kc_msgcount);
96
97 /* Counters for msgbuf logging */
98 SCALABLE_COUNTER_DEFINE(oslog_msgbuf_msgcount)
99 SCALABLE_COUNTER_DEFINE(oslog_msgbuf_dropped_msgcount)
100
101 /* Log subsystem counters */
102 SCALABLE_COUNTER_DEFINE(oslog_subsystem_count);
103 SCALABLE_COUNTER_DEFINE(oslog_subsystem_found);
104 SCALABLE_COUNTER_DEFINE(oslog_subsystem_dropped);
105
106 LCK_GRP_DECLARE(oslog_cache_lock_grp, "oslog_cache_lock_grp");
107 LCK_TICKET_DECLARE(oslog_cache_lock, &oslog_cache_lock_grp);
108
109 static bool oslog_boot_done = false;
110 static SECURITY_READ_ONLY_LATE(bool) oslog_disabled = false;
111
112 static uint16_t os_log_subsystem_id = OS_LOG_SUBSYSTEM_BASE;
113
114 static struct logmem_s os_log_mem;
115 static struct smr_hash os_log_cache;
116
117 static struct firehose_chunk_s firehose_boot_chunk = {
118 .fc_pos = {
119 .fcp_next_entry_offs = offsetof(struct firehose_chunk_s, fc_data),
120 .fcp_private_offs = FIREHOSE_CHUNK_SIZE,
121 .fcp_refcnt = 1, // Indicate that there is a writer to this chunk
122 .fcp_stream = firehose_stream_persist,
123 .fcp_flag_io = 1, // Lets assume this is coming from the io bank
124 },
125 };
126
127 bool os_log_disabled(void);
128
129 extern vm_offset_t kernel_firehose_addr;
130 extern bool bsd_log_lock(bool);
131 extern void bsd_log_unlock(void);
132 extern void logwakeup(void);
133 extern void oslog_stream(bool, firehose_tracepoint_id_u, uint64_t, const void *, size_t);
134 extern void *OSKextKextForAddress(const void *);
135
136 static bool
os_log_safe(void)137 os_log_safe(void)
138 {
139 return oslog_is_safe() || startup_phase < STARTUP_SUB_EARLY_BOOT;
140 }
141
142 static bool
os_log_turned_off(void)143 os_log_turned_off(void)
144 {
145 return oslog_disabled || (atm_get_diagnostic_config() & ATM_TRACE_OFF);
146 }
147
148 bool
os_log_info_enabled(os_log_t log __unused)149 os_log_info_enabled(os_log_t log __unused)
150 {
151 return !os_log_turned_off();
152 }
153
154 bool
os_log_debug_enabled(os_log_t log __unused)155 os_log_debug_enabled(os_log_t log __unused)
156 {
157 return !os_log_turned_off();
158 }
159
160 bool
os_log_disabled(void)161 os_log_disabled(void)
162 {
163 return oslog_disabled;
164 }
165
166 static inline size_t
log_payload_priv_data_size(const log_payload_s * lp)167 log_payload_priv_data_size(const log_payload_s *lp)
168 {
169 assert3u(lp->lp_pub_data_size, <=, lp->lp_data_size);
170 return lp->lp_data_size - lp->lp_pub_data_size;
171 }
172
173 static inline const uint8_t *
log_payload_priv_data(const log_payload_s * lp,const uint8_t * lp_data)174 log_payload_priv_data(const log_payload_s *lp, const uint8_t *lp_data)
175 {
176 if (log_payload_priv_data_size(lp) == 0) {
177 return NULL;
178 }
179 return &lp_data[lp->lp_pub_data_size];
180 }
181
182 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,size_t pub_data_size)183 log_payload_init(log_payload_t lp, firehose_stream_t stream, firehose_tracepoint_id_u ftid,
184 uint64_t timestamp, size_t data_size, size_t pub_data_size)
185 {
186 assert3u(pub_data_size, <=, data_size);
187 assert3u(data_size, <, UINT16_MAX);
188
189 lp->lp_stream = stream;
190 lp->lp_ftid = ftid;
191 lp->lp_timestamp = timestamp;
192 lp->lp_pub_data_size = (uint16_t)pub_data_size;
193 lp->lp_data_size = (uint16_t)data_size;
194 }
195
196 static firehose_stream_t
firehose_stream(os_log_type_t type)197 firehose_stream(os_log_type_t type)
198 {
199 return (type == OS_LOG_TYPE_INFO || type == OS_LOG_TYPE_DEBUG) ?
200 firehose_stream_memory : firehose_stream_persist;
201 }
202
203 static firehose_tracepoint_id_t
firehose_ftid(os_log_type_t type,const char * fmt,firehose_tracepoint_flags_t flags,void * dso,void * addr,bool driverKit)204 firehose_ftid(os_log_type_t type, const char *fmt, firehose_tracepoint_flags_t flags,
205 void *dso, void *addr, bool driverKit)
206 {
207 uint32_t off;
208
209 if (driverKit) {
210 /*
211 * Set FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT so logd will not try
212 * to find the format string in the executable text.
213 */
214 off = (uint32_t)((uintptr_t)addr | FIREHOSE_TRACEPOINT_PC_DYNAMIC_BIT);
215 } else {
216 off = _os_trace_offset(dso, fmt, (_firehose_tracepoint_flags_activity_t)flags);
217 }
218
219 return FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_log, type, flags, off);
220 }
221
222 static firehose_tracepoint_flags_t
firehose_ftid_flags(const void * dso,bool driverKit)223 firehose_ftid_flags(const void *dso, bool driverKit)
224 {
225 kc_format_t kcformat = KCFormatUnknown;
226 __assert_only bool result = PE_get_primary_kc_format(&kcformat);
227 assert(result);
228
229 if (kcformat == KCFormatStatic || kcformat == KCFormatKCGEN) {
230 return _firehose_tracepoint_flags_pc_style_shared_cache;
231 }
232
233 /*
234 * driverKit will have the dso set as MH_EXECUTE (it is logging from a
235 * syscall in the kernel) but needs logd to parse the address as an
236 * absolute pc.
237 */
238 const kernel_mach_header_t *mh = dso;
239 if (mh->filetype == MH_EXECUTE && !driverKit) {
240 return _firehose_tracepoint_flags_pc_style_main_exe;
241 }
242
243 return _firehose_tracepoint_flags_pc_style_absolute;
244 }
245
246 static void *
resolve_dso(const char * fmt,void * dso,void * addr,bool driverKit)247 resolve_dso(const char *fmt, void *dso, void *addr, bool driverKit)
248 {
249 kc_format_t kcformat = KCFormatUnknown;
250
251 if (!addr || !PE_get_primary_kc_format(&kcformat)) {
252 return NULL;
253 }
254
255 switch (kcformat) {
256 case KCFormatStatic:
257 case KCFormatKCGEN:
258 dso = PE_get_kc_baseaddress(KCKindPrimary);
259 break;
260 case KCFormatDynamic:
261 case KCFormatFileset:
262 if (!dso && (dso = (void *)OSKextKextForAddress(fmt)) == NULL) {
263 return NULL;
264 }
265 if (!_os_trace_addr_in_text_segment(dso, fmt)) {
266 return NULL;
267 }
268 if (!driverKit && (dso != (void *)OSKextKextForAddress(addr))) {
269 return NULL;
270 }
271 break;
272 default:
273 panic("unknown KC format type");
274 }
275
276 return dso;
277 }
278
279 static inline uintptr_t
resolve_location(firehose_tracepoint_flags_t flags,uintptr_t dso,uintptr_t addr,bool driverKit,size_t * loc_size)280 resolve_location(firehose_tracepoint_flags_t flags, uintptr_t dso, uintptr_t addr,
281 bool driverKit, size_t *loc_size)
282 {
283 switch (flags & _firehose_tracepoint_flags_pc_style_mask) {
284 case _firehose_tracepoint_flags_pc_style_shared_cache:
285 case _firehose_tracepoint_flags_pc_style_main_exe:
286 *loc_size = sizeof(uint32_t);
287 return addr - dso;
288 case _firehose_tracepoint_flags_pc_style_absolute:
289 *loc_size = sizeof(uintptr_t);
290 return driverKit ? addr : VM_KERNEL_UNSLIDE(addr);
291 default:
292 panic("Unknown firehose tracepoint flags %x", flags);
293 }
294 }
295
296 __startup_func
297 static void
oslog_init(void)298 oslog_init(void)
299 {
300 /*
301 * Disable kernel logging if ATM_TRACE_DISABLE set. ATM_TRACE_DISABLE
302 * bit is not supposed to change during a system run but nothing really
303 * prevents userspace from unintentionally doing so => we stash initial
304 * value in a dedicated variable for a later reference, just in case.
305 */
306 oslog_disabled = atm_get_diagnostic_config() & ATM_TRACE_DISABLE;
307
308 if (!oslog_disabled) {
309 smr_hash_init(&os_log_cache, OS_LOG_SUBSYSTEM_MAX_CNT / 4);
310 }
311 }
312 STARTUP(OSLOG, STARTUP_RANK_FIRST, oslog_init);
313
314 __startup_func
315 static void
oslog_init_logmem(void)316 oslog_init_logmem(void)
317 {
318 if (os_log_disabled()) {
319 printf("Long logs support disabled: Logging disabled by ATM\n");
320 return;
321 }
322
323 const size_t logmem_size = logmem_required_size(OS_LOGMEM_BUF_ORDER, OS_LOGMEM_MIN_LOG_ORDER);
324 vm_offset_t addr;
325
326 if (kmem_alloc(kernel_map, &addr, logmem_size + ptoa(2),
327 KMA_KOBJECT | KMA_PERMANENT | KMA_ZERO | KMA_GUARD_FIRST | KMA_GUARD_LAST,
328 VM_KERN_MEMORY_LOG) == KERN_SUCCESS) {
329 logmem_init(&os_log_mem, (void *)(addr + PAGE_SIZE), logmem_size,
330 OS_LOGMEM_BUF_ORDER, OS_LOGMEM_MIN_LOG_ORDER, OS_LOGMEM_MAX_LOG_ORDER);
331 printf("Long logs support configured: size: %u\n", os_log_mem.lm_cnt_free);
332 } else {
333 printf("Long logs support disabled: Not enough memory\n");
334 }
335 }
336 STARTUP(OSLOG, STARTUP_RANK_SECOND, oslog_init_logmem);
337
338 bool
os_log_encoded_metadata(firehose_tracepoint_id_u ftid,uint64_t ts,const void * msg,size_t msg_size)339 os_log_encoded_metadata(firehose_tracepoint_id_u ftid, uint64_t ts, const void *msg,
340 size_t msg_size)
341 {
342 assert(ftid.ftid._namespace == firehose_tracepoint_namespace_metadata);
343 counter_inc(&oslog_p_total_msgcount);
344
345 if (!os_log_safe() || os_log_disabled()) {
346 counter_inc(&oslog_p_metadata_dropped_msgcount);
347 return false;
348 }
349
350 log_payload_s log;
351 log_payload_init(&log, firehose_stream_metadata, ftid, ts, msg_size, msg_size);
352
353 if (log_queue_log(&log, msg, true)) {
354 return true;
355 }
356
357 counter_inc(&oslog_p_metadata_dropped_msgcount);
358 return false;
359 }
360
361 bool
os_log_encoded_log(firehose_stream_t stream,firehose_tracepoint_id_u ftid,uint64_t ts,const void * msg,size_t msg_size,size_t pub_data_size)362 os_log_encoded_log(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
363 uint64_t ts, const void *msg, size_t msg_size, size_t pub_data_size)
364 {
365 assert(ftid.ftid._namespace == firehose_tracepoint_namespace_log);
366 counter_inc(&oslog_p_total_msgcount);
367
368 if (!os_log_safe() || os_log_disabled()) {
369 counter_inc(&oslog_p_dropped_msgcount);
370 return false;
371 }
372
373 log_payload_s log;
374 log_payload_init(&log, stream, ftid, ts, msg_size, pub_data_size);
375
376 if (log_queue_log(&log, msg, true)) {
377 return true;
378 }
379
380 counter_inc(&oslog_p_dropped_msgcount);
381 return false;
382 }
383
384 bool
os_log_encoded_signpost(firehose_stream_t stream,firehose_tracepoint_id_u ftid,uint64_t ts,const void * msg,size_t msg_size,size_t pub_data_size)385 os_log_encoded_signpost(firehose_stream_t stream, firehose_tracepoint_id_u ftid,
386 uint64_t ts, const void *msg, size_t msg_size, size_t pub_data_size)
387 {
388 assert(ftid.ftid._namespace == firehose_tracepoint_namespace_signpost);
389 counter_inc(&oslog_p_total_msgcount);
390
391 if (!os_log_safe() || os_log_disabled()) {
392 counter_inc(&oslog_p_signpost_dropped_msgcount);
393 return false;
394 }
395
396 log_payload_s log;
397 log_payload_init(&log, stream, ftid, ts, msg_size, pub_data_size);
398
399 if (log_queue_log(&log, msg, true)) {
400 return true;
401 }
402
403 counter_inc(&oslog_p_signpost_dropped_msgcount);
404 return false;
405 }
406
407 static inline size_t
os_log_subsystem_name_size(const char * name)408 os_log_subsystem_name_size(const char *name)
409 {
410 return MIN(strlen(name) + 1, OS_LOG_SUBSYSTEM_NAME_MAX_LEN);
411 }
412
413 bool
os_log_subsystem_id_valid(uint16_t sid)414 os_log_subsystem_id_valid(uint16_t sid)
415 {
416 return sid >= OS_LOG_SUBSYSTEM_BASE && sid <= os_log_subsystem_id;
417 }
418
419 static inline size_t
os_log_subsystem_id_length(const struct os_log_subsystem_s * sub)420 os_log_subsystem_id_length(const struct os_log_subsystem_s *sub)
421 {
422 return sub->ols_sub_size + sub->ols_cat_size;
423 }
424
425 static inline size_t
os_log_subsystem_size(const struct os_log_subsystem_s * tbs)426 os_log_subsystem_size(const struct os_log_subsystem_s *tbs)
427 {
428 assert(tbs->ols_sub_size <= OS_LOG_SUBSYSTEM_NAME_MAX_LEN);
429 assert(tbs->ols_cat_size <= OS_LOG_SUBSYSTEM_NAME_MAX_LEN);
430 return sizeof(*tbs) - sizeof(tbs->ols_name) + os_log_subsystem_id_length(tbs);
431 }
432
433 static void
os_log_subsystem_init(struct os_log_subsystem_s * ols,uint16_t sid,const char * sub,const char * cat)434 os_log_subsystem_init(struct os_log_subsystem_s *ols, uint16_t sid, const char *sub, const char *cat)
435 {
436 ols->ols_sub_size = os_log_subsystem_name_size(sub);
437 ols->ols_cat_size = os_log_subsystem_name_size(cat);
438 strlcpy(&ols->ols_name[0], sub, OS_LOG_SUBSYSTEM_NAME_MAX_LEN);
439 strlcpy(&ols->ols_name[ols->ols_sub_size], cat, OS_LOG_SUBSYSTEM_NAME_MAX_LEN);
440 ols->ols_id = sid;
441 }
442
443 static void
os_log_subsystem_register(const struct os_log_subsystem_s * ols,uint64_t stamp)444 os_log_subsystem_register(const struct os_log_subsystem_s *ols, uint64_t stamp)
445 {
446 assert(os_log_subsystem_id_valid(ols->ols_id));
447
448 firehose_tracepoint_id_u trace_id;
449 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_metadata,
450 _firehose_tracepoint_type_metadata_subsystem, 0, ols->ols_id);
451
452 os_log_encoded_metadata(trace_id, stamp, ols, os_log_subsystem_size(ols));
453 }
454
455 #if DEVELOPMENT || DEBUG
456 static bool
os_log_valid(const struct os_log_s * log)457 os_log_valid(const struct os_log_s *log)
458 {
459 return os_log_subsystem_id_valid(log->ol_subsystem.ols_id);
460 }
461 #endif // DEVELOPMENT || DEBUG
462
463 static smrh_key_t
os_log_key(const struct os_log_subsystem_s * sub)464 os_log_key(const struct os_log_subsystem_s *sub)
465 {
466 return (smrh_key_t) {
467 .smrk_opaque = sub->ols_name,
468 .smrk_len = os_log_subsystem_id_length(sub)
469 };
470 }
471
472 static uint32_t
os_log_key_hash(smrh_key_t key,uint32_t seed)473 os_log_key_hash(smrh_key_t key, uint32_t seed)
474 {
475 return smrh_key_hash_mem(key, seed);
476 }
477
478 static bool
os_log_key_equ(smrh_key_t k1,smrh_key_t k2)479 os_log_key_equ(smrh_key_t k1, smrh_key_t k2)
480 {
481 return k1.smrk_len == k2.smrk_len ? smrh_key_equ_mem(k1, k2) : false;
482 }
483
484 static uint32_t
os_log_hash(const struct smrq_slink * link,uint32_t seed)485 os_log_hash(const struct smrq_slink *link, uint32_t seed)
486 {
487 const os_log_t log = __container_of(link, struct os_log_s, ol_hash_link);
488 return os_log_key_hash(os_log_key(&log->ol_subsystem), seed);
489 }
490
491 static bool
os_log_equ(const struct smrq_slink * link,smrh_key_t key)492 os_log_equ(const struct smrq_slink *link, smrh_key_t key)
493 {
494 const os_log_t h = __container_of(link, struct os_log_s, ol_hash_link);
495 return os_log_key_equ(os_log_key(&h->ol_subsystem), key);
496 }
497
498 static bool
oslog_try_get(void * oslog __unused)499 oslog_try_get(void *oslog __unused)
500 {
501 return true;
502 }
503
504 SMRH_TRAITS_DEFINE(os_log_cache_traits, struct os_log_s, ol_hash_link,
505 .domain = &smr_oslog,
506 .key_hash = os_log_key_hash,
507 .key_equ = os_log_key_equ,
508 .obj_hash = os_log_hash,
509 .obj_equ = os_log_equ,
510 .obj_try_get = oslog_try_get
511 );
512
513 static os_log_t
os_log_cache_find_by_sub(const struct os_log_subsystem_s * sub)514 os_log_cache_find_by_sub(const struct os_log_subsystem_s *sub)
515 {
516 os_log_t log = smr_hash_get(&os_log_cache, os_log_key(sub), &os_log_cache_traits);
517 assert(!log || os_log_valid(log));
518 return log;
519 }
520
521 static os_log_t
os_log_cache_insert(os_log_t log)522 os_log_cache_insert(os_log_t log)
523 {
524 assert(os_log_subsystem_id_valid(log->ol_subsystem.ols_id));
525
526 os_log_t found = smr_hash_serialized_get_or_insert(&os_log_cache,
527 os_log_key(&log->ol_subsystem), &log->ol_hash_link,
528 &os_log_cache_traits);
529 assert(!found || os_log_valid(found));
530 return found;
531 }
532
533 static os_log_t
os_log_allocate(const struct os_log_subsystem_s * subsystem)534 os_log_allocate(const struct os_log_subsystem_s *subsystem)
535 {
536 os_log_t new = kalloc_type(struct os_log_s, Z_WAITOK_ZERO);
537 if (new) {
538 new->ol_subsystem = *subsystem;
539 }
540 return new;
541 }
542
543 static uint16_t
os_log_subsystem_id_next(void)544 os_log_subsystem_id_next(void)
545 {
546 assert(os_log_subsystem_id_valid(os_log_subsystem_id));
547
548 if (__improbable(os_log_subsystem_id == OS_LOG_SUBSYSTEM_LAST)) {
549 return OS_LOG_SUBSYSTEM_NONE;
550 }
551 return os_log_subsystem_id++;
552 }
553
554 static void
os_log_subsystem_id_revert(uint16_t __assert_only sid)555 os_log_subsystem_id_revert(uint16_t __assert_only sid)
556 {
557 assert(os_log_subsystem_id_valid(os_log_subsystem_id));
558 assert3u(os_log_subsystem_id, ==, sid + 1);
559 os_log_subsystem_id--;
560 }
561
562 static os_log_t
os_log_create_impl(const char * subsystem,const char * category,uint64_t stamp)563 os_log_create_impl(const char *subsystem, const char *category, uint64_t stamp)
564 {
565 if (os_log_disabled()) {
566 return OS_LOG_DISABLED;
567 }
568
569 struct os_log_subsystem_s new_sub;
570 os_log_subsystem_init(&new_sub, OS_LOG_SUBSYSTEM_NONE, subsystem, category);
571
572 os_log_t log = os_log_cache_find_by_sub(&new_sub);
573 if (log) {
574 counter_inc(&oslog_subsystem_found);
575 return log;
576 }
577
578 if (!(log = os_log_allocate(&new_sub))) {
579 counter_inc(&oslog_subsystem_dropped);
580 return OS_LOG_DEFAULT;
581 }
582
583 lck_ticket_lock(&oslog_cache_lock, &oslog_cache_lock_grp);
584
585 log->ol_subsystem.ols_id = os_log_subsystem_id_next();
586 if (__improbable(log->ol_subsystem.ols_id == OS_LOG_SUBSYSTEM_NONE)) {
587 lck_ticket_unlock(&oslog_cache_lock);
588 kfree_type(struct os_log_s, log);
589 counter_inc(&oslog_subsystem_dropped);
590 return OS_LOG_DEFAULT;
591 }
592
593 os_log_t found = os_log_cache_insert(log);
594 if (__improbable(found)) {
595 os_log_subsystem_id_revert(log->ol_subsystem.ols_id);
596 lck_ticket_unlock(&oslog_cache_lock);
597 kfree_type(struct os_log_s, log);
598 counter_inc(&oslog_subsystem_found);
599 return found;
600 }
601
602 lck_ticket_unlock(&oslog_cache_lock);
603
604 os_log_subsystem_register(&log->ol_subsystem, stamp);
605 counter_inc(&oslog_subsystem_count);
606
607 return log;
608 }
609
610 os_log_t
os_log_create(const char * subsystem,const char * category)611 os_log_create(const char *subsystem, const char *category)
612 {
613 uint64_t stamp = firehose_tracepoint_time(firehose_activity_flags_default);
614 return os_log_create_impl(subsystem, category, stamp);
615 }
616
617 static void
_os_log_to_msgbuf_internal(const char * format,va_list args,uint64_t timestamp,bool safe,bool logging,bool addcr)618 _os_log_to_msgbuf_internal(const char *format, va_list args, uint64_t timestamp,
619 bool safe, bool logging, bool addcr)
620 {
621 /*
622 * The following threshold was determined empirically as the point where
623 * it would be more advantageous to be able to fit in more log lines than
624 * to know exactly when a log line was printed out. We don't want to use up
625 * a large percentage of the log buffer on timestamps in a memory-constricted
626 * environment.
627 */
628 const int MSGBUF_TIMESTAMP_THRESHOLD = 4096;
629 static int msgbufreplay = -1;
630 static bool newlogline = true;
631 va_list args_copy;
632
633 if (!bsd_log_lock(safe)) {
634 counter_inc(&oslog_msgbuf_dropped_msgcount);
635 return;
636 }
637
638 if (!safe) {
639 if (-1 == msgbufreplay) {
640 msgbufreplay = msgbufp->msg_bufx;
641 }
642 } else if (logging && (-1 != msgbufreplay)) {
643 uint32_t i;
644 uint32_t localbuff_size;
645 int newl, position;
646 char *localbuff, *p, *s, *next, ch;
647
648 position = msgbufreplay;
649 msgbufreplay = -1;
650 localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */
651 /* Size for non-blocking */
652 if (localbuff_size > 4096) {
653 localbuff_size = 4096;
654 }
655 bsd_log_unlock();
656 /* Allocate a temporary non-circular buffer */
657 localbuff = kalloc_data(localbuff_size, Z_NOWAIT);
658 if (localbuff != NULL) {
659 /* in between here, the log could become bigger, but that's fine */
660 bsd_log_lock(true);
661 /*
662 * The message buffer is circular; start at the replay pointer, and
663 * make one loop up to write pointer - 1.
664 */
665 p = msgbufp->msg_bufc + position;
666 for (i = newl = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) {
667 if (p >= msgbufp->msg_bufc + msgbufp->msg_size) {
668 p = msgbufp->msg_bufc;
669 }
670 ch = *p;
671 if (ch == '\0') {
672 continue;
673 }
674 newl = (ch == '\n');
675 localbuff[i++] = ch;
676 if (i >= (localbuff_size - 2)) {
677 break;
678 }
679 }
680 bsd_log_unlock();
681
682 if (!newl) {
683 localbuff[i++] = '\n';
684 }
685 localbuff[i++] = 0;
686
687 s = localbuff;
688 while ((next = strchr(s, '\n'))) {
689 next++;
690 ch = next[0];
691 next[0] = 0;
692 os_log(&_os_log_replay, "%s", s);
693 next[0] = ch;
694 s = next;
695 }
696 kfree_data(localbuff, localbuff_size);
697 }
698 bsd_log_lock(true);
699 }
700
701 /* Do not prepend timestamps when we are memory-constricted */
702 if (newlogline && (msgbufp->msg_size > MSGBUF_TIMESTAMP_THRESHOLD)) {
703 clock_sec_t secs;
704 clock_usec_t microsecs;
705 absolutetime_to_microtime(timestamp, &secs, µsecs);
706 printf_log_locked(FALSE, "[%5lu.%06u]: ", (unsigned long)secs, microsecs);
707 }
708
709 #pragma clang diagnostic push
710 #pragma clang diagnostic ignored "-Wformat-nonliteral"
711 va_copy(args_copy, args);
712 newlogline = vprintf_log_locked(format, args_copy, addcr);
713 va_end(args_copy);
714 #pragma clang diagnostic pop
715
716 bsd_log_unlock();
717 logwakeup();
718 counter_inc(&oslog_msgbuf_msgcount);
719 }
720
721 static void
_os_log_to_log_internal(uint16_t sid,os_log_type_t type,const char * fmt,va_list args,uint64_t ts,void * addr,void * dso,bool driverKit)722 _os_log_to_log_internal(uint16_t sid, os_log_type_t type, const char *fmt, va_list args,
723 uint64_t ts, void *addr, void *dso, bool driverKit)
724 {
725 dso = resolve_dso(fmt, dso, addr, driverKit);
726 if (__improbable(!dso)) {
727 counter_inc(&oslog_p_unresolved_kc_msgcount);
728 return;
729 }
730
731 firehose_tracepoint_flags_t flags = firehose_ftid_flags(dso, driverKit);
732 if (sid != 0) {
733 flags |= _firehose_tracepoint_flags_log_has_subsystem;
734 }
735
736 size_t loc_sz = 0;
737 uintptr_t loc = resolve_location(flags, (uintptr_t)dso, (uintptr_t)addr,
738 driverKit, &loc_sz);
739
740 firehose_tracepoint_id_u ftid = {
741 .ftid_value = firehose_ftid(type, fmt, flags, dso, addr, driverKit)
742 };
743
744 __attribute__((uninitialized, aligned(8)))
745 uint8_t buffer[OS_LOG_BUFFER_MAX_SIZE];
746 struct os_log_context_s ctx;
747
748 os_log_context_init(&ctx, &os_log_mem, buffer, sizeof(buffer));
749
750 if (!os_log_context_encode(&ctx, fmt, args, loc, loc_sz, sid)) {
751 counter_inc(&oslog_p_error_count);
752 os_log_context_free(&ctx);
753 return;
754 }
755
756 log_payload_s log;
757 log_payload_init(&log, firehose_stream(type), ftid, ts, ctx.ctx_content_sz, ctx.ctx_content_sz);
758
759 if (!log_queue_log(&log, ctx.ctx_buffer, true)) {
760 counter_inc(&oslog_p_dropped_msgcount);
761 }
762
763 os_log_context_free(&ctx);
764 }
765
766 static void
_os_log_with_args_internal(os_log_t oslog,os_log_type_t type,const char * fmt,va_list args,uint64_t ts,void * addr,void * dso,bool driverKit,bool addcr)767 _os_log_with_args_internal(os_log_t oslog, os_log_type_t type, const char *fmt,
768 va_list args, uint64_t ts, void *addr, void *dso, bool driverKit, bool addcr)
769 {
770 counter_inc(&oslog_p_total_msgcount);
771
772 if (oslog == OS_LOG_DISABLED) {
773 counter_inc(&oslog_p_dropped_msgcount);
774 return;
775 }
776
777 if (__improbable(fmt[0] == '\0')) {
778 counter_inc(&oslog_p_error_count);
779 return;
780 }
781
782 /* early boot can log to dmesg for later replay (27307943) */
783 bool safe = os_log_safe();
784 bool logging = !os_log_turned_off();
785
786 if (oslog != &_os_log_replay) {
787 _os_log_to_msgbuf_internal(fmt, args, ts, safe, logging, addcr);
788 }
789
790 if (safe && logging) {
791 const uint16_t sid = oslog->ol_subsystem.ols_id;
792 _os_log_to_log_internal(sid, type, fmt, args, ts, addr, dso, driverKit);
793 }
794 }
795
796 __attribute__((noinline, not_tail_called)) void
_os_log_internal(void * dso,os_log_t log,uint8_t type,const char * fmt,...)797 _os_log_internal(void *dso, os_log_t log, uint8_t type, const char *fmt, ...)
798 {
799 uint64_t ts = firehose_tracepoint_time(firehose_activity_flags_default);
800 void *addr = __builtin_return_address(0);
801 va_list args;
802
803 va_start(args, fmt);
804 _os_log_with_args_internal(log, type, fmt, args, ts, addr, dso, FALSE, FALSE);
805 va_end(args);
806 }
807
808 __attribute__((noinline, not_tail_called)) void
_os_log_at_time(void * dso,os_log_t log,uint8_t type,uint64_t ts,const char * fmt,...)809 _os_log_at_time(void *dso, os_log_t log, uint8_t type, uint64_t ts, const char *fmt, ...)
810 {
811 void *addr = __builtin_return_address(0);
812 va_list args;
813
814 va_start(args, fmt);
815 _os_log_with_args_internal(log, type, fmt, args, ts, addr, dso, FALSE, FALSE);
816 va_end(args);
817 }
818
819 __attribute__((noinline, not_tail_called)) int
_os_log_internal_driverKit(void * dso,os_log_t log,uint8_t type,const char * fmt,...)820 _os_log_internal_driverKit(void *dso, os_log_t log, uint8_t type, const char *fmt, ...)
821 {
822 uint64_t ts = firehose_tracepoint_time(firehose_activity_flags_default);
823 void *addr = __builtin_return_address(0);
824 bool driverKitLog = FALSE;
825 va_list args;
826
827 /*
828 * We want to be able to identify dexts from the logs.
829 *
830 * Usually the addr is used to understand if the log line
831 * was generated by a kext or the kernel main executable.
832 * Logd uses copyKextUUIDForAddress with the addr specified
833 * in the log line to retrieve the kext UUID of the sender.
834 *
835 * Dext however are not loaded in kernel space so they do not
836 * have a kernel range of addresses.
837 *
838 * To make the same mechanism work, OSKext fakes a kernel
839 * address range for dexts using the loadTag,
840 * so we just need to use the loadTag as addr here
841 * to allow logd to retrieve the correct UUID.
842 *
843 * NOTE: loadTag is populated in the task when the dext is matching,
844 * so if log lines are generated before the matching they will be
845 * identified as kernel main executable.
846 */
847 task_t self_task = current_task();
848
849 /*
850 * Only dexts are supposed to use this log path. Verified in log_data()
851 * but worth of another check here in case this function gets called
852 * directly.
853 */
854 if (!task_is_driver(self_task)) {
855 return EPERM;
856 }
857
858 uint64_t loadTag = get_task_loadTag(self_task);
859 if (loadTag != 0) {
860 driverKitLog = TRUE;
861 addr = (void *)loadTag;
862 }
863
864 va_start(args, fmt);
865 _os_log_with_args_internal(log, type, fmt, args, ts, addr, dso, driverKitLog, true);
866 va_end(args);
867
868 return 0;
869 }
870
871 __attribute__((noinline, not_tail_called)) void
os_log_with_args(os_log_t oslog,os_log_type_t type,const char * fmt,va_list args,void * addr)872 os_log_with_args(os_log_t oslog, os_log_type_t type, const char *fmt,
873 va_list args, void *addr)
874 {
875 uint64_t ts = firehose_tracepoint_time(firehose_activity_flags_default);
876
877 // if no address passed, look it up
878 if (addr == NULL) {
879 addr = __builtin_return_address(0);
880 }
881
882 _os_log_with_args_internal(oslog, type, fmt, args, ts, addr, NULL, FALSE, FALSE);
883 }
884
885 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)886 os_log_coprocessor(void *buff, uint64_t buff_len, os_log_type_t type,
887 const char *uuid, uint64_t timestamp, uint32_t offset, bool stream_log)
888 {
889 counter_inc(&oslog_p_coprocessor_total_msgcount);
890
891 if (os_log_turned_off() || !os_log_safe()) {
892 counter_inc(&oslog_p_coprocessor_dropped_msgcount);
893 return false;
894 }
895
896 if (buff_len + 16 + sizeof(uint32_t) > OS_LOG_BUFFER_MAX_SIZE) {
897 counter_inc(&oslog_p_coprocessor_dropped_msgcount);
898 return false;
899 }
900
901 __attribute__((uninitialized))
902 uint8_t pubdata[OS_LOG_BUFFER_MAX_SIZE];
903 size_t wr_pos = 0;
904
905 memcpy(pubdata, &offset, sizeof(uint32_t));
906 wr_pos += sizeof(uint32_t);
907 memcpy(pubdata + wr_pos, uuid, 16);
908 wr_pos += 16;
909
910 memcpy(pubdata + wr_pos, buff, buff_len);
911 wr_pos += buff_len;
912
913 /*
914 * Unlike KEXTs, where PC is used to find UUID, in coprocessor logs the UUID
915 * is passed as part of the tracepoint.
916 */
917 firehose_tracepoint_namespace_t ns = firehose_tracepoint_namespace_log;
918 firehose_tracepoint_flags_t flags = _firehose_tracepoint_flags_pc_style_uuid_relative;
919 firehose_tracepoint_id_u trace_id = {
920 .ftid_value = FIREHOSE_TRACE_ID_MAKE(ns, type, flags, offset)
921 };
922
923 log_payload_s log;
924 log_payload_init(&log, firehose_stream(type), trace_id, timestamp, wr_pos, wr_pos);
925
926 if (!log_queue_log(&log, pubdata, stream_log)) {
927 counter_inc(&oslog_p_coprocessor_dropped_msgcount);
928 return false;
929 }
930
931 return true;
932 }
933
934 static inline firehose_tracepoint_t
firehose_trace_start(const log_payload_s * lp,uint8_t ** ft_priv_data)935 firehose_trace_start(const log_payload_s *lp, uint8_t **ft_priv_data)
936 {
937 uint16_t priv_data_size = (uint16_t)log_payload_priv_data_size(lp);
938 uint16_t pub_data_size = (uint16_t)lp->lp_pub_data_size;
939
940 if (priv_data_size > 0) {
941 pub_data_size += sizeof(struct firehose_buffer_range_s);
942 }
943
944 assert3u(offsetof(struct firehose_tracepoint_s, ft_data) +
945 pub_data_size + priv_data_size, <=, sizeof(((firehose_chunk_t)0)->fc_data));
946
947 firehose_tracepoint_t ft = __firehose_buffer_tracepoint_reserve(lp->lp_timestamp,
948 lp->lp_stream, pub_data_size, priv_data_size, ft_priv_data);
949
950 if (fastpath(ft)) {
951 oslog_boot_done = true;
952 return ft;
953 }
954
955 if (fastpath(oslog_boot_done)) {
956 return 0;
957 }
958
959 /*
960 * Early boot logging when kernel logging is up and running but logd is
961 * not yet ready. Only the persist firehose stream is available.
962 */
963
964 long offset = firehose_chunk_tracepoint_try_reserve(&firehose_boot_chunk,
965 lp->lp_timestamp, firehose_stream_persist, 0, pub_data_size,
966 priv_data_size, ft_priv_data);
967 if (offset <= 0) {
968 counter_inc(&oslog_p_boot_dropped_msgcount);
969 return 0;
970 }
971
972 return firehose_chunk_tracepoint_begin(&firehose_boot_chunk,
973 lp->lp_timestamp, pub_data_size, thread_tid(current_thread()),
974 offset);
975 }
976
977 static inline void
firehose_trace_finish(firehose_tracepoint_t ft,firehose_tracepoint_id_u ftid)978 firehose_trace_finish(firehose_tracepoint_t ft, firehose_tracepoint_id_u ftid)
979 {
980 if (__probable(oslog_boot_done)) {
981 __firehose_buffer_tracepoint_flush(ft, ftid);
982 } else {
983 firehose_chunk_tracepoint_end(&firehose_boot_chunk, ft, ftid);
984 }
985 }
986
987 static inline void
firehose_trace_save_payload(const log_payload_s * lp,const uint8_t * lp_data,firehose_tracepoint_t ft,uint8_t * ft_priv_data)988 firehose_trace_save_payload(const log_payload_s *lp, const uint8_t *lp_data,
989 firehose_tracepoint_t ft, uint8_t *ft_priv_data)
990 {
991 const size_t priv_data_size = log_payload_priv_data_size(lp);
992 uint8_t *ft_data = ft->ft_data;
993
994 if (priv_data_size > 0) {
995 assert(FIREHOSE_TRACE_ID_HAS_FLAG(lp->lp_ftid, log, has_private_data));
996 memcpy(ft_priv_data, log_payload_priv_data(lp, lp_data), priv_data_size);
997
998 firehose_chunk_t fc = firehose_chunk_for_address(ft);
999 struct firehose_buffer_range_s range = {
1000 .fbr_offset = (uint16_t)(ft_priv_data - fc->fc_start),
1001 .fbr_length = (uint16_t)priv_data_size
1002 };
1003 memcpy(ft_data, &range, sizeof(range));
1004 ft_data += sizeof(range);
1005 }
1006 memcpy(ft_data, lp_data, lp->lp_pub_data_size);
1007 }
1008
1009 static inline void
log_stats_update_saved(firehose_stream_t stream)1010 log_stats_update_saved(firehose_stream_t stream)
1011 {
1012 if (__improbable(!oslog_boot_done)) {
1013 stream = firehose_stream_persist;
1014 }
1015
1016 switch (stream) {
1017 case firehose_stream_metadata:
1018 counter_inc(&oslog_p_metadata_saved_msgcount);
1019 break;
1020 case firehose_stream_persist:
1021 case firehose_stream_memory:
1022 case firehose_stream_special:
1023 counter_inc(&oslog_p_saved_msgcount);
1024 break;
1025 case firehose_stream_signpost:
1026 counter_inc(&oslog_p_signpost_saved_msgcount);
1027 break;
1028 default:
1029 panic("Unexpected firehose stream type %u", stream);
1030 }
1031 }
1032
1033 static inline firehose_tracepoint_id_t
firehose_trace(const log_payload_s * lp,const uint8_t * lp_data)1034 firehose_trace(const log_payload_s *lp, const uint8_t *lp_data)
1035 {
1036 uint8_t *ft_priv_data = NULL;
1037 firehose_tracepoint_t ft = firehose_trace_start(lp, &ft_priv_data);
1038
1039 if (fastpath(ft)) {
1040 firehose_trace_save_payload(lp, lp_data, ft, ft_priv_data);
1041 firehose_trace_finish(ft, lp->lp_ftid);
1042 log_stats_update_saved(lp->lp_stream);
1043 return lp->lp_ftid.ftid_value;
1044 }
1045 return 0;
1046 }
1047
1048 static firehose_tracepoint_code_t
coproc_reg_type_to_firehost_code(os_log_coproc_reg_t reg_type)1049 coproc_reg_type_to_firehost_code(os_log_coproc_reg_t reg_type)
1050 {
1051 switch (reg_type) {
1052 case os_log_coproc_register_memory:
1053 return firehose_tracepoint_code_load_memory;
1054 case os_log_coproc_register_harvest_fs_ftab:
1055 return firehose_tracepoint_code_load_filesystem_ftab;
1056 default:
1057 return firehose_tracepoint_code_invalid;
1058 }
1059 }
1060
1061 void
os_log_coprocessor_register_with_type(const char * uuid,const char * file_path,os_log_coproc_reg_t reg_type)1062 os_log_coprocessor_register_with_type(const char *uuid, const char *file_path, os_log_coproc_reg_t reg_type)
1063 {
1064 size_t path_size = strlen(file_path) + 1;
1065 size_t uuid_info_len = sizeof(struct firehose_trace_uuid_info_s) + path_size;
1066
1067 if (os_log_disabled() || path_size > PATH_MAX) {
1068 return;
1069 }
1070
1071 __attribute__((uninitialized))
1072 union {
1073 struct firehose_trace_uuid_info_s uuid_info;
1074 char path[PATH_MAX + sizeof(struct firehose_trace_uuid_info_s)];
1075 } buf;
1076
1077 // write metadata to uuid_info
1078 memcpy(buf.uuid_info.ftui_uuid, uuid, sizeof(uuid_t));
1079 buf.uuid_info.ftui_size = 1;
1080 buf.uuid_info.ftui_address = 1;
1081
1082 uint64_t stamp = firehose_tracepoint_time(firehose_activity_flags_default);
1083
1084 // create tracepoint id
1085 firehose_tracepoint_id_u trace_id;
1086 trace_id.ftid_value = FIREHOSE_TRACE_ID_MAKE(firehose_tracepoint_namespace_metadata,
1087 _firehose_tracepoint_type_metadata_coprocessor, (firehose_tracepoint_flags_t)0,
1088 coproc_reg_type_to_firehost_code(reg_type));
1089
1090 // write path to buffer
1091 memcpy(buf.uuid_info.ftui_path, file_path, path_size);
1092
1093 // send metadata tracepoint to firehose for coprocessor registration in logd
1094 os_log_encoded_metadata(trace_id, stamp, (void *)&buf, uuid_info_len);
1095 }
1096
1097 bool
log_payload_send(log_payload_t lp,const void * lp_data,bool use_stream)1098 log_payload_send(log_payload_t lp, const void *lp_data, bool use_stream)
1099 {
1100 if (use_stream) {
1101 bool is_metadata = (lp->lp_stream == firehose_stream_metadata);
1102 oslog_stream(is_metadata, lp->lp_ftid, lp->lp_timestamp, lp_data, lp->lp_data_size);
1103 }
1104 return firehose_trace(lp, lp_data) != 0;
1105 }
1106
1107 void
__firehose_buffer_push_to_logd(firehose_buffer_t fb __unused,bool for_io __unused)1108 __firehose_buffer_push_to_logd(firehose_buffer_t fb __unused, bool for_io __unused)
1109 {
1110 oslogwakeup();
1111 return;
1112 }
1113
1114 void
__firehose_allocate(vm_offset_t * addr,vm_size_t size __unused)1115 __firehose_allocate(vm_offset_t *addr, vm_size_t size __unused)
1116 {
1117 firehose_chunk_t kernel_buffer = (firehose_chunk_t)kernel_firehose_addr;
1118
1119 if (kernel_firehose_addr) {
1120 *addr = kernel_firehose_addr;
1121 } else {
1122 *addr = 0;
1123 return;
1124 }
1125 // Now that we are done adding logs to this chunk, set the number of writers to 0
1126 // Without this, logd won't flush when the page is full
1127 firehose_boot_chunk.fc_pos.fcp_refcnt = 0;
1128 memcpy(&kernel_buffer[FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT - 1], (const void *)&firehose_boot_chunk, FIREHOSE_CHUNK_SIZE);
1129 return;
1130 }
1131 // There isnt a lock held in this case.
1132 void
__firehose_critical_region_enter(void)1133 __firehose_critical_region_enter(void)
1134 {
1135 disable_preemption();
1136 return;
1137 }
1138
1139 void
__firehose_critical_region_leave(void)1140 __firehose_critical_region_leave(void)
1141 {
1142 enable_preemption();
1143 return;
1144 }
1145
1146 #ifdef CONFIG_XNUPOST
1147
1148 static_assert(&_os_log_default == OS_LOG_DEFAULT,
1149 "OS_LOG_DEFAULT is an alias to _os_log_default");
1150 static_assert(OS_LOG_DISABLED == NULL,
1151 "OS_LOG_DISABLED handle is defined as NULL");
1152
1153 #include <dev/random/randomdev.h>
1154 #include <tests/xnupost.h>
1155
1156 typedef struct {
1157 size_t total;
1158 size_t saved;
1159 size_t dropped;
1160 size_t truncated;
1161 size_t errors;
1162 size_t errors_kc;
1163 size_t errors_fmt;
1164 size_t errors_max_args;
1165 } log_stats_t;
1166
1167 #define TESTBUFLEN 256
1168 #define TESTOSLOGFMT(fn_name) "%u^%llu/%llu^kernel^0^test^" fn_name
1169 #define TESTOSLOGPFX "TESTLOG:%u#"
1170 #define TESTOSLOG(fn_name) TESTOSLOGPFX TESTOSLOGFMT(fn_name "#")
1171
1172 #define LOG_STAT_CMP(cmp, b, a, e, stat, msg, test) \
1173 { \
1174 size_t n##stat = (a)->stat - (b)->stat; \
1175 size_t n##expected = (e)->stat; \
1176 cmp(n##stat, n##expected, (msg), (test), n##expected); \
1177 }
1178 #define LOG_STAT_EQ(b, a, e, stat, msg, test) \
1179 LOG_STAT_CMP(T_EXPECT_EQ_UINT, b, a, e, stat, msg, test)
1180 #define LOG_STAT_GE(b, a, e, stat, msg, test) \
1181 LOG_STAT_CMP(T_EXPECT_GE_UINT, b, a, e, stat, msg, test)
1182
1183 #define GENOSLOGHELPER(fname, ident, callout_f) \
1184 static void \
1185 fname(uint32_t uniqid, uint64_t count, log_stats_t *before, log_stats_t *after) \
1186 { \
1187 uint32_t checksum = 0; \
1188 char pattern[TESTBUFLEN]; \
1189 T_LOG("Testing " ident "() with %d logs", count); \
1190 log_stats_get(before); \
1191 for (uint64_t i = 0; i < count; i++) { \
1192 (void) save_pattern(pattern, &checksum, TESTOSLOGFMT(ident), uniqid, i + 1, count); \
1193 callout_f(OS_LOG_DEFAULT, TESTOSLOG(ident), checksum, uniqid, i + 1, count); \
1194 } \
1195 log_stats_get(after); \
1196 }
1197
1198 extern size_t find_pattern_in_buffer(const char *, size_t, size_t);
1199
1200 void test_oslog_handleOSLogCtl(int32_t *, int32_t *, int32_t);
1201 kern_return_t test_printf(void);
1202 kern_return_t test_os_log(void);
1203 kern_return_t test_os_log_handles(void);
1204 kern_return_t test_os_log_parallel(void);
1205
1206 SCALABLE_COUNTER_DECLARE(oslog_p_fmt_invalid_msgcount);
1207 SCALABLE_COUNTER_DECLARE(oslog_p_fmt_max_args_msgcount);
1208 SCALABLE_COUNTER_DECLARE(oslog_p_truncated_msgcount);
1209 SCALABLE_COUNTER_DECLARE(log_queue_cnt_received);
1210
1211 static void
log_stats_get(log_stats_t * stats)1212 log_stats_get(log_stats_t *stats)
1213 {
1214 if (!stats) {
1215 return;
1216 }
1217 stats->total = counter_load(&oslog_p_total_msgcount);
1218 stats->saved = counter_load(&log_queue_cnt_received);
1219 stats->dropped = counter_load(&oslog_p_dropped_msgcount);
1220 stats->truncated = counter_load(&oslog_p_truncated_msgcount);
1221 stats->errors = counter_load(&oslog_p_error_count);
1222 stats->errors_kc = counter_load(&oslog_p_unresolved_kc_msgcount);
1223 stats->errors_fmt = counter_load(&oslog_p_fmt_invalid_msgcount);
1224 stats->errors_max_args = counter_load(&oslog_p_fmt_max_args_msgcount);
1225 }
1226
1227 static void
log_stats_diff(const log_stats_t * before,const log_stats_t * after,log_stats_t * diff)1228 log_stats_diff(const log_stats_t *before, const log_stats_t *after, log_stats_t *diff)
1229 {
1230 log_stats_t d = {};
1231
1232 if (before) {
1233 d = *before;
1234 }
1235 if (after) {
1236 d.total = after->total - d.total;
1237 d.saved = after->saved - d.saved;
1238 d.dropped = after->dropped - d.dropped;
1239 d.truncated = after->truncated - d.truncated;
1240 d.errors = after->errors - d.errors;
1241 d.errors_kc = after->errors_kc - d.errors_kc;
1242 d.errors_fmt = after->errors_fmt - d.errors_fmt;
1243 d.errors_max_args = after->errors_max_args - d.errors_max_args;
1244 }
1245 *diff = d;
1246 }
1247
1248 static void
log_stats_check_errors(const log_stats_t * before,const log_stats_t * after,const log_stats_t * expected,const char * test)1249 log_stats_check_errors(const log_stats_t *before, const log_stats_t *after, const log_stats_t *expected,
1250 const char *test)
1251 {
1252 LOG_STAT_EQ(before, after, expected, errors, "%s: Expected %lu encoding errors", test);
1253 LOG_STAT_EQ(before, after, expected, errors_kc, "%s: Expected %lu DSO errors", test);
1254 LOG_STAT_EQ(before, after, expected, errors_fmt, "%s: Expected %lu bad format errors", test);
1255 LOG_STAT_EQ(before, after, expected, errors_max_args, "%s: Expected %lu max arguments errors", test);
1256 }
1257
1258 static void
log_stats_check(const log_stats_t * before,const log_stats_t * after,const log_stats_t * expected,const char * test)1259 log_stats_check(const log_stats_t *before, const log_stats_t *after, const log_stats_t *expected,
1260 const char *test)
1261 {
1262 /*
1263 * The comparison is >= (_GE) for total and saved counters because tests
1264 * are running while the system is up and potentially logging. Test and
1265 * regular logs interfere rather rarely but can make the test flaky
1266 * which is not desired.
1267 */
1268 LOG_STAT_GE(before, after, expected, total, "%s: Expected %lu logs total", test);
1269 LOG_STAT_GE(before, after, expected, saved, "%s: Expected %lu saved logs", test);
1270 LOG_STAT_EQ(before, after, expected, dropped, "%s: Expected %lu logs dropped", test);
1271 log_stats_check_errors(before, after, expected, test);
1272 }
1273
1274 static void
log_stats_report(const log_stats_t * before,const log_stats_t * after,const char * test)1275 log_stats_report(const log_stats_t *before, const log_stats_t *after, const char *test)
1276 {
1277 log_stats_t diff = {};
1278 log_stats_diff(before, after, &diff);
1279
1280 T_LOG("\n%s: Logging stats:\n\ttotal: %u\n\tsaved: %u\n\tdropped: %u\n"
1281 "\terrors: %u\n\terrors_kc: %u\n\terrors_fmt: %u\n\terrors_max_args: %u",
1282 test, diff.total, diff.saved, diff.dropped,
1283 diff.errors, diff.errors_kc, diff.errors_fmt, diff.errors_max_args);
1284 }
1285
1286 static int
save_pattern(char buf[static TESTBUFLEN],uint32_t * crc,const char * fmt,...)1287 save_pattern(char buf[static TESTBUFLEN], uint32_t *crc, const char *fmt, ...)
1288 {
1289 va_list va;
1290
1291 va_start(va, fmt);
1292 #pragma clang diagnostic push
1293 #pragma clang diagnostic ignored "-Wformat-nonliteral"
1294 int n = vscnprintf(buf, TESTBUFLEN, fmt, va);
1295 #pragma clang diagnostic pop
1296 va_end(va);
1297 if (crc) {
1298 *crc = crc32(0, buf, n);
1299 }
1300 return n;
1301 }
1302
1303 /*
1304 * Actual GENOSLOGHELPER() 2nd argument values below are expected by libtrace
1305 * test_kern_oslog test suite and shall not be changed without updating given
1306 * test suite.
1307 */
1308 GENOSLOGHELPER(test_oslog_info, "oslog_info_helper", os_log_info);
1309 GENOSLOGHELPER(test_oslog_fault, "oslog_fault_helper", os_log_fault);
1310 GENOSLOGHELPER(test_oslog_debug, "oslog_debug_helper", os_log_debug);
1311 GENOSLOGHELPER(test_oslog_error, "oslog_error_helper", os_log_error);
1312 GENOSLOGHELPER(test_oslog_default, "oslog_default_helper", os_log);
1313
1314 kern_return_t
test_printf(void)1315 test_printf(void)
1316 {
1317 const uint32_t uniqid = RandomULong();
1318 T_ASSERT_NE_UINT(0, uniqid, "Random number should not be zero");
1319
1320 uint64_t stamp = mach_absolute_time();
1321 T_ASSERT_NE_ULLONG(0, stamp, "Absolute time should not be zero");
1322
1323 T_LOG("Validating with atm_diagnostic_config=%#x uniqid=%u stamp=%llu",
1324 atm_get_diagnostic_config(), uniqid, stamp);
1325
1326 __attribute__((uninitialized))
1327 char pattern[TESTBUFLEN];
1328 log_stats_t before = {}, after = {};
1329 uint32_t checksum = 0;
1330
1331 log_stats_get(&before);
1332
1333 (void) save_pattern(pattern, &checksum, TESTOSLOGFMT("printf_only"), uniqid, 1LL, 2LL);
1334 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, 1LL, 2LL, stamp);
1335
1336 (void) save_pattern(pattern, &checksum, TESTOSLOGFMT("printf_only"), uniqid, 2LL, 2LL);
1337 printf(TESTOSLOG("printf_only") "mat%llu\n", checksum, uniqid, 2LL, 2LL, stamp);
1338
1339 size_t saved = save_pattern(pattern, NULL, "kernel^0^test^printf_only#mat%llu", stamp);
1340 size_t match_count = find_pattern_in_buffer(pattern, saved, 2LL);
1341 T_EXPECT_EQ_ULONG(match_count, 2LL, "printf() logs to msgbuf");
1342
1343 log_stats_get(&after);
1344
1345 if (!os_log_turned_off()) {
1346 // printf() should log to OSLog with OSLog enabled.
1347 log_stats_t expected = { .total = 2, .saved = 2 };
1348 log_stats_check(&before, &after, &expected, "printf() logs to oslog");
1349 }
1350
1351 log_stats_report(&before, &after, __FUNCTION__);
1352
1353 return KERN_SUCCESS;
1354 }
1355
1356 static void
verify_os_log(struct os_log_s * h,char * sub,char * cat)1357 verify_os_log(struct os_log_s *h, char *sub, char *cat)
1358 {
1359 const size_t sub_len = os_log_subsystem_name_size(sub);
1360 const size_t cat_len = os_log_subsystem_name_size(cat);
1361
1362 struct os_log_subsystem_s *s = &h->ol_subsystem;
1363
1364 T_ASSERT_EQ_STR(s->ols_name, sub, "Log subsystem name is %s", sub);
1365 T_ASSERT_EQ_ULONG(s->ols_sub_size, sub_len, "Log subsystem size is %lu", sub_len);
1366 T_ASSERT_EQ_ULONG(strlen(s->ols_name), sub_len - 1, "Log subsystem length is %lu", sub_len - 1);
1367
1368 T_ASSERT_EQ_STR(&s->ols_name[sub_len], cat, "Log category name is %s", cat);
1369 T_ASSERT_EQ_ULONG(s->ols_cat_size, cat_len, "Log category length is %lu", cat_len);
1370 T_ASSERT_EQ_ULONG(strlen(&s->ols_name[sub_len]), cat_len - 1, "Log category size is %lu", cat_len - 1);
1371 }
1372
1373 static os_log_t
create_verified_os_log(char * sub,char * cat)1374 create_verified_os_log(char *sub, char *cat)
1375 {
1376 os_log_t h = os_log_create(sub, cat);
1377 verify_os_log(h, sub, cat);
1378 return h;
1379 }
1380
1381 kern_return_t
test_os_log_handles(void)1382 test_os_log_handles(void)
1383 {
1384 os_log_t h1 = create_verified_os_log("xnu_post_subsystem1", "xnu_post_category1");
1385 if (os_log_disabled()) {
1386 T_ASSERT_EQ_PTR(h1, OS_LOG_DISABLED, "Disabled logging uses OS_LOG_DISABLED");
1387 return KERN_SUCCESS;
1388 }
1389 T_ASSERT_NE_PTR(h1, OS_LOG_DEFAULT, "Custom log handle is not OS_LOG_DEFAULT");
1390
1391 os_log_t h2 = create_verified_os_log("xnu_post_subsystem1", "xnu_post_category1");
1392 T_ASSERT_EQ_PTR(h1, h2, "os_log_create() finds an existing log handle in the cache");
1393
1394 h2 = create_verified_os_log("xnu_post_subsystem1", "xnu_post_category2");
1395 T_ASSERT_NE_PTR(h1, h2, "Subsystem is not enough to identify a log handle");
1396
1397 h2 = create_verified_os_log("xnu_post_subsystem2", "xnu_post_category1");
1398 T_ASSERT_NE_PTR(h1, h2, "Category is not enough to identify a log handle");
1399
1400 h2 = create_verified_os_log("xnu_post_subsystem3", "xnu_post_category3");
1401 T_ASSERT_NE_PTR(h1, h2, "Different subsystem and category yield a different handle");
1402
1403 h1 = h2;
1404 h2 = create_verified_os_log("xnu_post", "_subsystem3xnu_post_category3");
1405 T_ASSERT_NE_PTR(h1, h2, "Subsystem and category cannot be mixed");
1406
1407 const size_t name_size = sizeof(h1->ol_subsystem.ols_name) / 2;
1408
1409 char super_long_sub[name_size] = {
1410 [0 ... sizeof(super_long_sub) - 1] = 'X'
1411 };
1412
1413 char super_long_cat[name_size] = {
1414 [0 ... sizeof(super_long_sub) - 1] = 'Y'
1415 };
1416
1417 h1 = os_log_create(super_long_sub, super_long_cat);
1418 T_ASSERT_NE_PTR(h1, OS_LOG_DEFAULT, "Custom log handle is not OS_LOG_DEFAULT");
1419
1420 super_long_sub[name_size - 1] = '\0';
1421 super_long_cat[name_size - 1] = '\0';
1422
1423 verify_os_log(h1, super_long_sub, super_long_cat);
1424
1425 return KERN_SUCCESS;
1426 }
1427
1428 kern_return_t
test_os_log(void)1429 test_os_log(void)
1430 {
1431 const uint32_t uniqid = RandomULong();
1432 T_ASSERT_NE_UINT(0, uniqid, "Random number should not be zero");
1433
1434 uint64_t stamp = mach_absolute_time();
1435 T_ASSERT_NE_ULLONG(0, stamp, "Absolute time should not be zero");
1436
1437 T_LOG("Validating with atm_diagnostic_config=%#x uniqid=%u stamp=%llu",
1438 atm_get_diagnostic_config(), uniqid, stamp);
1439
1440 os_log_t log_handle = os_log_create("com.apple.xnu.test.t1", "kpost");
1441 T_ASSERT_NE_PTR(OS_LOG_DEFAULT, log_handle, "Log handle is not OS_LOG_DEFAULT");
1442
1443 const bool enabled = !os_log_turned_off();
1444 T_ASSERT_EQ_INT(enabled, os_log_info_enabled(log_handle), "Info log level is enabled");
1445 T_ASSERT_EQ_INT(enabled, os_log_debug_enabled(log_handle), "Debug log level is enabled");
1446
1447 __attribute__((uninitialized))
1448 char pattern[TESTBUFLEN];
1449 uint32_t checksum = 0;
1450
1451 (void) save_pattern(pattern, &checksum, TESTOSLOGFMT("oslog_info"), uniqid, 1LL, 1LL);
1452 os_log_info(log_handle, TESTOSLOG("oslog_info") "mat%llu", checksum, uniqid, 1LL, 1LL, stamp);
1453 size_t saved = save_pattern(pattern, NULL, "kernel^0^test^oslog_info#mat%llu", stamp);
1454 size_t match_count = find_pattern_in_buffer(pattern, saved, 1LL);
1455 T_EXPECT_EQ_ULONG(match_count, 1LL, "oslog_info() logs to system message buffer");
1456
1457 const size_t n = 10;
1458
1459 log_stats_t expected = {
1460 .total = enabled ? n : 0,
1461 .saved = enabled ? n : 0
1462 };
1463
1464 log_stats_t before = {}, after = {};
1465
1466 test_oslog_info(uniqid, n, &before, &after);
1467 log_stats_check(&before, &after, &expected, "test_oslog_info");
1468
1469 test_oslog_debug(uniqid, n, &before, &after);
1470 log_stats_check(&before, &after, &expected, "test_oslog_debug");
1471
1472 test_oslog_error(uniqid, n, &before, &after);
1473 log_stats_check(&before, &after, &expected, "test_oslog_error");
1474
1475 test_oslog_default(uniqid, n, &before, &after);
1476 log_stats_check(&before, &after, &expected, "test_oslog_default");
1477
1478 test_oslog_fault(uniqid, n, &before, &after);
1479 log_stats_check(&before, &after, &expected, "test_oslog_fault");
1480
1481 log_stats_report(&before, &after, __FUNCTION__);
1482
1483 return KERN_SUCCESS;
1484 }
1485
1486 static size_t _test_log_loop_count = 0;
1487
1488 static void
_test_log_loop(void * arg __unused,wait_result_t wres __unused)1489 _test_log_loop(void *arg __unused, wait_result_t wres __unused)
1490 {
1491 test_oslog_debug(RandomULong(), 100, NULL, NULL);
1492 os_atomic_add(&_test_log_loop_count, 100, relaxed);
1493 }
1494
1495 kern_return_t
test_os_log_parallel(void)1496 test_os_log_parallel(void)
1497 {
1498 if (os_log_turned_off()) {
1499 T_LOG("Logging disabled, skipping tests.");
1500 return KERN_SUCCESS;
1501 }
1502
1503 thread_t thread[2];
1504 kern_return_t kr;
1505 log_stats_t after, before, expected = {};
1506
1507 log_stats_get(&before);
1508
1509 kr = kernel_thread_start(_test_log_loop, NULL, &thread[0]);
1510 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
1511
1512 kr = kernel_thread_start(_test_log_loop, NULL, &thread[1]);
1513 T_ASSERT_EQ_INT(kr, KERN_SUCCESS, "kernel_thread_start returned successfully");
1514
1515 test_oslog_info(RandomULong(), 100, NULL, NULL);
1516
1517 /* wait until other thread has also finished */
1518 while (_test_log_loop_count < 200) {
1519 delay(1000);
1520 }
1521
1522 thread_deallocate(thread[0]);
1523 thread_deallocate(thread[1]);
1524
1525 log_stats_get(&after);
1526 log_stats_check_errors(&before, &after, &expected, __FUNCTION__);
1527 log_stats_report(&before, &after, __FUNCTION__);
1528
1529 return KERN_SUCCESS;
1530 }
1531
1532 static kern_return_t
test_stresslog_dropmsg(const uint32_t uniqid)1533 test_stresslog_dropmsg(const uint32_t uniqid)
1534 {
1535 if (os_log_turned_off()) {
1536 T_LOG("Logging disabled, skipping tests.");
1537 return KERN_SUCCESS;
1538 }
1539
1540 log_stats_t after, before, expected = {};
1541
1542 test_oslog_debug(uniqid, 100, &before, &after);
1543 while ((after.dropped - before.dropped) == 0) {
1544 test_oslog_debug(uniqid, 100, NULL, &after);
1545 }
1546
1547 log_stats_check_errors(&before, &after, &expected, __FUNCTION__);
1548 log_stats_report(&before, &after, __FUNCTION__);
1549
1550 return KERN_SUCCESS;
1551 }
1552
1553 void
test_oslog_handleOSLogCtl(int32_t * in,int32_t * out,int32_t len)1554 test_oslog_handleOSLogCtl(int32_t *in, int32_t *out, int32_t len)
1555 {
1556 if (!in || !out || len != 4) {
1557 return;
1558 }
1559 switch (in[0]) {
1560 case 1:
1561 {
1562 /* send out counters */
1563 out[1] = counter_load(&oslog_p_total_msgcount);
1564 out[2] = counter_load(&oslog_p_saved_msgcount);
1565 out[3] = counter_load(&oslog_p_dropped_msgcount);
1566 out[0] = KERN_SUCCESS;
1567 break;
1568 }
1569 case 2:
1570 {
1571 /* mini stress run */
1572 out[0] = test_os_log_parallel();
1573 break;
1574 }
1575 case 3:
1576 {
1577 /* drop msg tests */
1578 out[1] = RandomULong();
1579 out[0] = test_stresslog_dropmsg(out[1]);
1580 break;
1581 }
1582 case 4:
1583 {
1584 /* invoke log helpers */
1585 uint32_t uniqid = in[3];
1586 int32_t msgcount = in[2];
1587 if (uniqid == 0 || msgcount == 0) {
1588 out[0] = KERN_INVALID_VALUE;
1589 return;
1590 }
1591
1592 switch (in[1]) {
1593 case OS_LOG_TYPE_INFO:
1594 test_oslog_info(uniqid, msgcount, NULL, NULL);
1595 break;
1596 case OS_LOG_TYPE_DEBUG:
1597 test_oslog_debug(uniqid, msgcount, NULL, NULL);
1598 break;
1599 case OS_LOG_TYPE_ERROR:
1600 test_oslog_error(uniqid, msgcount, NULL, NULL);
1601 break;
1602 case OS_LOG_TYPE_FAULT:
1603 test_oslog_fault(uniqid, msgcount, NULL, NULL);
1604 break;
1605 case OS_LOG_TYPE_DEFAULT:
1606 default:
1607 test_oslog_default(uniqid, msgcount, NULL, NULL);
1608 break;
1609 }
1610 out[0] = KERN_SUCCESS;
1611 break;
1612 /* end of case 4 */
1613 }
1614 default:
1615 {
1616 out[0] = KERN_INVALID_VALUE;
1617 break;
1618 }
1619 }
1620 return;
1621 }
1622
1623 #endif /* CONFIG_XNUPOST */
1624