blob: 7b01bb637785b49f17bb95a231ad451c1907cbf8 [file] [log] [blame]
// Copyright 2022, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Console driver for 8250 UART.
use crate::uart::Uart;
use core::fmt::{write, Arguments, Write};
use spin::{mutex::SpinMutex, Once};
// Arbitrary limit on the number of consoles that can be registered.
//
// Matches the UART count in crosvm.
const MAX_CONSOLES: usize = 4;
static CONSOLES: [Once<SpinMutex<Uart>>; MAX_CONSOLES] =
[Once::new(), Once::new(), Once::new(), Once::new()];
static ADDRESSES: [Once<usize>; MAX_CONSOLES] =
[Once::new(), Once::new(), Once::new(), Once::new()];
/// Index of the console used by default for logging.
pub const DEFAULT_CONSOLE_INDEX: usize = 0;
/// Index of the console used by default for emergency logging.
pub const DEFAULT_EMERGENCY_CONSOLE_INDEX: usize = DEFAULT_CONSOLE_INDEX;
/// Initialises the global instance(s) of the UART driver.
///
/// This must be called before using the `print!` and `println!` macros.
///
/// # Safety
///
/// This must be called once with the bases of UARTs, mapped as device memory and (if necessary)
/// shared with the host as MMIO, to which no other references must be held.
pub unsafe fn init(base_addresses: &[usize]) {
for (i, &base_address) in base_addresses.iter().enumerate() {
// Remember the valid address, for emergency console accesses.
ADDRESSES[i].call_once(|| base_address);
// Initialize the console driver, for normal console accesses.
assert!(!CONSOLES[i].is_completed(), "console::init() called more than once");
// SAFETY: The caller promised that base_address is the base of a mapped UART with no
// aliases.
CONSOLES[i].call_once(|| SpinMutex::new(unsafe { Uart::new(base_address) }));
}
}
/// Writes a formatted string followed by a newline to the n-th console.
///
/// Panics if the n-th console was not initialized by calling [`init`] first.
pub fn writeln(n: usize, format_args: Arguments) {
let uart = &mut *CONSOLES[n].get().unwrap().lock();
write(uart, format_args).unwrap();
let _ = uart.write_str("\n");
}
/// Reinitializes the n-th UART driver and writes a formatted string followed by a newline to it.
///
/// This is intended for use in situations where the UART may be in an unknown state or the global
/// instance may be locked, such as in an exception handler or panic handler.
pub fn ewriteln(n: usize, format_args: Arguments) {
let Some(addr) = ADDRESSES[n].get() else { return };
// SAFETY: addr contains the base of a mapped UART, passed in init().
let mut uart = unsafe { Uart::new(*addr) };
let _ = write(&mut uart, format_args);
let _ = uart.write_str("\n");
}
/// Prints the given formatted string to the n-th console, followed by a newline.
///
/// Panics if the console has not yet been initialized. May hang if used in an exception context;
/// use `eprintln!` instead.
#[macro_export]
macro_rules! console_writeln {
($n:expr, $($arg:tt)*) => ({
$crate::console::writeln($n, format_args!($($arg)*))
})
}
pub(crate) use console_writeln;
/// Prints the given formatted string to the console, followed by a newline.
///
/// Panics if the console has not yet been initialized. May hang if used in an exception context;
/// use `eprintln!` instead.
macro_rules! println {
($($arg:tt)*) => ({
$crate::console::console_writeln!($crate::console::DEFAULT_CONSOLE_INDEX, $($arg)*)
})
}
pub(crate) use println; // Make it available in this crate.
/// Prints the given string followed by a newline to the console in an emergency, such as an
/// exception handler.
///
/// Never panics.
#[macro_export]
macro_rules! eprintln {
($($arg:tt)*) => ({
$crate::console::ewriteln($crate::console::DEFAULT_EMERGENCY_CONSOLE_INDEX, format_args!($($arg)*))
})
}