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
// SPDX-License-Identifier: MPL-2.0
// SPDX-FileCopyrightText: 2022 repnop
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at https://mozilla.org/MPL/2.0/.

use crate::{ecall2, SbiError};

/// System reset extension ID
pub const EXTENSION_ID: usize = 0x53525354;

/// The type of reset to perform
#[derive(Debug, Clone, Copy)]
pub enum ResetType {
    /// Shutdown the system
    Shutdown,
    /// Power off all hardware and perform a cold boot
    ColdReboot,
    /// Reset processors and some hardware
    WarmReboot,
    /// Platform specific reset type. The variant value is a value within the
    /// range `0x00000000..=0x0FFFFFFF`. A value outside of that range will be
    /// clamped to the maximum possible valid value for this reset type.
    PlatformSpecific(u32),
}

impl ResetType {
    fn to_u32(self) -> u32 {
        match self {
            ResetType::Shutdown => 0,
            ResetType::ColdReboot => 1,
            ResetType::WarmReboot => 2,
            ResetType::PlatformSpecific(n) => n.min(0x0FFFFFFF) + 0xF0000000,
        }
    }
}

/// The reason for performing the reset
#[derive(Debug, Clone, Copy)]
pub enum ResetReason {
    /// No reason for reset
    NoReason,
    /// System failure
    SystemFailure,
    /// SBI implementation specific reset reason. The variant value is a value
    /// within the range `0x00000000..=0x0FFFFFFF`. A value outside of that
    /// range will be clamped to the maximum possible valid value for this reset
    /// reason type.
    SbiSpecific(u32),
    /// Platform specific reset reason. The variant value is a value within the
    /// range `0x00000000..=0x0FFFFFFF`. A value outside of that range will be
    /// clamped to the maximum possible valid value for this reset reason type.
    PlatformSpecific(u32),
}

impl ResetReason {
    fn to_u32(self) -> u32 {
        match self {
            ResetReason::NoReason => 0,
            ResetReason::SystemFailure => 1,
            ResetReason::SbiSpecific(n) => n.min(0x0FFFFFFF) + 0xE0000000,
            ResetReason::PlatformSpecific(n) => n.min(0x0FFFFFFF) + 0xF0000000,
        }
    }
}

/// Attempt to reset the system in the provided method, with a reason for the
/// reset.
///
/// ### Possible errors
///
/// [`SbiError::NotSupported`]: The [`ResetType`] is valid but not implemented.
///
/// [`SbiError::Failed`]: The system reset request failed for an unknown reason.
pub fn system_reset(
    kind: ResetType,
    reason: ResetReason,
) -> Result<core::convert::Infallible, SbiError> {
    match unsafe {
        ecall2(
            kind.to_u32() as usize,
            reason.to_u32() as usize,
            EXTENSION_ID,
            0,
        )
    } {
        Ok(_) => unreachable!("SBI returned `Ok` after a system reset call"),
        Err(e) => Err(e),
    }
}