]>
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); | |
f427ee49 A |
108 | |
109 | SECURITY_READ_ONLY_LATE(vm_offset_t) kernel_firehose_addr = 0; | |
110 | SECURITY_READ_ONLY_LATE(uint8_t) __firehose_buffer_kernel_chunk_count = | |
111 | FIREHOSE_BUFFER_KERNEL_DEFAULT_CHUNK_COUNT; | |
112 | SECURITY_READ_ONLY_LATE(uint8_t) __firehose_num_kernel_io_pages = | |
113 | FIREHOSE_BUFFER_KERNEL_DEFAULT_IO_PAGES; | |
39037602 A |
114 | |
115 | /* log message counters for streaming mode */ | |
116 | uint32_t oslog_s_streamed_msgcount = 0; | |
117 | uint32_t oslog_s_dropped_msgcount = 0; | |
118 | extern uint32_t oslog_s_error_count; | |
1c79356b | 119 | |
f427ee49 A |
120 | uint32_t oslog_msgbuf_dropped_charcount = 0; |
121 | ||
0a7de745 | 122 | #define LOG_RDPRI (PZERO + 1) |
1c79356b | 123 | |
0a7de745 A |
124 | #define LOG_NBIO 0x02 |
125 | #define LOG_ASYNC 0x04 | |
126 | #define LOG_RDWAIT 0x08 | |
1c79356b | 127 | |
f427ee49 | 128 | /* All globals should be accessed under bsd_log_lock() or bsd_log_lock_safe() */ |
6d2010ae | 129 | |
cc8bc92a | 130 | static char amsg_bufc[1024]; |
cb323159 | 131 | 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 |
132 | struct msgbuf *aslbufp __attribute__((used)) = &aslbuf; |
133 | ||
6d2010ae | 134 | /* logsoftc only valid while log_open=1 */ |
1c79356b | 135 | struct logsoftc { |
0a7de745 A |
136 | int sc_state; /* see above for possibilities */ |
137 | struct selinfo sc_selp; /* thread waiting for select */ | |
138 | int sc_pgid; /* process/group for async I/O */ | |
cc8bc92a | 139 | struct msgbuf *sc_mbp; |
1c79356b A |
140 | } logsoftc; |
141 | ||
cc8bc92a | 142 | static int log_open; |
6d2010ae | 143 | char smsg_bufc[CONFIG_MSG_BSIZE]; /* static buffer */ |
813fb2f6 A |
144 | char oslog_stream_bufc[FIREHOSE_CHUNK_SIZE]; /* static buffer */ |
145 | struct firehose_chunk_s oslog_boot_buf = { | |
146 | .fc_pos = { | |
147 | .fcp_next_entry_offs = offsetof(struct firehose_chunk_s, fc_data), | |
148 | .fcp_private_offs = FIREHOSE_CHUNK_SIZE, | |
149 | .fcp_refcnt = 1, // indicate that there is a writer to this chunk | |
150 | .fcp_stream = firehose_stream_persist, | |
151 | .fcp_flag_io = 1, // for now, lets assume this is coming from the io bank | |
39037602 A |
152 | }, |
153 | }; /* static buffer */ | |
813fb2f6 | 154 | firehose_chunk_t firehose_boot_chunk = &oslog_boot_buf; |
cb323159 A |
155 | struct msgbuf msgbuf = {.msg_magic = MSG_MAGIC, .msg_size = sizeof(smsg_bufc), .msg_bufx = 0, .msg_bufr = 0, .msg_bufc = smsg_bufc}; |
156 | struct msgbuf oslog_stream_buf = {.msg_magic = MSG_MAGIC, .msg_size = 0, .msg_bufx = 0, .msg_bufr = 0, .msg_bufc = NULL}; | |
39236c6e | 157 | struct msgbuf *msgbufp __attribute__((used)) = &msgbuf; |
39037602 A |
158 | struct msgbuf *oslog_streambufp __attribute__((used)) = &oslog_stream_buf; |
159 | ||
160 | // List entries for keeping track of the streaming buffer | |
161 | static oslog_stream_buf_entry_t oslog_stream_buf_entries; | |
162 | ||
0a7de745 A |
163 | #define OSLOG_NUM_STREAM_ENTRIES 64 |
164 | #define OSLOG_STREAM_BUF_SIZE 4096 | |
39037602 | 165 | |
0a7de745 A |
166 | int oslog_open = 0; |
167 | int os_log_wakeup = 0; | |
168 | int oslog_stream_open = 0; | |
169 | int oslog_stream_buf_bytesavail = 0; | |
170 | int oslog_stream_buf_size = OSLOG_STREAM_BUF_SIZE; | |
171 | int oslog_stream_num_entries = OSLOG_NUM_STREAM_ENTRIES; | |
39037602 A |
172 | |
173 | /* oslogsoftc only valid while oslog_open=1 */ | |
174 | struct oslogsoftc { | |
0a7de745 A |
175 | int sc_state; /* see above for possibilities */ |
176 | struct selinfo sc_selp; /* thread waiting for select */ | |
177 | int sc_pgid; /* process/group for async I/O */ | |
39037602 A |
178 | } oslogsoftc; |
179 | ||
180 | struct oslog_streamsoftc { | |
0a7de745 A |
181 | int sc_state; /* see above for possibilities */ |
182 | struct selinfo sc_selp; /* thread waiting for select */ | |
183 | int sc_pgid; /* process/group for async I/O */ | |
cc8bc92a | 184 | } oslog_streamsoftc; |
39037602 A |
185 | |
186 | STAILQ_HEAD(, oslog_stream_buf_entry_s) oslog_stream_free_head = | |
0a7de745 | 187 | STAILQ_HEAD_INITIALIZER(oslog_stream_free_head); |
39037602 | 188 | STAILQ_HEAD(, oslog_stream_buf_entry_s) oslog_stream_buf_head = |
0a7de745 | 189 | STAILQ_HEAD_INITIALIZER(oslog_stream_buf_head); |
6d2010ae | 190 | |
39037602 | 191 | /* defined in osfmk/kern/printf.c */ |
f427ee49 A |
192 | extern bool bsd_log_lock(bool); |
193 | extern void bsd_log_lock_safe(void); | |
91447636 | 194 | extern void bsd_log_unlock(void); |
39037602 | 195 | |
f427ee49 A |
196 | LCK_GRP_DECLARE(oslog_stream_lock_grp, "oslog streaming"); |
197 | LCK_SPIN_DECLARE(oslog_stream_lock, &oslog_stream_lock_grp); | |
0a7de745 A |
198 | #define stream_lock() lck_spin_lock(&oslog_stream_lock) |
199 | #define stream_unlock() lck_spin_unlock(&oslog_stream_lock) | |
1c79356b | 200 | |
2d21ac55 A |
201 | /* XXX wants a linker set so these can be static */ |
202 | extern d_open_t logopen; | |
203 | extern d_close_t logclose; | |
204 | extern d_read_t logread; | |
205 | extern d_ioctl_t logioctl; | |
206 | extern d_select_t logselect; | |
207 | ||
39037602 A |
208 | /* XXX wants a linker set so these can be static */ |
209 | extern d_open_t oslogopen; | |
210 | extern d_close_t oslogclose; | |
211 | extern d_select_t oslogselect; | |
212 | extern d_ioctl_t oslogioctl; | |
213 | ||
214 | /* XXX wants a linker set so these can be static */ | |
215 | extern d_open_t oslog_streamopen; | |
216 | extern d_close_t oslog_streamclose; | |
217 | extern d_read_t oslog_streamread; | |
218 | extern d_ioctl_t oslog_streamioctl; | |
219 | extern d_select_t oslog_streamselect; | |
220 | ||
39037602 A |
221 | void oslog_setsize(int size); |
222 | void oslog_streamwrite_locked(firehose_tracepoint_id_u ftid, | |
0a7de745 | 223 | uint64_t stamp, const void *pubdata, size_t publen); |
39037602 A |
224 | void oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry); |
225 | static oslog_stream_buf_entry_t oslog_stream_find_free_buf_entry_locked(void); | |
226 | static void oslog_streamwrite_append_bytes(const char *buffer, int buflen); | |
227 | ||
1c79356b A |
228 | /* |
229 | * Serialize log access. Note that the log can be written at interrupt level, | |
230 | * so any log manipulations that can be done from, or affect, another processor | |
231 | * at interrupt level must be guarded with a spin lock. | |
232 | */ | |
91447636 | 233 | |
6d2010ae A |
234 | #if DEBUG |
235 | #define LOG_SETSIZE_DEBUG(x...) kprintf(x) | |
236 | #else | |
237 | #define LOG_SETSIZE_DEBUG(x...) do { } while(0) | |
238 | #endif | |
239 | ||
240 | static int sysctl_kern_msgbuf(struct sysctl_oid *oidp, | |
0a7de745 | 241 | void *arg1, int arg2, struct sysctl_req *req); |
1c79356b A |
242 | |
243 | /*ARGSUSED*/ | |
2d21ac55 A |
244 | int |
245 | logopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p) | |
1c79356b | 246 | { |
f427ee49 | 247 | bsd_log_lock_safe(); |
1c79356b | 248 | if (log_open) { |
f427ee49 | 249 | bsd_log_unlock(); |
0a7de745 | 250 | return EBUSY; |
1c79356b | 251 | } |
cc8bc92a A |
252 | if (atm_get_diagnostic_config() & ATM_ENABLE_LEGACY_LOGGING) { |
253 | logsoftc.sc_mbp = msgbufp; | |
254 | } else { | |
255 | /* | |
256 | * Support for messagetracer (kern_asl_msg()) | |
257 | * In this mode, /dev/klog exports only ASL-formatted messages | |
258 | * written into aslbufp via vaddlog(). | |
259 | */ | |
260 | logsoftc.sc_mbp = aslbufp; | |
261 | } | |
0a7de745 | 262 | logsoftc.sc_pgid = p->p_pid; /* signal process only */ |
6d2010ae | 263 | log_open = 1; |
1c79356b | 264 | |
f427ee49 | 265 | bsd_log_unlock(); |
9bccf70c | 266 | |
0a7de745 | 267 | return 0; |
1c79356b A |
268 | } |
269 | ||
270 | /*ARGSUSED*/ | |
271 | int | |
2d21ac55 | 272 | logclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p) |
1c79356b | 273 | { |
f427ee49 | 274 | bsd_log_lock_safe(); |
39037602 | 275 | logsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC); |
1c79356b | 276 | selwakeup(&logsoftc.sc_selp); |
1c79356b | 277 | selthreadclear(&logsoftc.sc_selp); |
6d2010ae | 278 | log_open = 0; |
f427ee49 | 279 | bsd_log_unlock(); |
0a7de745 | 280 | return 0; |
1c79356b A |
281 | } |
282 | ||
39037602 A |
283 | |
284 | int | |
285 | oslogopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p) | |
286 | { | |
f427ee49 | 287 | bsd_log_lock_safe(); |
39037602 | 288 | if (oslog_open) { |
f427ee49 | 289 | bsd_log_unlock(); |
0a7de745 | 290 | return EBUSY; |
39037602 | 291 | } |
0a7de745 | 292 | oslogsoftc.sc_pgid = p->p_pid; /* signal process only */ |
39037602 A |
293 | oslog_open = 1; |
294 | ||
f427ee49 | 295 | bsd_log_unlock(); |
0a7de745 | 296 | return 0; |
39037602 A |
297 | } |
298 | ||
299 | int | |
300 | oslogclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p) | |
301 | { | |
f427ee49 | 302 | bsd_log_lock_safe(); |
39037602 A |
303 | oslogsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC); |
304 | selwakeup(&oslogsoftc.sc_selp); | |
305 | selthreadclear(&oslogsoftc.sc_selp); | |
306 | oslog_open = 0; | |
f427ee49 | 307 | bsd_log_unlock(); |
0a7de745 | 308 | return 0; |
39037602 A |
309 | } |
310 | ||
311 | int | |
312 | oslog_streamopen(__unused dev_t dev, __unused int flags, __unused int mode, struct proc *p) | |
313 | { | |
314 | char *oslog_stream_msg_bufc = NULL; | |
315 | oslog_stream_buf_entry_t entries = NULL; | |
316 | ||
0a7de745 | 317 | stream_lock(); |
39037602 | 318 | if (oslog_stream_open) { |
0a7de745 | 319 | stream_unlock(); |
39037602 A |
320 | return EBUSY; |
321 | } | |
0a7de745 | 322 | stream_unlock(); |
39037602 A |
323 | |
324 | // Allocate the stream buffer | |
f427ee49 A |
325 | oslog_stream_msg_bufc = kheap_alloc(KHEAP_DATA_BUFFERS, |
326 | oslog_stream_buf_size, Z_WAITOK | Z_ZERO); | |
39037602 A |
327 | if (!oslog_stream_msg_bufc) { |
328 | return ENOMEM; | |
329 | } | |
330 | ||
331 | /* entries to support kernel logging in stream mode */ | |
ea3f0419 A |
332 | size_t entries_size = oslog_stream_num_entries * sizeof(struct oslog_stream_buf_entry_s); |
333 | entries = kalloc(entries_size); | |
39037602 | 334 | if (!entries) { |
f427ee49 A |
335 | kheap_free(KHEAP_DATA_BUFFERS, |
336 | oslog_stream_msg_bufc, oslog_stream_buf_size); | |
39037602 A |
337 | return ENOMEM; |
338 | } | |
ea3f0419 A |
339 | /* Zeroing to avoid copying uninitialized struct padding to userspace. */ |
340 | bzero(entries, entries_size); | |
39037602 | 341 | |
0a7de745 | 342 | stream_lock(); |
39037602 | 343 | if (oslog_stream_open) { |
0a7de745 | 344 | stream_unlock(); |
f427ee49 A |
345 | kheap_free(KHEAP_DATA_BUFFERS, |
346 | oslog_stream_msg_bufc, oslog_stream_buf_size); | |
ea3f0419 | 347 | kfree(entries, entries_size); |
39037602 A |
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; | |
39037602 A |
362 | STAILQ_INSERT_TAIL(&oslog_stream_free_head, &oslog_stream_buf_entries[i], buf_entries); |
363 | } | |
364 | ||
365 | /* there should be no pending entries in the stream */ | |
366 | assert(STAILQ_EMPTY(&oslog_stream_buf_head)); | |
367 | assert(oslog_streambufp->msg_bufx == 0); | |
368 | assert(oslog_streambufp->msg_bufr == 0); | |
369 | ||
370 | oslog_streambufp->msg_bufx = 0; | |
371 | oslog_streambufp->msg_bufr = 0; | |
372 | oslog_streamsoftc.sc_pgid = p->p_pid; /* signal process only */ | |
373 | oslog_stream_open = 1; | |
0a7de745 A |
374 | oslog_stream_buf_bytesavail = oslog_stream_buf_size; |
375 | stream_unlock(); | |
39037602 A |
376 | |
377 | return 0; | |
378 | } | |
379 | ||
380 | int | |
381 | oslog_streamclose(__unused dev_t dev, __unused int flag, __unused int devtype, __unused struct proc *p) | |
382 | { | |
383 | oslog_stream_buf_entry_t next_entry = NULL; | |
384 | char *oslog_stream_msg_bufc = NULL; | |
385 | oslog_stream_buf_entry_t entries = NULL; | |
386 | ||
0a7de745 | 387 | stream_lock(); |
39037602 A |
388 | |
389 | if (oslog_stream_open == 0) { | |
0a7de745 | 390 | stream_unlock(); |
39037602 A |
391 | return EBADF; |
392 | } | |
393 | ||
394 | // Consume all log lines | |
395 | while (!STAILQ_EMPTY(&oslog_stream_buf_head)) { | |
396 | next_entry = STAILQ_FIRST(&oslog_stream_buf_head); | |
397 | STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries); | |
398 | } | |
399 | oslog_streamwakeup_locked(); | |
400 | oslog_streamsoftc.sc_state &= ~(LOG_NBIO | LOG_ASYNC); | |
401 | selwakeup(&oslog_streamsoftc.sc_selp); | |
402 | selthreadclear(&oslog_streamsoftc.sc_selp); | |
403 | oslog_stream_open = 0; | |
404 | oslog_streambufp->msg_bufr = 0; | |
405 | oslog_streambufp->msg_bufx = 0; | |
406 | oslog_stream_msg_bufc = oslog_streambufp->msg_bufc; | |
407 | oslog_streambufp->msg_bufc = NULL; | |
408 | entries = oslog_stream_buf_entries; | |
409 | oslog_stream_buf_entries = NULL; | |
410 | oslog_streambufp->msg_size = 0; | |
411 | ||
0a7de745 | 412 | stream_unlock(); |
39037602 A |
413 | |
414 | // Free the stream buffer | |
f427ee49 A |
415 | kheap_free(KHEAP_DATA_BUFFERS, oslog_stream_msg_bufc, |
416 | oslog_stream_buf_size); | |
39037602 A |
417 | // Free the list entries |
418 | kfree(entries, oslog_stream_num_entries * sizeof(struct oslog_stream_buf_entry_s)); | |
419 | ||
420 | return 0; | |
421 | } | |
422 | ||
1c79356b A |
423 | /*ARGSUSED*/ |
424 | int | |
2d21ac55 | 425 | logread(__unused dev_t dev, struct uio *uio, int flag) |
1c79356b | 426 | { |
1c79356b | 427 | int error = 0; |
cc8bc92a | 428 | struct msgbuf *mbp = logsoftc.sc_mbp; |
f427ee49 | 429 | ssize_t resid; |
1c79356b | 430 | |
f427ee49 | 431 | bsd_log_lock_safe(); |
cc8bc92a | 432 | while (mbp->msg_bufr == mbp->msg_bufx) { |
1c79356b | 433 | if (flag & IO_NDELAY) { |
91447636 A |
434 | error = EWOULDBLOCK; |
435 | goto out; | |
1c79356b A |
436 | } |
437 | if (logsoftc.sc_state & LOG_NBIO) { | |
91447636 A |
438 | error = EWOULDBLOCK; |
439 | goto out; | |
1c79356b A |
440 | } |
441 | logsoftc.sc_state |= LOG_RDWAIT; | |
f427ee49 | 442 | bsd_log_unlock(); |
91447636 | 443 | /* |
0a7de745 A |
444 | * If the wakeup is missed |
445 | * then wait for 5 sec and reevaluate | |
91447636 | 446 | */ |
cc8bc92a | 447 | if ((error = tsleep((caddr_t)mbp, LOG_RDPRI | PCATCH, |
0a7de745 | 448 | "klog", 5 * hz)) != 0) { |
91447636 | 449 | /* if it times out; ignore */ |
0a7de745 A |
450 | if (error != EWOULDBLOCK) { |
451 | return error; | |
452 | } | |
1c79356b | 453 | } |
f427ee49 | 454 | bsd_log_lock_safe(); |
1c79356b | 455 | } |
1c79356b A |
456 | logsoftc.sc_state &= ~LOG_RDWAIT; |
457 | ||
f427ee49 A |
458 | while ((resid = uio_resid(uio)) > 0) { |
459 | size_t l; | |
6d2010ae | 460 | |
f427ee49 A |
461 | if (mbp->msg_bufx >= mbp->msg_bufr) { |
462 | l = mbp->msg_bufx - mbp->msg_bufr; | |
463 | } else { | |
cc8bc92a | 464 | l = mbp->msg_size - mbp->msg_bufr; |
0a7de745 | 465 | } |
f427ee49 | 466 | if ((l = MIN(l, (size_t)resid)) == 0) { |
1c79356b | 467 | break; |
0a7de745 | 468 | } |
6d2010ae | 469 | |
f427ee49 A |
470 | const size_t readpos = mbp->msg_bufr; |
471 | ||
472 | bsd_log_unlock(); | |
473 | error = uiomove((caddr_t)&mbp->msg_bufc[readpos], (int)l, uio); | |
474 | bsd_log_lock_safe(); | |
0a7de745 | 475 | if (error) { |
1c79356b | 476 | break; |
0a7de745 | 477 | } |
f427ee49 A |
478 | |
479 | mbp->msg_bufr = (int)(readpos + l); | |
0a7de745 | 480 | if (mbp->msg_bufr >= mbp->msg_size) { |
cc8bc92a | 481 | mbp->msg_bufr = 0; |
0a7de745 | 482 | } |
1c79356b | 483 | } |
91447636 | 484 | out: |
f427ee49 | 485 | bsd_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 | 558 | // Free the list entry |
f427ee49 | 559 | kfree(read_entry, sizeof(struct oslog_stream_buf_entry_s) + read_entry->size); |
0a7de745 A |
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 | ||
f427ee49 A |
609 | copy_size = min(logpos, (int) MIN(uio_resid(uio), INT_MAX)); |
610 | if (copy_size > 0) { | |
39037602 A |
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: |
f427ee49 | 626 | bsd_log_lock_safe(); |
cc8bc92a | 627 | if (mbp->msg_bufr != mbp->msg_bufx) { |
f427ee49 | 628 | bsd_log_unlock(); |
0a7de745 | 629 | return 1; |
1c79356b | 630 | } |
0b4e3aa0 | 631 | selrecord(p, &logsoftc.sc_selp, wql); |
f427ee49 | 632 | bsd_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 | 642 | case FREAD: |
f427ee49 | 643 | bsd_log_lock_safe(); |
39037602 | 644 | if (os_log_wakeup) { |
f427ee49 | 645 | bsd_log_unlock(); |
0a7de745 | 646 | return 1; |
39037602 A |
647 | } |
648 | selrecord(p, &oslogsoftc.sc_selp, wql); | |
f427ee49 | 649 | bsd_log_unlock(); |
39037602 A |
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 | ||
f427ee49 | 684 | bsd_log_lock_safe(); |
91447636 | 685 | if (!log_open) { |
f427ee49 | 686 | bsd_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; |
f427ee49 | 698 | bsd_log_unlock(); |
0a7de745 A |
699 | if (pgid < 0) { |
700 | gsignal(-pgid, SIGIO); | |
701 | } else { | |
2d21ac55 | 702 | proc_signal(pgid, SIGIO); |
0a7de745 | 703 | } |
f427ee49 | 704 | bsd_log_lock_safe(); |
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: |
f427ee49 | 711 | bsd_log_unlock(); |
1c79356b A |
712 | } |
713 | ||
39037602 A |
714 | void |
715 | oslogwakeup(void) | |
716 | { | |
f427ee49 A |
717 | if (!oslog_is_safe()) { |
718 | return; | |
719 | } | |
720 | ||
721 | bsd_log_lock_safe(); | |
39037602 | 722 | if (!oslog_open) { |
f427ee49 | 723 | bsd_log_unlock(); |
39037602 A |
724 | return; |
725 | } | |
726 | selwakeup(&oslogsoftc.sc_selp); | |
727 | os_log_wakeup = 1; | |
f427ee49 | 728 | bsd_log_unlock(); |
39037602 A |
729 | } |
730 | ||
731 | static void | |
732 | oslog_streamwakeup_locked(void) | |
733 | { | |
5ba3f43e | 734 | LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED); |
39037602 A |
735 | if (!oslog_stream_open) { |
736 | return; | |
737 | } | |
738 | selwakeup(&oslog_streamsoftc.sc_selp); | |
739 | if (oslog_streamsoftc.sc_state & LOG_RDWAIT) { | |
740 | wakeup((caddr_t)oslog_streambufp); | |
741 | oslog_streamsoftc.sc_state &= ~LOG_RDWAIT; | |
742 | } | |
743 | } | |
744 | ||
745 | void | |
746 | oslog_streamwakeup(void) | |
747 | { | |
748 | /* cf. r24974766 & r25201228*/ | |
749 | if (oslog_is_safe() == FALSE) { | |
750 | return; | |
751 | } | |
752 | ||
0a7de745 | 753 | stream_lock(); |
39037602 | 754 | oslog_streamwakeup_locked(); |
0a7de745 | 755 | stream_unlock(); |
39037602 | 756 | } |
1c79356b A |
757 | |
758 | /*ARGSUSED*/ | |
759 | int | |
2d21ac55 | 760 | logioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p) |
1c79356b | 761 | { |
6d2010ae | 762 | int l; |
cc8bc92a | 763 | const struct msgbuf *mbp = logsoftc.sc_mbp; |
1c79356b | 764 | |
f427ee49 | 765 | bsd_log_lock_safe(); |
1c79356b | 766 | switch (com) { |
1c79356b A |
767 | /* return number of characters immediately available */ |
768 | case FIONREAD: | |
cc8bc92a | 769 | l = mbp->msg_bufx - mbp->msg_bufr; |
0a7de745 | 770 | if (l < 0) { |
cc8bc92a | 771 | l += mbp->msg_size; |
0a7de745 | 772 | } |
1c79356b A |
773 | *(off_t *)data = l; |
774 | break; | |
775 | ||
776 | case FIONBIO: | |
0a7de745 | 777 | if (*(int *)data) { |
1c79356b | 778 | logsoftc.sc_state |= LOG_NBIO; |
0a7de745 | 779 | } else { |
1c79356b | 780 | logsoftc.sc_state &= ~LOG_NBIO; |
0a7de745 | 781 | } |
1c79356b A |
782 | break; |
783 | ||
784 | case FIOASYNC: | |
0a7de745 | 785 | if (*(int *)data) { |
1c79356b | 786 | logsoftc.sc_state |= LOG_ASYNC; |
0a7de745 | 787 | } else { |
1c79356b | 788 | logsoftc.sc_state &= ~LOG_ASYNC; |
0a7de745 | 789 | } |
1c79356b A |
790 | break; |
791 | ||
792 | case TIOCSPGRP: | |
1c79356b | 793 | logsoftc.sc_pgid = *(int *)data; |
1c79356b A |
794 | break; |
795 | ||
796 | case TIOCGPGRP: | |
1c79356b | 797 | *(int *)data = logsoftc.sc_pgid; |
1c79356b A |
798 | break; |
799 | ||
800 | default: | |
f427ee49 | 801 | bsd_log_unlock(); |
0a7de745 | 802 | return -1; |
1c79356b | 803 | } |
f427ee49 | 804 | bsd_log_unlock(); |
0a7de745 | 805 | return 0; |
1c79356b A |
806 | } |
807 | ||
39037602 A |
808 | /*ARGSUSED*/ |
809 | int | |
810 | oslogioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p) | |
811 | { | |
812 | int ret = 0; | |
d9a64523 | 813 | mach_vm_size_t buffer_size = (__firehose_buffer_kernel_chunk_count * FIREHOSE_CHUNK_SIZE); |
39037602 A |
814 | firehose_buffer_map_info_t map_info = {0, 0}; |
815 | firehose_buffer_t kernel_firehose_buffer = NULL; | |
816 | mach_vm_address_t user_addr = 0; | |
817 | mach_port_t mem_entry_ptr = MACH_PORT_NULL; | |
818 | ||
819 | switch (com) { | |
39037602 A |
820 | /* return number of characters immediately available */ |
821 | ||
822 | case LOGBUFFERMAP: | |
5ba3f43e | 823 | kernel_firehose_buffer = (firehose_buffer_t)kernel_firehose_addr; |
39037602 A |
824 | |
825 | ret = mach_make_memory_entry_64(kernel_map, | |
0a7de745 A |
826 | &buffer_size, |
827 | (mach_vm_offset_t) kernel_firehose_buffer, | |
828 | (MAP_MEM_VM_SHARE | VM_PROT_READ), | |
829 | &mem_entry_ptr, | |
830 | MACH_PORT_NULL); | |
39037602 | 831 | if (ret == KERN_SUCCESS) { |
5ba3f43e | 832 | ret = mach_vm_map_kernel(get_task_map(current_task()), |
0a7de745 A |
833 | &user_addr, |
834 | buffer_size, | |
835 | 0, /* mask */ | |
836 | VM_FLAGS_ANYWHERE, | |
837 | VM_MAP_KERNEL_FLAGS_NONE, | |
838 | VM_KERN_MEMORY_NONE, | |
839 | mem_entry_ptr, | |
840 | 0, /* offset */ | |
841 | FALSE, /* copy */ | |
842 | VM_PROT_READ, | |
843 | VM_PROT_READ, | |
844 | VM_INHERIT_SHARE); | |
39037602 A |
845 | } |
846 | ||
847 | if (ret == KERN_SUCCESS) { | |
848 | map_info.fbmi_addr = (uint64_t) (user_addr); | |
849 | map_info.fbmi_size = buffer_size; | |
850 | bcopy(&map_info, data, sizeof(firehose_buffer_map_info_t)); | |
851 | } | |
852 | break; | |
853 | case LOGFLUSHED: | |
f427ee49 | 854 | bsd_log_lock_safe(); |
39037602 | 855 | os_log_wakeup = 0; |
f427ee49 | 856 | bsd_log_unlock(); |
39037602 A |
857 | __firehose_merge_updates(*(firehose_push_reply_t *)(data)); |
858 | break; | |
859 | default: | |
0a7de745 | 860 | return -1; |
39037602 | 861 | } |
0a7de745 | 862 | return 0; |
39037602 A |
863 | } |
864 | ||
865 | /*ARGSUSED*/ | |
866 | int | |
867 | oslog_streamioctl(__unused dev_t dev, u_long com, caddr_t data, __unused int flag, __unused struct proc *p) | |
868 | { | |
869 | int err = 0; | |
870 | ||
0a7de745 | 871 | stream_lock(); |
39037602 A |
872 | |
873 | switch (com) { | |
874 | case FIONBIO: | |
0a7de745 | 875 | if (data && *(int *)data) { |
39037602 | 876 | oslog_streamsoftc.sc_state |= LOG_NBIO; |
0a7de745 | 877 | } else { |
39037602 | 878 | oslog_streamsoftc.sc_state &= ~LOG_NBIO; |
0a7de745 | 879 | } |
39037602 A |
880 | break; |
881 | case FIOASYNC: | |
0a7de745 | 882 | if (data && *(int *)data) { |
39037602 | 883 | oslog_streamsoftc.sc_state |= LOG_ASYNC; |
0a7de745 | 884 | } else { |
39037602 | 885 | oslog_streamsoftc.sc_state &= ~LOG_ASYNC; |
0a7de745 | 886 | } |
39037602 A |
887 | break; |
888 | default: | |
889 | err = -1; | |
890 | break; | |
891 | } | |
892 | ||
0a7de745 | 893 | stream_unlock(); |
39037602 A |
894 | return err; |
895 | } | |
896 | ||
f427ee49 A |
897 | __startup_func |
898 | static void | |
39037602 A |
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 | 914 | |
39037602 | 915 | kr = kmem_alloc_flags(kernel_map, &kernel_firehose_addr, |
0a7de745 | 916 | size + (2 * PAGE_SIZE), VM_KERN_MEMORY_LOG, |
f427ee49 | 917 | KMA_GUARD_FIRST | KMA_GUARD_LAST | KMA_ZERO); |
39037602 A |
918 | if (kr != KERN_SUCCESS) { |
919 | panic("Failed to allocate memory for firehose logging buffer"); | |
920 | } | |
921 | kernel_firehose_addr += PAGE_SIZE; | |
39037602 | 922 | /* register buffer with firehose */ |
5ba3f43e | 923 | kernel_firehose_addr = (vm_offset_t)__firehose_buffer_create((size_t *) &size); |
39037602 | 924 | |
f427ee49 A |
925 | printf("oslog_init completed, %u chunks, %u io pages\n", |
926 | __firehose_buffer_kernel_chunk_count, __firehose_num_kernel_io_pages); | |
39037602 | 927 | } |
f427ee49 | 928 | STARTUP(OSLOG, STARTUP_RANK_FIRST, oslog_init); |
2d21ac55 A |
929 | |
930 | /* | |
931 | * log_putc_locked | |
932 | * | |
f427ee49 A |
933 | * Decription: Output a character to the log; assumes the bsd_log_lock() or |
934 | * bsd_log_lock_safe() is held by the caller. | |
2d21ac55 A |
935 | * |
936 | * Parameters: c Character to output | |
937 | * | |
938 | * Returns: (void) | |
939 | * | |
940 | * Notes: This functions is used for multibyte output to the log; it | |
941 | * should be used preferrentially where possible to ensure that | |
942 | * log entries do not end up interspersed due to preemption or | |
943 | * SMP reentrancy. | |
944 | */ | |
1c79356b | 945 | void |
cc8bc92a | 946 | log_putc_locked(struct msgbuf *mbp, char c) |
1c79356b | 947 | { |
1c79356b | 948 | mbp->msg_bufc[mbp->msg_bufx++] = c; |
0a7de745 | 949 | if (mbp->msg_bufx >= mbp->msg_size) { |
1c79356b | 950 | mbp->msg_bufx = 0; |
0a7de745 | 951 | } |
2d21ac55 A |
952 | } |
953 | ||
39037602 A |
954 | static oslog_stream_buf_entry_t |
955 | oslog_stream_find_free_buf_entry_locked(void) | |
956 | { | |
957 | struct msgbuf *mbp; | |
958 | oslog_stream_buf_entry_t buf_entry = NULL; | |
959 | ||
5ba3f43e | 960 | LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED); |
39037602 A |
961 | |
962 | mbp = oslog_streambufp; | |
963 | ||
964 | buf_entry = STAILQ_FIRST(&oslog_stream_free_head); | |
965 | if (buf_entry) { | |
966 | STAILQ_REMOVE_HEAD(&oslog_stream_free_head, buf_entries); | |
0a7de745 | 967 | } else { |
39037602 A |
968 | // If no list elements are available in the free-list, |
969 | // consume the next log line so we can free up its list element | |
970 | oslog_stream_buf_entry_t prev_entry = NULL; | |
971 | ||
972 | buf_entry = STAILQ_FIRST(&oslog_stream_buf_head); | |
973 | while (buf_entry->type == oslog_stream_link_type_metadata) { | |
974 | prev_entry = buf_entry; | |
975 | buf_entry = STAILQ_NEXT(buf_entry, buf_entries); | |
976 | } | |
977 | ||
978 | if (prev_entry == NULL) { | |
979 | STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries); | |
0a7de745 | 980 | } else { |
39037602 A |
981 | STAILQ_REMOVE_AFTER(&oslog_stream_buf_head, prev_entry, buf_entries); |
982 | } | |
983 | ||
984 | mbp->msg_bufr += buf_entry->size; | |
985 | oslog_s_dropped_msgcount++; | |
986 | if (mbp->msg_bufr >= mbp->msg_size) { | |
987 | mbp->msg_bufr = (mbp->msg_bufr % mbp->msg_size); | |
988 | } | |
989 | } | |
990 | ||
991 | return buf_entry; | |
992 | } | |
993 | ||
994 | void | |
995 | oslog_streamwrite_metadata_locked(oslog_stream_buf_entry_t m_entry) | |
996 | { | |
5ba3f43e | 997 | LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED); |
39037602 A |
998 | STAILQ_INSERT_TAIL(&oslog_stream_buf_head, m_entry, buf_entries); |
999 | ||
1000 | return; | |
1001 | } | |
1002 | ||
cc8bc92a A |
1003 | static void |
1004 | oslog_streamwrite_append_bytes(const char *buffer, int buflen) | |
39037602 A |
1005 | { |
1006 | struct msgbuf *mbp; | |
1007 | ||
5ba3f43e | 1008 | LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED); |
39037602 | 1009 | |
0a7de745 A |
1010 | assert(oslog_stream_buf_bytesavail >= buflen); |
1011 | oslog_stream_buf_bytesavail -= buflen; | |
1012 | assert(oslog_stream_buf_bytesavail >= 0); | |
1013 | ||
39037602 | 1014 | mbp = oslog_streambufp; |
39037602 | 1015 | if (mbp->msg_bufx + buflen <= mbp->msg_size) { |
0a7de745 A |
1016 | /* |
1017 | * If this will fit without needing to be split across the end | |
1018 | * of the buffer, copy it directly in one go. | |
1019 | */ | |
39037602 A |
1020 | memcpy((void *)(mbp->msg_bufc + mbp->msg_bufx), buffer, buflen); |
1021 | ||
1022 | mbp->msg_bufx += buflen; | |
1023 | if (mbp->msg_bufx == mbp->msg_size) { | |
1024 | mbp->msg_bufx = 0; | |
1025 | } | |
1026 | } else { | |
0a7de745 A |
1027 | /* |
1028 | * Copy up to the end of the stream buffer, and then put what remains | |
1029 | * at the beginning. | |
1030 | */ | |
39037602 A |
1031 | int bytes_left = mbp->msg_size - mbp->msg_bufx; |
1032 | memcpy((void *)(mbp->msg_bufc + mbp->msg_bufx), buffer, bytes_left); | |
1033 | ||
1034 | buflen -= bytes_left; | |
1035 | buffer += bytes_left; | |
1036 | ||
1037 | // Copy the remainder of the data from the beginning of stream | |
1038 | memcpy((void *)mbp->msg_bufc, buffer, buflen); | |
1039 | mbp->msg_bufx = buflen; | |
1040 | } | |
1041 | return; | |
1042 | } | |
1043 | ||
39037602 A |
1044 | void |
1045 | oslog_streamwrite_locked(firehose_tracepoint_id_u ftid, | |
0a7de745 | 1046 | uint64_t stamp, const void *pubdata, size_t publen) |
39037602 A |
1047 | { |
1048 | struct msgbuf *mbp; | |
39037602 A |
1049 | oslog_stream_buf_entry_t buf_entry = NULL; |
1050 | oslog_stream_buf_entry_t next_entry = NULL; | |
1051 | ||
5ba3f43e | 1052 | LCK_SPIN_ASSERT(&oslog_stream_lock, LCK_ASSERT_OWNED); |
39037602 | 1053 | |
f427ee49 A |
1054 | assert(publen <= UINT16_MAX); |
1055 | const ssize_t ft_length = offsetof(struct firehose_tracepoint_s, ft_data) + publen; | |
1056 | ||
39037602 A |
1057 | mbp = oslog_streambufp; |
1058 | if (ft_length > mbp->msg_size) { | |
cb323159 | 1059 | os_atomic_inc(&oslog_s_error_count, relaxed); |
39037602 A |
1060 | return; |
1061 | } | |
1062 | ||
1063 | // Ensure that we have a list element for this record | |
1064 | buf_entry = oslog_stream_find_free_buf_entry_locked(); | |
1065 | ||
1066 | assert(buf_entry != NULL); | |
1067 | ||
0a7de745 | 1068 | while (ft_length > oslog_stream_buf_bytesavail) { |
39037602 A |
1069 | oslog_stream_buf_entry_t prev_entry = NULL; |
1070 | ||
1071 | next_entry = STAILQ_FIRST(&oslog_stream_buf_head); | |
1072 | assert(next_entry != NULL); | |
1073 | while (next_entry->type == oslog_stream_link_type_metadata) { | |
1074 | prev_entry = next_entry; | |
1075 | next_entry = STAILQ_NEXT(next_entry, buf_entries); | |
1076 | } | |
1077 | ||
1078 | if (prev_entry == NULL) { | |
1079 | STAILQ_REMOVE_HEAD(&oslog_stream_buf_head, buf_entries); | |
0a7de745 | 1080 | } else { |
39037602 A |
1081 | STAILQ_REMOVE_AFTER(&oslog_stream_buf_head, prev_entry, buf_entries); |
1082 | } | |
1083 | ||
1084 | mbp->msg_bufr += next_entry->size; | |
1085 | if (mbp->msg_bufr >= mbp->msg_size) { | |
1086 | mbp->msg_bufr = (mbp->msg_bufr % mbp->msg_size); | |
1087 | } | |
1088 | ||
1089 | oslog_s_dropped_msgcount++; | |
0a7de745 A |
1090 | oslog_stream_buf_bytesavail += next_entry->size; |
1091 | assert(oslog_stream_buf_bytesavail <= oslog_stream_buf_size); | |
39037602 A |
1092 | |
1093 | STAILQ_INSERT_TAIL(&oslog_stream_free_head, next_entry, buf_entries); | |
1094 | } | |
1095 | ||
0a7de745 | 1096 | assert(ft_length <= oslog_stream_buf_bytesavail); |
39037602 A |
1097 | |
1098 | // Write the log line and update the list entry for this record | |
1099 | buf_entry->offset = mbp->msg_bufx; | |
f427ee49 | 1100 | buf_entry->size = (uint16_t)ft_length; |
39037602 A |
1101 | buf_entry->timestamp = stamp; |
1102 | buf_entry->type = oslog_stream_link_type_log; | |
1103 | ||
1104 | // Construct a tracepoint | |
1105 | struct firehose_tracepoint_s fs = { | |
1106 | .ft_thread = thread_tid(current_thread()), | |
1107 | .ft_id.ftid_value = ftid.ftid_value, | |
1108 | .ft_length = publen | |
1109 | }; | |
1110 | ||
1111 | oslog_streamwrite_append_bytes((char *)&fs, sizeof(fs)); | |
f427ee49 | 1112 | oslog_streamwrite_append_bytes(pubdata, (int)publen); |
39037602 A |
1113 | |
1114 | assert(mbp->msg_bufr < mbp->msg_size); | |
1115 | // Insert the element to the buffer data list | |
1116 | STAILQ_INSERT_TAIL(&oslog_stream_buf_head, buf_entry, buf_entries); | |
1117 | ||
1118 | return; | |
1119 | } | |
1120 | ||
2d21ac55 A |
1121 | /* |
1122 | * log_putc | |
1123 | * | |
f427ee49 A |
1124 | * Decription: Output a character to the log; assumes the bsd_log_lock() or |
1125 | * bsd_log_lock_safe() is NOT held by the caller. | |
2d21ac55 A |
1126 | * |
1127 | * Parameters: c Character to output | |
1128 | * | |
1129 | * Returns: (void) | |
1130 | * | |
cc8bc92a | 1131 | * Notes: This function is used for single byte output to the log. It |
2d21ac55 A |
1132 | * primarily exists to maintain binary backward compatibility. |
1133 | */ | |
1134 | void | |
1135 | log_putc(char c) | |
1136 | { | |
f427ee49 A |
1137 | if (!bsd_log_lock(oslog_is_safe())) { |
1138 | os_atomic_inc(&oslog_msgbuf_dropped_charcount, relaxed); | |
1139 | return; | |
1140 | } | |
1141 | ||
cc8bc92a | 1142 | log_putc_locked(msgbufp, c); |
f427ee49 A |
1143 | int unread_count = msgbufp->msg_bufx - msgbufp->msg_bufr; |
1144 | ||
1145 | bsd_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; | |
f427ee49 A |
1184 | new_logdata = kheap_alloc(KHEAP_DATA_BUFFERS, size, Z_WAITOK | Z_ZERO); |
1185 | if (!new_logdata) { | |
2d21ac55 | 1186 | printf("log_setsize: unable to allocate memory\n"); |
0a7de745 | 1187 | return ENOMEM; |
2d21ac55 | 1188 | } |
6d2010ae | 1189 | |
f427ee49 | 1190 | bsd_log_lock_safe(); |
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 | 1242 | |
f427ee49 | 1243 | bsd_log_unlock(); |
6d2010ae | 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 */ | |
f427ee49 | 1250 | kheap_free(KHEAP_DATA_BUFFERS, old_logdata, old_logsize); |
6d2010ae A |
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 | ||
f427ee49 | 1286 | bsd_log_lock_safe(); |
6d2010ae | 1287 | old_bufsize = bufsize = msgbufp->msg_size; |
f427ee49 | 1288 | bsd_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 | |
f427ee49 | 1308 | log_dmesg(user_addr_t buffer, uint32_t buffersize, int32_t *retval) |
cc8bc92a | 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 | |
f427ee49 | 1316 | bsd_log_lock_safe(); |
6d2010ae | 1317 | localbuff_size = (msgbufp->msg_size + 2); /* + '\n' + '\0' */ |
f427ee49 | 1318 | bsd_log_unlock(); |
6d2010ae A |
1319 | |
1320 | /* Allocate a temporary non-circular buffer for copyout */ | |
f427ee49 A |
1321 | localbuff = kheap_alloc(KHEAP_DATA_BUFFERS, localbuff_size, Z_WAITOK); |
1322 | if (!localbuff) { | |
2d21ac55 | 1323 | printf("log_dmesg: unable to allocate memory\n"); |
0a7de745 | 1324 | return ENOMEM; |
2d21ac55 | 1325 | } |
6d2010ae | 1326 | |
2d21ac55 | 1327 | /* in between here, the log could become bigger, but that's fine */ |
f427ee49 | 1328 | bsd_log_lock_safe(); |
2d21ac55 A |
1329 | |
1330 | /* | |
1331 | * The message buffer is circular; start at the write pointer, and | |
1332 | * make one loop up to write pointer - 1. | |
1333 | */ | |
1334 | p = msgbufp->msg_bufc + msgbufp->msg_bufx; | |
1335 | for (i = newl = skip = 0; p != msgbufp->msg_bufc + msgbufp->msg_bufx - 1; ++p) { | |
0a7de745 | 1336 | if (p >= msgbufp->msg_bufc + msgbufp->msg_size) { |
2d21ac55 | 1337 | p = msgbufp->msg_bufc; |
0a7de745 | 1338 | } |
2d21ac55 A |
1339 | ch = *p; |
1340 | /* Skip "\n<.*>" syslog sequences. */ | |
1341 | if (skip) { | |
0a7de745 | 1342 | if (ch == '>') { |
2d21ac55 | 1343 | newl = skip = 0; |
0a7de745 | 1344 | } |
2d21ac55 A |
1345 | continue; |
1346 | } | |
1347 | if (newl && ch == '<') { | |
1348 | skip = 1; | |
1349 | continue; | |
1350 | } | |
0a7de745 | 1351 | if (ch == '\0') { |
2d21ac55 | 1352 | continue; |
0a7de745 | 1353 | } |
6d2010ae | 1354 | newl = (ch == '\n'); |
2d21ac55 | 1355 | localbuff[i++] = ch; |
593a1d5f A |
1356 | /* The original version of this routine contained a buffer |
1357 | * overflow. At the time, a "small" targeted fix was desired | |
1358 | * so the change below to check the buffer bounds was made. | |
1359 | * TODO: rewrite this needlessly convoluted routine. | |
1360 | */ | |
0a7de745 | 1361 | if (i == (localbuff_size - 2)) { |
593a1d5f | 1362 | break; |
0a7de745 | 1363 | } |
2d21ac55 | 1364 | } |
0a7de745 | 1365 | if (!newl) { |
2d21ac55 | 1366 | localbuff[i++] = '\n'; |
0a7de745 | 1367 | } |
2d21ac55 A |
1368 | localbuff[i++] = 0; |
1369 | ||
1370 | if (buffersize >= i) { | |
1371 | copystart = localbuff; | |
1372 | copysize = i; | |
1373 | } else { | |
1374 | copystart = localbuff + i - buffersize; | |
1375 | copysize = buffersize; | |
1376 | } | |
1377 | ||
f427ee49 | 1378 | bsd_log_unlock(); |
2d21ac55 A |
1379 | |
1380 | error = copyout(copystart, buffer, copysize); | |
0a7de745 | 1381 | if (!error) { |
f427ee49 | 1382 | *retval = (int32_t)copysize; |
0a7de745 | 1383 | } |
2d21ac55 | 1384 | |
f427ee49 | 1385 | kheap_free(KHEAP_DATA_BUFFERS, localbuff, localbuff_size); |
0a7de745 | 1386 | return error; |
2d21ac55 | 1387 | } |
39037602 | 1388 | |
d9a64523 A |
1389 | #ifdef CONFIG_XNUPOST |
1390 | ||
f427ee49 | 1391 | size_t find_pattern_in_buffer(const char *, size_t, size_t); |
d9a64523 A |
1392 | |
1393 | /* | |
1394 | * returns count of pattern found in systemlog buffer. | |
1395 | * stops searching further if count reaches expected_count. | |
1396 | */ | |
f427ee49 A |
1397 | size_t |
1398 | find_pattern_in_buffer(const char *pattern, size_t len, size_t expected_count) | |
d9a64523 | 1399 | { |
d9a64523 A |
1400 | if (pattern == NULL || len == 0 || expected_count == 0) { |
1401 | return 0; | |
1402 | } | |
1403 | ||
f427ee49 A |
1404 | size_t msg_bufx = msgbufp->msg_bufx; |
1405 | size_t msg_size = msgbufp->msg_size; | |
1406 | size_t match_count = 0; | |
1407 | ||
1408 | for (size_t i = 0; i < msg_size; i++) { | |
1409 | boolean_t match = TRUE; | |
1410 | for (size_t j = 0; j < len; j++) { | |
1411 | size_t pos = (msg_bufx + i + j) % msg_size; | |
1412 | if (msgbufp->msg_bufc[pos] != pattern[j]) { | |
1413 | match = FALSE; | |
d9a64523 A |
1414 | break; |
1415 | } | |
1416 | } | |
f427ee49 A |
1417 | if (match && ++match_count >= expected_count) { |
1418 | break; | |
d9a64523 A |
1419 | } |
1420 | } | |
f427ee49 | 1421 | |
d9a64523 A |
1422 | return match_count; |
1423 | } | |
1424 | ||
1425 | #endif |