| // Copyright 2016 The Fuchsia Authors |
| // Copyright (c) 2008-2015 Travis Geiselbrecht |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include "debug.h" |
| |
| #include <align.h> |
| #include <ctype.h> |
| #include <lib/crashlog.h> |
| #include <platform.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <zircon/boot/crash-reason.h> |
| #include <zircon/listnode.h> |
| #include <zircon/time.h> |
| #include <zircon/types.h> |
| |
| #include <arch/ops.h> |
| #include <dev/hw_rng.h> |
| #include <kernel/lockdep.h> |
| #include <kernel/spinlock.h> |
| #include <ktl/algorithm.h> |
| #include <platform/debug.h> |
| |
| #include <ktl/enforce.h> |
| |
| namespace { |
| |
| // Start a system panic, and print a header message. |
| // |
| // Calls should be followed by: |
| // |
| // * Calling "printf" with the reason for the panic, followed by |
| // a newline. |
| // |
| // * A call to "PanicFinish". |
| __ALWAYS_INLINE inline void PanicStart(void* pc, void* frame) { |
| platform_panic_start(); |
| |
| fprintf(&stdout_panic_buffer, |
| "\n" |
| "*** KERNEL PANIC (caller pc: %p, stack frame: %p):\n" |
| "*** ", |
| pc, frame); |
| } |
| |
| // Finish a system panic. |
| // |
| // This function will not return, but will perform an action such as |
| // rebooting the system or dropping the system into a debug shell. |
| // |
| // Marked "__ALWAYS_INLINE" to avoid an additional stack frame from |
| // appearing in the backtrace. |
| __ALWAYS_INLINE __NO_RETURN inline void PanicFinish() { |
| // Add a newline between the panic message and the stack trace. |
| fprintf(&stdout_panic_buffer, "\n"); |
| |
| platform_halt(HALT_ACTION_HALT, ZirconCrashReason::Panic); |
| } |
| |
| // Determine if the given string ends with the given character. |
| bool EndsWith(const char* str, char x) { |
| size_t len = strlen(str); |
| return len > 0 && str[len - 1] == x; |
| } |
| |
| } // namespace |
| |
| void spin(uint32_t usecs) { |
| zx_time_t start = current_time(); |
| |
| zx_duration_t nsecs = ZX_USEC(usecs); |
| while (zx_time_sub_time(current_time(), start) < nsecs) |
| ; |
| } |
| |
| void panic(const char* fmt, ...) { |
| PanicStart(__GET_CALLER(), __GET_FRAME()); |
| |
| // Print the user message. |
| va_list ap; |
| va_start(ap, fmt); |
| vfprintf(&stdout_panic_buffer, fmt, ap); |
| va_end(ap); |
| |
| // Add a newline to the end of the panic message if it was missing. |
| if (!EndsWith(fmt, '\n')) { |
| fprintf(&stdout_panic_buffer, "\n"); |
| } |
| |
| PanicFinish(); |
| } |
| |
| void assert_fail_msg(const char* file, int line, const char* expression, const char* fmt, ...) { |
| PanicStart(__GET_CALLER(), __GET_FRAME()); |
| |
| // Print the user message. |
| fprintf(&stdout_panic_buffer, "ASSERT FAILED at (%s:%d): %s\n", file, line, expression); |
| va_list ap; |
| va_start(ap, fmt); |
| vfprintf(&stdout_panic_buffer, fmt, ap); |
| va_end(ap); |
| |
| // Add a newline to the end of the panic message if it was missing. |
| if (!EndsWith(fmt, '\n')) { |
| fprintf(&stdout_panic_buffer, "\n"); |
| } |
| |
| PanicFinish(); |
| } |
| |
| void assert_fail(const char* file, int line, const char* expression) { |
| PanicStart(__GET_CALLER(), __GET_FRAME()); |
| fprintf(&stdout_panic_buffer, "ASSERT FAILED at (%s:%d): %s\n", file, line, expression); |
| PanicFinish(); |
| } |
| |
| __NO_SAFESTACK uintptr_t choose_stack_guard(void) { |
| uintptr_t guard; |
| if (hw_rng_get_entropy(&guard, sizeof(guard)) != sizeof(guard)) { |
| // We can't get a random value, so use a randomish value. |
| guard = 0xdeadbeef00ff00ffUL ^ (uintptr_t)&guard; |
| } |
| return guard; |
| } |