|  | /* | 
|  | * Copyright (c) 2008-2015 Travis Geiselbrecht | 
|  | * | 
|  | * Permission is hereby granted, free of charge, to any person obtaining | 
|  | * a copy of this software and associated documentation files | 
|  | * (the "Software"), to deal in the Software without restriction, | 
|  | * including without limitation the rights to use, copy, modify, merge, | 
|  | * publish, distribute, sublicense, and/or sell copies of the Software, | 
|  | * and to permit persons to whom the Software is furnished to do so, | 
|  | * subject to the following conditions: | 
|  | * | 
|  | * The above copyright notice and this permission notice shall be | 
|  | * included in all copies or substantial portions of the Software. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
|  | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
|  | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | 
|  | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | 
|  | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | 
|  | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | 
|  | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
|  | */ | 
|  |  | 
|  | #include <ctype.h> | 
|  | #include <debug.h> | 
|  | #include <stdlib.h> | 
|  | #include <printf.h> | 
|  | #include <stdio.h> | 
|  | #include <list.h> | 
|  | #include <string.h> | 
|  | #include <arch/ops.h> | 
|  | #include <platform.h> | 
|  | #include <platform/debug.h> | 
|  | #include <kernel/thread.h> | 
|  |  | 
|  | #if !DISABLE_DEBUG_OUTPUT | 
|  | static int _dvprintf(const char *fmt, va_list ap); | 
|  | #else | 
|  | static inline int _dvprintf(const char *fmt, va_list ap) { return 0; } | 
|  | #endif | 
|  |  | 
|  | #if WITH_LIB_SM | 
|  | #define PRINT_LOCK_FLAGS SPIN_LOCK_FLAG_IRQ_FIQ | 
|  | #else | 
|  | #define PRINT_LOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS | 
|  | #endif | 
|  |  | 
|  | static spin_lock_t print_spin_lock = 0; | 
|  | static struct list_node print_callbacks = LIST_INITIAL_VALUE(print_callbacks); | 
|  |  | 
|  | /* print lock must be held when invoking out, outs, outc */ | 
|  | static void out_count(const char *str, size_t len) | 
|  | { | 
|  | print_callback_t *cb; | 
|  | size_t i; | 
|  |  | 
|  | /* print to any registered loggers */ | 
|  | if (!list_is_empty(&print_callbacks)) { | 
|  | spin_lock_saved_state_t state; | 
|  | spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS); | 
|  |  | 
|  | list_for_every_entry(&print_callbacks, cb, print_callback_t, entry) { | 
|  | if (cb->print) | 
|  | cb->print(cb, str, len); | 
|  | } | 
|  |  | 
|  | spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS); | 
|  | } | 
|  |  | 
|  | /* write out the serial port */ | 
|  | for (i = 0; i < len; i++) { | 
|  | platform_dputc(str[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | void register_print_callback(print_callback_t *cb) | 
|  | { | 
|  | spin_lock_saved_state_t state; | 
|  | spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS); | 
|  |  | 
|  | list_add_head(&print_callbacks, &cb->entry); | 
|  |  | 
|  | spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS); | 
|  | } | 
|  |  | 
|  | void unregister_print_callback(print_callback_t *cb) | 
|  | { | 
|  | spin_lock_saved_state_t state; | 
|  | spin_lock_save(&print_spin_lock, &state, PRINT_LOCK_FLAGS); | 
|  |  | 
|  | list_delete(&cb->entry); | 
|  |  | 
|  | spin_unlock_restore(&print_spin_lock, state, PRINT_LOCK_FLAGS); | 
|  | } | 
|  |  | 
|  | void spin(uint32_t usecs) | 
|  | { | 
|  | lk_bigtime_t start = current_time_hires(); | 
|  |  | 
|  | while ((current_time_hires() - start) < usecs) | 
|  | ; | 
|  | } | 
|  |  | 
|  | void _panic(void *caller, const char *fmt, ...) | 
|  | { | 
|  | dprintf(ALWAYS, "panic (caller %p): ", caller); | 
|  |  | 
|  | va_list ap; | 
|  | va_start(ap, fmt); | 
|  | _dvprintf(fmt, ap); | 
|  | va_end(ap); | 
|  |  | 
|  | platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_PANIC); | 
|  | } | 
|  |  | 
|  | static int __debug_stdio_fputc(void *ctx, int c) | 
|  | { | 
|  | char x = c; | 
|  | out_count(&x, 1); | 
|  | return c; | 
|  | } | 
|  |  | 
|  | static int __debug_stdio_fputs(void *ctx, const char *s) | 
|  | { | 
|  | out_count(s, strlen(s)); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int __debug_stdio_fgetc(void *ctx) | 
|  | { | 
|  | char c; | 
|  | int err; | 
|  |  | 
|  | err = platform_dgetc(&c, true); | 
|  | if (err < 0) | 
|  | return err; | 
|  | return (unsigned char)c; | 
|  | } | 
|  |  | 
|  | static int __panic_stdio_fgetc(void *ctx) | 
|  | { | 
|  | char c; | 
|  | int err; | 
|  |  | 
|  | err = platform_pgetc(&c, false); | 
|  | if (err < 0) | 
|  | return err; | 
|  | return (unsigned char)c; | 
|  | } | 
|  |  | 
|  | static int __debug_stdio_vfprintf(void *ctx, const char *fmt, va_list ap) | 
|  | { | 
|  | return _dvprintf(fmt, ap); | 
|  | } | 
|  |  | 
|  | #define DEFINE_STDIO_DESC(id)						\ | 
|  | [(id)]	= {							\ | 
|  | .ctx		= &__stdio_FILEs[(id)],			\ | 
|  | .fputc		= __debug_stdio_fputc,			\ | 
|  | .fputs		= __debug_stdio_fputs,			\ | 
|  | .fgetc		= __debug_stdio_fgetc,			\ | 
|  | .vfprintf	= __debug_stdio_vfprintf,		\ | 
|  | } | 
|  |  | 
|  | FILE __stdio_FILEs[3] = { | 
|  | DEFINE_STDIO_DESC(0), /* stdin */ | 
|  | DEFINE_STDIO_DESC(1), /* stdout */ | 
|  | DEFINE_STDIO_DESC(2), /* stderr */ | 
|  | }; | 
|  | #undef DEFINE_STDIO_DESC | 
|  |  | 
|  | FILE get_panic_fd(void) | 
|  | { | 
|  | FILE panic_fd = {0}; | 
|  | panic_fd.fgetc = __panic_stdio_fgetc; | 
|  |  | 
|  | panic_fd.fputc = __debug_stdio_fputc; | 
|  | panic_fd.fputs = __debug_stdio_fputs; | 
|  | panic_fd.vfprintf = __debug_stdio_vfprintf; | 
|  | return panic_fd; | 
|  | } | 
|  |  | 
|  | #if !DISABLE_DEBUG_OUTPUT | 
|  |  | 
|  | static int _dprintf_output_func(const char *str, size_t len, void *state) | 
|  | { | 
|  | out_count(str, len); | 
|  | return len; | 
|  | } | 
|  |  | 
|  | int _dvprintf(const char *fmt, va_list ap) | 
|  | { | 
|  | return _printf_engine(&_dprintf_output_func, NULL, fmt, ap); | 
|  | } | 
|  |  | 
|  | int _dprintf(const char *fmt, ...) | 
|  | { | 
|  | int err; | 
|  | va_list ap; | 
|  |  | 
|  | va_start(ap, fmt); | 
|  | err = _printf_engine(&_dprintf_output_func, NULL, fmt, ap); | 
|  | va_end(ap); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | void hexdump(const void *ptr, size_t len) | 
|  | { | 
|  | addr_t address = (addr_t)ptr; | 
|  | size_t count; | 
|  |  | 
|  | for (count = 0 ; count < len; count += 16) { | 
|  | union { | 
|  | uint32_t buf[4]; | 
|  | uint8_t  cbuf[16]; | 
|  | } u; | 
|  | size_t s = ROUNDUP(MIN(len - count, 16), 4); | 
|  | size_t i; | 
|  |  | 
|  | printf("0x%08lx: ", address); | 
|  | for (i = 0; i < s / 4; i++) { | 
|  | u.buf[i] = ((const uint32_t *)address)[i]; | 
|  | printf("%08x ", u.buf[i]); | 
|  | } | 
|  | for (; i < 4; i++) { | 
|  | printf("         "); | 
|  | } | 
|  | printf("|"); | 
|  |  | 
|  | for (i=0; i < 16; i++) { | 
|  | char c = u.cbuf[i]; | 
|  | if (i < s && isprint(c)) { | 
|  | printf("%c", c); | 
|  | } else { | 
|  | printf("."); | 
|  | } | 
|  | } | 
|  | printf("|\n"); | 
|  | address += 16; | 
|  | } | 
|  | } | 
|  |  | 
|  | void hexdump8_ex(const void *ptr, size_t len, uint64_t disp_addr) | 
|  | { | 
|  | addr_t address = (addr_t)ptr; | 
|  | size_t count; | 
|  | size_t i; | 
|  | const char* addr_fmt = ((disp_addr + len) > 0xFFFFFFFF) | 
|  | ? "0x%016llx: " | 
|  | : "0x%08llx: "; | 
|  |  | 
|  | for (count = 0 ; count < len; count += 16) { | 
|  | printf(addr_fmt, disp_addr + count); | 
|  |  | 
|  | for (i=0; i < MIN(len - count, 16); i++) { | 
|  | printf("%02hhx ", *(const uint8_t *)(address + i)); | 
|  | } | 
|  |  | 
|  | for (; i < 16; i++) { | 
|  | printf("   "); | 
|  | } | 
|  |  | 
|  | printf("|"); | 
|  |  | 
|  | for (i=0; i < MIN(len - count, 16); i++) { | 
|  | char c = ((const char *)address)[i]; | 
|  | printf("%c", isprint(c) ? c : '.'); | 
|  | } | 
|  |  | 
|  | printf("\n"); | 
|  | address += 16; | 
|  | } | 
|  | } | 
|  |  | 
|  | #endif // !DISABLE_DEBUG_OUTPUT | 
|  |  | 
|  | // vim: set noexpandtab: |