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