dragonball: add legacy devices manager

The legacy devices manager is used for managing legacy devices.

Fixes: #4257

Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
Signed-off-by: wllenyj <wllenyj@linux.alibaba.com>
Signed-off-by: Chao Wu <chaowu@linux.alibaba.com>
This commit is contained in:
wllenyj 2022-05-05 01:10:58 +08:00 committed by Chao Wu
parent 3c45c0715f
commit 0bcb422fcb
2 changed files with 158 additions and 0 deletions

View File

@ -0,0 +1,155 @@
// Copyright (C) 2022 Alibaba Cloud. All rights reserved.
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the THIRD-PARTY file.
//! Device Manager for Legacy Devices.
use std::io;
use std::sync::{Arc, Mutex};
use dbs_device::device_manager::Error as IoManagerError;
use dbs_legacy_devices::SerialDevice;
use vmm_sys_util::eventfd::EventFd;
// The I8042 Data Port (IO Port 0x60) is used for reading data that was received from a I8042 device or from the I8042 controller itself and writing data to a I8042 device or to the I8042 controller itself.
const I8042_DATA_PORT: u16 = 0x60;
/// Errors generated by legacy device manager.
#[derive(Debug, thiserror::Error)]
pub enum Error {
/// Cannot add legacy device to Bus.
#[error("bus failure while managing legacy device")]
BusError(#[source] IoManagerError),
/// Cannot create EventFd.
#[error("failure while reading EventFd file descriptor")]
EventFd(#[source] io::Error),
/// Failed to register/deregister interrupt.
#[error("failure while managing interrupt for legacy device")]
IrqManager(#[source] vmm_sys_util::errno::Error),
}
/// The `LegacyDeviceManager` is a wrapper that is used for registering legacy devices
/// on an I/O Bus.
///
/// It currently manages the uart and i8042 devices. The `LegacyDeviceManger` should be initialized
/// only by using the constructor.
pub struct LegacyDeviceManager {
#[cfg(target_arch = "x86_64")]
i8042_reset_eventfd: EventFd,
pub(crate) com1_device: Arc<Mutex<SerialDevice>>,
_com1_eventfd: EventFd,
pub(crate) com2_device: Arc<Mutex<SerialDevice>>,
_com2_eventfd: EventFd,
}
impl LegacyDeviceManager {
/// Get the serial device for com1.
pub fn get_com1_serial(&self) -> Arc<Mutex<SerialDevice>> {
self.com1_device.clone()
}
/// Get the serial device for com2
pub fn get_com2_serial(&self) -> Arc<Mutex<SerialDevice>> {
self.com2_device.clone()
}
}
#[cfg(target_arch = "x86_64")]
pub(crate) mod x86_64 {
use super::*;
use dbs_device::device_manager::IoManager;
use dbs_device::resources::Resource;
use dbs_legacy_devices::{EventFdTrigger, I8042Device, I8042DeviceMetrics};
use kvm_ioctls::VmFd;
pub(crate) const COM1_IRQ: u32 = 4;
pub(crate) const COM1_PORT1: u16 = 0x3f8;
pub(crate) const COM2_IRQ: u32 = 3;
pub(crate) const COM2_PORT1: u16 = 0x2f8;
type Result<T> = ::std::result::Result<T, Error>;
impl LegacyDeviceManager {
/// Create a LegacyDeviceManager instance handling legacy devices (uart, i8042).
pub fn create_manager(bus: &mut IoManager, vm_fd: Option<Arc<VmFd>>) -> Result<Self> {
let (com1_device, com1_eventfd) =
Self::create_com_device(bus, vm_fd.as_ref(), COM1_IRQ, COM1_PORT1)?;
let (com2_device, com2_eventfd) =
Self::create_com_device(bus, vm_fd.as_ref(), COM2_IRQ, COM2_PORT1)?;
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).map_err(Error::EventFd)?;
let i8042_device = Arc::new(Mutex::new(I8042Device::new(
EventFdTrigger::new(exit_evt.try_clone().map_err(Error::EventFd)?),
Arc::new(I8042DeviceMetrics::default()),
)));
let resources = [Resource::PioAddressRange {
// 0x60 and 0x64 are the io ports that i8042 devices used.
// We register pio address range from 0x60 - 0x64 with base I8042_DATA_PORT for i8042 to use.
base: I8042_DATA_PORT,
size: 0x5,
}];
bus.register_device_io(i8042_device, &resources)
.map_err(Error::BusError)?;
Ok(LegacyDeviceManager {
i8042_reset_eventfd: exit_evt,
com1_device,
_com1_eventfd: com1_eventfd,
com2_device,
_com2_eventfd: com2_eventfd,
})
}
/// Get the eventfd for exit notification.
pub fn get_reset_eventfd(&self) -> Result<EventFd> {
self.i8042_reset_eventfd.try_clone().map_err(Error::EventFd)
}
fn create_com_device(
bus: &mut IoManager,
vm_fd: Option<&Arc<VmFd>>,
irq: u32,
port_base: u16,
) -> Result<(Arc<Mutex<SerialDevice>>, EventFd)> {
let eventfd = EventFd::new(libc::EFD_NONBLOCK).map_err(Error::EventFd)?;
let device = Arc::new(Mutex::new(SerialDevice::new(
eventfd.try_clone().map_err(Error::EventFd)?,
)));
// port_base defines the base port address for the COM devices.
// Since every COM device has 8 data registers so we register the pio address range as size 0x8.
let resources = [Resource::PioAddressRange {
base: port_base,
size: 0x8,
}];
bus.register_device_io(device.clone(), &resources)
.map_err(Error::BusError)?;
if let Some(fd) = vm_fd {
fd.register_irqfd(&eventfd, irq)
.map_err(Error::IrqManager)?;
}
Ok((device, eventfd))
}
}
}
#[cfg(test)]
mod tests {
#[cfg(target_arch = "x86_64")]
use super::*;
#[test]
#[cfg(target_arch = "x86_64")]
fn test_create_legacy_device_manager() {
let mut bus = dbs_device::device_manager::IoManager::new();
let mgr = LegacyDeviceManager::create_manager(&mut bus, None).unwrap();
let _exit_fd = mgr.get_reset_eventfd().unwrap();
}
}

View File

@ -8,6 +8,9 @@ pub mod console_manager;
/// Console Manager for virtual machines console device.
pub use self::console_manager::ConsoleManager;
mod legacy;
pub use self::legacy::{Error as LegacyDeviceError, LegacyDeviceManager};
/// Errors related to device manager operations.
#[derive(Debug, thiserror::Error)]
pub enum DeviceMgrError {