# qemu_exit.rs - source
Source of the Rust file `src/qemu_exit.rs`.
Keywords: rust, rustlang, rust-lang

    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

    // SPDX-License-Identifier: MIT OR Apache-2.0
    //
    // Copyright (c) 2020-2022 Esteban Blanc <estblcsk@gmail.com>

    //! RISCV64.

    use core::arch::asm;

    const EXIT_SUCCESS: u32 = 0x5555; // Equals `exit(0)`.

    const EXIT_FAILURE_FLAG: u32 = 0x3333;
    const EXIT_FAILURE: u32 = exit_code_encode(1); // Equals `exit(1)`.
    const EXIT_RESET: u32 = 0x7777;

    pub trait QEMUExit {
        /// Exit with specified return code.
        ///
        /// Note: For `X86`, code is binary-OR'ed with `0x1` inside QEMU.
        fn exit(&self, code: u32) -> !;

        /// Exit QEMU using `EXIT_SUCCESS`, aka `0`, if possible.
        ///
        /// Note: Not possible for `X86`.
        fn exit_success(&self) -> !;

        /// Exit QEMU using `EXIT_FAILURE`, aka `1`.
        fn exit_failure(&self) -> !;
    }

    /// RISCV64 configuration
    pub struct RISCV64 {
        /// Address of the sifive_test mapped device.
        addr: u64,
    }

    /// Encode the exit code using EXIT_FAILURE_FLAG.
    const fn exit_code_encode(code: u32) -> u32 {
        (code << 16) | EXIT_FAILURE_FLAG
    }

    impl RISCV64 {
        /// Create an instance.
        pub const fn new(addr: u64) -> Self {
            RISCV64 { addr }
        }
    }

    impl QEMUExit for RISCV64 {
        /// Exit qemu with specified exit code.
        fn exit(&self, code: u32) -> ! {
            // If code is not a special value, we need to encode it with EXIT_FAILURE_FLAG.
            let code_new = match code {
                EXIT_SUCCESS | EXIT_FAILURE | EXIT_RESET => code,
                _ => exit_code_encode(code),
            };

            unsafe {
                asm!(
                    "sw {0}, 0({1})",
                    in(reg)code_new, in(reg)self.addr
                );

                // For the case that the QEMU exit attempt did not work, transition into an infinite
                // loop. Calling `panic!()` here is unfeasible, since there is a good chance
                // this function here is the last expression in the `panic!()` handler
                // itself. This prevents a possible infinite loop.
                loop {
                    asm!("wfi", options(nomem, nostack));
                }
            }
        }

        fn exit_success(&self) -> ! {
            self.exit(EXIT_SUCCESS);
        }

        fn exit_failure(&self) -> ! {
            self.exit(EXIT_FAILURE);
        }
    }

    pub const QEMU_EXIT_HANDLE: RISCV64 = RISCV64::new(0x100000);