1*94d3b452SApple OSS Distributions /*
2*94d3b452SApple OSS Distributions * Copyright (c) 2015-2020 Apple Inc. All rights reserved.
3*94d3b452SApple OSS Distributions *
4*94d3b452SApple OSS Distributions * @APPLE_LICENSE_HEADER_START@
5*94d3b452SApple OSS Distributions *
6*94d3b452SApple OSS Distributions * This file contains Original Code and/or Modifications of Original Code
7*94d3b452SApple OSS Distributions * as defined in and that are subject to the Apple Public Source License
8*94d3b452SApple OSS Distributions * Version 2.0 (the 'License'). You may not use this file except in
9*94d3b452SApple OSS Distributions * compliance with the License. Please obtain a copy of the License at
10*94d3b452SApple OSS Distributions * http://www.opensource.apple.com/apsl/ and read it before using this
11*94d3b452SApple OSS Distributions * file.
12*94d3b452SApple OSS Distributions *
13*94d3b452SApple OSS Distributions * The Original Code and all software distributed under the License are
14*94d3b452SApple OSS Distributions * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15*94d3b452SApple OSS Distributions * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16*94d3b452SApple OSS Distributions * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17*94d3b452SApple OSS Distributions * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18*94d3b452SApple OSS Distributions * Please see the License for the specific language governing rights and
19*94d3b452SApple OSS Distributions * limitations under the License.
20*94d3b452SApple OSS Distributions *
21*94d3b452SApple OSS Distributions * @APPLE_LICENSE_HEADER_END@
22*94d3b452SApple OSS Distributions */
23*94d3b452SApple OSS Distributions
24*94d3b452SApple OSS Distributions #include <stdbool.h>
25*94d3b452SApple OSS Distributions #include <firehose/tracepoint_private.h>
26*94d3b452SApple OSS Distributions #include <kern/assert.h>
27*94d3b452SApple OSS Distributions #include <kern/counter.h>
28*94d3b452SApple OSS Distributions #include <kern/locks.h>
29*94d3b452SApple OSS Distributions #include <pexpert/pexpert.h>
30*94d3b452SApple OSS Distributions #include <sys/param.h>
31*94d3b452SApple OSS Distributions
32*94d3b452SApple OSS Distributions #if __has_feature(ptrauth_calls)
33*94d3b452SApple OSS Distributions #include <mach/vm_param.h>
34*94d3b452SApple OSS Distributions #include <ptrauth.h>
35*94d3b452SApple OSS Distributions #endif /* __has_feature(ptrauth_calls) */
36*94d3b452SApple OSS Distributions
37*94d3b452SApple OSS Distributions #include "log_encode.h"
38*94d3b452SApple OSS Distributions #include "log_internal.h"
39*94d3b452SApple OSS Distributions #include "log_mem.h"
40*94d3b452SApple OSS Distributions
41*94d3b452SApple OSS Distributions #define LOG_FMT_MAX_PRECISION (1024)
42*94d3b452SApple OSS Distributions #define log_context_cursor(ctx) &(ctx)->ctx_hdr->hdr_data[(ctx)->ctx_content_off]
43*94d3b452SApple OSS Distributions #define TRACEPOINT_BUF_MAX_SIZE (64)
44*94d3b452SApple OSS Distributions
45*94d3b452SApple OSS Distributions typedef struct {
46*94d3b452SApple OSS Distributions uint8_t *tp_buf;
47*94d3b452SApple OSS Distributions size_t tp_size;
48*94d3b452SApple OSS Distributions } tracepoint_buf_t;
49*94d3b452SApple OSS Distributions
50*94d3b452SApple OSS Distributions SCALABLE_COUNTER_DEFINE(oslog_p_fmt_invalid_msgcount);
51*94d3b452SApple OSS Distributions SCALABLE_COUNTER_DEFINE(oslog_p_fmt_max_args_msgcount);
52*94d3b452SApple OSS Distributions SCALABLE_COUNTER_DEFINE(oslog_p_truncated_msgcount);
53*94d3b452SApple OSS Distributions
54*94d3b452SApple OSS Distributions extern boolean_t doprnt_hide_pointers;
55*94d3b452SApple OSS Distributions
56*94d3b452SApple OSS Distributions static bool
is_digit(char ch)57*94d3b452SApple OSS Distributions is_digit(char ch)
58*94d3b452SApple OSS Distributions {
59*94d3b452SApple OSS Distributions return (ch >= '0') && (ch <= '9');
60*94d3b452SApple OSS Distributions }
61*94d3b452SApple OSS Distributions
62*94d3b452SApple OSS Distributions static bool
is_kernel_pointer(void * arg,size_t arg_len)63*94d3b452SApple OSS Distributions is_kernel_pointer(void *arg, size_t arg_len)
64*94d3b452SApple OSS Distributions {
65*94d3b452SApple OSS Distributions if (arg_len < sizeof(void *)) {
66*94d3b452SApple OSS Distributions return false;
67*94d3b452SApple OSS Distributions }
68*94d3b452SApple OSS Distributions
69*94d3b452SApple OSS Distributions unsigned long long value = 0;
70*94d3b452SApple OSS Distributions assert(arg_len <= sizeof(value));
71*94d3b452SApple OSS Distributions (void) memcpy(&value, arg, arg_len);
72*94d3b452SApple OSS Distributions
73*94d3b452SApple OSS Distributions #if __has_feature(ptrauth_calls)
74*94d3b452SApple OSS Distributions /**
75*94d3b452SApple OSS Distributions * Strip out the pointer authentication code before
76*94d3b452SApple OSS Distributions * checking whether the pointer is a kernel address.
77*94d3b452SApple OSS Distributions */
78*94d3b452SApple OSS Distributions value = (unsigned long long)VM_KERNEL_STRIP_PTR(value);
79*94d3b452SApple OSS Distributions #endif /* __has_feature(ptrauth_calls) */
80*94d3b452SApple OSS Distributions
81*94d3b452SApple OSS Distributions return value >= VM_MIN_KERNEL_AND_KEXT_ADDRESS && value <= VM_MAX_KERNEL_ADDRESS;
82*94d3b452SApple OSS Distributions }
83*94d3b452SApple OSS Distributions
84*94d3b452SApple OSS Distributions static void
log_context_cursor_advance(os_log_context_t ctx,size_t amount)85*94d3b452SApple OSS Distributions log_context_cursor_advance(os_log_context_t ctx, size_t amount)
86*94d3b452SApple OSS Distributions {
87*94d3b452SApple OSS Distributions ctx->ctx_content_off += amount;
88*94d3b452SApple OSS Distributions assert(log_context_cursor(ctx) <= (ctx->ctx_buffer + ctx->ctx_buffer_sz));
89*94d3b452SApple OSS Distributions }
90*94d3b452SApple OSS Distributions
91*94d3b452SApple OSS Distributions static bool
log_fits(os_log_context_t ctx,size_t data_size)92*94d3b452SApple OSS Distributions log_fits(os_log_context_t ctx, size_t data_size)
93*94d3b452SApple OSS Distributions {
94*94d3b452SApple OSS Distributions return (ctx->ctx_content_off + data_size) <= ctx->ctx_content_sz;
95*94d3b452SApple OSS Distributions }
96*94d3b452SApple OSS Distributions
97*94d3b452SApple OSS Distributions static bool
log_fits_cmd(os_log_context_t ctx,size_t data_size)98*94d3b452SApple OSS Distributions log_fits_cmd(os_log_context_t ctx, size_t data_size)
99*94d3b452SApple OSS Distributions {
100*94d3b452SApple OSS Distributions return log_fits(ctx, sizeof(*ctx->ctx_hdr) + data_size);
101*94d3b452SApple OSS Distributions }
102*94d3b452SApple OSS Distributions
103*94d3b452SApple OSS Distributions static void
log_range_update(os_log_fmt_range_t range,uint16_t offset,uint16_t length)104*94d3b452SApple OSS Distributions log_range_update(os_log_fmt_range_t range, uint16_t offset, uint16_t length)
105*94d3b452SApple OSS Distributions {
106*94d3b452SApple OSS Distributions range->offset = offset;
107*94d3b452SApple OSS Distributions /*
108*94d3b452SApple OSS Distributions * Truncated flag may have already been set earlier, hence do not
109*94d3b452SApple OSS Distributions * overwrite it blindly.
110*94d3b452SApple OSS Distributions */
111*94d3b452SApple OSS Distributions if (length < range->length) {
112*94d3b452SApple OSS Distributions range->truncated = true;
113*94d3b452SApple OSS Distributions }
114*94d3b452SApple OSS Distributions range->length = length;
115*94d3b452SApple OSS Distributions }
116*94d3b452SApple OSS Distributions
117*94d3b452SApple OSS Distributions /*
118*94d3b452SApple OSS Distributions * Stores a command in the main section. The value itself is wrapped in
119*94d3b452SApple OSS Distributions * the os_log_fmt_cmd_t struct.
120*94d3b452SApple OSS Distributions */
121*94d3b452SApple OSS Distributions static void
log_add_cmd(os_log_context_t ctx,os_log_fmt_cmd_type_t type,uint8_t flags,void * arg,size_t arg_size)122*94d3b452SApple OSS Distributions log_add_cmd(os_log_context_t ctx, os_log_fmt_cmd_type_t type, uint8_t flags,
123*94d3b452SApple OSS Distributions void *arg, size_t arg_size)
124*94d3b452SApple OSS Distributions {
125*94d3b452SApple OSS Distributions os_log_fmt_cmd_t cmd;
126*94d3b452SApple OSS Distributions const size_t cmd_sz = sizeof(*cmd) + arg_size;
127*94d3b452SApple OSS Distributions
128*94d3b452SApple OSS Distributions assert(log_fits_cmd(ctx, cmd_sz));
129*94d3b452SApple OSS Distributions assert(arg_size <= UINT8_MAX);
130*94d3b452SApple OSS Distributions
131*94d3b452SApple OSS Distributions cmd = (os_log_fmt_cmd_t)log_context_cursor(ctx);
132*94d3b452SApple OSS Distributions cmd->cmd_type = type;
133*94d3b452SApple OSS Distributions cmd->cmd_flags = flags;
134*94d3b452SApple OSS Distributions cmd->cmd_size = (uint8_t)arg_size;
135*94d3b452SApple OSS Distributions (void) memcpy(cmd->cmd_data, arg, cmd->cmd_size);
136*94d3b452SApple OSS Distributions
137*94d3b452SApple OSS Distributions assert(cmd_sz == sizeof(*cmd) + cmd->cmd_size);
138*94d3b452SApple OSS Distributions log_context_cursor_advance(ctx, cmd_sz);
139*94d3b452SApple OSS Distributions }
140*94d3b452SApple OSS Distributions
141*94d3b452SApple OSS Distributions /*
142*94d3b452SApple OSS Distributions * Collect details about argument which needs to be stored in the pubdata
143*94d3b452SApple OSS Distributions * section.
144*94d3b452SApple OSS Distributions */
145*94d3b452SApple OSS Distributions static void
log_collect_public_range_data(os_log_context_t ctx,os_log_fmt_range_t range,void * arg)146*94d3b452SApple OSS Distributions log_collect_public_range_data(os_log_context_t ctx, os_log_fmt_range_t range, void *arg)
147*94d3b452SApple OSS Distributions {
148*94d3b452SApple OSS Distributions ctx->ctx_pubdata[ctx->ctx_pubdata_cnt++] = (char *)arg;
149*94d3b452SApple OSS Distributions ctx->ctx_pubdata_sz += range->length;
150*94d3b452SApple OSS Distributions }
151*94d3b452SApple OSS Distributions
152*94d3b452SApple OSS Distributions static void
log_add_range_data(os_log_context_t ctx,os_log_fmt_range_t range,void * arg)153*94d3b452SApple OSS Distributions log_add_range_data(os_log_context_t ctx, os_log_fmt_range_t range, void *arg)
154*94d3b452SApple OSS Distributions {
155*94d3b452SApple OSS Distributions assert(log_fits(ctx, range->length));
156*94d3b452SApple OSS Distributions (void) memcpy(log_context_cursor(ctx), arg, range->length);
157*94d3b452SApple OSS Distributions log_context_cursor_advance(ctx, range->length);
158*94d3b452SApple OSS Distributions }
159*94d3b452SApple OSS Distributions
160*94d3b452SApple OSS Distributions static struct os_log_fmt_range_s
log_create_range(os_log_context_t ctx,size_t arg_len)161*94d3b452SApple OSS Distributions log_create_range(os_log_context_t ctx, size_t arg_len)
162*94d3b452SApple OSS Distributions {
163*94d3b452SApple OSS Distributions const size_t final_arg_len = MIN(arg_len, UINT16_MAX);
164*94d3b452SApple OSS Distributions
165*94d3b452SApple OSS Distributions return (struct os_log_fmt_range_s) {
166*94d3b452SApple OSS Distributions .offset = ctx->ctx_pubdata_sz,
167*94d3b452SApple OSS Distributions .length = (uint16_t)final_arg_len,
168*94d3b452SApple OSS Distributions .truncated = (final_arg_len < arg_len)
169*94d3b452SApple OSS Distributions };
170*94d3b452SApple OSS Distributions }
171*94d3b452SApple OSS Distributions
172*94d3b452SApple OSS Distributions static int
log_add_range_arg(os_log_context_t ctx,os_log_fmt_cmd_type_t type,os_log_fmt_cmd_flags_t flags,void * arg,size_t arg_len)173*94d3b452SApple OSS Distributions log_add_range_arg(os_log_context_t ctx, os_log_fmt_cmd_type_t type, os_log_fmt_cmd_flags_t flags,
174*94d3b452SApple OSS Distributions void *arg, size_t arg_len)
175*94d3b452SApple OSS Distributions {
176*94d3b452SApple OSS Distributions struct os_log_fmt_range_s range;
177*94d3b452SApple OSS Distributions
178*94d3b452SApple OSS Distributions if (!log_fits_cmd(ctx, sizeof(range))) {
179*94d3b452SApple OSS Distributions return ENOMEM;
180*94d3b452SApple OSS Distributions }
181*94d3b452SApple OSS Distributions
182*94d3b452SApple OSS Distributions range = log_create_range(ctx, arg_len);
183*94d3b452SApple OSS Distributions
184*94d3b452SApple OSS Distributions if (flags == OSLF_CMD_FLAG_PUBLIC) {
185*94d3b452SApple OSS Distributions if (ctx->ctx_pubdata_cnt == OS_LOG_MAX_PUB_ARGS) {
186*94d3b452SApple OSS Distributions return ENOMEM;
187*94d3b452SApple OSS Distributions }
188*94d3b452SApple OSS Distributions assert(ctx->ctx_pubdata_cnt < OS_LOG_MAX_PUB_ARGS);
189*94d3b452SApple OSS Distributions log_collect_public_range_data(ctx, &range, arg);
190*94d3b452SApple OSS Distributions }
191*94d3b452SApple OSS Distributions log_add_cmd(ctx, type, flags, &range, sizeof(range));
192*94d3b452SApple OSS Distributions ctx->ctx_hdr->hdr_cmd_cnt++;
193*94d3b452SApple OSS Distributions
194*94d3b452SApple OSS Distributions return 0;
195*94d3b452SApple OSS Distributions }
196*94d3b452SApple OSS Distributions
197*94d3b452SApple OSS Distributions /*
198*94d3b452SApple OSS Distributions * Adds a scalar argument value to the main section.
199*94d3b452SApple OSS Distributions */
200*94d3b452SApple OSS Distributions static int
log_add_arg(os_log_context_t ctx,os_log_fmt_cmd_type_t type,void * arg,size_t arg_len)201*94d3b452SApple OSS Distributions log_add_arg(os_log_context_t ctx, os_log_fmt_cmd_type_t type, void *arg, size_t arg_len)
202*94d3b452SApple OSS Distributions {
203*94d3b452SApple OSS Distributions assert(type == OSLF_CMD_TYPE_COUNT || type == OSLF_CMD_TYPE_SCALAR);
204*94d3b452SApple OSS Distributions assert(arg_len < UINT16_MAX);
205*94d3b452SApple OSS Distributions
206*94d3b452SApple OSS Distributions if (log_fits_cmd(ctx, arg_len)) {
207*94d3b452SApple OSS Distributions log_add_cmd(ctx, type, OSLF_CMD_FLAG_PUBLIC, arg, arg_len);
208*94d3b452SApple OSS Distributions ctx->ctx_hdr->hdr_cmd_cnt++;
209*94d3b452SApple OSS Distributions return 0;
210*94d3b452SApple OSS Distributions }
211*94d3b452SApple OSS Distributions
212*94d3b452SApple OSS Distributions return ENOMEM;
213*94d3b452SApple OSS Distributions }
214*94d3b452SApple OSS Distributions
215*94d3b452SApple OSS Distributions static void
log_encode_public_data(os_log_context_t ctx)216*94d3b452SApple OSS Distributions log_encode_public_data(os_log_context_t ctx)
217*94d3b452SApple OSS Distributions {
218*94d3b452SApple OSS Distributions const uint16_t orig_content_off = ctx->ctx_content_off;
219*94d3b452SApple OSS Distributions os_log_fmt_hdr_t const hdr = ctx->ctx_hdr;
220*94d3b452SApple OSS Distributions os_log_fmt_cmd_t cmd = (os_log_fmt_cmd_t)hdr->hdr_data;
221*94d3b452SApple OSS Distributions
222*94d3b452SApple OSS Distributions assert(ctx->ctx_pubdata_cnt <= hdr->hdr_cmd_cnt);
223*94d3b452SApple OSS Distributions
224*94d3b452SApple OSS Distributions for (int i = 0, pub_i = 0; i < hdr->hdr_cmd_cnt; i++, cmd = (os_log_fmt_cmd_t)(cmd->cmd_data + cmd->cmd_size)) {
225*94d3b452SApple OSS Distributions if (cmd->cmd_type != OSLF_CMD_TYPE_STRING) {
226*94d3b452SApple OSS Distributions continue;
227*94d3b452SApple OSS Distributions }
228*94d3b452SApple OSS Distributions
229*94d3b452SApple OSS Distributions os_log_fmt_range_t const range __attribute__((aligned(8))) = (os_log_fmt_range_t)&cmd->cmd_data;
230*94d3b452SApple OSS Distributions
231*94d3b452SApple OSS Distributions // Fix offset and length of the argument data in the hdr.
232*94d3b452SApple OSS Distributions log_range_update(range, ctx->ctx_content_off - orig_content_off,
233*94d3b452SApple OSS Distributions MIN(range->length, ctx->ctx_content_sz - ctx->ctx_content_off));
234*94d3b452SApple OSS Distributions
235*94d3b452SApple OSS Distributions if (range->truncated) {
236*94d3b452SApple OSS Distributions ctx->ctx_truncated = true;
237*94d3b452SApple OSS Distributions }
238*94d3b452SApple OSS Distributions
239*94d3b452SApple OSS Distributions assert(pub_i < ctx->ctx_pubdata_cnt);
240*94d3b452SApple OSS Distributions log_add_range_data(ctx, range, ctx->ctx_pubdata[pub_i++]);
241*94d3b452SApple OSS Distributions }
242*94d3b452SApple OSS Distributions }
243*94d3b452SApple OSS Distributions
244*94d3b452SApple OSS Distributions static bool
log_expand(os_log_context_t ctx,size_t new_size)245*94d3b452SApple OSS Distributions log_expand(os_log_context_t ctx, size_t new_size)
246*94d3b452SApple OSS Distributions {
247*94d3b452SApple OSS Distributions assert(new_size > ctx->ctx_buffer_sz);
248*94d3b452SApple OSS Distributions
249*94d3b452SApple OSS Distributions if (!oslog_is_safe()) {
250*94d3b452SApple OSS Distributions return false;
251*94d3b452SApple OSS Distributions }
252*94d3b452SApple OSS Distributions
253*94d3b452SApple OSS Distributions size_t final_size = new_size;
254*94d3b452SApple OSS Distributions
255*94d3b452SApple OSS Distributions void *buf = logmem_alloc_locked(ctx->ctx_logmem, &final_size);
256*94d3b452SApple OSS Distributions if (!buf) {
257*94d3b452SApple OSS Distributions return false;
258*94d3b452SApple OSS Distributions }
259*94d3b452SApple OSS Distributions assert(final_size >= new_size);
260*94d3b452SApple OSS Distributions
261*94d3b452SApple OSS Distributions // address length header + already stored data
262*94d3b452SApple OSS Distributions const size_t hdr_size = (uint8_t *)ctx->ctx_hdr - ctx->ctx_buffer;
263*94d3b452SApple OSS Distributions const size_t copy_size = hdr_size + sizeof(*ctx->ctx_hdr) + ctx->ctx_content_sz;
264*94d3b452SApple OSS Distributions assert(copy_size <= new_size);
265*94d3b452SApple OSS Distributions (void) memcpy(buf, ctx->ctx_buffer, copy_size);
266*94d3b452SApple OSS Distributions
267*94d3b452SApple OSS Distributions if (ctx->ctx_allocated) {
268*94d3b452SApple OSS Distributions logmem_free_locked(ctx->ctx_logmem, ctx->ctx_buffer, ctx->ctx_buffer_sz);
269*94d3b452SApple OSS Distributions }
270*94d3b452SApple OSS Distributions
271*94d3b452SApple OSS Distributions ctx->ctx_buffer = buf;
272*94d3b452SApple OSS Distributions ctx->ctx_buffer_sz = final_size;
273*94d3b452SApple OSS Distributions ctx->ctx_content_sz = (uint16_t)(ctx->ctx_buffer_sz - hdr_size - sizeof(*ctx->ctx_hdr));
274*94d3b452SApple OSS Distributions ctx->ctx_hdr = (os_log_fmt_hdr_t)&ctx->ctx_buffer[hdr_size];
275*94d3b452SApple OSS Distributions ctx->ctx_allocated = true;
276*94d3b452SApple OSS Distributions
277*94d3b452SApple OSS Distributions return true;
278*94d3b452SApple OSS Distributions }
279*94d3b452SApple OSS Distributions
280*94d3b452SApple OSS Distributions static int
log_encode_fmt_arg(void * arg,size_t arg_len,os_log_fmt_cmd_type_t type,os_log_context_t ctx)281*94d3b452SApple OSS Distributions log_encode_fmt_arg(void *arg, size_t arg_len, os_log_fmt_cmd_type_t type, os_log_context_t ctx)
282*94d3b452SApple OSS Distributions {
283*94d3b452SApple OSS Distributions int rc = 0;
284*94d3b452SApple OSS Distributions
285*94d3b452SApple OSS Distributions switch (type) {
286*94d3b452SApple OSS Distributions case OSLF_CMD_TYPE_COUNT:
287*94d3b452SApple OSS Distributions case OSLF_CMD_TYPE_SCALAR:
288*94d3b452SApple OSS Distributions // Scrub kernel pointers.
289*94d3b452SApple OSS Distributions if (doprnt_hide_pointers && is_kernel_pointer(arg, arg_len)) {
290*94d3b452SApple OSS Distributions rc = log_add_range_arg(ctx, type, OSLF_CMD_FLAG_PRIVATE, NULL, 0);
291*94d3b452SApple OSS Distributions ctx->ctx_hdr->hdr_flags |= OSLF_HDR_FLAG_HAS_PRIVATE;
292*94d3b452SApple OSS Distributions } else {
293*94d3b452SApple OSS Distributions rc = log_add_arg(ctx, type, arg, arg_len);
294*94d3b452SApple OSS Distributions }
295*94d3b452SApple OSS Distributions break;
296*94d3b452SApple OSS Distributions case OSLF_CMD_TYPE_STRING:
297*94d3b452SApple OSS Distributions rc = log_add_range_arg(ctx, type, OSLF_CMD_FLAG_PUBLIC, arg, arg_len);
298*94d3b452SApple OSS Distributions ctx->ctx_hdr->hdr_flags |= OSLF_HDR_FLAG_HAS_NON_SCALAR;
299*94d3b452SApple OSS Distributions break;
300*94d3b452SApple OSS Distributions default:
301*94d3b452SApple OSS Distributions panic("Unsupported log value type");
302*94d3b452SApple OSS Distributions }
303*94d3b452SApple OSS Distributions
304*94d3b452SApple OSS Distributions return rc;
305*94d3b452SApple OSS Distributions }
306*94d3b452SApple OSS Distributions
307*94d3b452SApple OSS Distributions static int
log_encode_fmt(os_log_context_t ctx,const char * format,va_list args)308*94d3b452SApple OSS Distributions log_encode_fmt(os_log_context_t ctx, const char *format, va_list args)
309*94d3b452SApple OSS Distributions {
310*94d3b452SApple OSS Distributions const char *position = format;
311*94d3b452SApple OSS Distributions
312*94d3b452SApple OSS Distributions while ((position = strchr(position, '%'))) {
313*94d3b452SApple OSS Distributions position++; // Look at character(s) after %.
314*94d3b452SApple OSS Distributions
315*94d3b452SApple OSS Distributions int type = OST_INT;
316*94d3b452SApple OSS Distributions boolean_t has_precision = false;
317*94d3b452SApple OSS Distributions int precision = 0;
318*94d3b452SApple OSS Distributions
319*94d3b452SApple OSS Distributions for (bool done = false; !done; position++) {
320*94d3b452SApple OSS Distributions union os_log_fmt_types_u value;
321*94d3b452SApple OSS Distributions size_t str_length;
322*94d3b452SApple OSS Distributions int err = 0;
323*94d3b452SApple OSS Distributions
324*94d3b452SApple OSS Distributions switch (position[0]) {
325*94d3b452SApple OSS Distributions case '%':
326*94d3b452SApple OSS Distributions // %% prints % character
327*94d3b452SApple OSS Distributions done = true;
328*94d3b452SApple OSS Distributions break;
329*94d3b452SApple OSS Distributions
330*94d3b452SApple OSS Distributions /* type of types or other */
331*94d3b452SApple OSS Distributions case 'l': // longer
332*94d3b452SApple OSS Distributions type++;
333*94d3b452SApple OSS Distributions break;
334*94d3b452SApple OSS Distributions
335*94d3b452SApple OSS Distributions case 'h': // shorter
336*94d3b452SApple OSS Distributions type--;
337*94d3b452SApple OSS Distributions break;
338*94d3b452SApple OSS Distributions
339*94d3b452SApple OSS Distributions case 'z':
340*94d3b452SApple OSS Distributions type = OST_SIZE;
341*94d3b452SApple OSS Distributions break;
342*94d3b452SApple OSS Distributions
343*94d3b452SApple OSS Distributions case 'j':
344*94d3b452SApple OSS Distributions type = OST_INTMAX;
345*94d3b452SApple OSS Distributions break;
346*94d3b452SApple OSS Distributions
347*94d3b452SApple OSS Distributions case 't':
348*94d3b452SApple OSS Distributions type = OST_PTRDIFF;
349*94d3b452SApple OSS Distributions break;
350*94d3b452SApple OSS Distributions
351*94d3b452SApple OSS Distributions case 'q':
352*94d3b452SApple OSS Distributions type = OST_LONGLONG;
353*94d3b452SApple OSS Distributions break;
354*94d3b452SApple OSS Distributions
355*94d3b452SApple OSS Distributions case '.': // precision
356*94d3b452SApple OSS Distributions if (position[1] == '*') {
357*94d3b452SApple OSS Distributions // Dynamic precision, argument holds actual value.
358*94d3b452SApple OSS Distributions precision = va_arg(args, int);
359*94d3b452SApple OSS Distributions position++;
360*94d3b452SApple OSS Distributions } else {
361*94d3b452SApple OSS Distributions // Static precision, the value follows in the fmt.
362*94d3b452SApple OSS Distributions precision = 0;
363*94d3b452SApple OSS Distributions while (is_digit(position[1])) {
364*94d3b452SApple OSS Distributions if (precision < LOG_FMT_MAX_PRECISION) {
365*94d3b452SApple OSS Distributions precision = 10 * precision + (position[1] - '0');
366*94d3b452SApple OSS Distributions }
367*94d3b452SApple OSS Distributions position++;
368*94d3b452SApple OSS Distributions }
369*94d3b452SApple OSS Distributions precision = MIN(precision, LOG_FMT_MAX_PRECISION);
370*94d3b452SApple OSS Distributions }
371*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&precision, sizeof(precision), OSLF_CMD_TYPE_COUNT, ctx);
372*94d3b452SApple OSS Distributions // A negative precision is treated as though it were missing.
373*94d3b452SApple OSS Distributions if (precision >= 0) {
374*94d3b452SApple OSS Distributions has_precision = true;
375*94d3b452SApple OSS Distributions }
376*94d3b452SApple OSS Distributions break;
377*94d3b452SApple OSS Distributions
378*94d3b452SApple OSS Distributions case '-': // left-align
379*94d3b452SApple OSS Distributions case '+': // force sign
380*94d3b452SApple OSS Distributions case ' ': // prefix non-negative with space
381*94d3b452SApple OSS Distributions case '#': // alternate
382*94d3b452SApple OSS Distributions case '\'': // group by thousands
383*94d3b452SApple OSS Distributions break;
384*94d3b452SApple OSS Distributions
385*94d3b452SApple OSS Distributions /* fixed types */
386*94d3b452SApple OSS Distributions case 'd': // integer
387*94d3b452SApple OSS Distributions case 'i': // integer
388*94d3b452SApple OSS Distributions case 'o': // octal
389*94d3b452SApple OSS Distributions case 'u': // unsigned
390*94d3b452SApple OSS Distributions case 'x': // hex
391*94d3b452SApple OSS Distributions case 'X': // upper-hex
392*94d3b452SApple OSS Distributions switch (type) {
393*94d3b452SApple OSS Distributions case OST_CHAR:
394*94d3b452SApple OSS Distributions value.ch = (char) va_arg(args, int);
395*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.ch, sizeof(value.ch), OSLF_CMD_TYPE_SCALAR, ctx);
396*94d3b452SApple OSS Distributions break;
397*94d3b452SApple OSS Distributions
398*94d3b452SApple OSS Distributions case OST_SHORT:
399*94d3b452SApple OSS Distributions value.s = (short) va_arg(args, int);
400*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.s, sizeof(value.s), OSLF_CMD_TYPE_SCALAR, ctx);
401*94d3b452SApple OSS Distributions break;
402*94d3b452SApple OSS Distributions
403*94d3b452SApple OSS Distributions case OST_INT:
404*94d3b452SApple OSS Distributions value.i = va_arg(args, int);
405*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.i, sizeof(value.i), OSLF_CMD_TYPE_SCALAR, ctx);
406*94d3b452SApple OSS Distributions break;
407*94d3b452SApple OSS Distributions
408*94d3b452SApple OSS Distributions case OST_LONG:
409*94d3b452SApple OSS Distributions value.l = va_arg(args, long);
410*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.l, sizeof(value.l), OSLF_CMD_TYPE_SCALAR, ctx);
411*94d3b452SApple OSS Distributions break;
412*94d3b452SApple OSS Distributions
413*94d3b452SApple OSS Distributions case OST_LONGLONG:
414*94d3b452SApple OSS Distributions value.ll = va_arg(args, long long);
415*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.ll, sizeof(value.ll), OSLF_CMD_TYPE_SCALAR, ctx);
416*94d3b452SApple OSS Distributions break;
417*94d3b452SApple OSS Distributions
418*94d3b452SApple OSS Distributions case OST_SIZE:
419*94d3b452SApple OSS Distributions value.z = va_arg(args, size_t);
420*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.z, sizeof(value.z), OSLF_CMD_TYPE_SCALAR, ctx);
421*94d3b452SApple OSS Distributions break;
422*94d3b452SApple OSS Distributions
423*94d3b452SApple OSS Distributions case OST_INTMAX:
424*94d3b452SApple OSS Distributions value.im = va_arg(args, intmax_t);
425*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.im, sizeof(value.im), OSLF_CMD_TYPE_SCALAR, ctx);
426*94d3b452SApple OSS Distributions break;
427*94d3b452SApple OSS Distributions
428*94d3b452SApple OSS Distributions case OST_PTRDIFF:
429*94d3b452SApple OSS Distributions value.pd = va_arg(args, ptrdiff_t);
430*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.pd, sizeof(value.pd), OSLF_CMD_TYPE_SCALAR, ctx);
431*94d3b452SApple OSS Distributions break;
432*94d3b452SApple OSS Distributions
433*94d3b452SApple OSS Distributions default:
434*94d3b452SApple OSS Distributions return EINVAL;
435*94d3b452SApple OSS Distributions }
436*94d3b452SApple OSS Distributions done = true;
437*94d3b452SApple OSS Distributions break;
438*94d3b452SApple OSS Distributions
439*94d3b452SApple OSS Distributions case 'p': // pointer
440*94d3b452SApple OSS Distributions value.p = va_arg(args, void *);
441*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.p, sizeof(value.p), OSLF_CMD_TYPE_SCALAR, ctx);
442*94d3b452SApple OSS Distributions done = true;
443*94d3b452SApple OSS Distributions break;
444*94d3b452SApple OSS Distributions
445*94d3b452SApple OSS Distributions case 'c': // char
446*94d3b452SApple OSS Distributions value.ch = (char) va_arg(args, int);
447*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.ch, sizeof(value.ch), OSLF_CMD_TYPE_SCALAR, ctx);
448*94d3b452SApple OSS Distributions done = true;
449*94d3b452SApple OSS Distributions break;
450*94d3b452SApple OSS Distributions
451*94d3b452SApple OSS Distributions case 's': // string
452*94d3b452SApple OSS Distributions value.pch = va_arg(args, char *);
453*94d3b452SApple OSS Distributions if (!value.pch) {
454*94d3b452SApple OSS Distributions str_length = 0;
455*94d3b452SApple OSS Distributions } else if (has_precision) {
456*94d3b452SApple OSS Distributions assert(precision >= 0);
457*94d3b452SApple OSS Distributions str_length = strnlen(value.pch, precision);
458*94d3b452SApple OSS Distributions } else {
459*94d3b452SApple OSS Distributions str_length = strlen(value.pch) + 1;
460*94d3b452SApple OSS Distributions }
461*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(value.pch, str_length, OSLF_CMD_TYPE_STRING, ctx);
462*94d3b452SApple OSS Distributions done = true;
463*94d3b452SApple OSS Distributions break;
464*94d3b452SApple OSS Distributions
465*94d3b452SApple OSS Distributions case 'm':
466*94d3b452SApple OSS Distributions value.i = 0; // Does %m make sense in the kernel?
467*94d3b452SApple OSS Distributions err = log_encode_fmt_arg(&value.i, sizeof(value.i), OSLF_CMD_TYPE_SCALAR, ctx);
468*94d3b452SApple OSS Distributions done = true;
469*94d3b452SApple OSS Distributions break;
470*94d3b452SApple OSS Distributions
471*94d3b452SApple OSS Distributions case '0' ... '9':
472*94d3b452SApple OSS Distributions // Skipping field width, libtrace takes care of it.
473*94d3b452SApple OSS Distributions break;
474*94d3b452SApple OSS Distributions
475*94d3b452SApple OSS Distributions default:
476*94d3b452SApple OSS Distributions return EINVAL;
477*94d3b452SApple OSS Distributions }
478*94d3b452SApple OSS Distributions
479*94d3b452SApple OSS Distributions if (slowpath(err)) {
480*94d3b452SApple OSS Distributions return err;
481*94d3b452SApple OSS Distributions }
482*94d3b452SApple OSS Distributions }
483*94d3b452SApple OSS Distributions }
484*94d3b452SApple OSS Distributions
485*94d3b452SApple OSS Distributions return 0;
486*94d3b452SApple OSS Distributions }
487*94d3b452SApple OSS Distributions
488*94d3b452SApple OSS Distributions OS_ALWAYS_INLINE
489*94d3b452SApple OSS Distributions static inline void
tracepoint_buf_add(tracepoint_buf_t * tp,const void * data,size_t size)490*94d3b452SApple OSS Distributions tracepoint_buf_add(tracepoint_buf_t *tp, const void *data, size_t size)
491*94d3b452SApple OSS Distributions {
492*94d3b452SApple OSS Distributions assert((tp->tp_size + size) <= TRACEPOINT_BUF_MAX_SIZE);
493*94d3b452SApple OSS Distributions memcpy(&tp->tp_buf[tp->tp_size], data, size);
494*94d3b452SApple OSS Distributions tp->tp_size += size;
495*94d3b452SApple OSS Distributions }
496*94d3b452SApple OSS Distributions
497*94d3b452SApple OSS Distributions static void
tracepoint_buf_location(tracepoint_buf_t * tpb,uintptr_t loc,size_t loc_size)498*94d3b452SApple OSS Distributions tracepoint_buf_location(tracepoint_buf_t *tpb, uintptr_t loc, size_t loc_size)
499*94d3b452SApple OSS Distributions {
500*94d3b452SApple OSS Distributions if (loc_size == sizeof(uintptr_t)) {
501*94d3b452SApple OSS Distributions #if __LP64__
502*94d3b452SApple OSS Distributions loc_size = 6; // 48 bits are enough
503*94d3b452SApple OSS Distributions #endif
504*94d3b452SApple OSS Distributions tracepoint_buf_add(tpb, (uintptr_t[]){ loc }, loc_size);
505*94d3b452SApple OSS Distributions } else {
506*94d3b452SApple OSS Distributions assert(loc_size == sizeof(uint32_t));
507*94d3b452SApple OSS Distributions tracepoint_buf_add(tpb, (uint32_t[]){ (uint32_t)loc }, loc_size);
508*94d3b452SApple OSS Distributions }
509*94d3b452SApple OSS Distributions }
510*94d3b452SApple OSS Distributions
511*94d3b452SApple OSS Distributions static void
os_log_context_prepare_header(os_log_context_t ctx,size_t hdr_size)512*94d3b452SApple OSS Distributions os_log_context_prepare_header(os_log_context_t ctx, size_t hdr_size)
513*94d3b452SApple OSS Distributions {
514*94d3b452SApple OSS Distributions assert(hdr_size > 0 && hdr_size <= TRACEPOINT_BUF_MAX_SIZE);
515*94d3b452SApple OSS Distributions ctx->ctx_hdr = (os_log_fmt_hdr_t)&ctx->ctx_buffer[hdr_size];
516*94d3b452SApple OSS Distributions bzero(ctx->ctx_hdr, sizeof(*ctx->ctx_hdr));
517*94d3b452SApple OSS Distributions ctx->ctx_content_sz = (uint16_t)(ctx->ctx_buffer_sz - hdr_size - sizeof(*ctx->ctx_hdr));
518*94d3b452SApple OSS Distributions }
519*94d3b452SApple OSS Distributions
520*94d3b452SApple OSS Distributions /*
521*94d3b452SApple OSS Distributions * Encodes argument (meta)data into a format consumed by libtrace. Stores
522*94d3b452SApple OSS Distributions * metadada for all arguments first. Metadata also include scalar argument
523*94d3b452SApple OSS Distributions * values. Second step saves data which are encoded separately from respective
524*94d3b452SApple OSS Distributions * metadata (like strings).
525*94d3b452SApple OSS Distributions */
526*94d3b452SApple OSS Distributions bool
os_log_context_encode(os_log_context_t ctx,const char * fmt,va_list args,uintptr_t loc,size_t loc_size,uint16_t subsystem_id)527*94d3b452SApple OSS Distributions os_log_context_encode(os_log_context_t ctx, const char *fmt, va_list args,
528*94d3b452SApple OSS Distributions uintptr_t loc, size_t loc_size, uint16_t subsystem_id)
529*94d3b452SApple OSS Distributions {
530*94d3b452SApple OSS Distributions tracepoint_buf_t tpb = {
531*94d3b452SApple OSS Distributions .tp_buf = ctx->ctx_buffer,
532*94d3b452SApple OSS Distributions .tp_size = 0
533*94d3b452SApple OSS Distributions };
534*94d3b452SApple OSS Distributions
535*94d3b452SApple OSS Distributions tracepoint_buf_location(&tpb, loc, loc_size);
536*94d3b452SApple OSS Distributions if (os_log_subsystem_id_valid(subsystem_id)) {
537*94d3b452SApple OSS Distributions tracepoint_buf_add(&tpb, &subsystem_id, sizeof(subsystem_id));
538*94d3b452SApple OSS Distributions }
539*94d3b452SApple OSS Distributions os_log_context_prepare_header(ctx, tpb.tp_size);
540*94d3b452SApple OSS Distributions
541*94d3b452SApple OSS Distributions va_list args_copy;
542*94d3b452SApple OSS Distributions va_copy(args_copy, args);
543*94d3b452SApple OSS Distributions
544*94d3b452SApple OSS Distributions int rc = log_encode_fmt(ctx, fmt, args);
545*94d3b452SApple OSS Distributions
546*94d3b452SApple OSS Distributions va_end(args_copy);
547*94d3b452SApple OSS Distributions
548*94d3b452SApple OSS Distributions switch (rc) {
549*94d3b452SApple OSS Distributions case EINVAL:
550*94d3b452SApple OSS Distributions // Bogus/Unsupported fmt string
551*94d3b452SApple OSS Distributions counter_inc(&oslog_p_fmt_invalid_msgcount);
552*94d3b452SApple OSS Distributions return false;
553*94d3b452SApple OSS Distributions case ENOMEM:
554*94d3b452SApple OSS Distributions /*
555*94d3b452SApple OSS Distributions * The fmt contains unreasonable number of arguments (> 32) and
556*94d3b452SApple OSS Distributions * we ran out of space. We could call log_expand()
557*94d3b452SApple OSS Distributions * here and retry. However, using such formatting strings rather
558*94d3b452SApple OSS Distributions * seem like a misuse of the logging system, hence error.
559*94d3b452SApple OSS Distributions */
560*94d3b452SApple OSS Distributions counter_inc(&oslog_p_fmt_max_args_msgcount);
561*94d3b452SApple OSS Distributions return false;
562*94d3b452SApple OSS Distributions case 0:
563*94d3b452SApple OSS Distributions break;
564*94d3b452SApple OSS Distributions default:
565*94d3b452SApple OSS Distributions panic("unhandled return value");
566*94d3b452SApple OSS Distributions }
567*94d3b452SApple OSS Distributions
568*94d3b452SApple OSS Distributions if (ctx->ctx_pubdata_sz == 0) {
569*94d3b452SApple OSS Distributions goto finish;
570*94d3b452SApple OSS Distributions }
571*94d3b452SApple OSS Distributions
572*94d3b452SApple OSS Distributions /*
573*94d3b452SApple OSS Distributions * Logmem may not have been set up yet when logging very early during
574*94d3b452SApple OSS Distributions * the boot. Be sure to check its state.
575*94d3b452SApple OSS Distributions */
576*94d3b452SApple OSS Distributions if (!log_fits(ctx, ctx->ctx_pubdata_sz) && logmem_ready(ctx->ctx_logmem)) {
577*94d3b452SApple OSS Distributions size_t space_needed = log_context_cursor(ctx) + ctx->ctx_pubdata_sz - ctx->ctx_buffer;
578*94d3b452SApple OSS Distributions space_needed = MIN(space_needed, logmem_max_size(ctx->ctx_logmem));
579*94d3b452SApple OSS Distributions (void) log_expand(ctx, space_needed);
580*94d3b452SApple OSS Distributions }
581*94d3b452SApple OSS Distributions
582*94d3b452SApple OSS Distributions log_encode_public_data(ctx);
583*94d3b452SApple OSS Distributions
584*94d3b452SApple OSS Distributions if (ctx->ctx_truncated) {
585*94d3b452SApple OSS Distributions counter_inc(&oslog_p_truncated_msgcount);
586*94d3b452SApple OSS Distributions }
587*94d3b452SApple OSS Distributions finish:
588*94d3b452SApple OSS Distributions ctx->ctx_content_sz = (uint16_t)(log_context_cursor(ctx) - ctx->ctx_buffer);
589*94d3b452SApple OSS Distributions ctx->ctx_content_off = 0;
590*94d3b452SApple OSS Distributions return true;
591*94d3b452SApple OSS Distributions }
592*94d3b452SApple OSS Distributions
593*94d3b452SApple OSS Distributions void
os_log_context_init(os_log_context_t ctx,logmem_t * logmem,uint8_t * buffer,size_t buffer_sz)594*94d3b452SApple OSS Distributions os_log_context_init(os_log_context_t ctx, logmem_t *logmem, uint8_t *buffer, size_t buffer_sz)
595*94d3b452SApple OSS Distributions {
596*94d3b452SApple OSS Distributions assert(logmem);
597*94d3b452SApple OSS Distributions assert(buffer);
598*94d3b452SApple OSS Distributions assert(buffer_sz > 0);
599*94d3b452SApple OSS Distributions
600*94d3b452SApple OSS Distributions bzero(ctx, sizeof(*ctx));
601*94d3b452SApple OSS Distributions ctx->ctx_logmem = logmem;
602*94d3b452SApple OSS Distributions ctx->ctx_buffer = buffer;
603*94d3b452SApple OSS Distributions ctx->ctx_buffer_sz = buffer_sz;
604*94d3b452SApple OSS Distributions }
605*94d3b452SApple OSS Distributions
606*94d3b452SApple OSS Distributions void
os_log_context_free(os_log_context_t ctx)607*94d3b452SApple OSS Distributions os_log_context_free(os_log_context_t ctx)
608*94d3b452SApple OSS Distributions {
609*94d3b452SApple OSS Distributions if (ctx->ctx_allocated) {
610*94d3b452SApple OSS Distributions logmem_free_locked(ctx->ctx_logmem, ctx->ctx_buffer, ctx->ctx_buffer_sz);
611*94d3b452SApple OSS Distributions }
612*94d3b452SApple OSS Distributions }
613