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 <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 */
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 : 1 : void __printk_hook_install(int (*fn)(int))
64 : : {
65 : 1 : _char_out = fn;
66 : 1 : }
67 : :
68 : : /**
69 : : * @brief Get the current character output routine for printk
70 : : *
71 : : * To be called by any console driver that would like to save
72 : : * current hook - if any - for later re-installation.
73 : : *
74 : : * @return a function pointer or NULL if no hook is set
75 : : */
76 : 0 : void *__printk_get_hook(void)
77 : : {
78 : 0 : return _char_out;
79 : : }
80 : :
81 : : struct buf_out_context {
82 : : int count;
83 : : unsigned int buf_count;
84 : : char buf[CONFIG_PRINTK_BUFFER_SIZE];
85 : : };
86 : :
87 : 0 : static void buf_flush(struct buf_out_context *ctx)
88 : : {
89 : 0 : k_str_out(ctx->buf, ctx->buf_count);
90 : 0 : ctx->buf_count = 0U;
91 : 0 : }
92 : :
93 : 0 : static int buf_char_out(int c, void *ctx_p)
94 : : {
95 : 0 : struct buf_out_context *ctx = ctx_p;
96 : :
97 : 0 : ctx->count++;
98 : 0 : ctx->buf[ctx->buf_count++] = c;
99 [ # # ]: 0 : if (ctx->buf_count == CONFIG_PRINTK_BUFFER_SIZE) {
100 : 0 : buf_flush(ctx);
101 : : }
102 : :
103 : 0 : return c;
104 : : }
105 : :
106 : : struct out_context {
107 : : int count;
108 : : };
109 : :
110 : 428016 : static int char_out(int c, void *ctx_p)
111 : : {
112 : 428016 : struct out_context *ctx = ctx_p;
113 : :
114 : 428016 : ctx->count++;
115 : 428016 : return _char_out(c);
116 : : }
117 : :
118 : 152518 : void vprintk(const char *fmt, va_list ap)
119 : : {
120 : : if (IS_ENABLED(CONFIG_LOG_PRINTK)) {
121 : : z_log_vprintk(fmt, ap);
122 : : return;
123 : : }
124 : :
125 [ - + ]: 152518 : if (k_is_user_context()) {
126 : 0 : struct buf_out_context ctx = { 0 };
127 : :
128 : 0 : cbvprintf(buf_char_out, &ctx, fmt, ap);
129 : :
130 [ # # ]: 0 : if (ctx.buf_count) {
131 : 0 : buf_flush(&ctx);
132 : : }
133 : : } else {
134 : 152518 : struct out_context ctx = { 0 };
135 : : #ifdef CONFIG_PRINTK_SYNC
136 : : k_spinlock_key_t key = k_spin_lock(&lock);
137 : : #endif
138 : :
139 : 152518 : cbvprintf(char_out, &ctx, fmt, ap);
140 : :
141 : : #ifdef CONFIG_PRINTK_SYNC
142 : : k_spin_unlock(&lock, key);
143 : : #endif
144 : : }
145 : : }
146 : :
147 : 0 : void z_impl_k_str_out(char *c, size_t n)
148 : : {
149 : : size_t i;
150 : : #ifdef CONFIG_PRINTK_SYNC
151 : : k_spinlock_key_t key = k_spin_lock(&lock);
152 : : #endif
153 : :
154 [ # # ]: 0 : for (i = 0; i < n; i++) {
155 : 0 : _char_out(c[i]);
156 : : }
157 : :
158 : : #ifdef CONFIG_PRINTK_SYNC
159 : : k_spin_unlock(&lock, key);
160 : : #endif
161 : 0 : }
162 : :
163 : : #ifdef CONFIG_USERSPACE
164 : : static inline void z_vrfy_k_str_out(char *c, size_t n)
165 : : {
166 : : Z_OOPS(Z_SYSCALL_MEMORY_READ(c, n));
167 : : z_impl_k_str_out((char *)c, n);
168 : : }
169 : : #include <syscalls/k_str_out_mrsh.c>
170 : : #endif /* CONFIG_USERSPACE */
171 : :
172 : : /**
173 : : * @brief Output a string
174 : : *
175 : : * Output a string on output installed by platform at init time. Some
176 : : * printf-like formatting is available.
177 : : *
178 : : * Available formatting:
179 : : * - %x/%X: outputs a number in hexadecimal format
180 : : * - %s: outputs a null-terminated string
181 : : * - %p: pointer, same as %x with a 0x prefix
182 : : * - %u: outputs a number in unsigned decimal format
183 : : * - %d/%i: outputs a number in signed decimal format
184 : : *
185 : : * Field width (with or without leading zeroes) is supported.
186 : : * Length attributes h, hh, l, ll and z are supported. However, integral
187 : : * values with %lld and %lli are only printed if they fit in a long
188 : : * otherwise 'ERR' is printed. Full 64-bit values may be printed with %llx.
189 : : *
190 : : * @param fmt formatted string to output
191 : : */
192 : :
193 : 152518 : void printk(const char *fmt, ...)
194 : : {
195 : : va_list ap;
196 : :
197 : 152518 : va_start(ap, fmt);
198 : :
199 : 152518 : vprintk(fmt, ap);
200 : :
201 : 152518 : va_end(ap);
202 : 152518 : }
203 : : #endif /* defined(CONFIG_PRINTK) */
204 : :
205 : : struct str_context {
206 : : char *str;
207 : : int max;
208 : : int count;
209 : : };
210 : :
211 : 0 : static int str_out(int c, struct str_context *ctx)
212 : : {
213 [ # # # # ]: 0 : if (ctx->str == NULL || ctx->count >= ctx->max) {
214 : 0 : ctx->count++;
215 : 0 : return c;
216 : : }
217 : :
218 [ # # ]: 0 : if (ctx->count == ctx->max - 1) {
219 : 0 : ctx->str[ctx->count++] = '\0';
220 : : } else {
221 : 0 : ctx->str[ctx->count++] = c;
222 : : }
223 : :
224 : 0 : return c;
225 : : }
226 : :
227 : 0 : int snprintk(char *str, size_t size, const char *fmt, ...)
228 : : {
229 : : va_list ap;
230 : : int ret;
231 : :
232 : 0 : va_start(ap, fmt);
233 : 0 : ret = vsnprintk(str, size, fmt, ap);
234 : 0 : va_end(ap);
235 : :
236 : 0 : return ret;
237 : : }
238 : :
239 : 0 : int vsnprintk(char *str, size_t size, const char *fmt, va_list ap)
240 : : {
241 : 0 : struct str_context ctx = { str, size, 0 };
242 : :
243 : 0 : cbvprintf(str_out, &ctx, fmt, ap);
244 : :
245 [ # # ]: 0 : if (ctx.count < ctx.max) {
246 : 0 : str[ctx.count] = '\0';
247 : : }
248 : :
249 : 0 : return ctx.count;
250 : : }
|