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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#![no_std]
#![warn(missing_docs)]
#![deny(warnings)]

//! mango_rt: mango runtime
//!
//! This crate contains the support for defining a mango kernel and its applications.
//! Kernels are defined with the macro [kernel!].
//!

extern crate alloc;

mod application;
pub mod panic_wait;
pub mod schedulers;

pub use application::Application;

/// A kernel is defined with the `mango_rt::kernel!` macro. This defines:
/// * the name of the initialisation function (`name`)
/// * the name of the static instance (`instance`). It is recommended that this is set to `KERNEL_INSTANCE`.
/// * the arguments from the bootloader given to the main function (`main_arguments`).
/// * the list of devices defined by the kernel (`devices`).
/// * the device used for stdout (`stdout`).
/// * the scheduler used (`scheduler`).
/// * the list of applications (`applications`).
///
/// Example of use:
/// ```rust
/// mango_rt::kernel!(
///   name: hello_world_kernel,
///   instance: KERNEL_INSTANCE,
///   main_arguments: (boot_info: &'static bootloader::BootInfo),
///   devices: {
///     system: mango_hal::systems::x86_64::System => () / (boot_info),
///     vga_output: mango_hal::io::VgaTextBuffer => () / (),
///   },
///   stdout: Some(&KERNEL_INSTANCE.vga_output),
///   scheduler: mango_rt::schedulers::NoScheduler => (),
///   applications: {
///     hello_world: HelloWorld => ()
///   }
/// );
/// ```

#[macro_export]
macro_rules! kernel
{
  (
    name: $kernel_name:ident,
    instance: $kernel_instance:ident,
    main_arguments: ( $($main_arg_name:ident : $main_arg_type:ty),* $(,)? ),
    devices: { $($dev_name:ident : $dev_type:ty => ($($dev_create_arg:tt)*) / ($($dev_init_arg:tt)*)),+ $(,)? },
    stdout: None,
    scheduler: $scheduler_type:ty => ($($scheduler_create_args:tt)*) / ($($scheduler_init_args:tt)*),
    applications: { $($app_name:ident : $app_type:ty => ($($app_create_arg:tt)*)),+ $(,)? }
  ) => (
    mango_os::boot::entry_point!($kernel_name);

    pub struct Kernel
    {
      $(pub $dev_name: spin::Mutex<$dev_type>,)+
      pub scheduler: spin::Mutex<$scheduler_type>,
    }

    lazy_static! {
      pub static ref $kernel_instance: Kernel = Kernel {
        $($dev_name: spin::Mutex::new(<$dev_type>::new($($dev_create_arg, )*)),)+
        scheduler: spin::Mutex::new(<$scheduler_type>::new($($scheduler_create_args, )*)),
      };
    }

    fn $kernel_name($($main_arg_name: $main_arg_type,)*) -> !
    {
      $($kernel_instance.$dev_name.lock().initialise($($dev_init_arg, )*);)+
      $kernel_instance.scheduler.lock().initialise($($scheduler_init_args)*);
      $(let mut $app_name = <$app_type>::new($($app_create_arg)*);)+
      $($app_name.start();)+

      $kernel_instance.scheduler.lock().run();
    }
  );
  (
    name: $kernel_name:ident,
    instance: $kernel_instance:ident,
    main_arguments: ( $($main_arg_name:ident : $main_arg_type:ty),* $(,)? ),
    devices: { $($dev_name:ident : $dev_type:ty => ($($dev_create_arg:tt)*) / ($($dev_init_arg:tt)*)),+ $(,)? },
    stdout: $stdout_name:expr,
    scheduler: $scheduler_type:ty => ($($scheduler_create_args:tt)*) / ($($scheduler_init_args:tt)*),
    applications: { $($app_name:ident : $app_type:ty => ($($app_create_arg:tt)*)),* $(,)? }
  ) => (
    mango_os::boot::entry_point!($kernel_name);

    pub struct Kernel
    {
      $(pub $dev_name: spin::Mutex<$dev_type>,)+
      pub scheduler: spin::Mutex<$scheduler_type>,
    }

    lazy_static! {
      pub static ref $kernel_instance: Kernel = Kernel {
        $($dev_name: spin::Mutex::new(<$dev_type>::new($($dev_create_arg, )*)),)+
        scheduler: spin::Mutex::new(<$scheduler_type>::new($($scheduler_create_args, )*)),
      };
    }

    fn $kernel_name($($main_arg_name: $main_arg_type,)*) -> !
    {
      $($kernel_instance.$dev_name.lock().initialise($($dev_init_arg, )*);)+
      $kernel_instance.scheduler.lock().initialise($($scheduler_init_args)*);
      mango_os::core::STDOUT.lock().set_printer_fn(
        |x| {
          if let Some(dev) = $stdout_name
          {
            use core::fmt::Write;
            dev.lock().write_str(x)
          } else {
            Ok(())
          }
        });
      $(let mut $app_name = <$app_type>::new($($app_create_arg)*);)*
      $($app_name.start();)*

      $kernel_instance.scheduler.lock().run();
    }
    #[panic_handler]
    fn panic_handler(info: &core::panic::PanicInfo) -> !
    {
      $crate::panic_wait::panic_handler(info, &$kernel_instance.system)
    }
  );
}