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
// SPDX-License-Identifier: MIT OR Apache-2.0
//
// Copyright (c) 2019-2021 Andre Richter <andre.o.richter@gmail.com>

//! Exit QEMU with user-defined code.
//!
//! Quit a running QEMU session with user-defined exit code. Useful for unit or integration tests
//! using QEMU.
//!
//! ## TL;DR
//!
//! ```rust
//! use qemu_exit::QEMUExit;
//!
//! #[cfg(target_arch = "aarch64")]
//! let qemu_exit_handle = qemu_exit::AArch64::new();
//!
//! // addr: The address of sifive_test.
//! #[cfg(target_arch = "riscv64")]
//! let qemu_exit_handle = qemu_exit::RISCV64::new(addr);
//!
//! // io_base:             I/O-base of isa-debug-exit.
//! // custom_exit_success: A custom success code; Must be an odd number.
//! #[cfg(target_arch = "x86_64")]
//! let qemu_exit_handle = qemu_exit::X86::new(io_base, custom_exit_success);
//!
//! qemu_exit_handle.exit(1337);
//! qemu_exit_handle.exit_success();
//! qemu_exit_handle.exit_failure();
//! ```
//!
//! ## Architecture Specific Configuration
//!
//! ### AArch64
//!
//! Pass the `-semihosting` argument to the QEMU invocation, e.g.:
//! ```
//! qemu-system-aarch64 -M raspi3 -serial stdio -semihosting -kernel kernel8.img
//! ```
//!
//! ### RISCV64
//!
//! You need to chose a machine with the `sifive_test` device, for exemple `-M virt`:
//! ```
//! qemu-system-riscv64 -M virt -nographic -monitor none -serial stdio -kernel kernel.elf
//! ```
//!
//! ### x86_64
//!
//! Add the special ISA debug exit device by passing the flags:
//! ```
//! -device isa-debug-exit,iobase=0xf4,iosize=0x04
//! ```
//!
//! When instantiating the handle, `iobase` must be given as the first parameter.
//!
//! The second parameter must be an `EXIT_SUCCESS` code of your choice that is an odd number, aka
//! bit number zero must be `1`. This is needed because in QEMU, the provided code is internally
//! binary-OR'ed with `0x1`. This is hardcoded and therefore, with `isa-debug-exit`, it is not
//! possible to let QEMU invoke `exit(0)`.
//!
//! ```rust
//! let qemu_exit_handle = qemu_exit::X86::new(io_base, custom_exit_success);
//! ```
//!
//! ## Literature
//!
//! - [Semihosting for AArch32 and AArch64](https://static.docs.arm.com/dui0003/b/semihosting.pdf)
//! - [QEMU isa-debug-exit source](https://git.qemu.org/?p=qemu.git;a=blob;f=hw/misc/debugexit.c)
//! - [QEMU sifive_test source](https://git.qemu.org/?p=qemu.git;a=blob;f=hw/misc/sifive_test.c)

#![deny(missing_docs)]
#![feature(asm)]
#![feature(const_fn)]
#![feature(const_panic)]
#![no_std]

#[cfg(target_arch = "aarch64")]
pub mod aarch64;

#[cfg(target_arch = "aarch64")]
pub use aarch64::*;

#[cfg(target_arch = "riscv64")]
pub mod riscv64;

#[cfg(target_arch = "riscv64")]
pub use riscv64::*;

#[cfg(target_arch = "x86_64")]
pub mod x86;

#[cfg(target_arch = "x86_64")]
pub use x86::*;

/// Generic interface for exiting QEMU.
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) -> !;
}