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