use std::io::Write;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::{Mutex, OnceLock};
use super::mmio_bus::MmioDevice;
pub static HEARTBEAT_COUNT: AtomicU64 = AtomicU64::new(0);
pub static LOG_SINK: Mutex<Option<std::fs::File>> = Mutex::new(None);
static LOG_SINK_SET: AtomicBool = AtomicBool::new(false);
static HEARTBEAT_DETECTION_ENABLED: AtomicBool = AtomicBool::new(false);
static PL011_LINE: Mutex<Vec<u8>> = Mutex::new(Vec::new());
const HEARTBEAT_MARKER: &[u8] = b"heartbeat counter=";
fn console_log_enabled() -> bool {
static ENABLED: OnceLock<bool> = OnceLock::new();
*ENABLED.get_or_init(|| {
!matches!(
std::env::var("SUPERMACHINE_CONSOLE_LOG").as_deref(),
Ok("0") | Ok("false") | Ok("no") | Ok("off")
)
})
}
pub fn set_log_sink(path: &str) -> std::io::Result<()> {
let f = std::fs::OpenOptions::new()
.create(true)
.append(true)
.open(path)?;
*LOG_SINK.lock().unwrap() = Some(f);
LOG_SINK_SET.store(true, Ordering::Release);
Ok(())
}
pub fn set_heartbeat_detection(enabled: bool) {
HEARTBEAT_DETECTION_ENABLED.store(enabled, Ordering::Release);
}
pub struct SerialPl011;
impl SerialPl011 {
pub fn new() -> Self {
Self
}
}
impl MmioDevice for SerialPl011 {
fn read(&self, offset: u64, _size: u8) -> u64 {
match offset {
0x000 => 0, 0x018 => (1 << 7) | (1 << 4), 0xfe0 => 0x11,
0xfe4 => 0x10,
0xfe8 => 0x14,
0xfec => 0x00,
0xff0 => 0x0d,
0xff4 => 0xf0,
0xff8 => 0x05,
0xffc => 0xb1,
_ => 0,
}
}
fn write(&self, offset: u64, value: u64, _size: u8) {
if offset != 0x000 {
return;
}
let b = (value & 0xff) as u8;
if !console_log_enabled()
&& !LOG_SINK_SET.load(Ordering::Acquire)
&& !HEARTBEAT_DETECTION_ENABLED.load(Ordering::Acquire)
{
return;
}
{
let mut sink = LOG_SINK.lock().unwrap();
if let Some(f) = sink.as_mut() {
let _ = f.write_all(&[b]);
} else if console_log_enabled() {
drop(sink);
let mut out = std::io::stdout().lock();
let _ = out.write_all(&[b]);
let _ = out.flush();
}
}
if !HEARTBEAT_DETECTION_ENABLED.load(Ordering::Acquire) {
return;
}
let mut line = PL011_LINE.lock().unwrap();
if b == b'\n' {
if line
.windows(HEARTBEAT_MARKER.len())
.any(|w| w == HEARTBEAT_MARKER)
{
HEARTBEAT_COUNT.fetch_add(1, Ordering::SeqCst);
}
line.clear();
} else if line.len() < 4096 {
line.push(b);
}
}
fn len(&self) -> u64 {
super::super::arch::aarch64::layout::SERIAL_MMIO_SIZE
}
}