diff --git a/devices/src/legacy/mod.rs b/devices/src/legacy/mod.rs index 8eceb4cfd..8deca1670 100644 --- a/devices/src/legacy/mod.rs +++ b/devices/src/legacy/mod.rs @@ -13,6 +13,8 @@ mod i8042; #[cfg(target_arch = "aarch64")] mod rtc_pl031; mod serial; +#[cfg(target_arch = "aarch64")] +mod uart_pl011; #[cfg(feature = "cmos")] pub use self::cmos::Cmos; @@ -23,3 +25,5 @@ pub use self::serial::Serial; #[cfg(target_arch = "aarch64")] pub use self::rtc_pl031::RTC; +#[cfg(target_arch = "aarch64")] +pub use self::uart_pl011::PL011; diff --git a/devices/src/legacy/rtc_pl031.rs b/devices/src/legacy/rtc_pl031.rs index e77726cd0..114d2820d 100644 --- a/devices/src/legacy/rtc_pl031.rs +++ b/devices/src/legacy/rtc_pl031.rs @@ -8,6 +8,7 @@ //! This is achieved by generating an interrupt signal after counting for a programmed number of cycles of //! a real-time clock input. //! +use crate::{read_le_u32, write_le_u32}; use std::fmt; use std::sync::{Arc, Barrier}; use std::time::Instant; @@ -38,52 +39,6 @@ const AMBA_ID_HIGH: u64 = 0x1000; /// Constant to convert seconds to nanoseconds. pub const NANOS_PER_SECOND: u64 = 1_000_000_000; -#[allow(unused_macros)] -macro_rules! generate_read_fn { - ($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => { - #[allow(dead_code)] - pub fn $fn_name(input: &[$byte_type]) -> $data_type { - assert!($type_size == std::mem::size_of::<$data_type>()); - let mut array = [0u8; $type_size]; - for (byte, read) in array.iter_mut().zip(input.iter().cloned()) { - *byte = read as u8; - } - <$data_type>::$endian_type(array) - } - }; -} - -#[allow(unused_macros)] -macro_rules! generate_write_fn { - ($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => { - #[allow(dead_code)] - pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) { - for (byte, read) in buf - .iter_mut() - .zip(<$data_type>::$endian_type(n).iter().cloned()) - { - *byte = read as $byte_type; - } - } - }; -} - -generate_read_fn!(read_le_u16, u16, u8, 2, from_le_bytes); -generate_read_fn!(read_le_u32, u32, u8, 4, from_le_bytes); -generate_read_fn!(read_le_u64, u64, u8, 8, from_le_bytes); -generate_read_fn!(read_le_i32, i32, i8, 4, from_le_bytes); - -generate_read_fn!(read_be_u16, u16, u8, 2, from_be_bytes); -generate_read_fn!(read_be_u32, u32, u8, 4, from_be_bytes); - -generate_write_fn!(write_le_u16, u16, u8, to_le_bytes); -generate_write_fn!(write_le_u32, u32, u8, to_le_bytes); -generate_write_fn!(write_le_u64, u64, u8, to_le_bytes); -generate_write_fn!(write_le_i32, i32, i8, to_le_bytes); - -generate_write_fn!(write_be_u16, u16, u8, to_be_bytes); -generate_write_fn!(write_be_u32, u32, u8, to_be_bytes); - #[derive(Debug)] pub enum Error { BadWriteOffset(u64), @@ -392,6 +347,10 @@ impl BusDevice for RTC { #[cfg(test)] mod tests { use super::*; + use crate::{ + read_be_u16, read_be_u32, read_le_i32, read_le_u16, read_le_u32, read_le_u64, write_be_u16, + write_be_u32, write_le_i32, write_le_u16, write_le_u32, write_le_u64, + }; use std::sync::Arc; use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig}; use vmm_sys_util::eventfd::EventFd; diff --git a/devices/src/legacy/uart_pl011.rs b/devices/src/legacy/uart_pl011.rs new file mode 100644 index 000000000..2dc7184c2 --- /dev/null +++ b/devices/src/legacy/uart_pl011.rs @@ -0,0 +1,506 @@ +// Copyright 2021 Arm Limited (or its affiliates). All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +//! ARM PrimeCell UART(PL011) +//! +//! This module implements an ARM PrimeCell UART(PL011). +//! + +use crate::{read_le_u32, write_le_u32}; +use anyhow::anyhow; +use std::collections::VecDeque; +use std::fmt; +use std::sync::{Arc, Barrier}; +use std::{io, result}; +use vm_device::interrupt::InterruptSourceGroup; +use vm_device::BusDevice; +use vm_migration::{ + Migratable, MigratableError, Pausable, Snapshot, SnapshotDataSection, Snapshottable, + Transportable, +}; + +/* Registers */ +const UARTDR: u64 = 0; +const UARTRSR_UARTECR: u64 = 1; +const UARTFR: u64 = 6; +const UARTILPR: u64 = 8; +const UARTIBRD: u64 = 9; +const UARTFBRD: u64 = 10; +const UARTLCR_H: u64 = 11; +const UARTCR: u64 = 12; +const UARTIFLS: u64 = 13; +const UARTIMSC: u64 = 14; +const UARTRIS: u64 = 15; +const UARTMIS: u64 = 16; +const UARTICR: u64 = 17; +const UARTDMACR: u64 = 18; + +const PL011_INT_TX: u32 = 0x20; +const PL011_INT_RX: u32 = 0x10; + +const PL011_FLAG_RXFF: u32 = 0x40; +const PL011_FLAG_RXFE: u32 = 0x10; + +const PL011_ID: [u8; 8] = [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1]; +// We are only interested in the margins. +const AMBA_ID_LOW: u64 = 0x3f8; +const AMBA_ID_HIGH: u64 = 0x401; + +#[derive(Debug)] +pub enum Error { + BadWriteOffset(u64), + DMANotImplemented, + InterruptFailure(io::Error), + WriteAllFailure(io::Error), + FlushFailure(io::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::BadWriteOffset(offset) => write!(f, "pl011_write: Bad Write Offset: {}", offset), + Error::DMANotImplemented => write!(f, "pl011: DMA not implemented."), + Error::InterruptFailure(e) => write!(f, "Failed to trigger interrupt: {}", e), + Error::WriteAllFailure(e) => write!(f, "Failed to write: {}", e), + Error::FlushFailure(e) => write!(f, "Failed to flush: {}", e), + } + } +} + +type Result = result::Result; + +/// A PL011 device following the PL011 specification. +pub struct PL011 { + id: String, + flags: u32, + lcr: u32, + rsr: u32, + cr: u32, + dmacr: u32, + int_enabled: u32, + int_level: u32, + read_fifo: VecDeque, + ilpr: u32, + ibrd: u32, + fbrd: u32, + ifl: u32, + read_count: u32, + read_trigger: u32, + irq: Arc>, + out: Option>, +} + +#[derive(Serialize, Deserialize)] +pub struct PL011State { + flags: u32, + lcr: u32, + rsr: u32, + cr: u32, + dmacr: u32, + int_enabled: u32, + int_level: u32, + read_fifo: VecDeque, + ilpr: u32, + ibrd: u32, + fbrd: u32, + ifl: u32, + read_count: u32, + read_trigger: u32, +} + +impl PL011 { + /// Constructs an AMBA PL011 UART device. + pub fn new( + id: String, + irq: Arc>, + out: Option>, + ) -> PL011 { + PL011 { + id, + flags: 0x90u32, + lcr: 0u32, + rsr: 0u32, + cr: 0x300u32, + dmacr: 0u32, + int_enabled: 0u32, + int_level: 0u32, + read_fifo: VecDeque::new(), + ilpr: 0u32, + ibrd: 0u32, + fbrd: 0u32, + ifl: 0x12u32, + read_count: 0u32, + read_trigger: 1u32, + irq, + out, + } + } + + fn state(&self) -> PL011State { + PL011State { + flags: self.flags, + lcr: self.lcr, + rsr: self.rsr, + cr: self.cr, + dmacr: self.dmacr, + int_enabled: self.int_enabled, + int_level: self.int_level, + read_fifo: self.read_fifo.clone(), + ilpr: self.ilpr, + ibrd: self.ibrd, + fbrd: self.fbrd, + ifl: self.ifl, + read_count: self.read_count, + read_trigger: self.read_trigger, + } + } + + fn set_state(&mut self, state: &PL011State) { + self.flags = state.flags; + self.lcr = state.lcr; + self.rsr = state.rsr; + self.cr = state.cr; + self.dmacr = state.dmacr; + self.int_enabled = state.int_enabled; + self.int_level = state.int_level; + self.read_fifo = state.read_fifo.clone(); + self.ilpr = state.ilpr; + self.ibrd = state.ibrd; + self.fbrd = state.fbrd; + self.ifl = state.ifl; + self.read_count = state.read_count; + self.read_trigger = state.read_trigger; + } + + /// Queues raw bytes for the guest to read and signals the interrupt + pub fn queue_input_bytes(&mut self, c: &[u8]) -> vmm_sys_util::errno::Result<()> { + self.read_fifo.extend(c); + self.read_count += c.len() as u32; + self.flags &= !PL011_FLAG_RXFE; + + if ((self.lcr & 0x10) == 0) || (self.read_count == 16) { + self.flags |= PL011_FLAG_RXFF; + } + + if self.read_count >= self.read_trigger { + self.int_level |= PL011_INT_RX; + self.trigger_interrupt()?; + } + + Ok(()) + } + + fn pl011_get_baudrate(&self) -> u32 { + if self.fbrd == 0 { + return 0; + } + + let clk = 24_000_000; // We set the APB_PLCK to 24M in device tree + (clk / ((self.ibrd << 6) + self.fbrd)) << 2 + } + + fn pl011_trace_baudrate_change(&self) { + debug!( + "=== New baudrate: {:#?} (clk: {:#?}Hz, ibrd: {:#?}, fbrd: {:#?}) ===", + self.pl011_get_baudrate(), + 24_000_000, // We set the APB_PLCK to 24M in device tree + self.ibrd, + self.fbrd + ); + } + + fn pl011_set_read_trigger(&mut self) { + self.read_trigger = 1; + } + + fn handle_write(&mut self, offset: u64, val: u32) -> Result<()> { + match offset >> 2 { + UARTDR => { + self.int_level |= PL011_INT_TX; + if let Some(out) = self.out.as_mut() { + out.write_all(&[val.to_le_bytes()[0]]) + .map_err(Error::WriteAllFailure)?; + out.flush().map_err(Error::FlushFailure)?; + } + } + UARTRSR_UARTECR => { + self.rsr = 0; + } + UARTFR => { /* Writes to Flag register are ignored.*/ } + UARTILPR => { + self.ilpr = val; + } + UARTIBRD => { + self.ibrd = val; + self.pl011_trace_baudrate_change(); + } + UARTFBRD => { + self.fbrd = val; + self.pl011_trace_baudrate_change(); + } + UARTLCR_H => { + /* Reset the FIFO state on FIFO enable or disable */ + if ((self.lcr ^ val) & 0x10) != 0 { + self.read_count = 0; + } + self.lcr = val; + self.pl011_set_read_trigger(); + } + UARTCR => { + self.cr = val; + } + UARTIFLS => { + self.ifl = val; + self.pl011_set_read_trigger(); + } + UARTIMSC => { + self.int_enabled = val; + self.trigger_interrupt().map_err(Error::InterruptFailure)?; + } + UARTICR => { + self.int_level &= !val; + self.trigger_interrupt().map_err(Error::InterruptFailure)?; + } + UARTDMACR => { + self.dmacr = val; + if (val & 3) != 0 { + return Err(Error::DMANotImplemented); + } + } + off => { + return Err(Error::BadWriteOffset(off)); + } + } + Ok(()) + } + + fn trigger_interrupt(&mut self) -> result::Result<(), io::Error> { + self.irq.trigger(0) + } +} + +impl BusDevice for PL011 { + fn read(&mut self, _base: u64, offset: u64, data: &mut [u8]) { + let v; + let mut read_ok = true; + if (AMBA_ID_LOW..AMBA_ID_HIGH).contains(&(offset >> 2)) { + let index = ((offset - 0xfe0) >> 2) as usize; + v = u32::from(PL011_ID[index]); + } else { + v = match offset >> 2 { + UARTDR => { + let c: u32; + let r: u32; + + self.flags &= !PL011_FLAG_RXFF; + c = self.read_fifo.pop_front().unwrap_or_default().into(); + if self.read_count > 0 { + self.read_count -= 1; + } + if self.read_count == 0 { + self.flags |= PL011_FLAG_RXFE; + } + if self.read_count == (self.read_trigger - 1) { + self.int_level &= !PL011_INT_RX; + } + self.rsr = c >> 8; + r = c; + r + } + UARTRSR_UARTECR => self.rsr, + UARTFR => self.flags, + UARTILPR => self.ilpr, + UARTIBRD => self.ibrd, + UARTFBRD => self.fbrd, + UARTLCR_H => self.lcr, + UARTCR => self.cr, + UARTIFLS => self.ifl, + UARTIMSC => self.int_enabled, + UARTRIS => self.int_level, + UARTMIS => (self.int_level & self.int_enabled), + UARTDMACR => self.dmacr, + _ => { + read_ok = false; + 0 + } + } + } + + if read_ok && data.len() <= 4 { + write_le_u32(data, v); + } else { + warn!( + "Invalid PL011 read: offset {}, data length {}", + offset, + data.len() + ); + } + } + + fn write(&mut self, _base: u64, offset: u64, data: &[u8]) -> Option> { + if data.len() <= 4 { + let v = read_le_u32(&data[..]); + if let Err(e) = self.handle_write(offset, v) { + warn!("Failed to write to PL011 device: {}", e); + } + } else { + warn!( + "Invalid PL011 write: offset {}, data length {}", + offset, + data.len() + ); + } + + None + } +} + +impl Snapshottable for PL011 { + fn id(&self) -> String { + self.id.clone() + } + + fn snapshot(&mut self) -> std::result::Result { + let snapshot = + serde_json::to_vec(&self.state()).map_err(|e| MigratableError::Snapshot(e.into()))?; + + let mut pl011_snapshot = Snapshot::new(self.id.as_str()); + pl011_snapshot.add_data_section(SnapshotDataSection { + id: format!("{}-section", self.id), + snapshot, + }); + + Ok(pl011_snapshot) + } + + fn restore(&mut self, snapshot: Snapshot) -> std::result::Result<(), MigratableError> { + if let Some(pl011_section) = snapshot.snapshot_data.get(&format!("{}-section", self.id)) { + let pl011_state = match serde_json::from_slice(&pl011_section.snapshot) { + Ok(state) => state, + Err(error) => { + return Err(MigratableError::Restore(anyhow!( + "Could not deserialize PL011 {}", + error + ))) + } + }; + + self.set_state(&pl011_state); + + return Ok(()); + } + + Err(MigratableError::Restore(anyhow!( + "Could not find the PL011 snapshot section" + ))) + } +} + +impl Pausable for PL011 {} +impl Transportable for PL011 {} +impl Migratable for PL011 {} + +#[cfg(test)] +mod tests { + use super::*; + use std::io; + use std::sync::{Arc, Mutex}; + use vm_device::interrupt::{InterruptIndex, InterruptSourceConfig}; + use vmm_sys_util::eventfd::EventFd; + + const SERIAL_NAME: &str = "serial"; + + struct TestInterrupt { + event_fd: EventFd, + } + + impl InterruptSourceGroup for TestInterrupt { + fn trigger(&self, _index: InterruptIndex) -> result::Result<(), std::io::Error> { + self.event_fd.write(1) + } + fn update( + &self, + _index: InterruptIndex, + _config: InterruptSourceConfig, + ) -> result::Result<(), std::io::Error> { + Ok(()) + } + fn notifier(&self, _index: InterruptIndex) -> Option { + Some(self.event_fd.try_clone().unwrap()) + } + } + + impl TestInterrupt { + fn new(event_fd: EventFd) -> Self { + TestInterrupt { event_fd } + } + } + + #[derive(Clone)] + struct SharedBuffer { + buf: Arc>>, + } + + impl SharedBuffer { + fn new() -> SharedBuffer { + SharedBuffer { + buf: Arc::new(Mutex::new(Vec::new())), + } + } + } + + impl io::Write for SharedBuffer { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.buf.lock().unwrap().write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.buf.lock().unwrap().flush() + } + } + + #[test] + fn pl011_output() { + let intr_evt = EventFd::new(0).unwrap(); + let pl011_out = SharedBuffer::new(); + let mut pl011 = PL011::new( + String::from(SERIAL_NAME), + Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))), + Some(Box::new(pl011_out.clone())), + ); + + pl011.write(0, UARTDR as u64, &[b'x', b'y']); + pl011.write(0, UARTDR as u64, &[b'a']); + pl011.write(0, UARTDR as u64, &[b'b']); + pl011.write(0, UARTDR as u64, &[b'c']); + assert_eq!( + pl011_out.buf.lock().unwrap().as_slice(), + &[b'x', b'a', b'b', b'c'] + ); + } + + #[test] + fn pl011_input() { + let intr_evt = EventFd::new(0).unwrap(); + let pl011_out = SharedBuffer::new(); + let mut pl011 = PL011::new( + String::from(SERIAL_NAME), + Arc::new(Box::new(TestInterrupt::new(intr_evt.try_clone().unwrap()))), + Some(Box::new(pl011_out)), + ); + + // write 1 to the interrupt event fd, so that read doesn't block in case the event fd + // counter doesn't change (for 0 it blocks) + assert!(intr_evt.write(1).is_ok()); + pl011.queue_input_bytes(&[b'a', b'b', b'c']).unwrap(); + + assert_eq!(intr_evt.read().unwrap(), 2); + + let mut data = [0u8]; + pl011.read(0, UARTDR as u64, &mut data[..]); + assert_eq!(data[0], b'a'); + pl011.read(0, UARTDR as u64, &mut data[..]); + assert_eq!(data[0], b'b'); + pl011.read(0, UARTDR as u64, &mut data[..]); + assert_eq!(data[0], b'c'); + } +} diff --git a/devices/src/lib.rs b/devices/src/lib.rs index 0f42f367a..c890e6424 100644 --- a/devices/src/lib.rs +++ b/devices/src/lib.rs @@ -46,3 +46,63 @@ bitflags! { const POWER_BUTTON_CHANGED = 0b1000; } } + +#[allow(unused_macros)] +#[cfg(target_arch = "aarch64")] +macro_rules! generate_read_fn { + ($fn_name: ident, $data_type: ty, $byte_type: ty, $type_size: expr, $endian_type: ident) => { + #[allow(dead_code)] + pub fn $fn_name(input: &[$byte_type]) -> $data_type { + assert!($type_size == std::mem::size_of::<$data_type>()); + let mut array = [0u8; $type_size]; + for (byte, read) in array.iter_mut().zip(input.iter().cloned()) { + *byte = read as u8; + } + <$data_type>::$endian_type(array) + } + }; +} + +#[allow(unused_macros)] +#[cfg(target_arch = "aarch64")] +macro_rules! generate_write_fn { + ($fn_name: ident, $data_type: ty, $byte_type: ty, $endian_type: ident) => { + #[allow(dead_code)] + pub fn $fn_name(buf: &mut [$byte_type], n: $data_type) { + for (byte, read) in buf + .iter_mut() + .zip(<$data_type>::$endian_type(n).iter().cloned()) + { + *byte = read as $byte_type; + } + } + }; +} + +#[cfg(target_arch = "aarch64")] +generate_read_fn!(read_le_u16, u16, u8, 2, from_le_bytes); +#[cfg(target_arch = "aarch64")] +generate_read_fn!(read_le_u32, u32, u8, 4, from_le_bytes); +#[cfg(target_arch = "aarch64")] +generate_read_fn!(read_le_u64, u64, u8, 8, from_le_bytes); +#[cfg(target_arch = "aarch64")] +generate_read_fn!(read_le_i32, i32, i8, 4, from_le_bytes); + +#[cfg(target_arch = "aarch64")] +generate_read_fn!(read_be_u16, u16, u8, 2, from_be_bytes); +#[cfg(target_arch = "aarch64")] +generate_read_fn!(read_be_u32, u32, u8, 4, from_be_bytes); + +#[cfg(target_arch = "aarch64")] +generate_write_fn!(write_le_u16, u16, u8, to_le_bytes); +#[cfg(target_arch = "aarch64")] +generate_write_fn!(write_le_u32, u32, u8, to_le_bytes); +#[cfg(target_arch = "aarch64")] +generate_write_fn!(write_le_u64, u64, u8, to_le_bytes); +#[cfg(target_arch = "aarch64")] +generate_write_fn!(write_le_i32, i32, i8, to_le_bytes); + +#[cfg(target_arch = "aarch64")] +generate_write_fn!(write_be_u16, u16, u8, to_be_bytes); +#[cfg(target_arch = "aarch64")] +generate_write_fn!(write_be_u32, u32, u8, to_be_bytes);