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