1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2018-2023 Andre Richter <andre.o.richter@gmail.com>

//! A panic handler that infinitely waits.

use core::panic::PanicInfo;
use mango_core::println;

//--------------------------------------------------------------------------------------------------
// Private Code
//--------------------------------------------------------------------------------------------------

/// The point of exit for `libkernel`.
///
/// It is linked weakly, so that the integration tests can overload its standard behavior.
fn _panic_exit(system: &spin::Mutex<impl mango_hal::devices::System>) -> !
{
  #[cfg(not(feature = "test_build"))]
  {
    system.lock().wait_forever()
  }

  #[cfg(feature = "test_build")]
  {
    mango_core::qemu::exit_qemu(mango_core::qemu::QemuExitCode::Failed);
  }
}

/// Stop immediately if called a second time.
///
/// # Note
///
/// Using atomics here relieves us from needing to use `unsafe` for the static variable.
///
/// On `AArch64`, which is the only implemented architecture at the time of writing this,
/// [`AtomicBool::load`] and [`AtomicBool::store`] are lowered to ordinary load and store
/// instructions. They are therefore safe to use even with MMU + caching deactivated.
///
/// [`AtomicBool::load`]: core::sync::atomic::AtomicBool::load
/// [`AtomicBool::store`]: core::sync::atomic::AtomicBool::store
fn panic_prevent_reenter(system: &spin::Mutex<impl mango_hal::devices::System>)
{
  use core::sync::atomic::{AtomicBool, Ordering};

  #[cfg(not(any(target_arch = "aarch64", target_arch = "x86_64")))]
  compile_error!("Add the target_arch to above's check if the following code is safe to use");

  static PANIC_IN_PROGRESS: AtomicBool = AtomicBool::new(false);

  if !PANIC_IN_PROGRESS.load(Ordering::Relaxed)
  {
    PANIC_IN_PROGRESS.store(true, Ordering::Relaxed);

    return;
  }

  _panic_exit(system)
}

/// panic handlers for kernels.
pub fn panic_handler(info: &PanicInfo, system: &spin::Mutex<impl mango_hal::devices::System>) -> !
{
  system.lock().disable_interruptions();
  // Protect against panic infinite loops if any of the following code panics itself.
  panic_prevent_reenter(system);

  let timestamp = mango_core::TIME.lock().get_uptime_or(0);
  let (location, line, column) = match info.location()
  {
    Some(loc) => (loc.file(), loc.line(), loc.column()),
    _ => ("???", 0, 0),
  };

  println!(
    "[  {:>3}.{:06}] Kernel panic!\n\n\
        Panic location:\n      File '{}', line {}, column {}\n\n\
        {}",
    timestamp.as_secs(),
    timestamp.subsec_micros(),
    location,
    line,
    column,
    info.message(),
  );

  _panic_exit(system)
}