Слияние кода завершено, страница обновится автоматически
// 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);
}
}
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарий ( 0 )