2  * Copyright (c) 2000-2016 Apple, Inc. All rights reserved. 
   4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 
   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. 
  15  * Please obtain a copy of the License at 
  16  * http://www.opensource.apple.com/apsl/ and read it before using this file. 
  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. 
  26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 
  28 /* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */ 
  30  * Copyright (c) 1982, 1986, 1993 
  31  *      The Regents of the University of California.  All rights reserved. 
  33  * Redistribution and use in source and binary forms, with or without 
  34  * modification, are permitted provided that the following conditions 
  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. 
  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 
  61  *      @(#)subr_log.c  8.3 (Berkeley) 2/14/95 
  65  * Error log buffer for kernel printf's. 
  68 #include <sys/param.h> 
  69 #include <sys/systm.h> 
  70 #include <sys/proc_internal.h> 
  71 #include <sys/vnode.h> 
  73 #include <firehose/tracepoint_private.h> 
  74 #include <firehose/chunk_private.h> 
  75 #include <firehose/ioctl_private.h> 
  76 #include <os/firehose_buffer_private.h> 
  78 #include <os/log_private.h> 
  79 #include <sys/ioctl.h> 
  80 #include <sys/msgbuf.h> 
  81 #include <sys/file_internal.h> 
  82 #include <sys/errno.h> 
  83 #include <sys/select.h> 
  84 #include <sys/kernel.h> 
  85 #include <kern/thread.h> 
  86 #include <kern/sched_prim.h> 
  87 #include <kern/simple_lock.h> 
  89 #include <sys/signalvar.h> 
  91 #include <sys/sysctl.h> 
  92 #include <sys/queue.h> 
  93 #include <kern/kalloc.h> 
  94 #include <pexpert/pexpert.h> 
  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> 
 102 /* XXX should be in a common header somewhere */ 
 103 extern void logwakeup(struct msgbuf 
*); 
 104 extern void oslogwakeup(void); 
 105 extern void oslog_streamwakeup(void); 
 106 static void oslog_streamwakeup_locked(void); 
 107 vm_offset_t kernel_firehose_addr 
= 0; 
 109 /* log message counters for streaming mode */ 
 110 uint32_t oslog_s_streamed_msgcount 
= 0; 
 111 uint32_t oslog_s_dropped_msgcount  
= 0; 
 112 extern uint32_t oslog_s_error_count
; 
 114 #define LOG_RDPRI       (PZERO + 1) 
 116 #define LOG_NBIO        0x02 
 117 #define LOG_ASYNC       0x04 
 118 #define LOG_RDWAIT      0x08 
 120 /* All globals should be accessed under LOG_LOCK() */ 
 122 static char amsg_bufc
[1024]; 
 123 static struct msgbuf aslbuf 
= {MSG_MAGIC
, sizeof (amsg_bufc
), 0, 0, amsg_bufc
}; 
 124 struct msgbuf 
*aslbufp 
__attribute__((used
)) = &aslbuf
; 
 126 /* logsoftc only valid while log_open=1 */ 
 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 */ 
 131         struct msgbuf 
*sc_mbp
; 
 135 char smsg_bufc
[CONFIG_MSG_BSIZE
]; /* static buffer */ 
 136 char oslog_stream_bufc
[FIREHOSE_CHUNK_SIZE
]; /* static buffer */ 
 137 struct firehose_chunk_s oslog_boot_buf 
= { 
 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 
 145 }; /* static buffer */ 
 146 firehose_chunk_t firehose_boot_chunk 
= &oslog_boot_buf
; 
 147 struct msgbuf msgbuf 
= {MSG_MAGIC
, sizeof(smsg_bufc
), 0, 0, smsg_bufc
}; 
 148 struct msgbuf oslog_stream_buf 
= {MSG_MAGIC
, 0, 0, 0, NULL
}; 
 149 struct msgbuf 
*msgbufp 
__attribute__((used
)) = &msgbuf
; 
 150 struct msgbuf 
*oslog_streambufp 
__attribute__((used
)) = &oslog_stream_buf
; 
 152 // List entries for keeping track of the streaming buffer 
 153 static oslog_stream_buf_entry_t oslog_stream_buf_entries
; 
 155 #define OSLOG_NUM_STREAM_ENTRIES        64 
 156 #define OSLOG_STREAM_BUF_SIZE           4096 
 159 int     os_log_wakeup 
= 0; 
 160 int     oslog_stream_open 
= 0; 
 161 int     oslog_stream_buf_size 
= OSLOG_STREAM_BUF_SIZE
; 
 162 int     oslog_stream_num_entries 
= OSLOG_NUM_STREAM_ENTRIES
; 
 164 /* oslogsoftc only valid while oslog_open=1 */ 
 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 */ 
 171 struct 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 */ 
 177 STAILQ_HEAD(, oslog_stream_buf_entry_s
) oslog_stream_free_head 
= 
 178                 STAILQ_HEAD_INITIALIZER(oslog_stream_free_head
); 
 179 STAILQ_HEAD(, oslog_stream_buf_entry_s
) oslog_stream_buf_head 
= 
 180                 STAILQ_HEAD_INITIALIZER(oslog_stream_buf_head
); 
 182 /* defined in osfmk/kern/printf.c  */ 
 183 extern void oslog_lock_init(void); 
 184 extern void bsd_log_lock(void); 
 185 extern void bsd_log_unlock(void); 
 187 /* defined for osfmk/kern/printf.c */ 
 188 void bsd_log_init(void); 
 191  * Ideally this file would define this lock, but bsd doesn't have the definition 
 194 decl_lck_spin_data(extern, oslog_stream_lock
) 
 196 /* XXX wants a linker set so these can be static */ 
 197 extern d_open_t         logopen
; 
 198 extern d_close_t        logclose
; 
 199 extern d_read_t         logread
; 
 200 extern d_ioctl_t        logioctl
; 
 201 extern d_select_t       logselect
; 
 203 /* XXX wants a linker set so these can be static */ 
 204 extern d_open_t         oslogopen
; 
 205 extern d_close_t        oslogclose
; 
 206 extern d_select_t       oslogselect
; 
 207 extern d_ioctl_t        oslogioctl
; 
 209 /* XXX wants a linker set so these can be static */ 
 210 extern d_open_t         oslog_streamopen
; 
 211 extern d_close_t        oslog_streamclose
; 
 212 extern d_read_t         oslog_streamread
; 
 213 extern d_ioctl_t        oslog_streamioctl
; 
 214 extern d_select_t       oslog_streamselect
; 
 216 void oslog_init(void); 
 217 void oslog_setsize(int size
); 
 218 void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid
, 
 219                 uint64_t stamp
, const void *pubdata
, size_t publen
); 
 220 void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry
); 
 221 static oslog_stream_buf_entry_t 
oslog_stream_find_free_buf_entry_locked(void); 
 222 static void oslog_streamwrite_append_bytes(const char *buffer
, int buflen
); 
 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. 
 230 #define LOG_LOCK() bsd_log_lock() 
 231 #define LOG_UNLOCK() bsd_log_unlock() 
 234 #define LOG_SETSIZE_DEBUG(x...) kprintf(x) 
 236 #define LOG_SETSIZE_DEBUG(x...) do { } while(0) 
 239 static int sysctl_kern_msgbuf(struct sysctl_oid 
*oidp
, 
 240         void *arg1
, int arg2
, struct sysctl_req 
*req
); 
 244 logopen(__unused dev_t dev
, __unused 
int flags
, __unused 
int mode
, struct proc 
*p
) 
 251         if (atm_get_diagnostic_config() & ATM_ENABLE_LEGACY_LOGGING
) { 
 252                 logsoftc
.sc_mbp 
= msgbufp
; 
 255                  * Support for messagetracer (kern_asl_msg()) 
 256                  * In this mode, /dev/klog exports only ASL-formatted messages 
 257                  * written into aslbufp via vaddlog(). 
 259                 logsoftc
.sc_mbp 
= aslbufp
; 
 261         logsoftc
.sc_pgid 
= p
->p_pid
;            /* signal process only */ 
 271 logclose(__unused dev_t dev
, __unused 
int flag
, __unused 
int devtype
, __unused 
struct proc 
*p
) 
 274         logsoftc
.sc_state 
&= ~(LOG_NBIO 
| LOG_ASYNC
); 
 275         selwakeup(&logsoftc
.sc_selp
); 
 276         selthreadclear(&logsoftc
.sc_selp
); 
 284 oslogopen(__unused dev_t dev
, __unused 
int flags
, __unused 
int mode
, struct proc 
*p
) 
 291         oslogsoftc
.sc_pgid 
= p
->p_pid
;          /* signal process only */ 
 299 oslogclose(__unused dev_t dev
, __unused 
int flag
, __unused 
int devtype
, __unused 
struct proc 
*p
) 
 302         oslogsoftc
.sc_state 
&= ~(LOG_NBIO 
| LOG_ASYNC
); 
 303         selwakeup(&oslogsoftc
.sc_selp
); 
 304         selthreadclear(&oslogsoftc
.sc_selp
); 
 311 oslog_streamopen(__unused dev_t dev
, __unused 
int flags
, __unused 
int mode
, struct proc 
*p
) 
 313         char *oslog_stream_msg_bufc 
= NULL
; 
 314         oslog_stream_buf_entry_t entries 
= NULL
; 
 316         lck_spin_lock(&oslog_stream_lock
); 
 317         if (oslog_stream_open
) { 
 318                 lck_spin_unlock(&oslog_stream_lock
); 
 321         lck_spin_unlock(&oslog_stream_lock
); 
 323         // Allocate the stream buffer 
 324         oslog_stream_msg_bufc 
= kalloc(oslog_stream_buf_size
); 
 325         if (!oslog_stream_msg_bufc
) { 
 329         /* entries to support kernel logging in stream mode */ 
 330         entries 
= kalloc(oslog_stream_num_entries 
* sizeof(struct oslog_stream_buf_entry_s
)); 
 332                 kfree(oslog_stream_msg_bufc
, oslog_stream_buf_size
); 
 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
)); 
 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
; 
 348         oslog_stream_buf_entries 
= entries
; 
 350         STAILQ_INIT(&oslog_stream_free_head
); 
 351         STAILQ_INIT(&oslog_stream_buf_head
); 
 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
); 
 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); 
 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
); 
 376 oslog_streamclose(__unused dev_t dev
, __unused 
int flag
, __unused 
int devtype
, __unused 
struct proc 
*p
) 
 378         oslog_stream_buf_entry_t next_entry 
= NULL
; 
 379         char *oslog_stream_msg_bufc 
= NULL
; 
 380         oslog_stream_buf_entry_t entries 
= NULL
; 
 382         lck_spin_lock(&oslog_stream_lock
); 
 384         if (oslog_stream_open 
== 0) { 
 385                 lck_spin_unlock(&oslog_stream_lock
); 
 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
); 
 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; 
 407         lck_spin_unlock(&oslog_stream_lock
); 
 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
)); 
 419 logread(__unused dev_t dev
, struct uio 
*uio
, int flag
) 
 423         struct msgbuf 
*mbp 
= logsoftc
.sc_mbp
; 
 426         while (mbp
->msg_bufr 
== mbp
->msg_bufx
) { 
 427                 if (flag 
& IO_NDELAY
) { 
 431                 if (logsoftc
.sc_state 
& LOG_NBIO
) { 
 435                 logsoftc
.sc_state 
|= LOG_RDWAIT
; 
 438                  * If the wakeup is missed  
 439                  * then wait for 5 sec and reevaluate  
 441                 if ((error 
= tsleep((caddr_t
)mbp
, LOG_RDPRI 
| PCATCH
, 
 442                                 "klog", 5 * hz
)) != 0) { 
 443                         /* if it times out; ignore */ 
 444                         if (error 
!= EWOULDBLOCK
) 
 449         logsoftc
.sc_state 
&= ~LOG_RDWAIT
; 
 451         while (uio_resid(uio
) > 0) { 
 454                 l 
= mbp
->msg_bufx 
- mbp
->msg_bufr
; 
 456                         l 
= mbp
->msg_size 
- mbp
->msg_bufr
; 
 457                 l 
= min(l
, uio_resid(uio
)); 
 461                 readpos 
= mbp
->msg_bufr
; 
 463                 error 
= uiomove((caddr_t
)&mbp
->msg_bufc
[readpos
], l
, uio
); 
 467                 mbp
->msg_bufr 
= readpos 
+ l
; 
 468                 if (mbp
->msg_bufr 
>= mbp
->msg_size
) 
 478 oslog_streamread(__unused dev_t dev
, struct uio 
*uio
, int flag
) 
 482         static char logline
[FIREHOSE_CHUNK_SIZE
]; 
 484         lck_spin_lock(&oslog_stream_lock
); 
 486         if (!oslog_stream_open
) { 
 487                 lck_spin_unlock(&oslog_stream_lock
); 
 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
); 
 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
); 
 507                         case THREAD_AWAKENED
: 
 508                         case THREAD_TIMED_OUT
: 
 511                                 lck_spin_unlock(&oslog_stream_lock
); 
 516         if (!oslog_stream_open
) { 
 517                 lck_spin_unlock(&oslog_stream_lock
); 
 522         oslog_stream_buf_entry_t read_entry 
= NULL
; 
 525         read_entry 
= STAILQ_FIRST(&oslog_stream_buf_head
); 
 526         assert(read_entry 
!= NULL
); 
 527         STAILQ_REMOVE_HEAD(&oslog_stream_buf_head
, buf_entries
); 
 529         // Copy the timestamp first 
 530         memcpy(logline 
+ logpos
, &read_entry
->timestamp
, sizeof(uint64_t)); 
 531         logpos 
+= sizeof(uint64_t); 
 533         switch (read_entry
->type
) { 
 534                 /* Handle metadata messages */ 
 535                 case oslog_stream_link_type_metadata
: 
 537                         memcpy(logline 
+ logpos
, 
 538                                 (read_entry
->metadata
), read_entry
->size
); 
 539                         logpos 
+= read_entry
->size
; 
 541                         lck_spin_unlock(&oslog_stream_lock
); 
 543                         // Free the list entry 
 544                         kfree(read_entry
, (sizeof(struct oslog_stream_buf_entry_s
) + read_entry
->size
)); 
 547                 /* Handle log messages */ 
 548                 case oslog_stream_link_type_log
: 
 550                         /* ensure that the correct read entry was dequeued */ 
 551                         assert(read_entry
->offset 
== oslog_streambufp
->msg_bufr
); 
 552                         rec_length 
= read_entry
->size
; 
 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
); 
 559                                 oslog_streambufp
->msg_bufr 
+= rec_length
; 
 560                                 if (oslog_streambufp
->msg_bufr 
== oslog_streambufp
->msg_size
) { 
 561                                         oslog_streambufp
->msg_bufr 
= 0; 
 563                                 logpos 
+= rec_length
; 
 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
; 
 573                                 memcpy(logline 
+ logpos
, (const void *)oslog_streambufp
->msg_bufc
, 
 575                                 oslog_streambufp
->msg_bufr 
= rec_length
; 
 576                                 logpos 
+= rec_length
; 
 578                         assert(oslog_streambufp
->msg_bufr 
< oslog_streambufp
->msg_size
); 
 579                         STAILQ_INSERT_TAIL(&oslog_stream_free_head
, read_entry
, buf_entries
); 
 581                         lck_spin_unlock(&oslog_stream_lock
); 
 586                         panic("Got unexpected log entry type: %hhu\n", read_entry
->type
); 
 590         copy_size 
= min(logpos
, uio_resid(uio
)); 
 591         if (copy_size 
!= 0) { 
 592                 error 
= uiomove((caddr_t
)logline
, copy_size
, uio
); 
 594         (void)hw_atomic_add(&oslog_s_streamed_msgcount
, 1); 
 601 logselect(__unused dev_t dev
, int rw
, void * wql
, struct proc 
*p
) 
 603         const struct msgbuf 
*mbp 
= logsoftc
.sc_mbp
; 
 609                 if (mbp
->msg_bufr 
!= mbp
->msg_bufx
) { 
 613                 selrecord(p
, &logsoftc
.sc_selp
, wql
); 
 621 oslogselect(__unused dev_t dev
, int rw
, void * wql
, struct proc 
*p
) 
 631                 selrecord(p
, &oslogsoftc
.sc_selp
, wql
); 
 639 oslog_streamselect(__unused dev_t dev
, int rw
, void * wql
, struct proc 
*p
) 
 643         lck_spin_lock(&oslog_stream_lock
); 
 647                 if (STAILQ_EMPTY(&oslog_stream_buf_head
)) { 
 648                         selrecord(p
, &oslog_streamsoftc
.sc_selp
, wql
); 
 655         lck_spin_unlock(&oslog_stream_lock
); 
 660 logwakeup(struct msgbuf 
*mbp
) 
 662         /* cf. r24974766 & r25201228*/ 
 663         if (oslog_is_safe() == FALSE
) { 
 673                 mbp 
= logsoftc
.sc_mbp
; 
 674         if (mbp 
!= logsoftc
.sc_mbp
) 
 676         selwakeup(&logsoftc
.sc_selp
); 
 677         if (logsoftc
.sc_state 
& LOG_ASYNC
) { 
 678                 int pgid 
= logsoftc
.sc_pgid
; 
 681                         gsignal(-pgid
, SIGIO
);  
 683                         proc_signal(pgid
, SIGIO
); 
 686         if (logsoftc
.sc_state 
& LOG_RDWAIT
) { 
 687                 wakeup((caddr_t
)mbp
); 
 688                 logsoftc
.sc_state 
&= ~LOG_RDWAIT
; 
 702         selwakeup(&oslogsoftc
.sc_selp
); 
 708 oslog_streamwakeup_locked(void) 
 710         LCK_SPIN_ASSERT(&oslog_stream_lock
, LCK_ASSERT_OWNED
); 
 711         if (!oslog_stream_open
) { 
 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
; 
 722 oslog_streamwakeup(void) 
 724         /* cf. r24974766 & r25201228*/ 
 725         if (oslog_is_safe() == FALSE
) { 
 729         lck_spin_lock(&oslog_stream_lock
); 
 730         oslog_streamwakeup_locked(); 
 731         lck_spin_unlock(&oslog_stream_lock
); 
 736 logioctl(__unused dev_t dev
, u_long com
, caddr_t data
, __unused 
int flag
, __unused 
struct proc 
*p
) 
 739         const struct msgbuf 
*mbp 
= logsoftc
.sc_mbp
; 
 744         /* return number of characters immediately available */ 
 746                 l 
= mbp
->msg_bufx 
- mbp
->msg_bufr
; 
 754                         logsoftc
.sc_state 
|= LOG_NBIO
; 
 756                         logsoftc
.sc_state 
&= ~LOG_NBIO
; 
 761                         logsoftc
.sc_state 
|= LOG_ASYNC
; 
 763                         logsoftc
.sc_state 
&= ~LOG_ASYNC
; 
 767                 logsoftc
.sc_pgid 
= *(int *)data
; 
 771                 *(int *)data 
= logsoftc
.sc_pgid
; 
 784 oslogioctl(__unused dev_t dev
, u_long com
, caddr_t data
, __unused 
int flag
, __unused 
struct proc 
*p
) 
 787         mach_vm_size_t buffer_size 
= (FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT 
* FIREHOSE_CHUNK_SIZE
); 
 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
; 
 795         /* return number of characters immediately available */ 
 798                 kernel_firehose_buffer 
= (firehose_buffer_t
)kernel_firehose_addr
; 
 800                 ret 
= mach_make_memory_entry_64(kernel_map
, 
 802                                                 (mach_vm_offset_t
) kernel_firehose_buffer
, 
 803                                                 ( MAP_MEM_VM_SHARE 
| VM_PROT_READ 
), 
 806                 if (ret 
== KERN_SUCCESS
) { 
 807                         ret 
= mach_vm_map_kernel(get_task_map(current_task()), 
 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
)); 
 831                 __firehose_merge_updates(*(firehose_push_reply_t 
*)(data
)); 
 841 oslog_streamioctl(__unused dev_t dev
, u_long com
, caddr_t data
, __unused 
int flag
, __unused 
struct proc 
*p
) 
 845         lck_spin_lock(&oslog_stream_lock
); 
 849                 if (data 
&& *(int *)data
) 
 850                         oslog_streamsoftc
.sc_state 
|= LOG_NBIO
; 
 852                         oslog_streamsoftc
.sc_state 
&= ~LOG_NBIO
; 
 855                 if (data 
&& *(int *)data
) 
 856                         oslog_streamsoftc
.sc_state 
|= LOG_ASYNC
; 
 858                         oslog_streamsoftc
.sc_state 
&= ~LOG_ASYNC
; 
 865         lck_spin_unlock(&oslog_stream_lock
); 
 872         /* After this point, we must be ready to accept characters */ 
 879         vm_size_t size 
= FIREHOSE_BUFFER_KERNEL_CHUNK_COUNT 
* FIREHOSE_CHUNK_SIZE
; 
 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"); 
 889         kernel_firehose_addr 
+= PAGE_SIZE
; 
 890         bzero((void *)kernel_firehose_addr
, size
); 
 891         /* register buffer with firehose */ 
 892         kernel_firehose_addr 
= (vm_offset_t
)__firehose_buffer_create((size_t *) &size
); 
 894         kprintf("oslog_init completed\n"); 
 900  * Decription:  Output a character to the log; assumes the LOG_LOCK() is held 
 903  * Parameters:  c                               Character to output 
 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 
 913 log_putc_locked(struct msgbuf 
*mbp
, char c
) 
 915         mbp
->msg_bufc
[mbp
->msg_bufx
++] = c
; 
 916         if (mbp
->msg_bufx 
>= mbp
->msg_size
) 
 920 static oslog_stream_buf_entry_t
 
 921 oslog_stream_find_free_buf_entry_locked(void) 
 924         oslog_stream_buf_entry_t buf_entry 
= NULL
; 
 926         LCK_SPIN_ASSERT(&oslog_stream_lock
, LCK_ASSERT_OWNED
); 
 928         mbp 
= oslog_streambufp
; 
 930         buf_entry 
= STAILQ_FIRST(&oslog_stream_free_head
); 
 932                 STAILQ_REMOVE_HEAD(&oslog_stream_free_head
, buf_entries
); 
 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
; 
 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
); 
 945                 if (prev_entry 
== NULL
) { 
 946                         STAILQ_REMOVE_HEAD(&oslog_stream_buf_head
, buf_entries
); 
 949                         STAILQ_REMOVE_AFTER(&oslog_stream_buf_head
, prev_entry
, buf_entries
); 
 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
); 
 963 oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry
) 
 965         LCK_SPIN_ASSERT(&oslog_stream_lock
, LCK_ASSERT_OWNED
); 
 966         STAILQ_INSERT_TAIL(&oslog_stream_buf_head
, m_entry
, buf_entries
); 
 972 oslog_streamwrite_append_bytes(const char *buffer
, int buflen
) 
 976         LCK_SPIN_ASSERT(&oslog_stream_lock
, LCK_ASSERT_OWNED
); 
 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
); 
 983                 mbp
->msg_bufx 
+= buflen
; 
 984                 if (mbp
->msg_bufx 
== mbp
->msg_size
) { 
 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
); 
 992                 buflen 
-= bytes_left
; 
 993                 buffer 
+= bytes_left
; 
 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
; 
1004 oslog_streamwrite_locked(firehose_tracepoint_id_u ftid
, 
1005                 uint64_t stamp
, const void *pubdata
, size_t publen
) 
1008         int available_space 
= 0; 
1009         oslog_stream_buf_entry_t buf_entry 
= NULL
; 
1010         oslog_stream_buf_entry_t next_entry 
= NULL
; 
1012         uint16_t ft_size 
= offsetof(struct firehose_tracepoint_s
, ft_data
); 
1013         int ft_length 
= ft_size 
+ publen
; 
1015         LCK_SPIN_ASSERT(&oslog_stream_lock
, LCK_ASSERT_OWNED
); 
1017         mbp 
= oslog_streambufp
; 
1018         if (ft_length 
> mbp
->msg_size
) { 
1019                 (void)hw_atomic_add(&oslog_s_error_count
, 1); 
1023         // Ensure that we have a list element for this record 
1024         buf_entry 
= oslog_stream_find_free_buf_entry_locked(); 
1026         assert(buf_entry 
!= NULL
); 
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
; 
1032                 available_space 
= mbp
->msg_size 
- mbp
->msg_bufx 
+ mbp
->msg_bufr
; 
1034         while(ft_length 
> available_space
) { 
1035                 oslog_stream_buf_entry_t prev_entry 
= NULL
; 
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
); 
1044                 if (prev_entry 
== NULL
) { 
1045                         STAILQ_REMOVE_HEAD(&oslog_stream_buf_head
, buf_entries
); 
1048                         STAILQ_REMOVE_AFTER(&oslog_stream_buf_head
, prev_entry
, buf_entries
); 
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
); 
1056                 oslog_s_dropped_msgcount
++; 
1057                 available_space 
+= next_entry
->size
; 
1059                 STAILQ_INSERT_TAIL(&oslog_stream_free_head
, next_entry
, buf_entries
); 
1062         assert(ft_length 
<= available_space
); 
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
; 
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
, 
1077         oslog_streamwrite_append_bytes((char *)&fs
, sizeof(fs
)); 
1078         oslog_streamwrite_append_bytes(pubdata
, publen
); 
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
); 
1092  * Decription:  Output a character to the log; assumes the LOG_LOCK() is NOT 
1093  *              held by the caller. 
1095  * Parameters:  c                               Character to output 
1099  * Notes:       This function is used for single byte output to the log.  It 
1100  *              primarily exists to maintain binary backward compatibility. 
1105         int unread_count 
= 0; 
1107         log_putc_locked(msgbufp
, c
); 
1108         unread_count 
= msgbufp
->msg_bufx 
- msgbufp
->msg_bufr
; 
1111         if (unread_count 
< 0) 
1112                 unread_count 
= 0 - unread_count
; 
1113         if (c 
== '\n' || unread_count 
>= (msgbufp
->msg_size 
/ 2)) 
1119  * it is possible to increase the kernel log buffer size by adding 
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 
1124  * allocated statically and is CONFIG_MSG_BSIZE characters in size, otherwise 
1125  * memory is dynamically allocated. Memory management must already be up. 
1128 log_setsize(int size
) 
1131         int new_logsize
, new_bufr
, new_bufx
; 
1133         int old_logsize
, old_bufr
, old_bufx
; 
1137         if (size 
> MAX_MSG_BSIZE
) 
1144         if (!(new_logdata 
= (char*)kalloc(size
))) { 
1145                 printf("log_setsize: unable to allocate memory\n"); 
1148         bzero(new_logdata
, new_logsize
); 
1152         old_logsize 
= msgbufp
->msg_size
; 
1153         old_logdata 
= msgbufp
->msg_bufc
; 
1154         old_bufr 
= msgbufp
->msg_bufr
; 
1155         old_bufx 
= msgbufp
->msg_bufx
; 
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
); 
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
; 
1166                  * if new buffer is bigger, copy what we have and let the 
1167                  * bzero above handle the difference 
1169                 count 
= MIN(new_logsize
, old_logsize
); 
1170                 p 
= old_logdata 
+ old_logsize 
- (count 
- old_bufx
); 
1172         for (i 
= 0; i 
< count
; i
++) { 
1173                 if (p 
>= old_logdata 
+ old_logsize
) 
1177                 new_logdata
[i
] = ch
; 
1181         if (new_bufx 
>= new_logsize
) 
1183         msgbufp
->msg_bufx 
= new_bufx
; 
1185         new_bufr 
= old_bufx 
- old_bufr
; /* how much were we trailing bufx by? */ 
1187                 new_bufr 
+= old_logsize
; 
1188         new_bufr 
= new_bufx 
- new_bufr
; /* now relative to oldest data in new buffer */ 
1190                 new_bufr 
+= new_logsize
; 
1191         msgbufp
->msg_bufr 
= new_bufr
; 
1193         msgbufp
->msg_size 
= new_logsize
; 
1194         msgbufp
->msg_bufc 
= new_logdata
; 
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
); 
1201         /* this memory is now dead - clear it so that it compresses better 
1202            in case of suspend to disk etc. */ 
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
); 
1209         printf("set system log size to %d bytes\n", new_logsize
); 
1214 void oslog_setsize(int size
) 
1217         // If the size is less than the default stream buffer 
1219         if (size 
<= OSLOG_STREAM_BUF_SIZE
) { 
1223         scale 
= (uint16_t) (size 
/ OSLOG_STREAM_BUF_SIZE
); 
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
); 
1230 SYSCTL_PROC(_kern
, OID_AUTO
, msgbuf
, 
1231         CTLTYPE_INT 
| CTLFLAG_RW 
| CTLFLAG_LOCKED
, 0, 0, 
1232         sysctl_kern_msgbuf
, "I", ""); 
1235 sysctl_kern_msgbuf(struct sysctl_oid 
*oidp __unused
, 
1236         void *arg1 __unused
, int arg2 __unused
, struct sysctl_req 
*req
) 
1238         int old_bufsize
, bufsize
; 
1242         old_bufsize 
= bufsize 
= msgbufp
->msg_size
; 
1245         error 
= sysctl_io_number(req
, bufsize
, sizeof(bufsize
), &bufsize
, NULL
); 
1249         if (bufsize 
!= old_bufsize
) { 
1250                 error 
= log_setsize(bufsize
); 
1258  * This should be called by /sbin/dmesg only via libproc. 
1259  * It returns as much data still in the buffer as possible. 
1262 log_dmesg(user_addr_t buffer
, uint32_t buffersize
, int32_t * retval
) 
1265         uint32_t localbuff_size
; 
1266         int error 
= 0, newl
, skip
; 
1267         char *localbuff
, *p
, *copystart
, ch
; 
1271         localbuff_size 
= (msgbufp
->msg_size 
+ 2); /* + '\n' + '\0' */ 
1274         /* Allocate a temporary non-circular buffer for copyout */ 
1275         if (!(localbuff 
= (char *)kalloc(localbuff_size
))) { 
1276                 printf("log_dmesg: unable to allocate memory\n"); 
1280         /* in between here, the log could become bigger, but that's fine */ 
1284          * The message buffer is circular; start at the write pointer, and 
1285          * make one loop up to write pointer - 1. 
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
; 
1292                 /* Skip "\n<.*>" syslog sequences. */ 
1298                 if (newl 
&& ch 
== '<') { 
1304                 newl 
= (ch 
== '\n'); 
1305                 localbuff
[i
++] = ch
; 
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. 
1311                 if (i 
== (localbuff_size 
- 2)) 
1315                 localbuff
[i
++] = '\n'; 
1318         if (buffersize 
>= i
) { 
1319                 copystart 
= localbuff
; 
1322                 copystart 
= localbuff 
+ i 
- buffersize
; 
1323                 copysize 
= buffersize
; 
1328         error 
= copyout(copystart
, buffer
, copysize
); 
1332         kfree(localbuff
, localbuff_size
);