// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. use std::sync::{Arc, Mutex}; use anyhow::{Context, Result}; use log::{debug, error}; use vmm_sys_util::eventfd::EventFd; use super::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; use acpi::{ AmlActiveLevel, AmlBuilder, AmlDevice, AmlEdgeLevel, AmlExtendedInterrupt, AmlIntShare, AmlInteger, AmlMemory32Fixed, AmlNameDecl, AmlReadAndWrite, AmlResTemplate, AmlResourceUsage, AmlScopeBuilder, AmlString, INTERRUPT_PPIS_COUNT, INTERRUPT_SGIS_COUNT, }; use address_space::GuestAddress; use chardev_backend::chardev::{Chardev, InputReceiver}; use machine_manager::{ config::{BootSource, Param, SerialConfig}, event_loop::EventLoop, }; use migration::{ snapshot::PL011_SNAPSHOT_ID, DeviceStateDesc, FieldDesc, MigrationError, MigrationHook, MigrationManager, StateTransfer, }; use migration_derive::{ByteCode, Desc}; use util::byte_code::ByteCode; use util::loop_context::EventNotifierHelper; use util::num_ops::read_data_u32; const PL011_FLAG_TXFE: u8 = 0x80; const PL011_FLAG_RXFF: u8 = 0x40; const PL011_FLAG_RXFE: u8 = 0x10; // Interrupt bits in UARTRIS, UARTMIS and UARTIMSC // Receive timeout interrupt bit const INT_RT: u32 = 1 << 6; // Transmit interrupt bit const INT_TX: u32 = 1 << 5; // Receive interrupt bit const INT_RX: u32 = 1 << 4; // Framing/Panity/Break/Overrun error bits, bits 7~10. const INT_E: u32 = 1 << 7 | 1 << 8 | 1 << 9 | 1 << 10; // nUARTRI/nUARTCTS/nUARTDCD/nUARTDSR modem interrupt bits, bits 0~3. const INT_MS: u32 = 1 | 1 << 1 | 1 << 2 | 1 << 3; const PL011_FIFO_SIZE: usize = 16; /// Device state of PL011. #[allow(clippy::upper_case_acronyms)] #[repr(C)] #[derive(Clone, Copy, Desc, ByteCode)] #[desc_version(compat_version = "0.1.0")] struct PL011State { /// Read FIFO. PL011_FIFO_SIZE is 16. rfifo: [u32; 16], /// Flag Register. flags: u32, /// Line Control Register. lcr: u32, /// Receive Status Register. rsr: u32, /// Control Register. cr: u32, /// DMA Control Register. dmacr: u32, /// IrDA Low-Power Counter Register. ilpr: u32, /// Integer Baud Rate Register. ibrd: u32, /// Fractional Baud Rate Register. fbrd: u32, /// Interrupt FIFO Level Select Register. ifl: u32, /// Identifier Register. Length is 8. id: [u8; 8], /// FIFO Status. read_pos: u32, read_count: u32, read_trigger: u32, /// Raw Interrupt Status Register. int_level: u32, /// Interrupt Mask Set/Clean Register. int_enabled: u32, } impl PL011State { fn new() -> Self { PL011State { rfifo: [0; PL011_FIFO_SIZE], flags: (PL011_FLAG_TXFE | PL011_FLAG_RXFE) as u32, lcr: 0, rsr: 0, cr: 0x300, dmacr: 0, ilpr: 0, ibrd: 0, fbrd: 0, ifl: 0x12, // Receive and transmit enable id: [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1], read_pos: 0, read_count: 0, read_trigger: 1, int_level: 0, int_enabled: 0, } } } #[allow(clippy::upper_case_acronyms)] pub struct PL011 { base: SysBusDevBase, /// Whether rx paused paused: bool, /// Device state. state: PL011State, /// Character device for redirection. chardev: Arc<Mutex<Chardev>>, } impl PL011 { /// Create a new `PL011` instance with default parameters. pub fn new(cfg: SerialConfig) -> Result<Self> { Ok(PL011 { base: SysBusDevBase { dev_type: SysBusDevType::PL011, interrupt_evt: Some(Arc::new(EventFd::new(libc::EFD_NONBLOCK)?)), ..Default::default() }, paused: false, state: PL011State::new(), chardev: Arc::new(Mutex::new(Chardev::new(cfg.chardev))), }) } fn interrupt(&mut self) { let irq_mask = INT_E | INT_MS | INT_RT | INT_TX | INT_RX; let flag = self.state.int_level & self.state.int_enabled; if flag & irq_mask != 0 { self.inject_interrupt(); trace::pl011_interrupt(flag); } } pub fn realize( mut self, sysbus: &mut SysBus, region_base: u64, region_size: u64, bs: &Arc<Mutex<BootSource>>, ) -> Result<()> { self.chardev .lock() .unwrap() .realize() .with_context(|| "Failed to realize chardev")?; self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| "Failed to set system resource for PL011.")?; let dev = Arc::new(Mutex::new(self)); sysbus .attach_device(&dev, region_base, region_size, "PL011") .with_context(|| "Failed to attach PL011 to system bus.")?; bs.lock().unwrap().kernel_cmdline.push(Param { param_type: "earlycon".to_string(), value: format!("pl011,mmio,0x{:08x}", region_base), }); MigrationManager::register_device_instance( PL011State::descriptor(), dev.clone(), PL011_SNAPSHOT_ID, ); let locked_dev = dev.lock().unwrap(); locked_dev.chardev.lock().unwrap().set_receiver(&dev); EventLoop::update_event( EventNotifierHelper::internal_notifiers(locked_dev.chardev.clone()), None, ) .with_context(|| LegacyError::RegNotifierErr)?; Ok(()) } fn unpause_rx(&mut self) { if self.paused { trace::pl011_unpause_rx(); self.paused = false; self.chardev.lock().unwrap().unpause_rx(); } } } impl InputReceiver for PL011 { fn receive(&mut self, data: &[u8]) { self.state.flags &= !PL011_FLAG_RXFE as u32; for val in data { let mut slot = (self.state.read_pos + self.state.read_count) as usize; if slot >= PL011_FIFO_SIZE { slot -= PL011_FIFO_SIZE; } self.state.rfifo[slot] = *val as u32; self.state.read_count += 1; trace::pl011_receive(self.state.rfifo[slot], self.state.read_count); } // If in character-mode, or in FIFO-mode and FIFO is full, trigger the interrupt. if ((self.state.lcr & 0x10) == 0) || (self.state.read_count as usize == PL011_FIFO_SIZE) { self.state.flags |= PL011_FLAG_RXFF as u32; trace::pl011_receive_full(); } if self.state.read_count >= self.state.read_trigger { self.state.int_level |= INT_RX; self.interrupt(); } } fn remain_size(&mut self) -> usize { PL011_FIFO_SIZE - self.state.read_count as usize } fn set_paused(&mut self) { trace::pl011_pause_rx(); self.paused = true; } } impl Device for PL011 { fn device_base(&self) -> &DeviceBase { &self.base.base } fn device_base_mut(&mut self) -> &mut DeviceBase { &mut self.base.base } } impl SysBusDevOps for PL011 { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base } fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { &mut self.base } fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { if data.len() > 4 { error!("Fail to read PL011, illegal data length {}", data.len()); return false; } let ret; match offset >> 2 { 0 => { // Data register. self.unpause_rx(); self.state.flags &= !(PL011_FLAG_RXFF as u32); let c = self.state.rfifo[self.state.read_pos as usize]; if self.state.read_count > 0 { self.state.read_count -= 1; self.state.read_pos += 1; if self.state.read_pos as usize == PL011_FIFO_SIZE { self.state.read_pos = 0; } } if self.state.read_count == 0 { self.state.flags |= PL011_FLAG_RXFE as u32; } if self.state.read_count == self.state.read_trigger - 1 { self.state.int_level &= !INT_RX; } trace::pl011_read_fifo(self.state.read_count); self.state.rsr = c >> 8; self.interrupt(); ret = c; } 1 => { ret = self.state.rsr; } 6 => { ret = self.state.flags; } 8 => { ret = self.state.ilpr; } 9 => { ret = self.state.ibrd; } 10 => { ret = self.state.fbrd; } 11 => { ret = self.state.lcr; } 12 => { ret = self.state.cr; } 13 => { ret = self.state.ifl; } 14 => { // Interrupt Mask Set/Clear Register ret = self.state.int_enabled; } 15 => { // Raw Interrupt Status Register ret = self.state.int_level; } 16 => { // Masked Interrupt Status Register ret = self.state.int_level & self.state.int_enabled; } 18 => { ret = self.state.dmacr; } 0x3f8..=0x400 => { // Register 0xFE0~0xFFC is UART Peripheral Identification Registers // and PrimeCell Identification Registers. ret = *self.state.id.get(((offset - 0xfe0) >> 2) as usize).unwrap() as u32; } _ => { error!("Failed to read pl011: Invalid offset 0x{:x}", offset); return false; } } data.copy_from_slice(&ret.as_bytes()[0..data.len()]); trace::pl011_read(offset, ret); true } fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { let mut value = 0; if !read_data_u32(data, &mut value) { return false; } trace::pl011_write(offset, value); match offset >> 2 { 0 => { let ch = value as u8; if let Some(output) = &mut self.chardev.lock().unwrap().output { let mut locked_output = output.lock().unwrap(); if let Err(e) = locked_output.write_all(&[ch]) { debug!("Failed to write to pl011 output fd, error is {:?}", e); } if let Err(e) = locked_output.flush() { debug!("Failed to flush pl011, error is {:?}", e); } } else { debug!("Failed to get output fd"); return false; } self.state.int_level |= INT_TX; self.interrupt(); } 1 => { self.state.rsr = 0; } 8 => { self.state.ilpr = value; } 9 => { self.state.ibrd = value; trace::pl011_baudrate_change(self.state.ibrd, self.state.fbrd); } 10 => { self.state.fbrd = value; trace::pl011_baudrate_change(self.state.ibrd, self.state.fbrd); } 11 => { // PL011 works in two modes: character mode or FIFO mode. // Reset FIFO if the mode is changed. if (self.state.lcr ^ value) & 0x10 != 0 { self.unpause_rx(); // fifo cleared, chardev-rx must be unpaused self.state.read_count = 0; self.state.read_pos = 0; } self.state.lcr = value; self.state.read_trigger = 1; } 12 => { self.state.cr = value; } 13 => { self.state.ifl = value; self.state.read_trigger = 1; } 14 => { self.state.int_enabled = value; self.interrupt(); } 17 => { // Interrupt Clear Register, write only self.state.int_level &= !value; self.interrupt(); } 18 => { self.state.dmacr = value; if value & 3 != 0 { error!("pl011: DMA not implemented"); } } _ => { error!("Failed to write pl011: Invalid offset 0x{:x}", offset); return false; } } true } fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } } impl StateTransfer for PL011 { fn get_state_vec(&self) -> Result<Vec<u8>> { Ok(self.state.as_bytes().to_vec()) } fn set_state_mut(&mut self, state: &[u8]) -> Result<()> { self.state = *PL011State::from_bytes(state) .with_context(|| MigrationError::FromBytesError("PL011"))?; self.unpause_rx(); Ok(()) } fn get_device_alias(&self) -> u64 { MigrationManager::get_desc_alias(&PL011State::descriptor().name).unwrap_or(!0) } } impl MigrationHook for PL011 {} impl AmlBuilder for PL011 { fn aml_bytes(&self) -> Vec<u8> { let mut acpi_dev = AmlDevice::new("COM0"); acpi_dev.append_child(AmlNameDecl::new("_HID", AmlString("ARMH0011".to_string()))); acpi_dev.append_child(AmlNameDecl::new("_UID", AmlInteger(0))); let mut res = AmlResTemplate::new(); res.append_child(AmlMemory32Fixed::new( AmlReadAndWrite::ReadWrite, self.base.res.region_base as u32, self.base.res.region_size as u32, )); // SPI start at interrupt number 32 on aarch64 platform. let irq_base = INTERRUPT_PPIS_COUNT + INTERRUPT_SGIS_COUNT; res.append_child(AmlExtendedInterrupt::new( AmlResourceUsage::Consumer, AmlEdgeLevel::Edge, AmlActiveLevel::High, AmlIntShare::Exclusive, vec![self.base.res.irq as u32 + irq_base], )); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); acpi_dev.aml_bytes() } } #[cfg(test)] mod test { use super::*; use machine_manager::config::{ChardevConfig, ChardevType}; #[test] fn test_receive() { let chardev_cfg = ChardevConfig { id: "chardev".to_string(), backend: ChardevType::Stdio, }; let mut pl011_dev = PL011::new(SerialConfig { chardev: chardev_cfg, }) .unwrap(); assert_eq!(pl011_dev.state.rfifo, [0; PL011_FIFO_SIZE]); assert_eq!(pl011_dev.state.flags, 0x90); assert_eq!(pl011_dev.state.lcr, 0); assert_eq!(pl011_dev.state.rsr, 0); assert_eq!(pl011_dev.state.cr, 0x300); assert_eq!(pl011_dev.state.dmacr, 0); assert_eq!(pl011_dev.state.ilpr, 0); assert_eq!(pl011_dev.state.ibrd, 0); assert_eq!(pl011_dev.state.fbrd, 0); assert_eq!(pl011_dev.state.ifl, 0x12); assert_eq!( pl011_dev.state.id, [0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1] ); assert_eq!(pl011_dev.state.read_pos, 0); assert_eq!(pl011_dev.state.read_count, 0); assert_eq!(pl011_dev.state.read_trigger, 1); assert_eq!(pl011_dev.state.int_level, 0); assert_eq!(pl011_dev.state.int_enabled, 0); let data = vec![0x12, 0x34, 0x56, 0x78, 0x90]; pl011_dev.receive(&data); assert_eq!(pl011_dev.state.read_count, data.len() as u32); for i in 0..data.len() { assert_eq!(pl011_dev.state.rfifo[i], data[i] as u32); } assert_eq!(pl011_dev.state.flags, 0xC0); assert_eq!(pl011_dev.state.int_level, INT_RX); } }