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