# qemu.rs - source
Source of the Rust file `src/boards/qemu.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
    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

    pub const CLOCK_FREQ: usize = 12500000;

    pub const MMIO: &[(usize, usize)] = &[
        (0x1000_0000, 0x1000),        // VIRT_UART0 in virt machine
        (0x1000_1000, 0x1000),        // VIRT_VIRTIO in virt machine
        (0x0C00_0000, 0x40_0000),  // VIRT_PLIC in virt machine
        (0x0010_0000, 0x00_2000),  // VIRT_TEST/RTC  in virt machine
    ];

    pub type BlockDeviceImpl = crate::drivers::block::VirtIOBlock;
    pub type CharDeviceImpl = crate::drivers::chardev::NS16550a<VIRT_UART>;

    pub const VIRT_PLIC: usize = 0xC00_0000;
    pub const VIRT_UART: usize = 0x1000_0000;

    use crate::drivers::block::BLOCK_DEVICE;
    use crate::drivers::chardev::{CharDevice, UART};
    use crate::drivers::plic::{IntrTargetPriority, PLIC};

    pub fn device_init() {
        use riscv::register::sie;
        let mut plic = unsafe { PLIC::new(VIRT_PLIC) };
        let hart_id: usize = 0;
        let supervisor = IntrTargetPriority::Supervisor;
        let machine = IntrTargetPriority::Machine;
        plic.set_threshold(hart_id, supervisor, 0);
        plic.set_threshold(hart_id, machine, 1);
        for intr_src_id in [1usize, 10] {
            plic.enable(hart_id, supervisor, intr_src_id);
            plic.set_priority(intr_src_id, 1);
        }
        unsafe {
            sie::set_sext();
        }
    }

    pub fn irq_handler() {
        let mut plic = unsafe { PLIC::new(VIRT_PLIC) };
        let intr_src_id = plic.claim(0, IntrTargetPriority::Supervisor);
        match intr_src_id {
            1 => BLOCK_DEVICE.handle_irq(),
            10 => UART.handle_irq(),
            _ => panic!("unsupported IRQ {}", intr_src_id),
        }
        plic.complete(0, IntrTargetPriority::Supervisor, intr_src_id);
    }

    //ref:: https://github.com/andre-richter/qemu-exit
    use core::arch::asm;

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

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

    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);
        }
    }

    const VIRT_TEST: u64 =0x100000;

    pub const QEMU_EXIT_HANDLE: RISCV64 = RISCV64::new(VIRT_TEST);