devices: legacy: Implement Arm PL011 UART

This commit implements the Arm PrimeCell UART(PL011) device.

Signed-off-by: Henry Wang <Henry.Wang@arm.com>
This commit is contained in:
Henry Wang 2021-03-07 19:31:07 +08:00 committed by Michael
parent c85fba0c43
commit fd95acc60f
4 changed files with 575 additions and 46 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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<T> = result::Result<T, Error>;
/// 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<u8>,
ilpr: u32,
ibrd: u32,
fbrd: u32,
ifl: u32,
read_count: u32,
read_trigger: u32,
irq: Arc<Box<dyn InterruptSourceGroup>>,
out: Option<Box<dyn io::Write + Send>>,
}
#[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<u8>,
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<Box<dyn InterruptSourceGroup>>,
out: Option<Box<dyn io::Write + Send>>,
) -> 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<Arc<Barrier>> {
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<Snapshot, MigratableError> {
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<EventFd> {
Some(self.event_fd.try_clone().unwrap())
}
}
impl TestInterrupt {
fn new(event_fd: EventFd) -> Self {
TestInterrupt { event_fd }
}
}
#[derive(Clone)]
struct SharedBuffer {
buf: Arc<Mutex<Vec<u8>>>,
}
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<usize> {
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');
}
}

View File

@ -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);