pub mod resolver;
use chipset_device::ChipsetDevice;
use chipset_device::io::IoError;
use chipset_device::io::IoResult;
use chipset_device::pio::PortIoIntercept;
use chipset_device::poll_device::PollDevice;
use futures::AsyncWrite;
use inspect::InspectMut;
use serial_core::SerialIo;
use std::collections::VecDeque;
use std::io::ErrorKind;
use std::ops::RangeInclusive;
use std::pin::Pin;
use std::task::Context;
use std::task::Poll;
use std::task::Waker;
use std::task::ready;
use vmcore::device_state::ChangeDeviceState;
const TX_BUFFER_MAX: usize = 1024 * 1024; #[derive(InspectMut)]
pub struct SerialDebugcon {
io_port: u16,
#[inspect(skip)]
io_region: (&'static str, RangeInclusive<u16>),
#[inspect(mut)]
io: Box<dyn SerialIo>,
#[inspect(with = "VecDeque::len")]
tx_buffer: VecDeque<u8>,
#[inspect(skip)]
tx_waker: Option<Waker>,
}
impl SerialDebugcon {
pub fn new(port: u16, io: Box<dyn SerialIo>) -> Self {
Self {
io_port: port,
io_region: ("debugcon", port..=port),
io,
tx_buffer: VecDeque::new(),
tx_waker: None,
}
}
fn sync(&mut self) {
if !self.tx_buffer.is_empty() {
if let Some(waker) = self.tx_waker.take() {
waker.wake();
}
}
}
fn poll_tx(&mut self, cx: &mut Context<'_>) -> Poll<()> {
while !self.tx_buffer.is_empty() {
let (buf, _) = self.tx_buffer.as_slices();
match ready!(Pin::new(&mut self.io).poll_write(cx, buf)) {
Ok(n) => {
assert_ne!(n, 0);
self.tx_buffer.drain(..n);
}
Err(err) if err.kind() == ErrorKind::BrokenPipe => {
tracing::info!("debugcon output broken pipe");
}
Err(err) => {
tracelimit::error_ratelimited!(
len = buf.len(),
error = &err as &dyn std::error::Error,
"debugcon write failed, dropping data"
);
self.tx_buffer.drain(..buf.len());
}
}
}
self.tx_waker = Some(cx.waker().clone());
Poll::Pending
}
}
impl ChangeDeviceState for SerialDebugcon {
fn start(&mut self) {}
async fn stop(&mut self) {}
async fn reset(&mut self) {
self.tx_buffer.clear();
}
}
impl ChipsetDevice for SerialDebugcon {
fn supports_pio(&mut self) -> Option<&mut dyn PortIoIntercept> {
Some(self)
}
fn supports_poll_device(&mut self) -> Option<&mut dyn PollDevice> {
Some(self)
}
}
impl PollDevice for SerialDebugcon {
fn poll_device(&mut self, cx: &mut Context<'_>) {
let _ = self.poll_tx(cx);
self.sync();
}
}
impl PortIoIntercept for SerialDebugcon {
fn io_read(&mut self, io_port: u16, data: &mut [u8]) -> IoResult {
if io_port != self.io_port {
return IoResult::Err(IoError::InvalidRegister);
}
if data.len() != 1 {
return IoResult::Err(IoError::InvalidAccessSize);
}
data[0] = 0xe9;
IoResult::Ok
}
fn io_write(&mut self, io_port: u16, data: &[u8]) -> IoResult {
if io_port != self.io_port {
return IoResult::Err(IoError::InvalidRegister);
}
if data.len() != 1 {
return IoResult::Err(IoError::InvalidAccessSize);
}
if self.tx_buffer.len() >= TX_BUFFER_MAX {
tracing::debug!("debugcon buffer overrun, dropping output data");
return IoResult::Ok;
}
self.tx_buffer.push_back(data[0]);
if data[0] == b'\n' {
self.tx_buffer.push_back(b'\r')
}
self.sync();
IoResult::Ok
}
fn get_static_regions(&mut self) -> &[(&str, RangeInclusive<u16>)] {
std::slice::from_ref(&self.io_region)
}
}
mod save_restore {
use crate::SerialDebugcon;
use vmcore::save_restore::NoSavedState;
use vmcore::save_restore::RestoreError;
use vmcore::save_restore::SaveError;
use vmcore::save_restore::SaveRestore;
impl SaveRestore for SerialDebugcon {
type SavedState = NoSavedState;
fn save(&mut self) -> Result<Self::SavedState, SaveError> {
Ok(NoSavedState)
}
fn restore(&mut self, state: Self::SavedState) -> Result<(), RestoreError> {
let NoSavedState = state;
Ok(())
}
}
}