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