]>
Commit | Line | Data |
---|---|---|
56f94be3 | 1 | /* |
2dc64451 | 2 | * (C) Copyright 2002-2007 |
56f94be3 WD |
3 | * Detlev Zundel, DENX Software Engineering, dzu@denx.de. |
4 | * | |
228f29ac WD |
5 | * Code used from linux/kernel/printk.c |
6 | * Copyright (C) 1991, 1992 Linus Torvalds | |
7 | * | |
1a459660 | 8 | * SPDX-License-Identifier: GPL-2.0+ |
228f29ac WD |
9 | * |
10 | * Comments: | |
11 | * | |
12 | * After relocating the code, the environment variable "loglevel" is | |
13 | * copied to console_loglevel. The functionality is similar to the | |
14 | * handling in the Linux kernel, i.e. messages logged with a priority | |
15 | * less than console_loglevel are also output to stdout. | |
16 | * | |
17 | * If you want messages with the default level (e.g. POST messages) to | |
18 | * appear on stdout also, make sure the environment variable | |
19 | * "loglevel" is set at boot time to a number higher than | |
20 | * default_message_loglevel below. | |
56f94be3 WD |
21 | */ |
22 | ||
23 | /* | |
24 | * Logbuffer handling routines | |
25 | */ | |
26 | ||
27 | #include <common.h> | |
28 | #include <command.h> | |
52cb4d4f | 29 | #include <stdio_dev.h> |
228f29ac | 30 | #include <post.h> |
56f94be3 WD |
31 | #include <logbuff.h> |
32 | ||
d87080b7 WD |
33 | DECLARE_GLOBAL_DATA_PTR; |
34 | ||
56f94be3 | 35 | /* Local prototypes */ |
709ea543 SG |
36 | static void logbuff_putc(struct stdio_dev *dev, const char c); |
37 | static void logbuff_puts(struct stdio_dev *dev, const char *s); | |
56f94be3 WD |
38 | static int logbuff_printk(const char *line); |
39 | ||
40 | static char buf[1024]; | |
41 | ||
228f29ac | 42 | /* This combination will not print messages with the default loglevel */ |
56f94be3 WD |
43 | static unsigned console_loglevel = 3; |
44 | static unsigned default_message_loglevel = 4; | |
2dc64451 | 45 | static unsigned log_version = 1; |
3d610186 YT |
46 | #ifdef CONFIG_ALT_LB_ADDR |
47 | static volatile logbuff_t *log; | |
48 | #else | |
2dc64451 | 49 | static logbuff_t *log; |
3d610186 YT |
50 | #endif |
51 | static char *lbuf; | |
56f94be3 | 52 | |
95d449ad MB |
53 | unsigned long __logbuffer_base(void) |
54 | { | |
e3866163 | 55 | return CONFIG_SYS_SDRAM_BASE + get_effective_memsize() - LOGBUFF_LEN; |
95d449ad | 56 | } |
1e8e7ae5 HS |
57 | unsigned long logbuffer_base(void) |
58 | __attribute__((weak, alias("__logbuffer_base"))); | |
95d449ad | 59 | |
1e8e7ae5 | 60 | void logbuff_init_ptrs(void) |
228f29ac | 61 | { |
2dc64451 | 62 | unsigned long tag, post_word; |
228f29ac WD |
63 | char *s; |
64 | ||
3d610186 YT |
65 | #ifdef CONFIG_ALT_LB_ADDR |
66 | log = (logbuff_t *)CONFIG_ALT_LH_ADDR; | |
67 | lbuf = (char *)CONFIG_ALT_LB_ADDR; | |
68 | #else | |
1e8e7ae5 | 69 | log = (logbuff_t *)(logbuffer_base()) - 1; |
a253b38b | 70 | lbuf = (char *)log->buf; |
3d610186 | 71 | #endif |
2dc64451 IL |
72 | |
73 | /* Set up log version */ | |
74 | if ((s = getenv ("logversion")) != NULL) | |
1e8e7ae5 | 75 | log_version = (int)simple_strtoul(s, NULL, 10); |
2dc64451 IL |
76 | |
77 | if (log_version == 2) | |
78 | tag = log->v2.tag; | |
79 | else | |
80 | tag = log->v1.tag; | |
2960b65a | 81 | post_word = post_word_load(); |
d1cbe85b WD |
82 | #ifdef CONFIG_POST |
83 | /* The post routines have setup the word so we can simply test it */ | |
1e8e7ae5 HS |
84 | if (tag != LOGBUFF_MAGIC || (post_word & POST_COLDBOOT)) |
85 | logbuff_reset(); | |
d1cbe85b WD |
86 | #else |
87 | /* No post routines, so we do our own checking */ | |
2dc64451 IL |
88 | if (tag != LOGBUFF_MAGIC || post_word != LOGBUFF_MAGIC) { |
89 | logbuff_reset (); | |
d1cbe85b | 90 | post_word_store (LOGBUFF_MAGIC); |
1636d1c8 | 91 | } |
d1cbe85b | 92 | #endif |
2dc64451 IL |
93 | if (log_version == 2 && (long)log->v2.start > (long)log->v2.con) |
94 | log->v2.start = log->v2.con; | |
95 | ||
228f29ac WD |
96 | /* Initialize default loglevel if present */ |
97 | if ((s = getenv ("loglevel")) != NULL) | |
1e8e7ae5 | 98 | console_loglevel = (int)simple_strtoul(s, NULL, 10); |
228f29ac | 99 | |
0e15ddd1 | 100 | gd->flags |= GD_FLG_LOGINIT; |
228f29ac WD |
101 | } |
102 | ||
1e8e7ae5 | 103 | void logbuff_reset(void) |
2dc64451 | 104 | { |
3d610186 | 105 | #ifndef CONFIG_ALT_LB_ADDR |
1e8e7ae5 | 106 | memset(log, 0, sizeof(logbuff_t)); |
3d610186 YT |
107 | #endif |
108 | if (log_version == 2) { | |
2dc64451 | 109 | log->v2.tag = LOGBUFF_MAGIC; |
3d610186 YT |
110 | #ifdef CONFIG_ALT_LB_ADDR |
111 | log->v2.start = 0; | |
112 | log->v2.con = 0; | |
113 | log->v2.end = 0; | |
114 | log->v2.chars = 0; | |
115 | #endif | |
116 | } else { | |
2dc64451 | 117 | log->v1.tag = LOGBUFF_MAGIC; |
3d610186 YT |
118 | #ifdef CONFIG_ALT_LB_ADDR |
119 | log->v1.dummy = 0; | |
120 | log->v1.start = 0; | |
121 | log->v1.size = 0; | |
122 | log->v1.chars = 0; | |
123 | #endif | |
124 | } | |
2dc64451 IL |
125 | } |
126 | ||
1e8e7ae5 | 127 | int drv_logbuff_init(void) |
56f94be3 | 128 | { |
52cb4d4f | 129 | struct stdio_dev logdev; |
56f94be3 WD |
130 | int rc; |
131 | ||
132 | /* Device initialization */ | |
133 | memset (&logdev, 0, sizeof (logdev)); | |
134 | ||
135 | strcpy (logdev.name, "logbuff"); | |
136 | logdev.ext = 0; /* No extensions */ | |
137 | logdev.flags = DEV_FLAGS_OUTPUT; /* Output only */ | |
138 | logdev.putc = logbuff_putc; /* 'putc' function */ | |
139 | logdev.puts = logbuff_puts; /* 'puts' function */ | |
140 | ||
1e8e7ae5 | 141 | rc = stdio_register(&logdev); |
56f94be3 WD |
142 | |
143 | return (rc == 0) ? 1 : rc; | |
144 | } | |
145 | ||
709ea543 | 146 | static void logbuff_putc(struct stdio_dev *dev, const char c) |
56f94be3 WD |
147 | { |
148 | char buf[2]; | |
228f29ac WD |
149 | buf[0] = c; |
150 | buf[1] = '\0'; | |
1e8e7ae5 | 151 | logbuff_printk(buf); |
56f94be3 WD |
152 | } |
153 | ||
709ea543 | 154 | static void logbuff_puts(struct stdio_dev *dev, const char *s) |
56f94be3 | 155 | { |
228f29ac | 156 | logbuff_printk (s); |
56f94be3 WD |
157 | } |
158 | ||
159 | void logbuff_log(char *msg) | |
160 | { | |
0e15ddd1 | 161 | if ((gd->flags & GD_FLG_LOGINIT)) { |
1e8e7ae5 | 162 | logbuff_printk(msg); |
56f94be3 | 163 | } else { |
1e8e7ae5 HS |
164 | /* |
165 | * Can happen only for pre-relocated errors as logging | |
166 | * at that stage should be disabled | |
167 | */ | |
228f29ac | 168 | puts (msg); |
56f94be3 WD |
169 | } |
170 | } | |
171 | ||
172 | /* | |
173 | * Subroutine: do_log | |
174 | * | |
175 | * Description: Handler for 'log' command.. | |
176 | * | |
177 | * Inputs: argv[1] contains the subcommand | |
178 | * | |
179 | * Return: None | |
180 | * | |
181 | */ | |
1e8e7ae5 | 182 | int do_log(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
56f94be3 | 183 | { |
709ea543 | 184 | struct stdio_dev *sdev = NULL; |
56f94be3 | 185 | char *s; |
2dc64451 | 186 | unsigned long i, start, size; |
56f94be3 | 187 | |
1e8e7ae5 | 188 | if (strcmp(argv[1], "append") == 0) { |
228f29ac | 189 | /* Log concatenation of all arguments separated by spaces */ |
1e8e7ae5 HS |
190 | for (i = 2; i < argc; i++) { |
191 | logbuff_printk(argv[i]); | |
709ea543 | 192 | logbuff_putc(sdev, (i < argc - 1) ? ' ' : '\n'); |
228f29ac WD |
193 | } |
194 | return 0; | |
56f94be3 WD |
195 | } |
196 | ||
197 | switch (argc) { | |
198 | ||
199 | case 2: | |
1e8e7ae5 | 200 | if (strcmp(argv[1], "show") == 0) { |
2dc64451 IL |
201 | if (log_version == 2) { |
202 | start = log->v2.start; | |
203 | size = log->v2.end - log->v2.start; | |
1e8e7ae5 | 204 | } else { |
2dc64451 IL |
205 | start = log->v1.start; |
206 | size = log->v1.size; | |
207 | } | |
c16a123f HS |
208 | if (size > LOGBUFF_LEN) |
209 | size = LOGBUFF_LEN; | |
210 | for (i = 0; i < size; i++) { | |
1e8e7ae5 HS |
211 | s = lbuf + ((start + i) & LOGBUFF_MASK); |
212 | putc(*s); | |
56f94be3 WD |
213 | } |
214 | return 0; | |
1e8e7ae5 HS |
215 | } else if (strcmp(argv[1], "reset") == 0) { |
216 | logbuff_reset(); | |
56f94be3 | 217 | return 0; |
1e8e7ae5 HS |
218 | } else if (strcmp(argv[1], "info") == 0) { |
219 | printf("Logbuffer at %08lx\n", (unsigned long)lbuf); | |
2dc64451 | 220 | if (log_version == 2) { |
1e8e7ae5 HS |
221 | printf("log_start = %08lx\n", |
222 | log->v2.start); | |
223 | printf("log_end = %08lx\n", log->v2.end); | |
c0b77e09 | 224 | printf("log_con = %08lx\n", log->v2.con); |
1e8e7ae5 HS |
225 | printf("logged_chars = %08lx\n", |
226 | log->v2.chars); | |
2dc64451 IL |
227 | } |
228 | else { | |
1e8e7ae5 HS |
229 | printf("log_start = %08lx\n", |
230 | log->v1.start); | |
231 | printf("log_size = %08lx\n", | |
232 | log->v1.size); | |
233 | printf("logged_chars = %08lx\n", | |
234 | log->v1.chars); | |
2dc64451 | 235 | } |
56f94be3 | 236 | return 0; |
56f94be3 | 237 | } |
4c12eeb8 | 238 | return CMD_RET_USAGE; |
56f94be3 WD |
239 | |
240 | default: | |
4c12eeb8 | 241 | return CMD_RET_USAGE; |
56f94be3 WD |
242 | } |
243 | } | |
2dc64451 | 244 | |
0d498393 WD |
245 | U_BOOT_CMD( |
246 | log, 255, 1, do_log, | |
2fb2604d | 247 | "manipulate logbuffer", |
b37c7e5e | 248 | "info - show pointer details\n" |
8bde7f77 WD |
249 | "log reset - clear contents\n" |
250 | "log show - show contents\n" | |
a89c33db | 251 | "log append <msg> - append <msg> to the logbuffer" |
8bde7f77 | 252 | ); |
2dc64451 | 253 | |
56f94be3 WD |
254 | static int logbuff_printk(const char *line) |
255 | { | |
256 | int i; | |
257 | char *msg, *p, *buf_end; | |
258 | int line_feed; | |
259 | static signed char msg_level = -1; | |
260 | ||
1e8e7ae5 HS |
261 | strcpy(buf + 3, line); |
262 | i = strlen(line); | |
56f94be3 WD |
263 | buf_end = buf + 3 + i; |
264 | for (p = buf + 3; p < buf_end; p++) { | |
265 | msg = p; | |
266 | if (msg_level < 0) { | |
267 | if ( | |
268 | p[0] != '<' || | |
269 | p[1] < '0' || | |
270 | p[1] > '7' || | |
271 | p[2] != '>' | |
272 | ) { | |
273 | p -= 3; | |
274 | p[0] = '<'; | |
275 | p[1] = default_message_loglevel + '0'; | |
276 | p[2] = '>'; | |
1e8e7ae5 | 277 | } else { |
56f94be3 | 278 | msg += 3; |
1e8e7ae5 | 279 | } |
56f94be3 WD |
280 | msg_level = p[1] - '0'; |
281 | } | |
282 | line_feed = 0; | |
283 | for (; p < buf_end; p++) { | |
2dc64451 | 284 | if (log_version == 2) { |
3d610186 | 285 | lbuf[log->v2.end & LOGBUFF_MASK] = *p; |
2dc64451 IL |
286 | log->v2.end++; |
287 | if (log->v2.end - log->v2.start > LOGBUFF_LEN) | |
288 | log->v2.start++; | |
289 | log->v2.chars++; | |
1e8e7ae5 | 290 | } else { |
3d610186 | 291 | lbuf[(log->v1.start + log->v1.size) & |
2dc64451 IL |
292 | LOGBUFF_MASK] = *p; |
293 | if (log->v1.size < LOGBUFF_LEN) | |
294 | log->v1.size++; | |
295 | else | |
296 | log->v1.start++; | |
297 | log->v1.chars++; | |
298 | } | |
56f94be3 WD |
299 | if (*p == '\n') { |
300 | line_feed = 1; | |
301 | break; | |
302 | } | |
303 | } | |
304 | if (msg_level < console_loglevel) { | |
305 | printf("%s", msg); | |
306 | } | |
307 | if (line_feed) | |
308 | msg_level = -1; | |
309 | } | |
310 | return i; | |
311 | } |