use serde::{Deserialize, Serialize};
use crate::common::{IrqId, LineAddr};
use crate::config::Console;
use crate::sim::components::ComponentId;
use crate::sim::handle::{Handle, HandleCtx};
use crate::sim::packet::{HitLevel, MemOp, MemRespData, MesiState, Packet, WriteData};
use crate::soc::devices::Device;
use std::collections::VecDeque;
use std::io::{self, Read, Write};
use std::sync::Mutex;
use std::sync::mpsc::{Receiver, channel};
use std::thread;
const REG_RBR: u64 = 0;
const REG_THR: u64 = 0;
const REG_IER: u64 = 1;
const REG_IIR: u64 = 2;
const _REG_FCR: u64 = 2;
const REG_LCR: u64 = 3;
const REG_MCR: u64 = 4;
const REG_LSR: u64 = 5;
const _REG_MSR: u64 = 6;
const REG_SCR: u64 = 7;
const IIR_NO_INTERRUPT: u8 = 0x01;
const IIR_THRE: u8 = 0x02;
const IIR_RDA: u8 = 0x04;
const IIR_ID_MASK: u8 = 0xC0;
const LSR_DATA_READY: u8 = 0x01;
const LSR_THRE: u8 = 0x20;
const LSR_TEMT: u8 = 0x40;
const LSR_DEFAULT: u8 = LSR_THRE | LSR_TEMT;
const LCR_DLAB: u8 = 0x80;
const IER_RDA: u8 = 0x01;
const INTERRUPT_DELAY_NS: u64 = 225;
const IER_THRE: u8 = 0x02;
#[derive(Debug)]
#[allow(clippy::struct_excessive_bools)]
pub struct Uart {
base_addr: u64,
rx_queue: VecDeque<u8>,
rx_receiver: Mutex<Receiver<u8>>,
ier: u8,
lcr: u8,
mcr: u8,
scr: u8,
div: u16,
tick_count: u8,
cycle: u64,
interrupt_delay: u64,
tx_interrupt_at: Option<u64>,
rx_interrupt_at: Option<u64>,
thre_ip: bool,
rx_ready: bool,
console: Console,
captured: Vec<u8>,
panic_match_state: usize,
panic_detected: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct UartState {
pub ier: u8,
pub lcr: u8,
pub mcr: u8,
pub scr: u8,
pub div: u16,
pub cycle: u64,
pub tx_interrupt_at: Option<u64>,
pub rx_interrupt_at: Option<u64>,
pub thre_ip: bool,
pub rx_ready: bool,
pub rx_queue: Vec<u8>,
}
impl Uart {
#[must_use]
pub fn state(&self) -> UartState {
UartState {
ier: self.ier,
lcr: self.lcr,
mcr: self.mcr,
scr: self.scr,
div: self.div,
cycle: self.cycle,
tx_interrupt_at: self.tx_interrupt_at,
rx_interrupt_at: self.rx_interrupt_at,
thre_ip: self.thre_ip,
rx_ready: self.rx_ready,
rx_queue: self.rx_queue.iter().copied().collect(),
}
}
pub fn set_state(&mut self, state: &UartState) {
self.ier = state.ier;
self.lcr = state.lcr;
self.mcr = state.mcr;
self.scr = state.scr;
self.div = state.div;
self.cycle = state.cycle;
self.tx_interrupt_at = state.tx_interrupt_at;
self.rx_interrupt_at = state.rx_interrupt_at;
self.thre_ip = state.thre_ip;
self.rx_ready = state.rx_ready;
self.rx_queue = state.rx_queue.iter().copied().collect();
}
pub fn new(base_addr: u64, console: Console, cpu_clock_mhz: u64) -> Self {
let (tx, rx) = channel();
if matches!(console, Console::Stdout | Console::Stderr) {
let _ = thread::spawn(move || {
let mut buffer = [0u8; 1];
let stdin = io::stdin();
let mut handle = stdin.lock();
while handle.read_exact(&mut buffer).is_ok() {
let _ = tx.send(buffer[0]);
}
});
}
Self {
base_addr,
rx_queue: VecDeque::new(),
rx_receiver: Mutex::new(rx),
ier: 0,
lcr: 0,
mcr: 0,
scr: 0,
div: 0,
tick_count: 0,
cycle: 0,
interrupt_delay: INTERRUPT_DELAY_NS * cpu_clock_mhz / 1000,
tx_interrupt_at: None,
rx_interrupt_at: None,
thre_ip: true,
rx_ready: false,
console,
captured: Vec::new(),
panic_match_state: 0,
panic_detected: false,
}
}
#[must_use]
pub const fn has_output(&self) -> bool {
!self.captured.is_empty()
}
pub fn take_output(&mut self) -> Vec<u8> {
std::mem::take(&mut self.captured)
}
pub fn send_input(&mut self, bytes: &[u8]) {
self.rx_queue.extend(bytes);
if !bytes.is_empty() && !self.rx_ready && self.rx_interrupt_at.is_none() {
self.rx_interrupt_at = Some(self.cycle + self.interrupt_delay);
}
}
fn check_stdin(&mut self) {
let Ok(rx) = self.rx_receiver.lock() else { return };
let mut arrived = false;
while let Ok(byte) = rx.try_recv() {
self.rx_queue.push_back(byte);
arrived = true;
}
if arrived && !self.rx_ready && self.rx_interrupt_at.is_none() {
self.rx_interrupt_at = Some(self.cycle + self.interrupt_delay);
}
}
const fn schedule_tx_interrupt(&mut self) {
self.thre_ip = false;
self.tx_interrupt_at = Some(self.cycle + self.interrupt_delay);
}
fn raise_due_interrupts(&mut self) {
if self.tx_interrupt_at.is_some_and(|at| at <= self.cycle) {
self.tx_interrupt_at = None;
self.thre_ip = true;
}
if self.rx_interrupt_at.is_some_and(|at| at <= self.cycle) {
self.rx_interrupt_at = None;
self.rx_ready = true;
}
}
fn update_interrupts(&self) -> u8 {
if (self.ier & IER_RDA) != 0 && self.rx_ready && !self.rx_queue.is_empty() {
return IIR_RDA;
}
if (self.ier & IER_THRE) != 0 && self.thre_ip {
return IIR_THRE;
}
IIR_NO_INTERRUPT
}
const fn check_char_for_panic(&mut self, ch: u8) -> bool {
const PATTERN: &[u8] = b"kernel panic";
let ch_lower = if ch.is_ascii_uppercase() { ch + 32 } else { ch };
if ch_lower == PATTERN[self.panic_match_state] {
self.panic_match_state += 1;
if self.panic_match_state == PATTERN.len() {
self.panic_detected = true;
self.panic_match_state = 0;
return true;
}
} else if ch_lower == b'k' {
self.panic_match_state = 1;
} else {
self.panic_match_state = 0;
}
false
}
pub const fn check_kernel_panic(&mut self) -> bool {
self.panic_detected
}
const fn dlab_set(&self) -> bool {
(self.lcr & LCR_DLAB) != 0
}
fn read_rbr_or_dll(&mut self) -> u8 {
if self.dlab_set() {
return (self.div & 0xFF) as u8;
}
let byte = self.rx_queue.pop_front().unwrap_or(0);
if self.rx_queue.is_empty() {
self.rx_ready = false;
}
byte
}
const fn read_ier_or_dlm(&self) -> u8 {
if self.dlab_set() { (self.div >> 8) as u8 } else { self.ier }
}
fn read_iir(&mut self) -> u8 {
let iir = self.update_interrupts();
if iir == IIR_THRE {
self.thre_ip = false;
}
IIR_ID_MASK | iir
}
fn read_lsr(&self) -> u8 {
let mut lsr = LSR_DEFAULT;
if !self.rx_queue.is_empty() {
lsr |= LSR_DATA_READY;
}
lsr
}
fn write_thr_or_dll(&mut self, val: u8) {
if self.dlab_set() {
self.div = (self.div & 0xFF00) | (val as u16);
} else {
if self.check_char_for_panic(val) {
return;
}
match self.console {
Console::Stdout => {
print!("{}", val as char);
let _ = io::stdout().flush();
}
Console::Stderr => {
eprint!("{}", val as char);
let _ = io::stderr().flush();
}
Console::Quiet => {}
Console::Captured => self.captured.push(val),
}
self.schedule_tx_interrupt();
}
}
const fn write_ier_or_dlm(&mut self, val: u8) {
if self.dlab_set() {
self.div = (self.div & 0x00FF) | ((val as u16) << 8);
} else {
self.ier = val;
if (self.ier & IER_THRE) != 0 {
self.schedule_tx_interrupt();
}
}
}
}
impl Uart {
fn read_register(&mut self, offset: u64) -> u8 {
match offset {
REG_RBR => self.read_rbr_or_dll(),
REG_IER => self.read_ier_or_dlm(),
REG_IIR => self.read_iir(),
REG_LCR => self.lcr,
REG_MCR => self.mcr,
REG_LSR => self.read_lsr(),
REG_SCR => self.scr,
_ => 0,
}
}
fn write_register(&mut self, offset: u64, val: u8) {
match offset {
REG_THR => self.write_thr_or_dll(val),
REG_IER => self.write_ier_or_dlm(val),
REG_LCR => self.lcr = val,
REG_MCR => self.mcr = val,
REG_SCR => self.scr = val,
_ => {}
}
}
}
impl Handle for Uart {
fn handle(&mut self, packet: Packet, source: ComponentId, ctx: &mut HandleCtx<'_>) {
if let Packet::MemReq { req_id, paddr, op, .. } = packet {
let offset = paddr.val().saturating_sub(self.base_addr);
let value: u64 = match op {
MemOp::Read | MemOp::ReadOwn | MemOp::Fetch | MemOp::Atomic { .. } => {
u64::from(self.read_register(offset))
}
MemOp::Write { data: WriteData::Small(val), .. } => {
self.write_register(offset, val as u8);
0
}
MemOp::Write { .. }
| MemOp::Writeback { .. }
| MemOp::Maintain { .. }
| MemOp::Prefetch { .. } => 0,
};
ctx.scheduler.schedule(
ctx.cycle + ctx.config.system.device_access_cycles(self.name()),
source,
ctx.self_id,
Packet::MemResp {
req_id,
line_addr: LineAddr::from_phys(paddr, 64),
data: MemRespData::Small(value),
hit_level: HitLevel::Mmio,
state: MesiState::Exclusive,
},
);
}
}
}
impl Device for Uart {
fn name(&self) -> &'static str {
"UART0"
}
fn address_range(&self) -> (u64, u64) {
(self.base_addr, 0x100)
}
fn tick(&mut self) -> bool {
self.cycle += 1;
self.tick_count = self.tick_count.wrapping_add(1);
if self.tick_count == 0 {
self.check_stdin();
}
self.raise_due_interrupts();
let iir = self.update_interrupts();
(iir & IIR_NO_INTERRUPT) == 0
}
fn quiet_ticks(&self) -> Option<u64> {
let until = |at: Option<u64>| at.map(|at| at.saturating_sub(self.cycle).saturating_sub(1));
let stdin_poll = matches!(self.console, Console::Stdout | Console::Stderr)
.then(|| u64::from(u8::MAX - self.tick_count));
[until(self.tx_interrupt_at), until(self.rx_interrupt_at), stdin_poll]
.into_iter()
.flatten()
.min()
}
fn skip_ticks(&mut self, ticks: u64) {
self.cycle += ticks;
self.tick_count = self.tick_count.wrapping_add((ticks % 256) as u8);
}
fn get_irq_id(&self) -> Option<IrqId> {
Some(IrqId::new(10))
}
fn checkpoint(&self) -> Option<serde_json::Value> {
serde_json::to_value(self.state()).ok()
}
fn restore(&mut self, state: &serde_json::Value) -> Result<(), String> {
let state = serde_json::from_value::<UartState>(state.clone())
.map_err(|error| format!("UART state: {error}"))?;
self.set_state(&state);
Ok(())
}
fn as_uart_mut(&mut self) -> Option<&mut Uart> {
Some(self)
}
}