Branch data Line data Source code
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 */
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 : :
58 : 1 : void __printk_hook_install(printk_hook_fn_t fn)
59 : : {
60 : 1 : _char_out = fn;
61 : 1 : }
62 : :
63 : 0 : printk_hook_fn_t __printk_get_hook(void)
64 : : {
65 : 0 : 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 : :
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 : :
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 : :
95 : : static int char_out(int c, void *ctx_p)
96 : : {
97 : : ARG_UNUSED(ctx_p);
98 : : return _char_out(c);
99 : : }
100 : :
101 : 177 : void vprintk(const char *fmt, va_list ap)
102 : : {
103 : 177 : if (IS_ENABLED(CONFIG_LOG_PRINTK)) {
104 : 177 : z_log_vprintk(fmt, ap);
105 : 177 : 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 : :
146 : 0 : void z_impl_k_str_out(char *c, size_t n)
147 : : {
148 : 0 : size_t i;
149 : : #ifdef CONFIG_PRINTK_SYNC
150 : : k_spinlock_key_t key = k_spin_lock(&lock);
151 : : #endif
152 : :
153 [ # # ]: 0 : for (i = 0; i < n; i++) {
154 : 0 : _char_out(c[i]);
155 : : }
156 : :
157 : : #ifdef CONFIG_PRINTK_SYNC
158 : : k_spin_unlock(&lock, key);
159 : : #endif
160 : 0 : }
161 : :
162 : : #ifdef CONFIG_USERSPACE
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 : :
192 : 177 : void printk(const char *fmt, ...)
193 : : {
194 : 177 : va_list ap;
195 : :
196 : 177 : va_start(ap, fmt);
197 : :
198 : 177 : vprintk(fmt, ap);
199 : :
200 : 177 : va_end(ap);
201 : 177 : }
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 : :
213 : 0 : static int str_out(int c, struct str_context *ctx)
214 : : {
215 [ # # # # ]: 0 : if ((ctx->str == NULL) || (ctx->count >= ctx->max)) {
216 : 0 : ++ctx->count;
217 : 0 : return c;
218 : : }
219 : :
220 [ # # ]: 0 : if (ctx->count == (ctx->max - 1)) {
221 : 0 : ctx->str[ctx->count] = '\0';
222 : : } else {
223 : 0 : ctx->str[ctx->count] = c;
224 : : }
225 : 0 : ++ctx->count;
226 : :
227 : 0 : return c;
228 : : }
229 : :
230 : 0 : int snprintk(char *str, size_t size, const char *fmt, ...)
231 : : {
232 : 0 : va_list ap;
233 : 0 : int ret;
234 : :
235 : 0 : va_start(ap, fmt);
236 : 0 : ret = vsnprintk(str, size, fmt, ap);
237 : 0 : va_end(ap);
238 : :
239 : 0 : return ret;
240 : : }
241 : :
242 : 0 : int vsnprintk(char *str, size_t size, const char *fmt, va_list ap)
243 : : {
244 : 0 : struct str_context ctx = { str, size, 0 };
245 : :
246 : 0 : cbvprintf(str_out, &ctx, fmt, ap);
247 : :
248 [ # # ]: 0 : if (ctx.count < ctx.max) {
249 : 0 : str[ctx.count] = '\0';
250 : : }
251 : :
252 : 0 : return ctx.count;
253 : : }
254 : :
255 : : #endif
|