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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! Virtual machine scheduler interface

use {
    crate::hypercall,
    xen_sys::{
        evtchn_port_t, sched_pin_override_t, sched_poll_t, sched_remote_shutdown_t,
        sched_shutdown_t, sched_watchdog_t, SCHEDOP_block, SCHEDOP_pin_override, SCHEDOP_poll,
        SCHEDOP_remote_shutdown, SCHEDOP_shutdown, SCHEDOP_shutdown_code, SCHEDOP_watchdog,
        SCHEDOP_yield, SHUTDOWN_crash, SHUTDOWN_poweroff, SHUTDOWN_reboot, SHUTDOWN_suspend,
        SHUTDOWN_watchdog, __HYPERVISOR_sched_op,
    },
};

/// Scheduling command
pub enum Command<'a> {
    /// Yield remaining of domain's quantum
    Yield,

    /// Deschedule domain until event is received
    Block,

    /// "Halt execution of this domain (all VCPUs) and notify the system controller.
    /// @arg == pointer to sched_shutdown_t structure.
    ///
    /// If the sched_shutdown_t reason is SHUTDOWN_suspend then
    /// x86 PV guests must also set RDX (EDX for 32-bit guests) to the MFN
    /// of the guest's start info page.  RDX/EDX is the third hypercall
    /// argument.
    ///
    ///  In addition, which reason is SHUTDOWN_suspend this hypercall
    ///  returns 1 if suspend was cancelled or the domain was merely
    /// checkpointed, and 0 if it is resuming in a new domain."
    Shutdown(ShutdownReason),

    /// "Poll a set of event-channel ports. Return when one or more are pending"
    Poll {
        /// Event-channel ports
        ports: &'a mut [evtchn_port_t],
        /// Nanoseconds since UNIX epoch that if reached while blocked on an event
        /// will result in a non-zero return value of the hypercall
        timeout: u64,
    },

    /// "Declare a shutdown for another domain. The main use of this function is
    /// in interpreting shutdown requests and reasons for fully-virtualized
    /// domains. A para-virtualized domain may use SCHEDOP_shutdown directly."
    RemoteShutdown {
        /// Remote domain ID
        domain_id: u16,
        /// Reason for shutdown
        reason: ShutdownReason,
    },

    /// "Latch a shutdown code, so that when the domain later shuts down it
    /// reports this code to the control tools"
    ShutdownCode(ShutdownReason),

    /// "Setup, poke and destroy a domain watchdog timer.
    /// @arg == pointer to sched_watchdog_t structure.
    /// With id == 0, setup a domain watchdog timer to cause domain shutdown
    ///               after timeout, returns watchdog id.
    /// With id != 0 and timeout == 0, destroy domain watchdog timer.
    /// With id != 0 and timeout != 0, poke watchdog timer and set new timeout."
    Watchdog {
        /// ID of watchdog timer
        id: u32,
        /// Timeout
        timeout: u32,
    },

    /// "Override the current vcpu affinity by pinning it to one physical cpu or
    /// undo this override restoring the previous affinity.
    /// @arg == pointer to sched_pin_override_t structure.
    ///
    /// A negative pcpu value will undo a previous pin override and restore the
    /// previous cpu affinity.
    /// This call is allowed for the hardware domain only and requires the cpu
    /// to be part of the domain's cpupool."
    PinOverride {
        /// Physical CPU ID to pin to
        pcpu: i32,
    },
}

/// Reasons for `Command::Shutdown`
pub enum ShutdownReason {
    /// "Domain exited normally. Clean up and kill."
    Poweroff = SHUTDOWN_poweroff as isize,
    /// "Clean up, kill, and then restart."
    Reboot = SHUTDOWN_reboot as isize,
    /// "Clean up, save suspend info, kill."
    Suspend = SHUTDOWN_suspend as isize,
    /// "Tell controller we've crashed."
    Crash = SHUTDOWN_crash as isize,
    /// "Restart because watchdog time expired."
    Watchdog = SHUTDOWN_watchdog as isize,
}

unsafe fn sched_op(cmd: u32, arg: u64) {
    hypercall!(__HYPERVISOR_sched_op, cmd, arg).expect("Failed schedule operation");
}

///
pub fn schedule_operation(cmd: Command) {
    unsafe {
        match cmd {
            Command::Yield => sched_op(SCHEDOP_yield, 0),
            Command::Block => sched_op(SCHEDOP_block, 0),
            Command::Shutdown(reason) => {
                let arg = sched_shutdown_t {
                    reason: reason as u32,
                };

                sched_op(SCHEDOP_shutdown, &arg as *const sched_shutdown_t as u64)
            }
            Command::Poll { ports, timeout } => {
                let arg = sched_poll_t {
                    ports: ports.as_mut_ptr(),
                    nr_ports: ports.len() as u32,
                    timeout,
                };

                sched_op(SCHEDOP_poll, &arg as *const sched_poll_t as u64)
            }
            Command::RemoteShutdown { domain_id, reason } => {
                let arg = sched_remote_shutdown_t {
                    domain_id,
                    reason: reason as u32,
                };

                sched_op(
                    SCHEDOP_remote_shutdown,
                    &arg as *const sched_remote_shutdown_t as u64,
                )
            }
            Command::ShutdownCode(reason) => {
                let arg = sched_shutdown_t {
                    reason: reason as u32,
                };

                sched_op(
                    SCHEDOP_shutdown_code,
                    &arg as *const sched_shutdown_t as u64,
                )
            }
            Command::Watchdog { id, timeout } => {
                let arg = sched_watchdog_t { id, timeout };

                sched_op(SCHEDOP_watchdog, &arg as *const sched_watchdog_t as u64)
            }
            Command::PinOverride { pcpu } => {
                let arg = sched_pin_override_t { pcpu };

                sched_op(
                    SCHEDOP_pin_override,
                    &arg as *const sched_pin_override_t as u64,
                )
            }
        };
    }
}