xref: /xnu-8020.101.4/bsd/kern/subr_log.c (revision e7776783b89a353188416a9a346c6cdb4928faad)
1 /*
2  * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  */
28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29 /*
30  * Copyright (c) 1982, 1986, 1993
31  *	The Regents of the University of California.  All rights reserved.
32  *
33  * Redistribution and use in source and binary forms, with or without
34  * modification, are permitted provided that the following conditions
35  * are met:
36  * 1. Redistributions of source code must retain the above copyright
37  *    notice, this list of conditions and the following disclaimer.
38  * 2. Redistributions in binary form must reproduce the above copyright
39  *    notice, this list of conditions and the following disclaimer in the
40  *    documentation and/or other materials provided with the distribution.
41  * 3. All advertising materials mentioning features or use of this software
42  *    must display the following acknowledgement:
43  *	This product includes software developed by the University of
44  *	California, Berkeley and its contributors.
45  * 4. Neither the name of the University nor the names of its contributors
46  *    may be used to endorse or promote products derived from this software
47  *    without specific prior written permission.
48  *
49  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59  * SUCH DAMAGE.
60  *
61  *	@(#)subr_log.c	8.3 (Berkeley) 2/14/95
62  */
63 
64 /*
65  * Error log buffer for kernel printf's.
66  */
67 
68 #include <machine/atomic.h>
69 #include <sys/param.h>
70 #include <sys/systm.h>
71 #include <sys/proc_internal.h>
72 #include <sys/vnode.h>
73 #include <stdbool.h>
74 #include <firehose/tracepoint_private.h>
75 #include <firehose/chunk_private.h>
76 #include <firehose/ioctl_private.h>
77 #include <os/firehose_buffer_private.h>
78 
79 #include <os/log_private.h>
80 #include <sys/ioctl.h>
81 #include <sys/msgbuf.h>
82 #include <sys/file_internal.h>
83 #include <sys/errno.h>
84 #include <sys/select.h>
85 #include <sys/kernel.h>
86 #include <kern/thread.h>
87 #include <kern/sched_prim.h>
88 #include <kern/simple_lock.h>
89 #include <sys/lock.h>
90 #include <sys/signalvar.h>
91 #include <sys/conf.h>
92 #include <sys/sysctl.h>
93 #include <sys/queue.h>
94 #include <kern/kalloc.h>
95 #include <pexpert/pexpert.h>
96 #include <mach/mach_port.h>
97 #include <mach/mach_vm.h>
98 #include <mach/vm_map.h>
99 #include <vm/vm_kern.h>
100 #include <kern/task.h>
101 #include <kern/locks.h>
102 
103 extern void logwakeup(struct msgbuf *);
104 extern void oslogwakeup(void);
105 extern void oslog_streamwakeup(void);
106 extern bool os_log_disabled(void);
107 
108 SECURITY_READ_ONLY_LATE(vm_offset_t) kernel_firehose_addr = 0;
109 SECURITY_READ_ONLY_LATE(uint8_t) __firehose_buffer_kernel_chunk_count =
110     FIREHOSE_BUFFER_KERNEL_DEFAULT_CHUNK_COUNT;
111 SECURITY_READ_ONLY_LATE(uint8_t) __firehose_num_kernel_io_pages =
112     FIREHOSE_BUFFER_KERNEL_DEFAULT_IO_PAGES;
113 
114 uint32_t oslog_msgbuf_dropped_charcount = 0;
115 
116 /* Counters for streaming mode */
117 SCALABLE_COUNTER_DEFINE(oslog_s_total_msgcount);
118 SCALABLE_COUNTER_DEFINE(oslog_s_metadata_msgcount);
119 SCALABLE_COUNTER_DEFINE(oslog_s_streamed_msgcount);
120 SCALABLE_COUNTER_DEFINE(oslog_s_dropped_msgcount);
121 SCALABLE_COUNTER_DEFINE(oslog_s_error_count);
122 
123 #define LOG_RDPRI       (PZERO + 1)
124 
125 #define LOG_NBIO        0x02
126 #define LOG_ASYNC       0x04
127 #define LOG_RDWAIT      0x08
128 
129 /* All globals should be accessed under bsd_log_lock() or bsd_log_lock_safe() */
130 
131 static char amsg_bufc[1024];
132 static struct msgbuf aslbuf = {.msg_magic = MSG_MAGIC, .msg_size = sizeof(amsg_bufc), .msg_bufx = 0, .msg_bufr = 0, .msg_bufc = amsg_bufc};
133 struct msgbuf *aslbufp __attribute__((used)) = &aslbuf;
134 
135 /* logsoftc only valid while log_open=1 */
136 struct logsoftc {
137 	int     sc_state;               /* see above for possibilities */
138 	struct  selinfo sc_selp;        /* thread waiting for select */
139 	int     sc_pgid;                /* process/group for async I/O */
140 	struct msgbuf *sc_mbp;
141 } logsoftc;
142 
143 static int log_open;
144 char smsg_bufc[CONFIG_MSG_BSIZE]; /* static buffer */
145 struct firehose_chunk_s oslog_boot_buf = {
146 	.fc_pos = {
147 		.fcp_next_entry_offs = offsetof(struct firehose_chunk_s, fc_data),
148 		.fcp_private_offs = FIREHOSE_CHUNK_SIZE,
149 		.fcp_refcnt = 1, // indicate that there is a writer to this chunk
150 		.fcp_stream = firehose_stream_persist,
151 		.fcp_flag_io = 1, // for now, lets assume this is coming from the io bank
152 	},
153 }; /* static buffer */
154 firehose_chunk_t firehose_boot_chunk = &oslog_boot_buf;
155 struct msgbuf msgbuf = {.msg_magic  = MSG_MAGIC, .msg_size = sizeof(smsg_bufc), .msg_bufx = 0, .msg_bufr = 0, .msg_bufc = smsg_bufc};
156 struct msgbuf oslog_stream_buf = {.msg_magic = MSG_MAGIC, .msg_size = 0, .msg_bufx = 0, .msg_bufr = 0, .msg_bufc = NULL};
157 struct msgbuf *msgbufp __attribute__((used)) = &msgbuf;
158 struct msgbuf *oslog_streambufp __attribute__((used)) = &oslog_stream_buf;
159 
160 // List entries for keeping track of the streaming buffer
161 static oslog_stream_buf_entry_t oslog_stream_buf_entries;
162 
163 #define OSLOG_NUM_STREAM_ENTRIES        64
164 #define OSLOG_STREAM_BUF_SIZE           4096
165 #define OSLOG_STREAM_MAX_BUF_SIZE       (1024 * 1024)
166 
167 int     oslog_open = 0;
168 bool    os_log_wakeup = false;
169 bool    oslog_stream_open = false;
170 int     oslog_stream_buf_bytesavail = 0;
171 int     oslog_stream_buf_size = OSLOG_STREAM_BUF_SIZE;
172 int     oslog_stream_num_entries = OSLOG_NUM_STREAM_ENTRIES;
173 
174 /* oslogsoftc only valid while oslog_open=1 */
175 struct oslogsoftc {
176 	int     sc_state;               /* see above for possibilities */
177 	struct  selinfo sc_selp;        /* thread waiting for select */
178 	int     sc_pgid;                /* process/group for async I/O */
179 } oslogsoftc;
180 
181 struct oslog_streamsoftc {
182 	int     sc_state;               /* see above for possibilities */
183 	struct  selinfo sc_selp;        /* thread waiting for select */
184 	int     sc_pgid;                /* process/group for async I/O */
185 } oslog_streamsoftc;
186 
187 STAILQ_HEAD(, oslog_stream_buf_entry_s) oslog_stream_free_head =
188     STAILQ_HEAD_INITIALIZER(oslog_stream_free_head);
189 STAILQ_HEAD(, oslog_stream_buf_entry_s) oslog_stream_buf_head =
190     STAILQ_HEAD_INITIALIZER(oslog_stream_buf_head);
191 
192 /* defined in osfmk/kern/printf.c  */
193 extern bool bsd_log_lock(bool);
194 extern void bsd_log_lock_safe(void);
195 extern void bsd_log_unlock(void);
196 
197 LCK_GRP_DECLARE(oslog_stream_lock_grp, "oslog streaming");
198 LCK_SPIN_DECLARE(oslog_stream_lock, &oslog_stream_lock_grp);
199 #define stream_lock() lck_spin_lock(&oslog_stream_lock)
200 #define stream_unlock() lck_spin_unlock(&oslog_stream_lock)
201 
202 /* XXX wants a linker set so these can be static */
203 extern d_open_t         logopen;
204 extern d_close_t        logclose;
205 extern d_read_t         logread;
206 extern d_ioctl_t        logioctl;
207 extern d_select_t       logselect;
208 
209 /* XXX wants a linker set so these can be static */
210 extern d_open_t         oslogopen;
211 extern d_close_t        oslogclose;
212 extern d_select_t       oslogselect;
213 extern d_ioctl_t        oslogioctl;
214 
215 /* XXX wants a linker set so these can be static */
216 extern d_open_t         oslog_streamopen;
217 extern d_close_t        oslog_streamclose;
218 extern d_read_t         oslog_streamread;
219 extern d_ioctl_t        oslog_streamioctl;
220 extern d_select_t       oslog_streamselect;
221 
222 void oslog_stream(bool is_metadata, firehose_tracepoint_id_u ftid, uint64_t stamp,
223     const void *data, size_t datalen);
224 
225 /*
226  * Serialize log access.  Note that the log can be written at interrupt level,
227  * so any log manipulations that can be done from, or affect, another processor
228  * at interrupt level must be guarded with a spin lock.
229  */
230 
231 static int sysctl_kern_msgbuf(struct sysctl_oid *oidp,
232     void *arg1, int arg2, struct sysctl_req *req);
233 
234 /*ARGSUSED*/
235 int
logopen(__unused dev_t dev,__unused int flags,__unused int mode,struct proc * p)236 logopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
237 {
238 	bsd_log_lock_safe();
239 	if (log_open) {
240 		bsd_log_unlock();
241 		return EBUSY;
242 	}
243 	if (atm_get_diagnostic_config() & ATM_ENABLE_LEGACY_LOGGING) {
244 		logsoftc.sc_mbp = msgbufp;
245 	} else {
246 		/*
247 		 * Support for messagetracer (kern_asl_msg())
248 		 * In this mode, /dev/klog exports only ASL-formatted messages
249 		 * written into aslbufp via vaddlog().
250 		 */
251 		logsoftc.sc_mbp = aslbufp;
252 	}
253 	logsoftc.sc_pgid = proc_getpid(p);            /* signal process only */
254 	log_open = 1;
255 
256 	bsd_log_unlock();
257 
258 	return 0;
259 }
260 
261 /*ARGSUSED*/
262 int
logclose(__unused dev_t dev,__unused int flag,__unused int devtype,__unused struct proc * p)263 logclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
264 {
265 	bsd_log_lock_safe();
266 	logsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC);
267 	selthreadclear(&logsoftc.sc_selp);
268 	log_open = 0;
269 	bsd_log_unlock();
270 	return 0;
271 }
272 
273 
274 int
oslogopen(__unused dev_t dev,__unused int flags,__unused int mode,struct proc * p)275 oslogopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
276 {
277 	bsd_log_lock_safe();
278 	if (oslog_open) {
279 		bsd_log_unlock();
280 		return EBUSY;
281 	}
282 	oslogsoftc.sc_pgid = proc_getpid(p);          /* signal process only */
283 	oslog_open = 1;
284 
285 	bsd_log_unlock();
286 	return 0;
287 }
288 
289 int
oslogclose(__unused dev_t dev,__unused int flag,__unused int devtype,__unused struct proc * p)290 oslogclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
291 {
292 	bsd_log_lock_safe();
293 	oslogsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC);
294 	selthreadclear(&oslogsoftc.sc_selp);
295 	oslog_open = 0;
296 	bsd_log_unlock();
297 	return 0;
298 }
299 
300 static void
oslog_streamwakeup_locked(void)301 oslog_streamwakeup_locked(void)
302 {
303 	LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
304 	if (!oslog_stream_open) {
305 		return;
306 	}
307 	selwakeup(&oslog_streamsoftc.sc_selp);
308 	if (oslog_streamsoftc.sc_state & LOG_RDWAIT) {
309 		wakeup((caddr_t)oslog_streambufp);
310 		oslog_streamsoftc.sc_state &= ~LOG_RDWAIT;
311 	}
312 }
313 
314 int
oslog_streamopen(__unused dev_t dev,__unused int flags,__unused int mode,struct proc * p)315 oslog_streamopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
316 {
317 	char *oslog_stream_msg_bufc = NULL;
318 	oslog_stream_buf_entry_t entries = NULL;
319 
320 	stream_lock();
321 	if (oslog_stream_open) {
322 		stream_unlock();
323 		return EBUSY;
324 	}
325 	stream_unlock();
326 
327 	// Allocate the stream buffer
328 	oslog_stream_msg_bufc = kalloc_data(oslog_stream_buf_size,
329 	    Z_WAITOK | Z_ZERO);
330 	if (!oslog_stream_msg_bufc) {
331 		return ENOMEM;
332 	}
333 
334 	/* entries to support kernel logging in stream mode */
335 	/* Zeroing to avoid copying uninitialized struct padding to userspace. */
336 	entries = kalloc_type(struct oslog_stream_buf_entry_s,
337 	    oslog_stream_num_entries, Z_WAITOK | Z_ZERO);
338 	if (!entries) {
339 		kfree_data(oslog_stream_msg_bufc, oslog_stream_buf_size);
340 		return ENOMEM;
341 	}
342 
343 	stream_lock();
344 	if (oslog_stream_open) {
345 		stream_unlock();
346 		kfree_data(oslog_stream_msg_bufc, oslog_stream_buf_size);
347 		kfree_type(struct oslog_stream_buf_entry_s,
348 		    oslog_stream_num_entries, entries);
349 		return EBUSY;
350 	}
351 
352 	assert(oslog_streambufp->msg_bufc == NULL);
353 	oslog_streambufp->msg_bufc = oslog_stream_msg_bufc;
354 	oslog_streambufp->msg_size = oslog_stream_buf_size;
355 
356 	oslog_stream_buf_entries = entries;
357 
358 	STAILQ_INIT(&oslog_stream_free_head);
359 	STAILQ_INIT(&oslog_stream_buf_head);
360 
361 	for (int i = 0; i < oslog_stream_num_entries; i++) {
362 		oslog_stream_buf_entries[i].type = oslog_stream_link_type_log;
363 		STAILQ_INSERT_TAIL(&oslog_stream_free_head, &oslog_stream_buf_entries[i], buf_entries);
364 	}
365 
366 	/* there should be no pending entries in the stream */
367 	assert(STAILQ_EMPTY(&oslog_stream_buf_head));
368 	assert(oslog_streambufp->msg_bufx == 0);
369 	assert(oslog_streambufp->msg_bufr == 0);
370 
371 	oslog_streambufp->msg_bufx = 0;
372 	oslog_streambufp->msg_bufr = 0;
373 	oslog_streamsoftc.sc_pgid = proc_getpid(p); /* signal process only */
374 	oslog_stream_open = true;
375 	oslog_stream_buf_bytesavail = oslog_stream_buf_size;
376 	stream_unlock();
377 
378 	return 0;
379 }
380 
381 static oslog_stream_buf_entry_t
stream_metadata_create(firehose_tracepoint_id_u ftid,uint64_t stamp,const void * data,size_t data_len)382 stream_metadata_create(firehose_tracepoint_id_u ftid, uint64_t stamp, const void *data, size_t data_len)
383 {
384 	const size_t ft_size = sizeof(struct firehose_tracepoint_s) + data_len;
385 
386 	if (!data || data_len > UINT16_MAX || ft_size > UINT16_MAX) {
387 		return NULL;
388 	}
389 
390 	oslog_stream_buf_entry_t e = kalloc_type(struct oslog_stream_buf_entry_s, Z_WAITOK);
391 	if (!e) {
392 		return NULL;
393 	}
394 
395 	e->type         = oslog_stream_link_type_metadata;
396 	e->timestamp    = stamp;
397 	e->offset       = 0; // always zero for metadata
398 	e->size         = (uint16_t)ft_size;
399 	e->metadata     = kalloc_data(e->size, Z_WAITOK | Z_ZERO);
400 
401 	if (!e->metadata) {
402 		kfree_type(struct oslog_stream_buf_entry_s, e);
403 		return NULL;
404 	}
405 
406 	firehose_tracepoint_t ft    = e->metadata;
407 	ft->ft_id.ftid_value        = ftid.ftid_value;
408 	ft->ft_thread               = thread_tid(current_thread());
409 	ft->ft_length               = (uint16_t)data_len;
410 	memcpy(ft->ft_data, data, ft->ft_length);
411 
412 	return e;
413 }
414 
415 static void
stream_metadata_free(oslog_stream_buf_entry_t e)416 stream_metadata_free(oslog_stream_buf_entry_t e)
417 {
418 	kfree_data(e->metadata, e->size);
419 	kfree_type(struct oslog_stream_buf_entry_s, e);
420 }
421 
422 static void
oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry)423 oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry)
424 {
425 	LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
426 	STAILQ_INSERT_TAIL(&oslog_stream_buf_head, m_entry, buf_entries);
427 }
428 
429 static void
oslog_streamwrite_append_bytes(const char * buffer,int buflen)430 oslog_streamwrite_append_bytes(const char *buffer, int buflen)
431 {
432 	struct msgbuf *mbp;
433 
434 	LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
435 
436 	assert(oslog_stream_buf_bytesavail >= buflen);
437 	oslog_stream_buf_bytesavail -= buflen;
438 	assert(oslog_stream_buf_bytesavail >= 0);
439 
440 	mbp = oslog_streambufp;
441 	if (mbp->msg_bufx + buflen <= mbp->msg_size) {
442 		/*
443 		 * If this will fit without needing to be split across the end
444 		 * of the buffer, copy it directly in one go.
445 		 */
446 		memcpy((void *)(mbp->msg_bufc + mbp->msg_bufx), buffer, buflen);
447 
448 		mbp->msg_bufx += buflen;
449 		if (mbp->msg_bufx == mbp->msg_size) {
450 			mbp->msg_bufx = 0;
451 		}
452 	} else {
453 		/*
454 		 * Copy up to the end of the stream buffer, and then put what remains
455 		 * at the beginning.
456 		 */
457 		int bytes_left = mbp->msg_size - mbp->msg_bufx;
458 		memcpy((void *)(mbp->msg_bufc + mbp->msg_bufx), buffer, bytes_left);
459 
460 		buflen -= bytes_left;
461 		buffer += bytes_left;
462 
463 		// Copy the remainder of the data from the beginning of stream
464 		memcpy((void *)mbp->msg_bufc, buffer, buflen);
465 		mbp->msg_bufx = buflen;
466 	}
467 	return;
468 }
469 
470 static oslog_stream_buf_entry_t
oslog_stream_find_free_buf_entry_locked(void)471 oslog_stream_find_free_buf_entry_locked(void)
472 {
473 	struct msgbuf *mbp;
474 	oslog_stream_buf_entry_t buf_entry = NULL;
475 
476 	LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
477 
478 	mbp = oslog_streambufp;
479 
480 	buf_entry = STAILQ_FIRST(&oslog_stream_free_head);
481 	if (buf_entry) {
482 		STAILQ_REMOVE_HEAD(&oslog_stream_free_head, buf_entries);
483 	} else {
484 		// If no list elements are available in the free-list,
485 		// consume the next log line so we can free up its list element
486 		oslog_stream_buf_entry_t prev_entry = NULL;
487 
488 		buf_entry = STAILQ_FIRST(&oslog_stream_buf_head);
489 		while (buf_entry->type == oslog_stream_link_type_metadata) {
490 			prev_entry = buf_entry;
491 			buf_entry = STAILQ_NEXT(buf_entry, buf_entries);
492 		}
493 
494 		if (prev_entry == NULL) {
495 			STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
496 		} else {
497 			STAILQ_REMOVE_AFTER(&oslog_stream_buf_head, prev_entry, buf_entries);
498 		}
499 
500 		mbp->msg_bufr += buf_entry->size;
501 		counter_inc(&oslog_s_dropped_msgcount);
502 		if (mbp->msg_bufr >= mbp->msg_size) {
503 			mbp->msg_bufr = (mbp->msg_bufr % mbp->msg_size);
504 		}
505 	}
506 
507 	return buf_entry;
508 }
509 
510 static void
oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,uint64_t stamp,const void * pubdata,size_t publen)511 oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,
512     uint64_t stamp, const void *pubdata, size_t publen)
513 {
514 	struct msgbuf *mbp;
515 	oslog_stream_buf_entry_t buf_entry = NULL;
516 	oslog_stream_buf_entry_t next_entry = NULL;
517 
518 	LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
519 
520 	assert(publen <= UINT16_MAX);
521 	const ssize_t ft_length = offsetof(struct firehose_tracepoint_s, ft_data) + publen;
522 
523 	mbp = oslog_streambufp;
524 	if (ft_length > mbp->msg_size) {
525 		counter_inc(&oslog_s_error_count);
526 		counter_inc(&oslog_s_dropped_msgcount);
527 		return;
528 	}
529 
530 	// Ensure that we have a list element for this record
531 	buf_entry = oslog_stream_find_free_buf_entry_locked();
532 
533 	assert(buf_entry != NULL);
534 
535 	while (ft_length > oslog_stream_buf_bytesavail) {
536 		oslog_stream_buf_entry_t prev_entry = NULL;
537 
538 		next_entry = STAILQ_FIRST(&oslog_stream_buf_head);
539 		assert(next_entry != NULL);
540 		while (next_entry->type == oslog_stream_link_type_metadata) {
541 			prev_entry = next_entry;
542 			next_entry = STAILQ_NEXT(next_entry, buf_entries);
543 		}
544 
545 		if (prev_entry == NULL) {
546 			STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
547 		} else {
548 			STAILQ_REMOVE_AFTER(&oslog_stream_buf_head, prev_entry, buf_entries);
549 		}
550 
551 		mbp->msg_bufr += next_entry->size;
552 		if (mbp->msg_bufr >= mbp->msg_size) {
553 			mbp->msg_bufr = (mbp->msg_bufr % mbp->msg_size);
554 		}
555 
556 		counter_inc(&oslog_s_dropped_msgcount);
557 		oslog_stream_buf_bytesavail += next_entry->size;
558 		assert(oslog_stream_buf_bytesavail <= oslog_stream_buf_size);
559 
560 		STAILQ_INSERT_TAIL(&oslog_stream_free_head, next_entry, buf_entries);
561 	}
562 
563 	assert(ft_length <= oslog_stream_buf_bytesavail);
564 
565 	// Write the log line and update the list entry for this record
566 	buf_entry->offset = mbp->msg_bufx;
567 	buf_entry->size = (uint16_t)ft_length;
568 	buf_entry->timestamp = stamp;
569 	buf_entry->type = oslog_stream_link_type_log;
570 
571 	// Construct a tracepoint
572 	struct firehose_tracepoint_s fs = {
573 		.ft_thread = thread_tid(current_thread()),
574 		.ft_id.ftid_value = ftid.ftid_value,
575 		.ft_length = publen
576 	};
577 
578 	oslog_streamwrite_append_bytes((char *)&fs, sizeof(fs));
579 	oslog_streamwrite_append_bytes(pubdata, (int)publen);
580 
581 	assert(mbp->msg_bufr < mbp->msg_size);
582 	// Insert the element to the buffer data list
583 	STAILQ_INSERT_TAIL(&oslog_stream_buf_head, buf_entry, buf_entries);
584 
585 	return;
586 }
587 
588 static void
oslog_stream_metadata(firehose_tracepoint_id_u ftid,uint64_t stamp,const void * data,size_t datalen)589 oslog_stream_metadata(firehose_tracepoint_id_u ftid, uint64_t stamp, const void *data, size_t datalen)
590 {
591 	counter_inc(&oslog_s_metadata_msgcount);
592 
593 	if (!oslog_stream_open) {
594 		counter_inc(&oslog_s_dropped_msgcount);
595 		return;
596 	}
597 
598 	oslog_stream_buf_entry_t me = stream_metadata_create(ftid, stamp, data, datalen);
599 	if (!me) {
600 		counter_inc(&oslog_s_error_count);
601 		counter_inc(&oslog_s_dropped_msgcount);
602 		return;
603 	}
604 
605 	stream_lock();
606 	if (!oslog_stream_open) {
607 		stream_unlock();
608 		stream_metadata_free(me);
609 		counter_inc(&oslog_s_dropped_msgcount);
610 		return;
611 	}
612 	oslog_streamwrite_metadata_locked(me);
613 	stream_unlock();
614 
615 	oslog_streamwakeup();
616 }
617 
618 static void
oslog_stream_data(firehose_tracepoint_id_u ftid,uint64_t stamp,const void * data,size_t datalen)619 oslog_stream_data(firehose_tracepoint_id_u ftid, uint64_t stamp, const void *data, size_t datalen)
620 {
621 	counter_inc(&oslog_s_total_msgcount);
622 
623 	if (!oslog_stream_open) {
624 		counter_inc(&oslog_s_dropped_msgcount);
625 		return;
626 	}
627 
628 	stream_lock();
629 	if (!oslog_stream_open) {
630 		stream_unlock();
631 		counter_inc(&oslog_s_dropped_msgcount);
632 		return;
633 	}
634 	oslog_streamwrite_locked(ftid, stamp, data, datalen);
635 	stream_unlock();
636 
637 	oslog_streamwakeup();
638 }
639 
640 void
oslog_stream(bool is_metadata,firehose_tracepoint_id_u ftid,uint64_t stamp,const void * data,size_t datalen)641 oslog_stream(bool is_metadata, firehose_tracepoint_id_u ftid, uint64_t stamp, const void *data, size_t datalen)
642 {
643 	if (is_metadata) {
644 		oslog_stream_metadata(ftid, stamp, data, datalen);
645 	} else {
646 		oslog_stream_data(ftid, stamp, data, datalen);
647 	}
648 }
649 
650 int
oslog_streamclose(__unused dev_t dev,__unused int flag,__unused int devtype,__unused struct proc * p)651 oslog_streamclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
652 {
653 	stream_lock();
654 
655 	if (!oslog_stream_open) {
656 		stream_unlock();
657 		return EBADF;
658 	}
659 
660 	/*
661 	 * Discard all not consumed logs but free metadata explicitly since
662 	 * they are allocated by stream_metadata_create() from a heap.
663 	 */
664 	STAILQ_HEAD(, oslog_stream_buf_entry_s) metadata = STAILQ_HEAD_INITIALIZER(metadata);
665 
666 	while (!STAILQ_EMPTY(&oslog_stream_buf_head)) {
667 		counter_inc(&oslog_s_dropped_msgcount);
668 		oslog_stream_buf_entry_t e = STAILQ_FIRST(&oslog_stream_buf_head);
669 		STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
670 		if (e->type == oslog_stream_link_type_metadata) {
671 			STAILQ_INSERT_TAIL(&metadata, e, buf_entries);
672 		}
673 	}
674 
675 	oslog_streamwakeup_locked();
676 	oslog_streamsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC);
677 	selthreadclear(&oslog_streamsoftc.sc_selp);
678 
679 	oslog_stream_open = false;
680 
681 	char *oslog_stream_msg_bufc = oslog_streambufp->msg_bufc;
682 	oslog_stream_buf_entry_t entries = oslog_stream_buf_entries;
683 
684 	oslog_streambufp->msg_bufr = 0;
685 	oslog_streambufp->msg_bufx = 0;
686 	oslog_streambufp->msg_bufc = NULL;
687 	oslog_stream_buf_entries = NULL;
688 	oslog_streambufp->msg_size = 0;
689 
690 	stream_unlock();
691 
692 	// Free metadata
693 	while (!STAILQ_EMPTY(&metadata)) {
694 		oslog_stream_buf_entry_t e = STAILQ_FIRST(&metadata);
695 		STAILQ_REMOVE_HEAD(&metadata, buf_entries);
696 		stream_metadata_free(e);
697 	}
698 
699 	// Free the stream buffer
700 	kfree_data(oslog_stream_msg_bufc, oslog_stream_buf_size);
701 	// Free the list entries
702 	kfree_type(struct oslog_stream_buf_entry_s, oslog_stream_num_entries, entries);
703 
704 	return 0;
705 }
706 
707 /*ARGSUSED*/
708 int
logread(__unused dev_t dev,struct uio * uio,int flag)709 logread(__unused dev_t dev, struct uio *uio, int flag)
710 {
711 	int error = 0;
712 	struct msgbuf *mbp = logsoftc.sc_mbp;
713 	ssize_t resid;
714 
715 	bsd_log_lock_safe();
716 	while (mbp->msg_bufr == mbp->msg_bufx) {
717 		if (flag & IO_NDELAY) {
718 			error = EWOULDBLOCK;
719 			goto out;
720 		}
721 		if (logsoftc.sc_state & LOG_NBIO) {
722 			error = EWOULDBLOCK;
723 			goto out;
724 		}
725 		logsoftc.sc_state |= LOG_RDWAIT;
726 		bsd_log_unlock();
727 		/*
728 		 * If the wakeup is missed
729 		 * then wait for 5 sec and reevaluate
730 		 */
731 		if ((error = tsleep((caddr_t)mbp, LOG_RDPRI | PCATCH,
732 		    "klog", 5 * hz)) != 0) {
733 			/* if it times out; ignore */
734 			if (error != EWOULDBLOCK) {
735 				return error;
736 			}
737 		}
738 		bsd_log_lock_safe();
739 	}
740 	logsoftc.sc_state &= ~LOG_RDWAIT;
741 
742 	while ((resid = uio_resid(uio)) > 0) {
743 		size_t l;
744 
745 		if (mbp->msg_bufx >= mbp->msg_bufr) {
746 			l = mbp->msg_bufx - mbp->msg_bufr;
747 		} else {
748 			l = mbp->msg_size - mbp->msg_bufr;
749 		}
750 		if ((l = MIN(l, (size_t)resid)) == 0) {
751 			break;
752 		}
753 
754 		const size_t readpos = mbp->msg_bufr;
755 
756 		bsd_log_unlock();
757 		error = uiomove((caddr_t)&mbp->msg_bufc[readpos], (int)l, uio);
758 		bsd_log_lock_safe();
759 		if (error) {
760 			break;
761 		}
762 
763 		mbp->msg_bufr = (int)(readpos + l);
764 		if (mbp->msg_bufr >= mbp->msg_size) {
765 			mbp->msg_bufr = 0;
766 		}
767 	}
768 out:
769 	bsd_log_unlock();
770 	return error;
771 }
772 
773 /*ARGSUSED*/
774 int
oslog_streamread(__unused dev_t dev,struct uio * uio,int flag)775 oslog_streamread(__unused dev_t dev, struct uio *uio, int flag)
776 {
777 	int error = 0;
778 	int copy_size = 0;
779 	static char logline[FIREHOSE_CHUNK_SIZE];
780 
781 	stream_lock();
782 
783 	if (!oslog_stream_open) {
784 		stream_unlock();
785 		return EBADF;
786 	}
787 
788 	while (STAILQ_EMPTY(&oslog_stream_buf_head)) {
789 		assert(oslog_stream_buf_bytesavail == oslog_stream_buf_size);
790 
791 		if (flag & IO_NDELAY || oslog_streamsoftc.sc_state & LOG_NBIO) {
792 			stream_unlock();
793 			return EWOULDBLOCK;
794 		}
795 
796 		oslog_streamsoftc.sc_state |= LOG_RDWAIT;
797 		wait_result_t wr = assert_wait((event_t)oslog_streambufp,
798 		    THREAD_INTERRUPTIBLE);
799 		if (wr == THREAD_WAITING) {
800 			stream_unlock();
801 			wr = thread_block(THREAD_CONTINUE_NULL);
802 			stream_lock();
803 		}
804 
805 		switch (wr) {
806 		case THREAD_AWAKENED:
807 		case THREAD_TIMED_OUT:
808 			break;
809 		default:
810 			stream_unlock();
811 			return EINTR;
812 		}
813 	}
814 
815 	if (!oslog_stream_open) {
816 		stream_unlock();
817 		return EBADF;
818 	}
819 
820 	int logpos = 0;
821 	oslog_stream_buf_entry_t read_entry = NULL;
822 	uint16_t rec_length;
823 
824 	read_entry = STAILQ_FIRST(&oslog_stream_buf_head);
825 	assert(read_entry != NULL);
826 	STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
827 
828 	// Copy the timestamp first
829 	memcpy(logline + logpos, &read_entry->timestamp, sizeof(uint64_t));
830 	logpos += sizeof(uint64_t);
831 
832 	switch (read_entry->type) {
833 	/* Handle metadata messages */
834 	case oslog_stream_link_type_metadata:
835 	{
836 		memcpy(logline + logpos, read_entry->metadata, read_entry->size);
837 		logpos += read_entry->size;
838 
839 		stream_unlock();
840 
841 		stream_metadata_free(read_entry);
842 		break;
843 	}
844 	/* Handle log messages */
845 	case oslog_stream_link_type_log:
846 	{
847 		/* ensure that the correct read entry was dequeued */
848 		assert(read_entry->offset == oslog_streambufp->msg_bufr);
849 		rec_length = read_entry->size;
850 
851 		// If the next log line is contiguous in the buffer, copy it out.
852 		if (read_entry->offset + rec_length <= oslog_streambufp->msg_size) {
853 			memcpy(logline + logpos,
854 			    oslog_streambufp->msg_bufc + read_entry->offset, rec_length);
855 
856 			oslog_streambufp->msg_bufr += rec_length;
857 			if (oslog_streambufp->msg_bufr == oslog_streambufp->msg_size) {
858 				oslog_streambufp->msg_bufr = 0;
859 			}
860 			logpos += rec_length;
861 		} else {
862 			// Otherwise, copy until the end of the buffer, and
863 			// copy the remaining bytes starting at index 0.
864 			int bytes_left = oslog_streambufp->msg_size - read_entry->offset;
865 			memcpy(logline + logpos,
866 			    oslog_streambufp->msg_bufc + read_entry->offset, bytes_left);
867 			logpos += bytes_left;
868 			rec_length -= bytes_left;
869 
870 			memcpy(logline + logpos, (const void *)oslog_streambufp->msg_bufc,
871 			    rec_length);
872 			oslog_streambufp->msg_bufr = rec_length;
873 			logpos += rec_length;
874 		}
875 
876 		oslog_stream_buf_bytesavail += read_entry->size;
877 		assert(oslog_stream_buf_bytesavail <= oslog_stream_buf_size);
878 
879 		assert(oslog_streambufp->msg_bufr < oslog_streambufp->msg_size);
880 		STAILQ_INSERT_TAIL(&oslog_stream_free_head, read_entry, buf_entries);
881 
882 		stream_unlock();
883 		break;
884 	}
885 	default:
886 	{
887 		panic("Got unexpected log entry type: %hhu", read_entry->type);
888 	}
889 	}
890 
891 	copy_size = min(logpos, (int) MIN(uio_resid(uio), INT_MAX));
892 	if (copy_size > 0) {
893 		error = uiomove((caddr_t)logline, copy_size, uio);
894 	}
895 	counter_inc(&oslog_s_streamed_msgcount);
896 
897 	return error;
898 }
899 
900 /*ARGSUSED*/
901 int
logselect(__unused dev_t dev,int rw,void * wql,struct proc * p)902 logselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
903 {
904 	const struct msgbuf *mbp = logsoftc.sc_mbp;
905 
906 	switch (rw) {
907 	case FREAD:
908 		bsd_log_lock_safe();
909 		if (mbp->msg_bufr != mbp->msg_bufx) {
910 			bsd_log_unlock();
911 			return 1;
912 		}
913 		selrecord(p, &logsoftc.sc_selp, wql);
914 		bsd_log_unlock();
915 		break;
916 	}
917 	return 0;
918 }
919 
920 int
oslogselect(__unused dev_t dev,int rw,void * wql,struct proc * p)921 oslogselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
922 {
923 	switch (rw) {
924 	case FREAD:
925 		bsd_log_lock_safe();
926 		if (os_log_wakeup) {
927 			bsd_log_unlock();
928 			return 1;
929 		}
930 		selrecord(p, &oslogsoftc.sc_selp, wql);
931 		bsd_log_unlock();
932 		break;
933 	}
934 	return 0;
935 }
936 
937 int
oslog_streamselect(__unused dev_t dev,int rw,void * wql,struct proc * p)938 oslog_streamselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
939 {
940 	int ret = 0;
941 
942 	stream_lock();
943 
944 	switch (rw) {
945 	case FREAD:
946 		if (STAILQ_EMPTY(&oslog_stream_buf_head)) {
947 			selrecord(p, &oslog_streamsoftc.sc_selp, wql);
948 		} else {
949 			ret = 1;
950 		}
951 		break;
952 	}
953 
954 	stream_unlock();
955 	return ret;
956 }
957 
958 void
logwakeup(struct msgbuf * mbp)959 logwakeup(struct msgbuf *mbp)
960 {
961 	/* cf. r24974766 & r25201228*/
962 	if (oslog_is_safe() == FALSE) {
963 		return;
964 	}
965 
966 	bsd_log_lock_safe();
967 	if (!log_open) {
968 		bsd_log_unlock();
969 		return;
970 	}
971 	if (NULL == mbp) {
972 		mbp = logsoftc.sc_mbp;
973 	}
974 	if (mbp != logsoftc.sc_mbp) {
975 		goto out;
976 	}
977 	selwakeup(&logsoftc.sc_selp);
978 	if (logsoftc.sc_state & LOG_ASYNC) {
979 		int pgid = logsoftc.sc_pgid;
980 		bsd_log_unlock();
981 		if (pgid < 0) {
982 			gsignal(-pgid, SIGIO);
983 		} else {
984 			proc_signal(pgid, SIGIO);
985 		}
986 		bsd_log_lock_safe();
987 	}
988 	if (logsoftc.sc_state & LOG_RDWAIT) {
989 		wakeup((caddr_t)mbp);
990 		logsoftc.sc_state &= ~LOG_RDWAIT;
991 	}
992 out:
993 	bsd_log_unlock();
994 }
995 
996 void
oslogwakeup(void)997 oslogwakeup(void)
998 {
999 	if (!oslog_is_safe()) {
1000 		return;
1001 	}
1002 
1003 	bsd_log_lock_safe();
1004 	if (!oslog_open) {
1005 		bsd_log_unlock();
1006 		return;
1007 	}
1008 	selwakeup(&oslogsoftc.sc_selp);
1009 	os_log_wakeup = true;
1010 	bsd_log_unlock();
1011 }
1012 
1013 void
oslog_streamwakeup(void)1014 oslog_streamwakeup(void)
1015 {
1016 	/* cf. r24974766 & r25201228*/
1017 	if (oslog_is_safe() == FALSE) {
1018 		return;
1019 	}
1020 
1021 	stream_lock();
1022 	oslog_streamwakeup_locked();
1023 	stream_unlock();
1024 }
1025 
1026 /*ARGSUSED*/
1027 int
logioctl(__unused dev_t dev,u_long com,caddr_t data,__unused int flag,__unused struct proc * p)1028 logioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
1029 {
1030 	int l;
1031 	const struct msgbuf *mbp = logsoftc.sc_mbp;
1032 
1033 	bsd_log_lock_safe();
1034 	switch (com) {
1035 	/* return number of characters immediately available */
1036 	case FIONREAD:
1037 		l = mbp->msg_bufx - mbp->msg_bufr;
1038 		if (l < 0) {
1039 			l += mbp->msg_size;
1040 		}
1041 		*(off_t *)data = l;
1042 		break;
1043 
1044 	case FIONBIO:
1045 		if (*(int *)data) {
1046 			logsoftc.sc_state |= LOG_NBIO;
1047 		} else {
1048 			logsoftc.sc_state &= ~LOG_NBIO;
1049 		}
1050 		break;
1051 
1052 	case FIOASYNC:
1053 		if (*(int *)data) {
1054 			logsoftc.sc_state |= LOG_ASYNC;
1055 		} else {
1056 			logsoftc.sc_state &= ~LOG_ASYNC;
1057 		}
1058 		break;
1059 
1060 	case TIOCSPGRP:
1061 		logsoftc.sc_pgid = *(int *)data;
1062 		break;
1063 
1064 	case TIOCGPGRP:
1065 		*(int *)data = logsoftc.sc_pgid;
1066 		break;
1067 
1068 	default:
1069 		bsd_log_unlock();
1070 		return -1;
1071 	}
1072 	bsd_log_unlock();
1073 	return 0;
1074 }
1075 
1076 /*ARGSUSED*/
1077 int
oslogioctl(__unused dev_t dev,u_long com,caddr_t data,__unused int flag,__unused struct proc * p)1078 oslogioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
1079 {
1080 	int ret = 0;
1081 	mach_vm_size_t buffer_size = (__firehose_buffer_kernel_chunk_count * FIREHOSE_CHUNK_SIZE);
1082 	firehose_buffer_map_info_t map_info = {0, 0};
1083 	firehose_buffer_t kernel_firehose_buffer = NULL;
1084 	mach_vm_address_t user_addr = 0;
1085 	mach_port_t mem_entry_ptr = MACH_PORT_NULL;
1086 	bool has_more;
1087 
1088 	switch (com) {
1089 	/* return number of characters immediately available */
1090 
1091 	case LOGBUFFERMAP:
1092 		kernel_firehose_buffer = (firehose_buffer_t)kernel_firehose_addr;
1093 
1094 		ret = mach_make_memory_entry_64(kernel_map,
1095 		    &buffer_size,
1096 		    (mach_vm_offset_t) kernel_firehose_buffer,
1097 		    (MAP_MEM_VM_SHARE | VM_PROT_READ),
1098 		    &mem_entry_ptr,
1099 		    MACH_PORT_NULL);
1100 		if (ret == KERN_SUCCESS) {
1101 			ret = mach_vm_map_kernel(get_task_map(current_task()),
1102 			    &user_addr,
1103 			    buffer_size,
1104 			    0,               /*  mask */
1105 			    VM_FLAGS_ANYWHERE,
1106 			    VM_MAP_KERNEL_FLAGS_NONE,
1107 			    VM_KERN_MEMORY_NONE,
1108 			    mem_entry_ptr,
1109 			    0,               /* offset */
1110 			    FALSE,               /* copy */
1111 			    VM_PROT_READ,
1112 			    VM_PROT_READ,
1113 			    VM_INHERIT_SHARE);
1114 		}
1115 
1116 		if (ret == KERN_SUCCESS) {
1117 			map_info.fbmi_addr = (uint64_t) (user_addr);
1118 			map_info.fbmi_size = buffer_size;
1119 			bcopy(&map_info, data, sizeof(firehose_buffer_map_info_t));
1120 		}
1121 		break;
1122 	case LOGFLUSHED:
1123 		has_more = __firehose_merge_updates(*(firehose_push_reply_t *)(data));
1124 		bsd_log_lock_safe();
1125 		os_log_wakeup = has_more;
1126 		if (os_log_wakeup) {
1127 			selwakeup(&oslogsoftc.sc_selp);
1128 		}
1129 		bsd_log_unlock();
1130 		break;
1131 	default:
1132 		return -1;
1133 	}
1134 	return 0;
1135 }
1136 
1137 /*ARGSUSED*/
1138 int
oslog_streamioctl(__unused dev_t dev,u_long com,caddr_t data,__unused int flag,__unused struct proc * p)1139 oslog_streamioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
1140 {
1141 	int err = 0;
1142 
1143 	stream_lock();
1144 
1145 	switch (com) {
1146 	case FIONBIO:
1147 		if (data && *(int *)data) {
1148 			oslog_streamsoftc.sc_state |= LOG_NBIO;
1149 		} else {
1150 			oslog_streamsoftc.sc_state &= ~LOG_NBIO;
1151 		}
1152 		break;
1153 	case FIOASYNC:
1154 		if (data && *(int *)data) {
1155 			oslog_streamsoftc.sc_state |= LOG_ASYNC;
1156 		} else {
1157 			oslog_streamsoftc.sc_state &= ~LOG_ASYNC;
1158 		}
1159 		break;
1160 	default:
1161 		err = -1;
1162 		break;
1163 	}
1164 
1165 	stream_unlock();
1166 	return err;
1167 }
1168 
1169 __startup_func
1170 static void
oslog_init_firehose(void)1171 oslog_init_firehose(void)
1172 {
1173 	if (os_log_disabled()) {
1174 		printf("Firehose disabled: Logging disabled by ATM\n");
1175 		return;
1176 	}
1177 
1178 	kern_return_t kr;
1179 	if (!PE_parse_boot_argn("firehose_chunk_count", &__firehose_buffer_kernel_chunk_count, sizeof(__firehose_buffer_kernel_chunk_count))) {
1180 		__firehose_buffer_kernel_chunk_count = FIREHOSE_BUFFER_KERNEL_DEFAULT_CHUNK_COUNT;
1181 	}
1182 	if (!PE_parse_boot_argn("firehose_io_pages", &__firehose_num_kernel_io_pages, sizeof(__firehose_num_kernel_io_pages))) {
1183 		__firehose_num_kernel_io_pages = FIREHOSE_BUFFER_KERNEL_DEFAULT_IO_PAGES;
1184 	}
1185 	if (!__firehose_kernel_configuration_valid(__firehose_buffer_kernel_chunk_count, __firehose_num_kernel_io_pages)) {
1186 		printf("illegal firehose configuration %u/%u, using defaults\n", __firehose_buffer_kernel_chunk_count, __firehose_num_kernel_io_pages);
1187 		__firehose_buffer_kernel_chunk_count = FIREHOSE_BUFFER_KERNEL_DEFAULT_CHUNK_COUNT;
1188 		__firehose_num_kernel_io_pages = FIREHOSE_BUFFER_KERNEL_DEFAULT_IO_PAGES;
1189 	}
1190 	vm_size_t size = __firehose_buffer_kernel_chunk_count * FIREHOSE_CHUNK_SIZE;
1191 
1192 	kr = kernel_memory_allocate(kernel_map, &kernel_firehose_addr,
1193 	    size + (2 * PAGE_SIZE), 0,
1194 	    KMA_GUARD_FIRST | KMA_GUARD_LAST | KMA_ZERO | KMA_PERMANENT,
1195 	    VM_KERN_MEMORY_LOG);
1196 	if (kr != KERN_SUCCESS) {
1197 		panic("Failed to allocate memory for firehose logging buffer");
1198 	}
1199 	kernel_firehose_addr += PAGE_SIZE;
1200 	/* register buffer with firehose */
1201 	kernel_firehose_addr = (vm_offset_t)__firehose_buffer_create((size_t *) &size);
1202 
1203 	printf("Firehose configured: %u chunks, %u io pages\n",
1204 	    __firehose_buffer_kernel_chunk_count, __firehose_num_kernel_io_pages);
1205 }
1206 STARTUP(OSLOG, STARTUP_RANK_SECOND, oslog_init_firehose);
1207 
1208 /*
1209  * log_putc_locked
1210  *
1211  * Decription:	Output a character to the log; assumes the bsd_log_lock() or
1212  *              bsd_log_lock_safe() is held by the caller.
1213  *
1214  * Parameters:	c				Character to output
1215  *
1216  * Returns:	(void)
1217  *
1218  * Notes:	This functions is used for multibyte output to the log; it
1219  *		should be used preferrentially where possible to ensure that
1220  *		log entries do not end up interspersed due to preemption or
1221  *		SMP reentrancy.
1222  */
1223 void
log_putc_locked(struct msgbuf * mbp,char c)1224 log_putc_locked(struct msgbuf *mbp, char c)
1225 {
1226 	mbp->msg_bufc[mbp->msg_bufx++] = c;
1227 	if (mbp->msg_bufx >= mbp->msg_size) {
1228 		mbp->msg_bufx = 0;
1229 	}
1230 }
1231 
1232 /*
1233  * log_putc
1234  *
1235  * Decription:	Output a character to the log; assumes the bsd_log_lock() or
1236  *              bsd_log_lock_safe() is NOT held by the caller.
1237  *
1238  * Parameters:	c				Character to output
1239  *
1240  * Returns:	(void)
1241  *
1242  * Notes:	This function is used for single byte output to the log.  It
1243  *		primarily exists to maintain binary backward compatibility.
1244  */
1245 void
log_putc(char c)1246 log_putc(char c)
1247 {
1248 	if (!bsd_log_lock(oslog_is_safe())) {
1249 		os_atomic_inc(&oslog_msgbuf_dropped_charcount, relaxed);
1250 		return;
1251 	}
1252 
1253 	log_putc_locked(msgbufp, c);
1254 	int unread_count = msgbufp->msg_bufx - msgbufp->msg_bufr;
1255 
1256 	bsd_log_unlock();
1257 
1258 	if (unread_count < 0) {
1259 		unread_count = 0 - unread_count;
1260 	}
1261 	if (c == '\n' || unread_count >= (msgbufp->msg_size / 2)) {
1262 		logwakeup(msgbufp);
1263 	}
1264 }
1265 
1266 /*
1267  * it is possible to increase the kernel log buffer size by adding
1268  *   msgbuf=n
1269  * to the kernel command line, and to read the current size using
1270  *   sysctl kern.msgbuf
1271  * If there is no parameter on the kernel command line, the buffer is
1272  * allocated statically and is CONFIG_MSG_BSIZE characters in size, otherwise
1273  * memory is dynamically allocated. Memory management must already be up.
1274  */
1275 static int
log_setsize(size_t size)1276 log_setsize(size_t size)
1277 {
1278 	int i, count;
1279 	char *p;
1280 
1281 	if (size == 0 || size > MAX_MSG_BSIZE) {
1282 		return EINVAL;
1283 	}
1284 
1285 	int new_logsize = (int)size;
1286 	char *new_logdata = kalloc_data(size, Z_WAITOK | Z_ZERO);
1287 	if (!new_logdata) {
1288 		printf("Cannot resize system message buffer: Not enough memory\n");
1289 		return ENOMEM;
1290 	}
1291 
1292 	bsd_log_lock_safe();
1293 
1294 	char *old_logdata = msgbufp->msg_bufc;
1295 	int old_logsize = msgbufp->msg_size;
1296 	int old_bufr = msgbufp->msg_bufr;
1297 	int old_bufx = msgbufp->msg_bufx;
1298 
1299 	/* start "new_logsize" bytes before the write pointer */
1300 	if (new_logsize <= old_bufx) {
1301 		count = new_logsize;
1302 		p = old_logdata + old_bufx - count;
1303 	} else {
1304 		/*
1305 		 * if new buffer is bigger, copy what we have and let the
1306 		 * bzero above handle the difference
1307 		 */
1308 		count = MIN(new_logsize, old_logsize);
1309 		p = old_logdata + old_logsize - (count - old_bufx);
1310 	}
1311 	for (i = 0; i < count; i++) {
1312 		if (p >= old_logdata + old_logsize) {
1313 			p = old_logdata;
1314 		}
1315 		new_logdata[i] = *p++;
1316 	}
1317 
1318 	int new_bufx = i;
1319 	if (new_bufx >= new_logsize) {
1320 		new_bufx = 0;
1321 	}
1322 	msgbufp->msg_bufx = new_bufx;
1323 
1324 	int new_bufr = old_bufx - old_bufr; /* how much were we trailing bufx by? */
1325 	if (new_bufr < 0) {
1326 		new_bufr += old_logsize;
1327 	}
1328 	new_bufr = new_bufx - new_bufr; /* now relative to oldest data in new buffer */
1329 	if (new_bufr < 0) {
1330 		new_bufr += new_logsize;
1331 	}
1332 	msgbufp->msg_bufr = new_bufr;
1333 
1334 	msgbufp->msg_size = new_logsize;
1335 	msgbufp->msg_bufc = new_logdata;
1336 
1337 	bsd_log_unlock();
1338 
1339 	/*
1340 	 * This memory is now dead - clear it so that it compresses better
1341 	 * in case of suspend to disk etc.
1342 	 */
1343 	bzero(old_logdata, old_logsize);
1344 	if (old_logdata != smsg_bufc) {
1345 		/* dynamic memory that must be freed */
1346 		kfree_data(old_logdata, old_logsize);
1347 	}
1348 
1349 	printf("System message buffer configured: %lu bytes\n", size);
1350 
1351 	return 0;
1352 }
1353 
1354 /*
1355  * This function should have its own boot argument instead of being side
1356  * effect of 'msgbuf' boot argument.
1357  */
1358 __startup_func
1359 static void
oslog_setsize(size_t size)1360 oslog_setsize(size_t size)
1361 {
1362 	if (size <= OSLOG_STREAM_BUF_SIZE || size > OSLOG_STREAM_MAX_BUF_SIZE) {
1363 		return;
1364 	}
1365 
1366 	size_t scale = (size / OSLOG_STREAM_BUF_SIZE);
1367 	oslog_stream_buf_size = (int)size;
1368 	oslog_stream_num_entries = (int)(scale * OSLOG_NUM_STREAM_ENTRIES);
1369 
1370 	printf("OSLog stream buffer configured: %d bytes, %d entries\n",
1371 	    oslog_stream_buf_size, oslog_stream_num_entries);
1372 }
1373 
1374 SYSCTL_PROC(_kern, OID_AUTO, msgbuf,
1375     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0,
1376     sysctl_kern_msgbuf, "I", "");
1377 
1378 static int
sysctl_kern_msgbuf(struct sysctl_oid * oidp __unused,void * arg1 __unused,int arg2 __unused,struct sysctl_req * req)1379 sysctl_kern_msgbuf(struct sysctl_oid *oidp __unused,
1380     void *arg1 __unused, int arg2 __unused, struct sysctl_req *req)
1381 {
1382 	int old_bufsize, bufsize;
1383 	int error;
1384 
1385 	bsd_log_lock_safe();
1386 	old_bufsize = bufsize = msgbufp->msg_size;
1387 	bsd_log_unlock();
1388 
1389 	error = sysctl_io_number(req, bufsize, sizeof(bufsize), &bufsize, NULL);
1390 	if (error) {
1391 		return error;
1392 	}
1393 
1394 	if (bufsize < 0) {
1395 		return EINVAL;
1396 	}
1397 
1398 	if (bufsize != old_bufsize) {
1399 		error = log_setsize(bufsize);
1400 	}
1401 
1402 	return error;
1403 }
1404 
1405 /*
1406  * This should be called by /sbin/dmesg only via libproc.
1407  * It returns as much data still in the buffer as possible.
1408  */
1409 int
log_dmesg(user_addr_t buffer,uint32_t buffersize,int32_t * retval)1410 log_dmesg(user_addr_t buffer, uint32_t buffersize, int32_t *retval)
1411 {
1412 	uint32_t i;
1413 	uint32_t localbuff_size;
1414 	int error = 0, newl, skip;
1415 	char *localbuff, *p, *copystart, ch;
1416 	size_t copysize;
1417 
1418 	bsd_log_lock_safe();
1419 	localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */
1420 	bsd_log_unlock();
1421 
1422 	/* Allocate a temporary non-circular buffer for copyout */
1423 	localbuff = kalloc_data(localbuff_size, Z_WAITOK);
1424 	if (!localbuff) {
1425 		printf("log_dmesg: unable to allocate memory\n");
1426 		return ENOMEM;
1427 	}
1428 
1429 	/* in between here, the log could become bigger, but that's fine */
1430 	bsd_log_lock_safe();
1431 
1432 	/*
1433 	 * The message buffer is circular; start at the write pointer, and
1434 	 * make one loop up to write pointer - 1.
1435 	 */
1436 	p = msgbufp->msg_bufc + msgbufp->msg_bufx;
1437 	for (i = newl = skip = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) {
1438 		if (p >= msgbufp->msg_bufc + msgbufp->msg_size) {
1439 			p = msgbufp->msg_bufc;
1440 		}
1441 		ch = *p;
1442 		/* Skip "\n<.*>" syslog sequences. */
1443 		if (skip) {
1444 			if (ch == '>') {
1445 				newl = skip = 0;
1446 			}
1447 			continue;
1448 		}
1449 		if (newl && ch == '<') {
1450 			skip = 1;
1451 			continue;
1452 		}
1453 		if (ch == '\0') {
1454 			continue;
1455 		}
1456 		newl = (ch == '\n');
1457 		localbuff[i++] = ch;
1458 		/* The original version of this routine contained a buffer
1459 		 * overflow. At the time, a "small" targeted fix was desired
1460 		 * so the change below to check the buffer bounds was made.
1461 		 * TODO: rewrite this needlessly convoluted routine.
1462 		 */
1463 		if (i == (localbuff_size - 2)) {
1464 			break;
1465 		}
1466 	}
1467 	if (!newl) {
1468 		localbuff[i++] = '\n';
1469 	}
1470 	localbuff[i++] = 0;
1471 
1472 	if (buffersize >= i) {
1473 		copystart = localbuff;
1474 		copysize = i;
1475 	} else {
1476 		copystart = localbuff + i - buffersize;
1477 		copysize = buffersize;
1478 	}
1479 
1480 	bsd_log_unlock();
1481 
1482 	error = copyout(copystart, buffer, copysize);
1483 	if (!error) {
1484 		*retval = (int32_t)copysize;
1485 	}
1486 
1487 	kfree_data(localbuff, localbuff_size);
1488 	return error;
1489 }
1490 
1491 #ifdef CONFIG_XNUPOST
1492 
1493 size_t find_pattern_in_buffer(const char *, size_t, size_t);
1494 
1495 /*
1496  * returns count of pattern found in systemlog buffer.
1497  * stops searching further if count reaches expected_count.
1498  */
1499 size_t
find_pattern_in_buffer(const char * pattern,size_t len,size_t expected_count)1500 find_pattern_in_buffer(const char *pattern, size_t len, size_t expected_count)
1501 {
1502 	if (pattern == NULL || len == 0 || expected_count == 0) {
1503 		return 0;
1504 	}
1505 
1506 	size_t msg_bufx = msgbufp->msg_bufx;
1507 	size_t msg_size = msgbufp->msg_size;
1508 	size_t match_count = 0;
1509 
1510 	for (size_t i = 0; i < msg_size; i++) {
1511 		boolean_t match = TRUE;
1512 		for (size_t j = 0; j < len; j++) {
1513 			size_t pos = (msg_bufx + i + j) % msg_size;
1514 			if (msgbufp->msg_bufc[pos] != pattern[j]) {
1515 				match = FALSE;
1516 				break;
1517 			}
1518 		}
1519 		if (match && ++match_count >= expected_count) {
1520 			break;
1521 		}
1522 	}
1523 
1524 	return match_count;
1525 }
1526 
1527 __startup_func
1528 static void
oslog_init_msgbuf(void)1529 oslog_init_msgbuf(void)
1530 {
1531 	size_t msgbuf_size = 0;
1532 
1533 	if (PE_parse_boot_argn("msgbuf", &msgbuf_size, sizeof(msgbuf_size))) {
1534 		(void) log_setsize(msgbuf_size);
1535 		oslog_setsize(msgbuf_size);
1536 	}
1537 }
1538 STARTUP(OSLOG, STARTUP_RANK_SECOND, oslog_init_msgbuf);
1539 
1540 #endif
1541