blob: cd05250be5f3bff175909092fb6d5043884c9f36 [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: [SpinMutex<Option<Uart>>; MAX_CONSOLES] =
[SpinMutex::new(None), SpinMutex::new(None), SpinMutex::new(None), SpinMutex::new(None)];
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.
let mut console = CONSOLES[i].lock();
assert!(console.is_none(), "console::init() called more than once");
// SAFETY: base_address must be the base of a mapped UART.
console.replace(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 mut guard = CONSOLES[n].lock();
let uart = guard.as_mut().unwrap();
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)*))
})
}