1 /*
2 * Copyright (c) 2013 Google, Inc.
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8 #include <lib/klog.h>
9
10 #include <lk/err.h>
11 #include <lk/debug.h>
12 #include <assert.h>
13 #include <lk/trace.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <platform.h>
18 #include <lib/cksum.h>
19 #include <lk/console_cmd.h>
20
21 #define LOCAL_TRACE 0
22
23 #ifndef MAX_KLOG_SIZE
24 #define MAX_KLOG_SIZE (32*1024)
25 #endif
26
27 #define KLOG_BUFFER_HEADER_MAGIC 'KLGB'
28
29 struct klog_buffer_header {
30 uint32_t magic;
31 uint32_t header_crc32;
32 uint32_t log_count;
33 uint32_t current_log;
34 uint32_t total_size;
35 };
36
37 #define KLOG_HEADER_MAGIC 'KLOG'
38
39 struct klog_header {
40 uint32_t magic;
41 uint32_t size;
42 uint32_t head;
43 uint32_t tail;
44 uint32_t data_checksum;
45 uint8_t data[0];
46 };
47
48 /* current klog buffer */
49 static struct klog_buffer_header *klog_buf;
50
51 /* current klog */
52 static struct klog_header *klog;
53
find_nth_log(uint log)54 static struct klog_header *find_nth_log(uint log) {
55 DEBUG_ASSERT(klog_buf);
56 DEBUG_ASSERT(klog_buf->magic == KLOG_BUFFER_HEADER_MAGIC);
57 DEBUG_ASSERT(log < klog_buf->log_count);
58
59 struct klog_header *k = (struct klog_header *)(klog_buf + 1);
60 while (log > 0) {
61 DEBUG_ASSERT(k->magic == KLOG_HEADER_MAGIC);
62
63 uint8_t *ptr = (uint8_t *)k->data;
64 ptr += k->size;
65 k = (struct klog_header *)ptr;
66
67 log--;
68 }
69
70 return k;
71 }
72
get_checksum_klog_buffer_header(const struct klog_buffer_header * kb)73 static uint32_t get_checksum_klog_buffer_header(const struct klog_buffer_header *kb) {
74 DEBUG_ASSERT(kb);
75 DEBUG_ASSERT(kb->magic == KLOG_BUFFER_HEADER_MAGIC);
76
77 return crc32(0, (const void *)(&kb->header_crc32 + 1), sizeof(*kb) - 8);
78 }
79
get_checksum_klog_data(const struct klog_header * k)80 static uint32_t get_checksum_klog_data(const struct klog_header *k) {
81 DEBUG_ASSERT(k);
82 DEBUG_ASSERT(k->magic == KLOG_HEADER_MAGIC);
83
84 uint32_t sum = 0;
85 for (uint i = 0; i < k->size; i++) {
86 sum += k->data[i];
87 }
88
89 return sum;
90 }
91
checksum_klog_buffer_header(struct klog_buffer_header * kb)92 static void checksum_klog_buffer_header(struct klog_buffer_header *kb) {
93 DEBUG_ASSERT(kb);
94 DEBUG_ASSERT(kb->magic == KLOG_BUFFER_HEADER_MAGIC);
95
96 kb->header_crc32 = get_checksum_klog_buffer_header(kb);
97 }
98
checksum_klog_data(struct klog_header * k)99 static void checksum_klog_data(struct klog_header *k) {
100 DEBUG_ASSERT(k);
101 DEBUG_ASSERT(k->magic == KLOG_HEADER_MAGIC);
102
103 k->data_checksum = get_checksum_klog_data(k);
104 }
105
klog_init(void)106 void klog_init(void) {
107 }
108
klog_create(void * _ptr,size_t len,uint count)109 status_t klog_create(void *_ptr, size_t len, uint count) {
110 uint8_t *ptr = _ptr;
111 LTRACEF("ptr %p len %zu count %u\n", ptr, len, count);
112
113 /* check args */
114 if (!ptr)
115 return ERR_INVALID_ARGS;
116 if (count == 0)
117 return ERR_INVALID_ARGS;
118
119 /* check that the size is big enough */
120 if (len < (sizeof(struct klog_buffer_header) + sizeof(struct klog_header) * count + 4 * count))
121 return ERR_INVALID_ARGS;
122
123 /* set up the buffer header */
124 klog_buf = (struct klog_buffer_header *)ptr;
125 klog_buf->magic = KLOG_BUFFER_HEADER_MAGIC;
126 klog_buf->log_count = count;
127 klog_buf->current_log = 0;
128 klog_buf->total_size = len;
129 checksum_klog_buffer_header(klog_buf);
130 ptr += sizeof(struct klog_buffer_header);
131
132 /* set up each buffer */
133 uint bufsize = len - sizeof(struct klog_buffer_header) - sizeof(struct klog_header) * count;
134 bufsize /= count;
135 bufsize = ROUNDDOWN(bufsize, 4);
136 while (count > 0) {
137 klog = (struct klog_header *)ptr;
138 klog->magic = KLOG_HEADER_MAGIC;
139 klog->size = bufsize;
140 klog->head = 0;
141 klog->tail = 0;
142 klog->data_checksum = 0;
143 memset(klog + 1, 0, bufsize);
144 checksum_klog_data(klog);
145 ptr += sizeof(struct klog_header) + bufsize;
146 count--;
147 }
148
149 klog_set_current_buffer(0);
150
151 DEBUG_ASSERT(klog_buf);
152 DEBUG_ASSERT(klog);
153
154 return NO_ERROR;
155 }
156
klog_recover(void * _ptr)157 ssize_t klog_recover(void *_ptr) {
158 uint8_t *ptr = _ptr;
159 LTRACEF("ptr %p\n", ptr);
160
161 if (!ptr)
162 return ERR_INVALID_ARGS;
163
164 /* look for header at pointer */
165 struct klog_buffer_header *kbuf = (struct klog_buffer_header *)ptr;
166 if (kbuf->magic != KLOG_BUFFER_HEADER_MAGIC)
167 return ERR_NOT_FOUND;
168 uint32_t crc = get_checksum_klog_buffer_header(kbuf);
169 if (crc != kbuf->header_crc32)
170 return ERR_NOT_FOUND;
171
172 /* some sanity checks */
173 if (kbuf->total_size > MAX_KLOG_SIZE)
174 return ERR_NOT_FOUND;
175 if (kbuf->current_log >= kbuf->log_count)
176 return ERR_NOT_FOUND;
177
178 /* walk the list of klogs, validating */
179 ptr += sizeof(struct klog_buffer_header);
180 for (uint i = 0; i < kbuf->log_count; i++) {
181 struct klog_header *k = (struct klog_header *)ptr;
182
183 /* validate the individual klog */
184 if (k->magic != KLOG_HEADER_MAGIC)
185 return ERR_NOT_FOUND;
186
187 /* validate some fields */
188 if ((k->size > 0) && (k->size & 3))
189 return ERR_NOT_FOUND;
190 if (k->size > MAX_KLOG_SIZE)
191 return ERR_NOT_FOUND;
192 if (k->head >= k->size)
193 return ERR_NOT_FOUND;
194 if (k->tail >= k->size)
195 return ERR_NOT_FOUND;
196
197 /* data checksum */
198 if (k->data_checksum) {
199 crc = get_checksum_klog_data(k);
200 if (crc != k->data_checksum)
201 return ERR_NOT_FOUND;
202 }
203
204 ptr += sizeof(struct klog_header) + k->size;
205 }
206
207 /* everything checks out */
208 klog_buf = kbuf;
209 klog_set_current_buffer(klog_buf->current_log);
210
211 LTRACEF("found buffer at %p, current log %u (%p) head %u tail %u size %u\n",
212 klog_buf, klog_buf->current_log, klog, klog->head, klog->tail, klog->size);
213
214 return NO_ERROR;
215 }
216
klog_buffer_count(void)217 uint klog_buffer_count(void) {
218 if (!klog_buf)
219 return 0;
220
221 DEBUG_ASSERT(klog_buf);
222 DEBUG_ASSERT(klog_buf->magic == KLOG_BUFFER_HEADER_MAGIC);
223
224 return klog_buf->log_count;
225 }
226
klog_current_buffer(void)227 uint klog_current_buffer(void) {
228 if (!klog_buf)
229 return 0;
230
231 DEBUG_ASSERT(klog_buf);
232 DEBUG_ASSERT(klog_buf->magic == KLOG_BUFFER_HEADER_MAGIC);
233
234 return klog_buf->current_log;
235 }
236
klog_set_current_buffer(uint buffer)237 status_t klog_set_current_buffer(uint buffer) {
238 if (!klog_buf)
239 return ERR_NOT_FOUND;
240
241 DEBUG_ASSERT(klog_buf);
242 DEBUG_ASSERT(klog_buf->magic == KLOG_BUFFER_HEADER_MAGIC);
243
244 if (buffer >= klog_buf->log_count)
245 return ERR_INVALID_ARGS;
246
247 /* find the nth buffer */
248 klog = find_nth_log(buffer);
249
250 /* update the klog buffer header */
251 if (buffer != klog_buf->current_log) {
252 klog_buf->current_log = buffer;
253 checksum_klog_buffer_header(klog_buf);
254 }
255
256 return NO_ERROR;
257 }
258
259 #include <arch/ops.h>
260
klog_read(char * buf,size_t len,int buf_id)261 ssize_t klog_read(char *buf, size_t len, int buf_id) {
262 size_t offset = 0;
263 size_t tmp_len;
264 iovec_t vec[2];
265 LTRACEF("read (len %zu, buf %u)\n", len, buf_id);
266
267 DEBUG_ASSERT(klog);
268 DEBUG_ASSERT(klog->magic == KLOG_HEADER_MAGIC);
269
270 /* If a klog wraps around at the end then it becomes two iovecs with
271 * tail being the start of 1 and head being the end of 0. This means we
272 * need to check where we are in the overall klog to properly determine
273 * which iovec we want to read from */
274 int vec_cnt = klog_get_buffer(buf_id, vec);
275 if (vec_cnt < 1)
276 return vec_cnt;
277
278 tmp_len = MIN(len, vec[0].iov_len);
279 memcpy(buf, (const char *)vec[0].iov_base, tmp_len);
280 offset += tmp_len;
281 len -= tmp_len;
282
283 if (len != 0 && vec_cnt > 1) {
284 tmp_len = MIN(len, vec[1].iov_len);
285 memcpy(buf + offset, (const char *)vec[1].iov_base, tmp_len);
286 offset += tmp_len;
287 }
288
289 /* Since iovecs are generated by get_buffer we only need to update the tail pointer */
290 klog->tail += offset;
291 if (klog->tail >= klog->size)
292 klog->tail -= klog->size;
293
294 return offset;
295 }
296
klog_getc(int buf_id)297 char klog_getc(int buf_id) {
298 char c = '\0';
299 int err = klog_read(&c, 1, buf_id);
300
301 return (err < 0) ? err : c;
302 }
303
klog_getchar(void)304 char klog_getchar(void) {
305 return klog_getc(-1);
306 }
307
308 /* Returns whether the currently selected klog contains data */
klog_has_data(void)309 bool klog_has_data(void) {
310 DEBUG_ASSERT(klog);
311
312 return (klog->head != klog->tail);
313 }
314
klog_puts_len(const char * str,size_t len)315 static size_t klog_puts_len(const char *str, size_t len) {
316 LTRACEF("puts '%s'\n", str);
317
318 DEBUG_ASSERT(klog);
319 DEBUG_ASSERT(klog->magic == KLOG_HEADER_MAGIC);
320
321 LTRACEF("before write head %u tail %u size %u\n", klog->head, klog->tail, klog->size);
322 uint32_t deltasum = 0;
323 size_t count = 0;
324 while (count < len && *str) {
325 /* compute the delta checksum */
326 deltasum += *str - klog->data[klog->head];
327
328 /* store the data */
329 klog->data[klog->head] = *str;
330
331 /* bump the head */
332 uint newhead = klog->head + 1;
333 if (newhead >= klog->size)
334 newhead -= klog->size;
335 DEBUG_ASSERT(newhead < klog->size);
336
337 /* bump the tail if the head collided with it */
338 if (klog->tail == newhead) {
339 uint newtail = klog->tail + 1;
340 if (newtail >= klog->size)
341 newtail -= klog->size;
342 DEBUG_ASSERT(newtail < klog->size);
343 klog->tail = newtail;
344 }
345
346 /* writeback the head */
347 klog->head = newhead;
348
349 str++;
350 count++;
351 }
352 LTRACEF("after write head %u tail %u\n", klog->head, klog->tail);
353
354 klog->data_checksum += deltasum;
355
356 LTRACEF("kputs len %u\n", count);
357
358 return count;
359 }
360
klog_putchar(char c)361 void klog_putchar(char c) {
362 klog_puts_len(&c, 1);
363 }
364
klog_puts(const char * str)365 void klog_puts(const char *str) {
366 if (!klog_buf)
367 return;
368
369 klog_puts_len(str, SIZE_MAX);
370 }
371
_klog_output_func(const char * str,size_t len,void * state)372 static int _klog_output_func(const char *str, size_t len, void *state) {
373 return klog_puts_len(str, len);
374 }
375
klog_printf(const char * fmt,...)376 void klog_printf(const char *fmt, ...) {
377 if (!klog_buf)
378 return;
379
380 va_list ap;
381 va_start(ap, fmt);
382 _printf_engine(&_klog_output_func, NULL, fmt, ap);
383 va_end(ap);
384 }
385
klog_vprintf(const char * fmt,va_list ap)386 void klog_vprintf(const char *fmt, va_list ap) {
387 if (!klog_buf)
388 return;
389
390 _printf_engine(&_klog_output_func, NULL, fmt, ap);
391 }
392
klog_get_buffer(int buffer,iovec_t * vec)393 int klog_get_buffer(int buffer, iovec_t *vec) {
394 if (!klog_buf)
395 return 0;
396 if (!vec)
397 return ERR_INVALID_ARGS;
398 if (buffer >= 0 && (uint)buffer >= klog_buf->log_count)
399 return ERR_INVALID_ARGS;
400
401 struct klog_header *k;
402 if (buffer < 0)
403 k = klog;
404 else
405 k = find_nth_log(buffer);
406
407 DEBUG_ASSERT(k);
408 DEBUG_ASSERT(k->magic == KLOG_HEADER_MAGIC);
409
410 vec[0].iov_base = &k->data[k->tail];
411 if (k->head == k->tail) {
412 return 0;
413 } else if (k->head > k->tail) {
414 /* single run of data, between tail and head */
415 vec[0].iov_len = k->head - k->tail;
416
417 return 1;
418 } else {
419 vec[0].iov_len = k->size - k->tail;
420
421 /* two segments */
422 vec[1].iov_base = &k->data[0];
423 vec[1].iov_len = k->head;
424
425 return 2;
426 }
427 }
428
klog_dump(int buffer)429 void klog_dump(int buffer) {
430 iovec_t vec[2];
431
432 int err = klog_get_buffer(buffer, vec);
433 if (err <= 0)
434 return;
435
436 for (uint i = 0; i < vec[0].iov_len; i++)
437 putchar(*((const char *)vec[0].iov_base + i));
438 if (err > 1) {
439 for (uint i = 0; i < vec[1].iov_len; i++)
440 putchar(*((const char *)vec[1].iov_base + i));
441 }
442 }
443
444 #define KLOG_RETENTION_TEST 0
445 #if KLOG_RETENTION_TEST
446 #include <platform/retention.h>
447
448 _RETENTION_NOCLEAR(static uint8_t klog_test_buf[512]);
449 #endif
450
cmd_klog(int argc,const console_cmd_args * argv)451 static int cmd_klog(int argc, const console_cmd_args *argv) {
452 status_t err;
453
454 if (argc < 2) {
455 notenoughargs:
456 printf("ERROR not enough arguments\n");
457 usage:
458 printf("usage: %s create <size> <count>\n", argv[0].str);
459 #if KLOG_RETENTION_TEST
460 printf("usage: %s createret \n", argv[0].str);
461 printf("usage: %s recoverret \n", argv[0].str);
462 #endif
463 printf("usage: %s getbufcount\n", argv[0].str);
464 printf("usage: %s getbufnum\n", argv[0].str);
465 printf("usage: %s getbufptr\n", argv[0].str);
466 printf("usage: %s setbufnum <num>\n", argv[0].str);
467 printf("usage: %s puts <string>\n", argv[0].str);
468 printf("usage: %s read <length> <buffer num>\n", argv[0].str);
469 printf("usage: %s printftest\n", argv[0].str);
470 printf("usage: %s dump [buffer num]\n", argv[0].str);
471 printf("usage: %s vec [buffer num]\n", argv[0].str);
472 return -1;
473 }
474
475 if (!strcmp(argv[1].str, "create")) {
476 if (argc < 4) goto notenoughargs;
477
478 uint size = argv[2].u;
479 uint count = argv[3].u;
480
481 void *ptr = malloc(size);
482 if (!ptr) {
483 printf("error allocating memory for klog\n");
484 return -1;
485 }
486 err = klog_create(ptr, size, count);
487 printf("klog_create returns %d\n", err);
488 if (err < 0)
489 free(ptr);
490 #if KLOG_RETENTION_TEST
491 } else if (!strcmp(argv[1].str, "createret")) {
492 err = klog_create(klog_test_buf, sizeof(klog_test_buf), 1);
493 printf("klog_create returns %d\n", err);
494 } else if (!strcmp(argv[1].str, "recoverret")) {
495 err = klog_recover(klog_test_buf);
496 printf("klog_recover returns %d\n", err);
497 #endif
498 } else if (!strcmp(argv[1].str, "getbufcount")) {
499 printf("%d buffers\n", klog_buffer_count());
500 } else if (!strcmp(argv[1].str, "getbufnum")) {
501 printf("%d current buffer\n", klog_current_buffer());
502 } else if (!strcmp(argv[1].str, "getbufptr")) {
503 printf("ptr %p\n", klog);
504 } else if (!strcmp(argv[1].str, "setbufnum")) {
505 if (argc < 3) goto notenoughargs;
506
507 err = klog_set_current_buffer(argv[2].u);
508 printf("klog_set_current_buffer returns %d\n", err);
509 } else if (!strcmp(argv[1].str, "puts")) {
510 if (argc < 3) goto notenoughargs;
511
512 klog_puts(argv[2].str);
513 klog_putchar('\n');
514 } else if (!strcmp(argv[1].str, "read")) {
515 if (argc < 4) goto notenoughargs;
516 size_t len = argv[2].u;
517 int buf_id = argv[3].i;
518 char *buf = malloc(len);
519 if (!buf) {
520 printf("error allocating memory for klog read\n");
521 return -1;
522 }
523 size_t count = klog_read(buf, len, buf_id);
524 if (count > 0) {
525 printf("read %zu byte(s): \"", count);
526 for (size_t i = 0; i < count; i++)
527 putchar(buf[i]);
528 putchar('\"');
529 putchar('\n');
530 } else {
531 printf("read returned error: %d\n", count);
532 }
533 free(buf);
534 } else if (!strcmp(argv[1].str, "getc")) {
535 if (argc < 3) goto notenoughargs;
536 int buf_id = argv[2].i;
537 printf("read: '%c'\n", klog_getc(buf_id));
538 } else if (!strcmp(argv[1].str, "printftest")) {
539 klog_printf("a plain string\n");
540 klog_printf("numbers: %d %d %d %u\n", 1, 2, 3, 99);
541 klog_printf("strings: '%s' '%s'\n", "a little string", "another one");
542 } else if (!strcmp(argv[1].str, "dump")) {
543 int buffer = -1;
544
545 if (argc >= 3)
546 buffer = argv[2].u;
547
548 klog_dump(buffer);
549 } else if (!strcmp(argv[1].str, "vec")) {
550 int buffer = -1;
551
552 if (argc >= 3)
553 buffer = argv[2].u;
554
555 iovec_t vec[2];
556 memset(vec, 0x99, sizeof(vec));
557 err = klog_get_buffer(buffer, vec);
558 printf("klog_get_buffer returns %d\n", err);
559 printf("vec %d: base %p, len %zu\n", 0, vec[0].iov_base, vec[0].iov_len);
560 printf("vec %d: base %p, len %zu\n", 1, vec[1].iov_base, vec[1].iov_len);
561 } else {
562 printf("ERROR unknown command\n");
563 goto usage;
564 }
565
566 return 0;
567 }
568
569 STATIC_COMMAND_START
570 STATIC_COMMAND("klog", "commands for manipulating klog", &cmd_klog)
571 STATIC_COMMAND_END(klog);
572