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