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