1 /*
2  * Copyright (c) 2010, 2013-2014 Wind River Systems, Inc.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief Low-level debug output
10  *
11  * Low-level debugging output. Platform installs a character output routine at
12  * init time. If no routine is installed, a nop routine is called.
13  */
14 
15 #include <zephyr/kernel.h>
16 #include <zephyr/sys/printk.h>
17 #include <zephyr/sys/printk-hooks.h>
18 #include <stdarg.h>
19 #include <zephyr/toolchain.h>
20 #include <zephyr/linker/sections.h>
21 #include <zephyr/internal/syscall_handler.h>
22 #include <zephyr/logging/log.h>
23 #include <zephyr/sys/cbprintf.h>
24 #include <zephyr/llext/symbol.h>
25 #include <sys/types.h>
26 
27 /* Option present only when CONFIG_USERSPACE enabled. */
28 #ifndef CONFIG_PRINTK_BUFFER_SIZE
29 #define CONFIG_PRINTK_BUFFER_SIZE 0
30 #endif
31 
32 #if defined(CONFIG_PRINTK_SYNC)
33 static struct k_spinlock lock;
34 #endif
35 
36 #ifdef CONFIG_PRINTK
37 /**
38  * @brief Default character output routine that does nothing
39  * @param c Character to swallow
40  *
41  * Note this is defined as a weak symbol, allowing architecture code
42  * to override it where possible to enable very early logging.
43  *
44  * @return 0
45  */
46 /* LCOV_EXCL_START */
arch_printk_char_out(int c)47 __attribute__((weak)) int arch_printk_char_out(int c)
48 {
49 	ARG_UNUSED(c);
50 
51 	/* do nothing */
52 	return 0;
53 }
54 /* LCOV_EXCL_STOP */
55 
56 static printk_hook_fn_t _char_out = arch_printk_char_out;
57 
__printk_hook_install(printk_hook_fn_t fn)58 void __printk_hook_install(printk_hook_fn_t fn)
59 {
60 	_char_out = fn;
61 }
62 
__printk_get_hook(void)63 printk_hook_fn_t __printk_get_hook(void)
64 {
65 	return _char_out;
66 }
67 
68 struct buf_out_context {
69 #ifdef CONFIG_PICOLIBC
70 	FILE file;
71 #endif
72 	unsigned int buf_count;
73 	char buf[CONFIG_PRINTK_BUFFER_SIZE];
74 };
75 
buf_flush(struct buf_out_context * ctx)76 static void buf_flush(struct buf_out_context *ctx)
77 {
78 	k_str_out(ctx->buf, ctx->buf_count);
79 	ctx->buf_count = 0U;
80 }
81 
buf_char_out(int c,void * ctx_p)82 static int buf_char_out(int c, void *ctx_p)
83 {
84 	struct buf_out_context *ctx = ctx_p;
85 
86 	ctx->buf[ctx->buf_count] = c;
87 	++ctx->buf_count;
88 	if (ctx->buf_count == CONFIG_PRINTK_BUFFER_SIZE) {
89 		buf_flush(ctx);
90 	}
91 
92 	return c;
93 }
94 
char_out(int c,void * ctx_p)95 static int char_out(int c, void *ctx_p)
96 {
97 	ARG_UNUSED(ctx_p);
98 	return _char_out(c);
99 }
100 
vprintk(const char * fmt,va_list ap)101 void vprintk(const char *fmt, va_list ap)
102 {
103 	if (IS_ENABLED(CONFIG_LOG_PRINTK)) {
104 		z_log_vprintk(fmt, ap);
105 		return;
106 	}
107 
108 	if (k_is_user_context()) {
109 		struct buf_out_context ctx = {
110 #ifdef CONFIG_PICOLIBC
111 			.file = FDEV_SETUP_STREAM((int(*)(char, FILE *))buf_char_out,
112 						  NULL, NULL, _FDEV_SETUP_WRITE),
113 #else
114 			0
115 #endif
116 		};
117 
118 #ifdef CONFIG_PICOLIBC
119 		(void) vfprintf(&ctx.file, fmt, ap);
120 #else
121 		cbvprintf(buf_char_out, &ctx, fmt, ap);
122 #endif
123 		if (ctx.buf_count) {
124 			buf_flush(&ctx);
125 		}
126 	} else {
127 #ifdef CONFIG_PRINTK_SYNC
128 		k_spinlock_key_t key = k_spin_lock(&lock);
129 #endif
130 
131 #ifdef CONFIG_PICOLIBC
132 		FILE console = FDEV_SETUP_STREAM((int(*)(char, FILE *))char_out,
133 						 NULL, NULL, _FDEV_SETUP_WRITE);
134 		(void) vfprintf(&console, fmt, ap);
135 #else
136 		cbvprintf(char_out, NULL, fmt, ap);
137 #endif
138 
139 #ifdef CONFIG_PRINTK_SYNC
140 		k_spin_unlock(&lock, key);
141 #endif
142 	}
143 }
144 EXPORT_SYMBOL(vprintk);
145 
z_impl_k_str_out(char * c,size_t n)146 void z_impl_k_str_out(char *c, size_t n)
147 {
148 	size_t i;
149 #ifdef CONFIG_PRINTK_SYNC
150 	k_spinlock_key_t key = k_spin_lock(&lock);
151 #endif
152 
153 	for (i = 0; i < n; i++) {
154 		_char_out(c[i]);
155 	}
156 
157 #ifdef CONFIG_PRINTK_SYNC
158 	k_spin_unlock(&lock, key);
159 #endif
160 }
161 
162 #ifdef CONFIG_USERSPACE
z_vrfy_k_str_out(char * c,size_t n)163 static inline void z_vrfy_k_str_out(char *c, size_t n)
164 {
165 	K_OOPS(K_SYSCALL_MEMORY_READ(c, n));
166 	z_impl_k_str_out((char *)c, n);
167 }
168 #include <zephyr/syscalls/k_str_out_mrsh.c>
169 #endif /* CONFIG_USERSPACE */
170 
171 /**
172  * @brief Output a string
173  *
174  * Output a string on output installed by platform at init time. Some
175  * printf-like formatting is available.
176  *
177  * Available formatting:
178  * - %x/%X:  outputs a number in hexadecimal format
179  * - %s:     outputs a null-terminated string
180  * - %p:     pointer, same as %x with a 0x prefix
181  * - %u:     outputs a number in unsigned decimal format
182  * - %d/%i:  outputs a number in signed decimal format
183  *
184  * Field width (with or without leading zeroes) is supported.
185  * Length attributes h, hh, l, ll and z are supported. However, integral
186  * values with %lld and %lli are only printed if they fit in a long
187  * otherwise 'ERR' is printed. Full 64-bit values may be printed with %llx.
188  *
189  * @param fmt formatted string to output
190  */
191 
printk(const char * fmt,...)192 void printk(const char *fmt, ...)
193 {
194 	va_list ap;
195 
196 	va_start(ap, fmt);
197 
198 	vprintk(fmt, ap);
199 
200 	va_end(ap);
201 }
202 EXPORT_SYMBOL(printk);
203 #endif /* defined(CONFIG_PRINTK) */
204 
205 #ifndef CONFIG_PICOLIBC
206 
207 struct str_context {
208 	char *str;
209 	int max;
210 	int count;
211 };
212 
str_out(int c,struct str_context * ctx)213 static int str_out(int c, struct str_context *ctx)
214 {
215 	if ((ctx->str == NULL) || (ctx->count >= ctx->max)) {
216 		++ctx->count;
217 		return c;
218 	}
219 
220 	if (ctx->count == (ctx->max - 1)) {
221 		ctx->str[ctx->count] = '\0';
222 	} else {
223 		ctx->str[ctx->count] = c;
224 	}
225 	++ctx->count;
226 
227 	return c;
228 }
229 
snprintk(char * str,size_t size,const char * fmt,...)230 int snprintk(char *str, size_t size, const char *fmt, ...)
231 {
232 	va_list ap;
233 	int ret;
234 
235 	va_start(ap, fmt);
236 	ret = vsnprintk(str, size, fmt, ap);
237 	va_end(ap);
238 
239 	return ret;
240 }
241 
vsnprintk(char * str,size_t size,const char * fmt,va_list ap)242 int vsnprintk(char *str, size_t size, const char *fmt, va_list ap)
243 {
244 	struct str_context ctx = { str, size, 0 };
245 
246 	cbvprintf(str_out, &ctx, fmt, ap);
247 
248 	if (ctx.count < ctx.max) {
249 		str[ctx.count] = '\0';
250 	}
251 
252 	return ctx.count;
253 }
254 
255 #endif
256