]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/subr_log.c
xnu-4570.41.2.tar.gz
[apple/xnu.git] / bsd / kern / subr_log.c
CommitLineData
1c79356b 1/*
39037602 2 * Copyright (c) 2000-2016 Apple, Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
39037602 5 *
2d21ac55
A
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.
39037602 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
39037602 17 *
2d21ac55
A
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
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
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.
39037602 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
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 <sys/param.h>
69#include <sys/systm.h>
91447636 70#include <sys/proc_internal.h>
1c79356b 71#include <sys/vnode.h>
39037602
A
72#include <stdbool.h>
73#include <firehose/tracepoint_private.h>
813fb2f6 74#include <firehose/chunk_private.h>
39037602
A
75#include <firehose/ioctl_private.h>
76#include <os/firehose_buffer_private.h>
77
78#include <os/log_private.h>
1c79356b
A
79#include <sys/ioctl.h>
80#include <sys/msgbuf.h>
91447636 81#include <sys/file_internal.h>
1c79356b
A
82#include <sys/errno.h>
83#include <sys/select.h>
91447636 84#include <sys/kernel.h>
1c79356b 85#include <kern/thread.h>
39037602
A
86#include <kern/sched_prim.h>
87#include <kern/simple_lock.h>
91447636 88#include <sys/lock.h>
2d21ac55
A
89#include <sys/signalvar.h>
90#include <sys/conf.h>
91#include <sys/sysctl.h>
39037602 92#include <sys/queue.h>
2d21ac55 93#include <kern/kalloc.h>
6d2010ae 94#include <pexpert/pexpert.h>
39037602
A
95#include <mach/mach_port.h>
96#include <mach/mach_vm.h>
97#include <mach/vm_map.h>
98#include <vm/vm_kern.h>
99#include <kern/task.h>
100#include <kern/locks.h>
2d21ac55
A
101
102/* XXX should be in a common header somewhere */
cc8bc92a 103extern void logwakeup(struct msgbuf *);
39037602
A
104extern void oslogwakeup(void);
105extern void oslog_streamwakeup(void);
106static void oslog_streamwakeup_locked(void);
107vm_offset_t kernel_firehose_addr = 0;
108
109/* log message counters for streaming mode */
110uint32_t oslog_s_streamed_msgcount = 0;
111uint32_t oslog_s_dropped_msgcount = 0;
112extern uint32_t oslog_s_error_count;
1c79356b
A
113
114#define LOG_RDPRI (PZERO + 1)
115
116#define LOG_NBIO 0x02
117#define LOG_ASYNC 0x04
118#define LOG_RDWAIT 0x08
119
6d2010ae
A
120/* All globals should be accessed under LOG_LOCK() */
121
cc8bc92a
A
122static char amsg_bufc[1024];
123static struct msgbuf aslbuf = {MSG_MAGIC, sizeof (amsg_bufc), 0, 0, amsg_bufc};
124struct msgbuf *aslbufp __attribute__((used)) = &aslbuf;
125
6d2010ae 126/* logsoftc only valid while log_open=1 */
1c79356b
A
127struct logsoftc {
128 int sc_state; /* see above for possibilities */
129 struct selinfo sc_selp; /* thread waiting for select */
130 int sc_pgid; /* process/group for async I/O */
cc8bc92a 131 struct msgbuf *sc_mbp;
1c79356b
A
132} logsoftc;
133
cc8bc92a 134static int log_open;
6d2010ae 135char smsg_bufc[CONFIG_MSG_BSIZE]; /* static buffer */
813fb2f6
A
136char oslog_stream_bufc[FIREHOSE_CHUNK_SIZE]; /* static buffer */
137struct firehose_chunk_s oslog_boot_buf = {
138 .fc_pos = {
139 .fcp_next_entry_offs = offsetof(struct firehose_chunk_s, fc_data),
140 .fcp_private_offs = FIREHOSE_CHUNK_SIZE,
141 .fcp_refcnt = 1, // indicate that there is a writer to this chunk
142 .fcp_stream = firehose_stream_persist,
143 .fcp_flag_io = 1, // for now, lets assume this is coming from the io bank
39037602
A
144 },
145}; /* static buffer */
813fb2f6 146firehose_chunk_t firehose_boot_chunk = &oslog_boot_buf;
cc8bc92a
A
147struct msgbuf msgbuf = {MSG_MAGIC, sizeof(smsg_bufc), 0, 0, smsg_bufc};
148struct msgbuf oslog_stream_buf = {MSG_MAGIC, 0, 0, 0, NULL};
39236c6e 149struct msgbuf *msgbufp __attribute__((used)) = &msgbuf;
39037602
A
150struct msgbuf *oslog_streambufp __attribute__((used)) = &oslog_stream_buf;
151
152// List entries for keeping track of the streaming buffer
153static oslog_stream_buf_entry_t oslog_stream_buf_entries;
154
155#define OSLOG_NUM_STREAM_ENTRIES 64
156#define OSLOG_STREAM_BUF_SIZE 4096
157
158int oslog_open = 0;
159int os_log_wakeup = 0;
160int oslog_stream_open = 0;
161int oslog_stream_buf_size = OSLOG_STREAM_BUF_SIZE;
162int oslog_stream_num_entries = OSLOG_NUM_STREAM_ENTRIES;
163
164/* oslogsoftc only valid while oslog_open=1 */
165struct oslogsoftc {
166 int sc_state; /* see above for possibilities */
167 struct selinfo sc_selp; /* thread waiting for select */
168 int sc_pgid; /* process/group for async I/O */
169} oslogsoftc;
170
171struct oslog_streamsoftc {
172 int sc_state; /* see above for possibilities */
173 struct selinfo sc_selp; /* thread waiting for select */
174 int sc_pgid; /* process/group for async I/O */
cc8bc92a 175} oslog_streamsoftc;
39037602
A
176
177STAILQ_HEAD(, oslog_stream_buf_entry_s) oslog_stream_free_head =
178 STAILQ_HEAD_INITIALIZER(oslog_stream_free_head);
179STAILQ_HEAD(, oslog_stream_buf_entry_s) oslog_stream_buf_head =
180 STAILQ_HEAD_INITIALIZER(oslog_stream_buf_head);
6d2010ae 181
39037602
A
182/* defined in osfmk/kern/printf.c */
183extern void oslog_lock_init(void);
2d21ac55 184extern void bsd_log_lock(void);
91447636 185extern void bsd_log_unlock(void);
39037602
A
186
187/* defined for osfmk/kern/printf.c */
188void bsd_log_init(void);
189
190/*
191 * Ideally this file would define this lock, but bsd doesn't have the definition
192 * for lock groups.
193 */
194decl_lck_spin_data(extern, oslog_stream_lock)
1c79356b 195
2d21ac55
A
196/* XXX wants a linker set so these can be static */
197extern d_open_t logopen;
198extern d_close_t logclose;
199extern d_read_t logread;
200extern d_ioctl_t logioctl;
201extern d_select_t logselect;
202
39037602
A
203/* XXX wants a linker set so these can be static */
204extern d_open_t oslogopen;
205extern d_close_t oslogclose;
206extern d_select_t oslogselect;
207extern d_ioctl_t oslogioctl;
208
209/* XXX wants a linker set so these can be static */
210extern d_open_t oslog_streamopen;
211extern d_close_t oslog_streamclose;
212extern d_read_t oslog_streamread;
213extern d_ioctl_t oslog_streamioctl;
214extern d_select_t oslog_streamselect;
215
216void oslog_init(void);
217void oslog_setsize(int size);
218void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,
219 uint64_t stamp, const void *pubdata, size_t publen);
220void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry);
221static oslog_stream_buf_entry_t oslog_stream_find_free_buf_entry_locked(void);
222static void oslog_streamwrite_append_bytes(const char *buffer, int buflen);
223
1c79356b
A
224/*
225 * Serialize log access. Note that the log can be written at interrupt level,
226 * so any log manipulations that can be done from, or affect, another processor
227 * at interrupt level must be guarded with a spin lock.
228 */
91447636
A
229
230#define LOG_LOCK() bsd_log_lock()
231#define LOG_UNLOCK() bsd_log_unlock()
232
6d2010ae
A
233#if DEBUG
234#define LOG_SETSIZE_DEBUG(x...) kprintf(x)
235#else
236#define LOG_SETSIZE_DEBUG(x...) do { } while(0)
237#endif
238
239static int sysctl_kern_msgbuf(struct sysctl_oid *oidp,
cc8bc92a 240 void *arg1, int arg2, struct sysctl_req *req);
1c79356b
A
241
242/*ARGSUSED*/
2d21ac55
A
243int
244logopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
1c79356b 245{
1c79356b
A
246 LOG_LOCK();
247 if (log_open) {
248 LOG_UNLOCK();
1c79356b
A
249 return (EBUSY);
250 }
cc8bc92a
A
251 if (atm_get_diagnostic_config() & ATM_ENABLE_LEGACY_LOGGING) {
252 logsoftc.sc_mbp = msgbufp;
253 } else {
254 /*
255 * Support for messagetracer (kern_asl_msg())
256 * In this mode, /dev/klog exports only ASL-formatted messages
257 * written into aslbufp via vaddlog().
258 */
259 logsoftc.sc_mbp = aslbufp;
260 }
1c79356b 261 logsoftc.sc_pgid = p->p_pid; /* signal process only */
6d2010ae 262 log_open = 1;
1c79356b 263
1c79356b 264 LOG_UNLOCK();
9bccf70c 265
1c79356b
A
266 return (0);
267}
268
269/*ARGSUSED*/
270int
2d21ac55 271logclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
1c79356b 272{
1c79356b 273 LOG_LOCK();
39037602 274 logsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC);
1c79356b 275 selwakeup(&logsoftc.sc_selp);
1c79356b 276 selthreadclear(&logsoftc.sc_selp);
6d2010ae 277 log_open = 0;
1c79356b
A
278 LOG_UNLOCK();
279 return (0);
280}
281
39037602
A
282
283int
284oslogopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
285{
286 LOG_LOCK();
287 if (oslog_open) {
288 LOG_UNLOCK();
289 return(EBUSY);
290 }
291 oslogsoftc.sc_pgid = p->p_pid; /* signal process only */
292 oslog_open = 1;
293
294 LOG_UNLOCK();
295 return (0);
296}
297
298int
299oslogclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
300{
301 LOG_LOCK();
302 oslogsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC);
303 selwakeup(&oslogsoftc.sc_selp);
304 selthreadclear(&oslogsoftc.sc_selp);
305 oslog_open = 0;
306 LOG_UNLOCK();
307 return (0);
308}
309
310int
311oslog_streamopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p)
312{
313 char *oslog_stream_msg_bufc = NULL;
314 oslog_stream_buf_entry_t entries = NULL;
315
316 lck_spin_lock(&oslog_stream_lock);
317 if (oslog_stream_open) {
318 lck_spin_unlock(&oslog_stream_lock);
319 return EBUSY;
320 }
321 lck_spin_unlock(&oslog_stream_lock);
322
323 // Allocate the stream buffer
324 oslog_stream_msg_bufc = kalloc(oslog_stream_buf_size);
325 if (!oslog_stream_msg_bufc) {
326 return ENOMEM;
327 }
328
329 /* entries to support kernel logging in stream mode */
330 entries = kalloc(oslog_stream_num_entries * sizeof(struct oslog_stream_buf_entry_s));
331 if (!entries) {
332 kfree(oslog_stream_msg_bufc, oslog_stream_buf_size);
333 return ENOMEM;
334 }
335
336 lck_spin_lock(&oslog_stream_lock);
337 if (oslog_stream_open) {
338 lck_spin_unlock(&oslog_stream_lock);
339 kfree(oslog_stream_msg_bufc, oslog_stream_buf_size);
340 kfree(entries, oslog_stream_num_entries * sizeof(struct oslog_stream_buf_entry_s));
341 return EBUSY;
342 }
343
344 assert(oslog_streambufp->msg_bufc == NULL);
345 oslog_streambufp->msg_bufc = oslog_stream_msg_bufc;
346 oslog_streambufp->msg_size = oslog_stream_buf_size;
347
348 oslog_stream_buf_entries = entries;
349
350 STAILQ_INIT(&oslog_stream_free_head);
351 STAILQ_INIT(&oslog_stream_buf_head);
352
353 for (int i = 0; i < oslog_stream_num_entries; i++) {
354 oslog_stream_buf_entries[i].type = oslog_stream_link_type_log;
355 oslog_stream_buf_entries[i].offset = 0;
356 oslog_stream_buf_entries[i].size = 0;
357 oslog_stream_buf_entries[i].timestamp = 0;
358 STAILQ_INSERT_TAIL(&oslog_stream_free_head, &oslog_stream_buf_entries[i], buf_entries);
359 }
360
361 /* there should be no pending entries in the stream */
362 assert(STAILQ_EMPTY(&oslog_stream_buf_head));
363 assert(oslog_streambufp->msg_bufx == 0);
364 assert(oslog_streambufp->msg_bufr == 0);
365
366 oslog_streambufp->msg_bufx = 0;
367 oslog_streambufp->msg_bufr = 0;
368 oslog_streamsoftc.sc_pgid = p->p_pid; /* signal process only */
369 oslog_stream_open = 1;
370 lck_spin_unlock(&oslog_stream_lock);
371
372 return 0;
373}
374
375int
376oslog_streamclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p)
377{
378 oslog_stream_buf_entry_t next_entry = NULL;
379 char *oslog_stream_msg_bufc = NULL;
380 oslog_stream_buf_entry_t entries = NULL;
381
382 lck_spin_lock(&oslog_stream_lock);
383
384 if (oslog_stream_open == 0) {
385 lck_spin_unlock(&oslog_stream_lock);
386 return EBADF;
387 }
388
389 // Consume all log lines
390 while (!STAILQ_EMPTY(&oslog_stream_buf_head)) {
391 next_entry = STAILQ_FIRST(&oslog_stream_buf_head);
392 STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
393 }
394 oslog_streamwakeup_locked();
395 oslog_streamsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC);
396 selwakeup(&oslog_streamsoftc.sc_selp);
397 selthreadclear(&oslog_streamsoftc.sc_selp);
398 oslog_stream_open = 0;
399 oslog_streambufp->msg_bufr = 0;
400 oslog_streambufp->msg_bufx = 0;
401 oslog_stream_msg_bufc = oslog_streambufp->msg_bufc;
402 oslog_streambufp->msg_bufc = NULL;
403 entries = oslog_stream_buf_entries;
404 oslog_stream_buf_entries = NULL;
405 oslog_streambufp->msg_size = 0;
406
407 lck_spin_unlock(&oslog_stream_lock);
408
409 // Free the stream buffer
410 kfree(oslog_stream_msg_bufc, oslog_stream_buf_size);
411 // Free the list entries
412 kfree(entries, oslog_stream_num_entries * sizeof(struct oslog_stream_buf_entry_s));
413
414 return 0;
415}
416
1c79356b
A
417/*ARGSUSED*/
418int
2d21ac55 419logread(__unused dev_t dev, struct uio *uio, int flag)
1c79356b 420{
6d2010ae 421 int l;
1c79356b 422 int error = 0;
cc8bc92a 423 struct msgbuf *mbp = logsoftc.sc_mbp;
1c79356b 424
91447636 425 LOG_LOCK();
cc8bc92a 426 while (mbp->msg_bufr == mbp->msg_bufx) {
1c79356b 427 if (flag & IO_NDELAY) {
91447636
A
428 error = EWOULDBLOCK;
429 goto out;
1c79356b
A
430 }
431 if (logsoftc.sc_state & LOG_NBIO) {
91447636
A
432 error = EWOULDBLOCK;
433 goto out;
1c79356b
A
434 }
435 logsoftc.sc_state |= LOG_RDWAIT;
91447636
A
436 LOG_UNLOCK();
437 /*
39236c6e 438 * If the wakeup is missed
91447636
A
439 * then wait for 5 sec and reevaluate
440 */
cc8bc92a 441 if ((error = tsleep((caddr_t)mbp, LOG_RDPRI | PCATCH,
2d21ac55 442 "klog", 5 * hz)) != 0) {
91447636
A
443 /* if it times out; ignore */
444 if (error != EWOULDBLOCK)
445 return (error);
1c79356b 446 }
91447636 447 LOG_LOCK();
1c79356b 448 }
1c79356b
A
449 logsoftc.sc_state &= ~LOG_RDWAIT;
450
91447636 451 while (uio_resid(uio) > 0) {
6d2010ae
A
452 int readpos;
453
cc8bc92a 454 l = mbp->msg_bufx - mbp->msg_bufr;
1c79356b 455 if (l < 0)
cc8bc92a 456 l = mbp->msg_size - mbp->msg_bufr;
91447636 457 l = min(l, uio_resid(uio));
1c79356b
A
458 if (l == 0)
459 break;
6d2010ae 460
cc8bc92a 461 readpos = mbp->msg_bufr;
91447636 462 LOG_UNLOCK();
cc8bc92a 463 error = uiomove((caddr_t)&mbp->msg_bufc[readpos], l, uio);
91447636 464 LOG_LOCK();
1c79356b
A
465 if (error)
466 break;
cc8bc92a
A
467 mbp->msg_bufr = readpos + l;
468 if (mbp->msg_bufr >= mbp->msg_size)
469 mbp->msg_bufr = 0;
1c79356b 470 }
91447636
A
471out:
472 LOG_UNLOCK();
1c79356b
A
473 return (error);
474}
475
39037602
A
476/*ARGSUSED*/
477int
478oslog_streamread(__unused dev_t dev, struct uio *uio, int flag)
479{
480 int error = 0;
481 int copy_size = 0;
813fb2f6 482 static char logline[FIREHOSE_CHUNK_SIZE];
39037602
A
483
484 lck_spin_lock(&oslog_stream_lock);
485
486 if (!oslog_stream_open) {
487 lck_spin_unlock(&oslog_stream_lock);
488 return EBADF;
489 }
490
491 while (STAILQ_EMPTY(&oslog_stream_buf_head)) {
492 if (flag & IO_NDELAY || oslog_streamsoftc.sc_state & LOG_NBIO) {
493 lck_spin_unlock(&oslog_stream_lock);
494 return EWOULDBLOCK;
495 }
496
497 oslog_streamsoftc.sc_state |= LOG_RDWAIT;
498 wait_result_t wr = assert_wait((event_t)oslog_streambufp,
499 THREAD_INTERRUPTIBLE);
500 if (wr == THREAD_WAITING) {
501 lck_spin_unlock(&oslog_stream_lock);
502 wr = thread_block(THREAD_CONTINUE_NULL);
503 lck_spin_lock(&oslog_stream_lock);
504 }
505
506 switch (wr) {
507 case THREAD_AWAKENED:
508 case THREAD_TIMED_OUT:
509 break;
510 default:
511 lck_spin_unlock(&oslog_stream_lock);
512 return EINTR;
513 }
514 }
515
516 if (!oslog_stream_open) {
517 lck_spin_unlock(&oslog_stream_lock);
518 return EBADF;
519 }
520
521 int logpos = 0;
522 oslog_stream_buf_entry_t read_entry = NULL;
523 uint16_t rec_length;
524
525 read_entry = STAILQ_FIRST(&oslog_stream_buf_head);
526 assert(read_entry != NULL);
527 STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
528
529 // Copy the timestamp first
530 memcpy(logline + logpos, &read_entry->timestamp, sizeof(uint64_t));
531 logpos += sizeof(uint64_t);
532
533 switch (read_entry->type) {
534 /* Handle metadata messages */
535 case oslog_stream_link_type_metadata:
536 {
537 memcpy(logline + logpos,
538 (read_entry->metadata), read_entry->size);
539 logpos += read_entry->size;
540
541 lck_spin_unlock(&oslog_stream_lock);
542
543 // Free the list entry
544 kfree(read_entry, (sizeof(struct oslog_stream_buf_entry_s) + read_entry->size));
545 break;
546 }
547 /* Handle log messages */
548 case oslog_stream_link_type_log:
549 {
550 /* ensure that the correct read entry was dequeued */
551 assert(read_entry->offset == oslog_streambufp->msg_bufr);
552 rec_length = read_entry->size;
553
554 // If the next log line is contiguous in the buffer, copy it out.
555 if(read_entry->offset + rec_length <= oslog_streambufp->msg_size) {
556 memcpy(logline + logpos,
557 oslog_streambufp->msg_bufc + read_entry->offset, rec_length);
558
559 oslog_streambufp->msg_bufr += rec_length;
560 if (oslog_streambufp->msg_bufr == oslog_streambufp->msg_size) {
561 oslog_streambufp->msg_bufr = 0;
562 }
563 logpos += rec_length;
564 } else {
565 // Otherwise, copy until the end of the buffer, and
566 // copy the remaining bytes starting at index 0.
567 int bytes_left = oslog_streambufp->msg_size - read_entry->offset;
568 memcpy(logline + logpos,
569 oslog_streambufp->msg_bufc + read_entry->offset, bytes_left);
570 logpos += bytes_left;
571 rec_length -= bytes_left;
572
573 memcpy(logline + logpos, (const void *)oslog_streambufp->msg_bufc,
574 rec_length);
575 oslog_streambufp->msg_bufr = rec_length;
576 logpos += rec_length;
577 }
578 assert(oslog_streambufp->msg_bufr < oslog_streambufp->msg_size);
579 STAILQ_INSERT_TAIL(&oslog_stream_free_head, read_entry, buf_entries);
580
581 lck_spin_unlock(&oslog_stream_lock);
582 break;
583 }
584 default:
585 {
586 panic("Got unexpected log entry type: %hhu\n", read_entry->type);
587 }
588 }
589
590 copy_size = min(logpos, uio_resid(uio));
591 if (copy_size != 0) {
592 error = uiomove((caddr_t)logline, copy_size, uio);
593 }
594 (void)hw_atomic_add(&oslog_s_streamed_msgcount, 1);
595
596 return error;
597}
598
1c79356b
A
599/*ARGSUSED*/
600int
2d21ac55 601logselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
1c79356b 602{
cc8bc92a
A
603 const struct msgbuf *mbp = logsoftc.sc_mbp;
604
1c79356b
A
605 switch (rw) {
606
607 case FREAD:
91447636 608 LOG_LOCK();
cc8bc92a 609 if (mbp->msg_bufr != mbp->msg_bufx) {
91447636 610 LOG_UNLOCK();
1c79356b
A
611 return (1);
612 }
0b4e3aa0 613 selrecord(p, &logsoftc.sc_selp, wql);
91447636 614 LOG_UNLOCK();
1c79356b
A
615 break;
616 }
1c79356b
A
617 return (0);
618}
619
39037602
A
620int
621oslogselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
622{
623 switch (rw) {
624
625 case FREAD:
626 LOG_LOCK();
627 if (os_log_wakeup) {
628 LOG_UNLOCK();
629 return (1);
630 }
631 selrecord(p, &oslogsoftc.sc_selp, wql);
632 LOG_UNLOCK();
633 break;
634 }
635 return (0);
636}
637
638int
639oslog_streamselect(__unused dev_t dev, int rw, void * wql, struct proc *p)
640{
641 int ret = 0;
642
643 lck_spin_lock(&oslog_stream_lock);
644
645 switch (rw) {
646 case FREAD:
647 if (STAILQ_EMPTY(&oslog_stream_buf_head)) {
648 selrecord(p, &oslog_streamsoftc.sc_selp, wql);
649 } else {
650 ret = 1;
651 }
652 break;
653 }
654
655 lck_spin_unlock(&oslog_stream_lock);
656 return ret;
657}
658
1c79356b 659void
cc8bc92a 660logwakeup(struct msgbuf *mbp)
1c79356b 661{
39037602
A
662 /* cf. r24974766 & r25201228*/
663 if (oslog_is_safe() == FALSE) {
664 return;
665 }
666
91447636
A
667 LOG_LOCK();
668 if (!log_open) {
669 LOG_UNLOCK();
1c79356b 670 return;
91447636 671 }
cc8bc92a
A
672 if (NULL == mbp)
673 mbp = logsoftc.sc_mbp;
674 if (mbp != logsoftc.sc_mbp)
675 goto out;
1c79356b
A
676 selwakeup(&logsoftc.sc_selp);
677 if (logsoftc.sc_state & LOG_ASYNC) {
cc8bc92a 678 int pgid = logsoftc.sc_pgid;
1c79356b
A
679 LOG_UNLOCK();
680 if (pgid < 0)
681 gsignal(-pgid, SIGIO);
2d21ac55
A
682 else
683 proc_signal(pgid, SIGIO);
91447636 684 LOG_LOCK();
1c79356b
A
685 }
686 if (logsoftc.sc_state & LOG_RDWAIT) {
cc8bc92a 687 wakeup((caddr_t)mbp);
1c79356b
A
688 logsoftc.sc_state &= ~LOG_RDWAIT;
689 }
cc8bc92a 690out:
91447636 691 LOG_UNLOCK();
1c79356b
A
692}
693
39037602
A
694void
695oslogwakeup(void)
696{
697 LOG_LOCK();
698 if (!oslog_open) {
699 LOG_UNLOCK();
700 return;
701 }
702 selwakeup(&oslogsoftc.sc_selp);
703 os_log_wakeup = 1;
704 LOG_UNLOCK();
705}
706
707static void
708oslog_streamwakeup_locked(void)
709{
5ba3f43e 710 LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
39037602
A
711 if (!oslog_stream_open) {
712 return;
713 }
714 selwakeup(&oslog_streamsoftc.sc_selp);
715 if (oslog_streamsoftc.sc_state & LOG_RDWAIT) {
716 wakeup((caddr_t)oslog_streambufp);
717 oslog_streamsoftc.sc_state &= ~LOG_RDWAIT;
718 }
719}
720
721void
722oslog_streamwakeup(void)
723{
724 /* cf. r24974766 & r25201228*/
725 if (oslog_is_safe() == FALSE) {
726 return;
727 }
728
729 lck_spin_lock(&oslog_stream_lock);
730 oslog_streamwakeup_locked();
731 lck_spin_unlock(&oslog_stream_lock);
732}
1c79356b
A
733
734/*ARGSUSED*/
735int
2d21ac55 736logioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
1c79356b 737{
6d2010ae 738 int l;
cc8bc92a 739 const struct msgbuf *mbp = logsoftc.sc_mbp;
1c79356b 740
2d21ac55 741 LOG_LOCK();
1c79356b
A
742 switch (com) {
743
744 /* return number of characters immediately available */
745 case FIONREAD:
cc8bc92a 746 l = mbp->msg_bufx - mbp->msg_bufr;
1c79356b 747 if (l < 0)
cc8bc92a 748 l += mbp->msg_size;
1c79356b
A
749 *(off_t *)data = l;
750 break;
751
752 case FIONBIO:
753 if (*(int *)data)
754 logsoftc.sc_state |= LOG_NBIO;
755 else
756 logsoftc.sc_state &= ~LOG_NBIO;
757 break;
758
759 case FIOASYNC:
760 if (*(int *)data)
761 logsoftc.sc_state |= LOG_ASYNC;
762 else
763 logsoftc.sc_state &= ~LOG_ASYNC;
764 break;
765
766 case TIOCSPGRP:
1c79356b 767 logsoftc.sc_pgid = *(int *)data;
1c79356b
A
768 break;
769
770 case TIOCGPGRP:
1c79356b 771 *(int *)data = logsoftc.sc_pgid;
1c79356b
A
772 break;
773
774 default:
91447636 775 LOG_UNLOCK();
1c79356b
A
776 return (-1);
777 }
91447636 778 LOG_UNLOCK();
1c79356b
A
779 return (0);
780}
781
39037602
A
782/*ARGSUSED*/
783int
784oslogioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
785{
786 int ret = 0;
813fb2f6 787 mach_vm_size_t buffer_size = (FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT * FIREHOSE_CHUNK_SIZE);
39037602
A
788 firehose_buffer_map_info_t map_info = {0, 0};
789 firehose_buffer_t kernel_firehose_buffer = NULL;
790 mach_vm_address_t user_addr = 0;
791 mach_port_t mem_entry_ptr = MACH_PORT_NULL;
792
793 switch (com) {
794
795 /* return number of characters immediately available */
796
797 case LOGBUFFERMAP:
5ba3f43e 798 kernel_firehose_buffer = (firehose_buffer_t)kernel_firehose_addr;
39037602
A
799
800 ret = mach_make_memory_entry_64(kernel_map,
801 &buffer_size,
802 (mach_vm_offset_t) kernel_firehose_buffer,
803 ( MAP_MEM_VM_SHARE | VM_PROT_READ ),
804 &mem_entry_ptr,
805 MACH_PORT_NULL);
806 if (ret == KERN_SUCCESS) {
5ba3f43e 807 ret = mach_vm_map_kernel(get_task_map(current_task()),
39037602
A
808 &user_addr,
809 buffer_size,
810 0, /* mask */
811 VM_FLAGS_ANYWHERE,
5ba3f43e 812 VM_KERN_MEMORY_NONE,
39037602
A
813 mem_entry_ptr,
814 0, /* offset */
815 FALSE, /* copy */
816 VM_PROT_READ,
817 VM_PROT_READ,
818 VM_INHERIT_SHARE);
819 }
820
821 if (ret == KERN_SUCCESS) {
822 map_info.fbmi_addr = (uint64_t) (user_addr);
823 map_info.fbmi_size = buffer_size;
824 bcopy(&map_info, data, sizeof(firehose_buffer_map_info_t));
825 }
826 break;
827 case LOGFLUSHED:
828 LOG_LOCK();
829 os_log_wakeup = 0;
830 LOG_UNLOCK();
831 __firehose_merge_updates(*(firehose_push_reply_t *)(data));
832 break;
833 default:
834 return (-1);
835 }
836 return (0);
837}
838
839/*ARGSUSED*/
840int
841oslog_streamioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p)
842{
843 int err = 0;
844
845 lck_spin_lock(&oslog_stream_lock);
846
847 switch (com) {
848 case FIONBIO:
849 if (data && *(int *)data)
850 oslog_streamsoftc.sc_state |= LOG_NBIO;
851 else
852 oslog_streamsoftc.sc_state &= ~LOG_NBIO;
853 break;
854 case FIOASYNC:
855 if (data && *(int *)data)
856 oslog_streamsoftc.sc_state |= LOG_ASYNC;
857 else
858 oslog_streamsoftc.sc_state &= ~LOG_ASYNC;
859 break;
860 default:
861 err = -1;
862 break;
863 }
864
865 lck_spin_unlock(&oslog_stream_lock);
866 return err;
867}
868
1c79356b 869void
2d21ac55 870bsd_log_init(void)
1c79356b 871{
6d2010ae 872 /* After this point, we must be ready to accept characters */
1c79356b
A
873}
874
39037602
A
875void
876oslog_init(void)
877{
878 kern_return_t kr;
813fb2f6 879 vm_size_t size = FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT * FIREHOSE_CHUNK_SIZE;
39037602
A
880
881 oslog_lock_init();
882
883 kr = kmem_alloc_flags(kernel_map, &kernel_firehose_addr,
884 size + (2 * PAGE_SIZE), VM_KERN_MEMORY_LOG,
885 KMA_GUARD_FIRST | KMA_GUARD_LAST);
886 if (kr != KERN_SUCCESS) {
887 panic("Failed to allocate memory for firehose logging buffer");
888 }
889 kernel_firehose_addr += PAGE_SIZE;
5ba3f43e 890 bzero((void *)kernel_firehose_addr, size);
39037602 891 /* register buffer with firehose */
5ba3f43e 892 kernel_firehose_addr = (vm_offset_t)__firehose_buffer_create((size_t *) &size);
39037602
A
893
894 kprintf("oslog_init completed\n");
895}
2d21ac55
A
896
897/*
898 * log_putc_locked
899 *
900 * Decription: Output a character to the log; assumes the LOG_LOCK() is held
901 * by the caller.
902 *
903 * Parameters: c Character to output
904 *
905 * Returns: (void)
906 *
907 * Notes: This functions is used for multibyte output to the log; it
908 * should be used preferrentially where possible to ensure that
909 * log entries do not end up interspersed due to preemption or
910 * SMP reentrancy.
911 */
1c79356b 912void
cc8bc92a 913log_putc_locked(struct msgbuf *mbp, char c)
1c79356b 914{
1c79356b 915 mbp->msg_bufc[mbp->msg_bufx++] = c;
cc8bc92a 916 if (mbp->msg_bufx >= mbp->msg_size)
1c79356b 917 mbp->msg_bufx = 0;
2d21ac55
A
918}
919
39037602
A
920static oslog_stream_buf_entry_t
921oslog_stream_find_free_buf_entry_locked(void)
922{
923 struct msgbuf *mbp;
924 oslog_stream_buf_entry_t buf_entry = NULL;
925
5ba3f43e 926 LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
39037602
A
927
928 mbp = oslog_streambufp;
929
930 buf_entry = STAILQ_FIRST(&oslog_stream_free_head);
931 if (buf_entry) {
932 STAILQ_REMOVE_HEAD(&oslog_stream_free_head, buf_entries);
933 }
934 else {
935 // If no list elements are available in the free-list,
936 // consume the next log line so we can free up its list element
937 oslog_stream_buf_entry_t prev_entry = NULL;
938
939 buf_entry = STAILQ_FIRST(&oslog_stream_buf_head);
940 while (buf_entry->type == oslog_stream_link_type_metadata) {
941 prev_entry = buf_entry;
942 buf_entry = STAILQ_NEXT(buf_entry, buf_entries);
943 }
944
945 if (prev_entry == NULL) {
946 STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
947 }
948 else {
949 STAILQ_REMOVE_AFTER(&oslog_stream_buf_head, prev_entry, buf_entries);
950 }
951
952 mbp->msg_bufr += buf_entry->size;
953 oslog_s_dropped_msgcount++;
954 if (mbp->msg_bufr >= mbp->msg_size) {
955 mbp->msg_bufr = (mbp->msg_bufr % mbp->msg_size);
956 }
957 }
958
959 return buf_entry;
960}
961
962void
963oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry)
964{
5ba3f43e 965 LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
39037602
A
966 STAILQ_INSERT_TAIL(&oslog_stream_buf_head, m_entry, buf_entries);
967
968 return;
969}
970
cc8bc92a
A
971static void
972oslog_streamwrite_append_bytes(const char *buffer, int buflen)
39037602
A
973{
974 struct msgbuf *mbp;
975
5ba3f43e 976 LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
39037602
A
977
978 mbp = oslog_streambufp;
979 // Check if we have enough space in the stream buffer to write the data
980 if (mbp->msg_bufx + buflen <= mbp->msg_size) {
981 memcpy((void *)(mbp->msg_bufc + mbp->msg_bufx), buffer, buflen);
982
983 mbp->msg_bufx += buflen;
984 if (mbp->msg_bufx == mbp->msg_size) {
985 mbp->msg_bufx = 0;
986 }
987 } else {
988 // Copy part of the data until the end of the stream
989 int bytes_left = mbp->msg_size - mbp->msg_bufx;
990 memcpy((void *)(mbp->msg_bufc + mbp->msg_bufx), buffer, bytes_left);
991
992 buflen -= bytes_left;
993 buffer += bytes_left;
994
995 // Copy the remainder of the data from the beginning of stream
996 memcpy((void *)mbp->msg_bufc, buffer, buflen);
997 mbp->msg_bufx = buflen;
998 }
999 return;
1000}
1001
1002
1003void
1004oslog_streamwrite_locked(firehose_tracepoint_id_u ftid,
1005 uint64_t stamp, const void *pubdata, size_t publen)
1006{
1007 struct msgbuf *mbp;
1008 int available_space = 0;
1009 oslog_stream_buf_entry_t buf_entry = NULL;
1010 oslog_stream_buf_entry_t next_entry = NULL;
1011
1012 uint16_t ft_size = offsetof(struct firehose_tracepoint_s, ft_data);
1013 int ft_length = ft_size + publen;
1014
5ba3f43e 1015 LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED);
39037602
A
1016
1017 mbp = oslog_streambufp;
1018 if (ft_length > mbp->msg_size) {
1019 (void)hw_atomic_add(&oslog_s_error_count, 1);
1020 return;
1021 }
1022
1023 // Ensure that we have a list element for this record
1024 buf_entry = oslog_stream_find_free_buf_entry_locked();
1025
1026 assert(buf_entry != NULL);
1027
1028 // Ensure that we have space in the ring buffer for the current logline
1029 if (mbp->msg_bufr > mbp->msg_bufx) {
1030 available_space = mbp->msg_bufr - mbp->msg_bufx;
1031 } else {
1032 available_space = mbp->msg_size - mbp->msg_bufx + mbp->msg_bufr;
1033 }
1034 while(ft_length > available_space) {
1035 oslog_stream_buf_entry_t prev_entry = NULL;
1036
1037 next_entry = STAILQ_FIRST(&oslog_stream_buf_head);
1038 assert(next_entry != NULL);
1039 while (next_entry->type == oslog_stream_link_type_metadata) {
1040 prev_entry = next_entry;
1041 next_entry = STAILQ_NEXT(next_entry, buf_entries);
1042 }
1043
1044 if (prev_entry == NULL) {
1045 STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries);
1046 }
1047 else {
1048 STAILQ_REMOVE_AFTER(&oslog_stream_buf_head, prev_entry, buf_entries);
1049 }
1050
1051 mbp->msg_bufr += next_entry->size;
1052 if (mbp->msg_bufr >= mbp->msg_size) {
1053 mbp->msg_bufr = (mbp->msg_bufr % mbp->msg_size);
1054 }
1055
1056 oslog_s_dropped_msgcount++;
1057 available_space += next_entry->size;
1058
1059 STAILQ_INSERT_TAIL(&oslog_stream_free_head, next_entry, buf_entries);
1060 }
1061
1062 assert(ft_length <= available_space);
1063
1064 // Write the log line and update the list entry for this record
1065 buf_entry->offset = mbp->msg_bufx;
1066 buf_entry->size = ft_length;
1067 buf_entry->timestamp = stamp;
1068 buf_entry->type = oslog_stream_link_type_log;
1069
1070 // Construct a tracepoint
1071 struct firehose_tracepoint_s fs = {
1072 .ft_thread = thread_tid(current_thread()),
1073 .ft_id.ftid_value = ftid.ftid_value,
1074 .ft_length = publen
1075 };
1076
1077 oslog_streamwrite_append_bytes((char *)&fs, sizeof(fs));
1078 oslog_streamwrite_append_bytes(pubdata, publen);
1079
1080 assert(mbp->msg_bufr < mbp->msg_size);
1081 // Insert the element to the buffer data list
1082 STAILQ_INSERT_TAIL(&oslog_stream_buf_head, buf_entry, buf_entries);
1083
1084 return;
1085}
1086
1087
2d21ac55
A
1088
1089/*
1090 * log_putc
1091 *
1092 * Decription: Output a character to the log; assumes the LOG_LOCK() is NOT
1093 * held by the caller.
1094 *
1095 * Parameters: c Character to output
1096 *
1097 * Returns: (void)
1098 *
cc8bc92a 1099 * Notes: This function is used for single byte output to the log. It
2d21ac55
A
1100 * primarily exists to maintain binary backward compatibility.
1101 */
1102void
1103log_putc(char c)
1104{
39236c6e 1105 int unread_count = 0;
2d21ac55 1106 LOG_LOCK();
cc8bc92a 1107 log_putc_locked(msgbufp, c);
39236c6e 1108 unread_count = msgbufp->msg_bufx - msgbufp->msg_bufr;
91447636 1109 LOG_UNLOCK();
39236c6e
A
1110
1111 if (unread_count < 0)
1112 unread_count = 0 - unread_count;
cc8bc92a
A
1113 if (c == '\n' || unread_count >= (msgbufp->msg_size / 2))
1114 logwakeup(msgbufp);
1c79356b 1115}
91447636 1116
2d21ac55
A
1117
1118/*
1119 * it is possible to increase the kernel log buffer size by adding
1120 * msgbuf=n
1121 * to the kernel command line, and to read the current size using
1122 * sysctl kern.msgbuf
1123 * If there is no parameter on the kernel command line, the buffer is
6d2010ae
A
1124 * allocated statically and is CONFIG_MSG_BSIZE characters in size, otherwise
1125 * memory is dynamically allocated. Memory management must already be up.
2d21ac55 1126 */
6d2010ae 1127int
cc8bc92a
A
1128log_setsize(int size)
1129{
2d21ac55 1130 char *new_logdata;
6d2010ae
A
1131 int new_logsize, new_bufr, new_bufx;
1132 char *old_logdata;
1133 int old_logsize, old_bufr, old_bufx;
1134 int i, count;
1135 char *p, ch;
1136
1137 if (size > MAX_MSG_BSIZE)
1138 return (EINVAL);
1139
1140 if (size <= 0)
1141 return (EINVAL);
1142
1143 new_logsize = size;
2d21ac55
A
1144 if (!(new_logdata = (char*)kalloc(size))) {
1145 printf("log_setsize: unable to allocate memory\n");
6d2010ae 1146 return (ENOMEM);
2d21ac55 1147 }
6d2010ae
A
1148 bzero(new_logdata, new_logsize);
1149
2d21ac55 1150 LOG_LOCK();
6d2010ae
A
1151
1152 old_logsize = msgbufp->msg_size;
1153 old_logdata = msgbufp->msg_bufc;
1154 old_bufr = msgbufp->msg_bufr;
1155 old_bufx = msgbufp->msg_bufx;
1156
1157 LOG_SETSIZE_DEBUG("log_setsize(%d): old_logdata %p old_logsize %d old_bufr %d old_bufx %d\n",
1158 size, old_logdata, old_logsize, old_bufr, old_bufx);
1159
1160 /* start "new_logsize" bytes before the write pointer */
1161 if (new_logsize <= old_bufx) {
1162 count = new_logsize;
1163 p = old_logdata + old_bufx - count;
1164 } else {
1165 /*
1166 * if new buffer is bigger, copy what we have and let the
1167 * bzero above handle the difference
1168 */
1169 count = MIN(new_logsize, old_logsize);
1170 p = old_logdata + old_logsize - (count - old_bufx);
1171 }
1172 for (i = 0; i < count; i++) {
1173 if (p >= old_logdata + old_logsize)
1174 p = old_logdata;
1175
1176 ch = *p++;
1177 new_logdata[i] = ch;
1178 }
1179
1180 new_bufx = i;
1181 if (new_bufx >= new_logsize)
1182 new_bufx = 0;
1183 msgbufp->msg_bufx = new_bufx;
1184
1185 new_bufr = old_bufx - old_bufr; /* how much were we trailing bufx by? */
1186 if (new_bufr < 0)
1187 new_bufr += old_logsize;
1188 new_bufr = new_bufx - new_bufr; /* now relative to oldest data in new buffer */
1189 if (new_bufr < 0)
1190 new_bufr += new_logsize;
1191 msgbufp->msg_bufr = new_bufr;
1192
1193 msgbufp->msg_size = new_logsize;
1194 msgbufp->msg_bufc = new_logdata;
1195
1196 LOG_SETSIZE_DEBUG("log_setsize(%d): new_logdata %p new_logsize %d new_bufr %d new_bufx %d\n",
1197 size, new_logdata, new_logsize, new_bufr, new_bufx);
1198
1199 LOG_UNLOCK();
1200
2d21ac55
A
1201 /* this memory is now dead - clear it so that it compresses better
1202 in case of suspend to disk etc. */
6d2010ae
A
1203 bzero(old_logdata, old_logsize);
1204 if (old_logdata != smsg_bufc) {
1205 /* dynamic memory that must be freed */
1206 kfree(old_logdata, old_logsize);
1207 }
1208
1209 printf("set system log size to %d bytes\n", new_logsize);
1210
1211 return 0;
1212}
1213
39037602
A
1214void oslog_setsize(int size)
1215{
1216 uint16_t scale = 0;
1217 // If the size is less than the default stream buffer
1218 // do nothing
1219 if (size <= OSLOG_STREAM_BUF_SIZE) {
1220 return;
1221 }
1222
1223 scale = (uint16_t) (size / OSLOG_STREAM_BUF_SIZE);
1224
1225 oslog_stream_buf_size = size;
1226 oslog_stream_num_entries = scale * OSLOG_NUM_STREAM_ENTRIES;
1227 printf("oslog_setsize: new buffer size = %d, new num entries= %d\n", oslog_stream_buf_size, oslog_stream_num_entries);
1228}
1229
cc8bc92a
A
1230SYSCTL_PROC(_kern, OID_AUTO, msgbuf,
1231 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED, 0, 0,
1232 sysctl_kern_msgbuf, "I", "");
6d2010ae 1233
cc8bc92a
A
1234static int
1235sysctl_kern_msgbuf(struct sysctl_oid *oidp __unused,
1236 void *arg1 __unused, int arg2 __unused, struct sysctl_req *req)
6d2010ae
A
1237{
1238 int old_bufsize, bufsize;
1239 int error;
1240
1241 LOG_LOCK();
1242 old_bufsize = bufsize = msgbufp->msg_size;
2d21ac55 1243 LOG_UNLOCK();
6d2010ae
A
1244
1245 error = sysctl_io_number(req, bufsize, sizeof(bufsize), &bufsize, NULL);
1246 if (error)
1247 return (error);
1248
1249 if (bufsize != old_bufsize) {
1250 error = log_setsize(bufsize);
1251 }
1252
1253 return (error);
2d21ac55
A
1254}
1255
2d21ac55
A
1256
1257/*
6d2010ae 1258 * This should be called by /sbin/dmesg only via libproc.
2d21ac55
A
1259 * It returns as much data still in the buffer as possible.
1260 */
1261int
cc8bc92a
A
1262log_dmesg(user_addr_t buffer, uint32_t buffersize, int32_t * retval)
1263{
b0d623f7 1264 uint32_t i;
6d2010ae 1265 uint32_t localbuff_size;
2d21ac55
A
1266 int error = 0, newl, skip;
1267 char *localbuff, *p, *copystart, ch;
6d2010ae 1268 size_t copysize;
2d21ac55 1269
6d2010ae
A
1270 LOG_LOCK();
1271 localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */
1272 LOG_UNLOCK();
1273
1274 /* Allocate a temporary non-circular buffer for copyout */
2d21ac55
A
1275 if (!(localbuff = (char *)kalloc(localbuff_size))) {
1276 printf("log_dmesg: unable to allocate memory\n");
1277 return (ENOMEM);
1278 }
6d2010ae 1279
2d21ac55
A
1280 /* in between here, the log could become bigger, but that's fine */
1281 LOG_LOCK();
1282
1283 /*
1284 * The message buffer is circular; start at the write pointer, and
1285 * make one loop up to write pointer - 1.
1286 */
1287 p = msgbufp->msg_bufc + msgbufp->msg_bufx;
1288 for (i = newl = skip = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) {
1289 if (p >= msgbufp->msg_bufc + msgbufp->msg_size)
1290 p = msgbufp->msg_bufc;
1291 ch = *p;
1292 /* Skip "\n<.*>" syslog sequences. */
1293 if (skip) {
1294 if (ch == '>')
1295 newl = skip = 0;
1296 continue;
1297 }
1298 if (newl && ch == '<') {
1299 skip = 1;
1300 continue;
1301 }
1302 if (ch == '\0')
1303 continue;
6d2010ae 1304 newl = (ch == '\n');
2d21ac55 1305 localbuff[i++] = ch;
593a1d5f
A
1306 /* The original version of this routine contained a buffer
1307 * overflow. At the time, a "small" targeted fix was desired
1308 * so the change below to check the buffer bounds was made.
1309 * TODO: rewrite this needlessly convoluted routine.
1310 */
1311 if (i == (localbuff_size - 2))
1312 break;
2d21ac55
A
1313 }
1314 if (!newl)
1315 localbuff[i++] = '\n';
1316 localbuff[i++] = 0;
1317
1318 if (buffersize >= i) {
1319 copystart = localbuff;
1320 copysize = i;
1321 } else {
1322 copystart = localbuff + i - buffersize;
1323 copysize = buffersize;
1324 }
1325
1326 LOG_UNLOCK();
1327
1328 error = copyout(copystart, buffer, copysize);
1329 if (!error)
1330 *retval = copysize;
1331
1332 kfree(localbuff, localbuff_size);
1333 return (error);
1334}
39037602 1335