From 20054513bb80c0635ee3ea29a225ef90764f4e82 Mon Sep 17 00:00:00 2001
From: Yang Xiaohe <yangxiaohe2@huawei.com>
Date: Mon, 2 Nov 2020 09:45:38 +0800
Subject: [PATCH 2/3] address_space: modify RegionOps(trait) to struct which
 contains read/write callbacks

When activating device, the RegionOps callback is constructed and used
for initialization of IO-type Region.

Signed-off-by: Yang Xiaohe <yangxiaohe2@huawei.com>
---
 address_space/src/address_space.rs   |  62 ++++++--------
 address_space/src/lib.rs             |  34 +++++---
 address_space/src/region.rs          | 122 ++++++++++++++++-----------
 device_model/src/legacy/pl031.rs     |   6 +-
 device_model/src/legacy/serial.rs    |   7 +-
 device_model/src/lib.rs              |  21 +++++
 device_model/src/mmio/bus.rs         |  14 ++-
 device_model/src/mmio/mod.rs         |  55 +++++++++---
 device_model/src/mmio/virtio_mmio.rs |  52 ++++++------
 9 files changed, 222 insertions(+), 151 deletions(-)

diff --git a/address_space/src/address_space.rs b/address_space/src/address_space.rs
index ed00b9b..e87ccfa 100644
--- a/address_space/src/address_space.rs
+++ b/address_space/src/address_space.rs
@@ -433,17 +433,6 @@ mod test {
         }
     }
 
-    struct TestDevice;
-    impl RegionOps for TestDevice {
-        fn read(&mut self, _data: &mut [u8], _base: GuestAddress, _offset: u64) -> bool {
-            true
-        }
-
-        fn write(&mut self, _data: &[u8], _base: GuestAddress, _offset: u64) -> bool {
-            true
-        }
-    }
-
     // the listeners in AddressSpace is settled in ascending order by priority
     #[test]
     fn test_listeners() {
@@ -501,6 +490,11 @@ mod test {
         let listener = TestListener::default();
         space.register_listener(Box::new(listener.clone())).unwrap();
 
+        let default_ops = RegionOps {
+            read: Arc::new(|_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }),
+            write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }),
+        };
+
         // memory region layout
         //        0      1000   2000   3000   4000   5000   6000   7000   8000
         //        |------|------|------|------|------|------|------|------|
@@ -510,7 +504,7 @@ mod test {
         // the flat_view is as follows, region-b is container which will not appear in the flat-view
         //        [CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC]
         let region_b = Region::init_container_region(4000);
-        let region_c = Region::init_io_region(6000, Arc::new(Mutex::new(TestDevice)));
+        let region_c = Region::init_io_region(6000, default_ops.clone());
         region_b.set_priority(2);
         region_c.set_priority(1);
         root.add_subregion(region_b.clone(), 2000).unwrap();
@@ -532,7 +526,7 @@ mod test {
         //  D:                  [DDDDDD]
         // the flat_view is as follows,
         //        [CCCCCCCCCCCC][DDDDDD][CCCCCCCCCCCCCCCCCCC]
-        let region_d = Region::init_io_region(1000, Arc::new(Mutex::new(TestDevice)));
+        let region_d = Region::init_io_region(1000, default_ops);
         region_b.add_subregion(region_d.clone(), 0).unwrap();
 
         assert_eq!(space.flat_view.read().unwrap().0.len(), 3);
@@ -562,25 +556,16 @@ mod test {
 
     #[test]
     fn test_update_ioeventfd() {
-        struct TestIoEventFd;
-        impl RegionOps for TestIoEventFd {
-            fn read(&mut self, _data: &mut [u8], _base: GuestAddress, _offset: u64) -> bool {
-                true
-            }
-
-            fn write(&mut self, _data: &[u8], _base: GuestAddress, _offset: u64) -> bool {
-                true
-            }
-
-            fn ioeventfds(&self) -> Vec<RegionIoEventFd> {
-                vec![RegionIoEventFd {
-                    fd: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
-                    addr_range: AddressRange::from((0, 4)),
-                    data_match: true,
-                    data: 0_64,
-                }]
-            }
-        }
+        let ioeventfds = vec![RegionIoEventFd {
+            fd: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
+            addr_range: AddressRange::from((0, 4)),
+            data_match: true,
+            data: 0_64,
+        }];
+        let default_ops = RegionOps {
+            read: Arc::new(|_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }),
+            write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }),
+        };
 
         // region layout
         //        0      1000   2000   3000   4000   5000   6000   7000   8000
@@ -594,9 +579,11 @@ mod test {
         let listener = TestListener::default();
         space.register_listener(Box::new(listener.clone())).unwrap();
 
-        let region_b = Region::init_io_region(2000, Arc::new(Mutex::new(TestIoEventFd)));
+        let region_b = Region::init_io_region(2000, default_ops.clone());
         region_b.set_priority(1);
-        let region_c = Region::init_io_region(2000, Arc::new(Mutex::new(TestIoEventFd)));
+        region_b.set_ioeventfds(&ioeventfds);
+        let region_c = Region::init_io_region(2000, default_ops);
+        region_c.set_ioeventfds(&ioeventfds);
 
         root.add_subregion(region_c, 2000).unwrap();
         assert_eq!(listener.reqs.lock().unwrap().len(), 2);
@@ -625,6 +612,11 @@ mod test {
         let root = Region::init_container_region(8000);
         let space = AddressSpace::new(root.clone()).unwrap();
 
+        let default_ops = RegionOps {
+            read: Arc::new(|_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }),
+            write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }),
+        };
+
         let ram1 = Arc::new(HostMemMapping::new(GuestAddress(0), 1000, false).unwrap());
         let ram2 = Arc::new(HostMemMapping::new(GuestAddress(2000), 1000, false).unwrap());
         let region_a = Region::init_ram_region(ram1.clone());
@@ -660,7 +652,7 @@ mod test {
         //  c:            [CCCCCCCCC]
         // the flat_view is as follows,
         //        [AAAAAA][CCCCCCCCC][BB]
-        let region_c = Region::init_io_region(1500, Arc::new(Mutex::new(TestDevice)));
+        let region_c = Region::init_io_region(1500, default_ops);
         region_c.set_priority(1);
         root.add_subregion(region_c, 1000).unwrap();
 
diff --git a/address_space/src/lib.rs b/address_space/src/lib.rs
index ab9014f..98a7ae6 100644
--- a/address_space/src/lib.rs
+++ b/address_space/src/lib.rs
@@ -21,11 +21,13 @@
 //! use address_space::{AddressSpace, Region, GuestAddress, HostMemMapping, RegionOps};
 //!
 //! struct DummyDevice;
-//! impl RegionOps for DummyDevice {
+//! impl DummyDevice {
 //!     fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool {
+//!         // read operation ommited
 //!         true
 //!     }
 //!     fn write(&mut self, data: &[u8], base: GuestAddress, offset: u64) -> bool {
+//!         // write operation ommited
 //!         true
 //!     }
 //! }
@@ -41,7 +43,22 @@
 //!
 //!     // 3. create a IO-type Region
 //!     let dev = Arc::new(Mutex::new(DummyDevice));
-//!     let io_region = Region::init_io_region(0x1000, dev);
+//!     let dev_clone = dev.clone();
+//!     let read_ops = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool {
+//!         let mut dev_locked = dev_clone.lock().unwrap();
+//!         dev_locked.read(data, addr, offset)
+//!     };
+//!     let dev_clone = dev.clone();
+//!     let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool {
+//!         let mut dev_locked = dev_clone.lock().unwrap();
+//!         dev_locked.write(data, addr, offset)
+//!     };
+//!     let dev_ops = RegionOps {
+//!         read: Arc::new(read_ops),
+//!         write: Arc::new(write_ops),
+//!     };
+//!
+//!     let io_region = Region::init_io_region(0x1000, dev_ops);
 //!
 //!     // 4. add sub_region to address_space's root region
 //!     space.root().add_subregion(ram_region, mem_mapping.start_address().raw_value());
@@ -121,7 +138,8 @@ pub mod errors {
 }
 
 /// Provide Some operations of `Region`, mainly used by Vm's devices.
-pub trait RegionOps: Send {
+#[derive(Clone)]
+pub struct RegionOps {
     /// Read data from Region to argument `data`,
     /// return `true` if read successfully, or return `false`.
     ///
@@ -130,8 +148,7 @@ pub trait RegionOps: Send {
     /// * `data` - A u8-type array.
     /// * `base` - Base address.
     /// * `offset` - Offset from base address.
-    fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool;
-
+    pub read: std::sync::Arc<dyn Fn(&mut [u8], GuestAddress, u64) -> bool + Send + Sync>,
     /// Write `data` to memory,
     /// return `true` if write successfully, or return `false`.
     ///
@@ -140,12 +157,7 @@ pub trait RegionOps: Send {
     /// * `data` - A u8-type array.
     /// * `base` - Base address.
     /// * `offset` - Offset from base address.
-    fn write(&mut self, data: &[u8], base: GuestAddress, offset: u64) -> bool;
-
-    /// Create a group of IoEvents for `region`.
-    fn ioeventfds(&self) -> Vec<RegionIoEventFd> {
-        Vec::new()
-    }
+    pub write: std::sync::Arc<dyn Fn(&[u8], GuestAddress, u64) -> bool + Send + Sync>,
 }
 
 /// Gets the page size of system.
diff --git a/address_space/src/region.rs b/address_space/src/region.rs
index f18bf30..45e70c0 100644
--- a/address_space/src/region.rs
+++ b/address_space/src/region.rs
@@ -41,7 +41,9 @@ pub struct Region {
     /// If not Ram-type Region, `mem_mapping` is None. It won't be changed once initialized.
     mem_mapping: Option<Arc<HostMemMapping>>,
     /// `ops` provides read/write function.
-    ops: Option<Arc<Mutex<dyn RegionOps>>>,
+    ops: Option<RegionOps>,
+    /// ioeventfds within this Region.
+    io_evtfds: Arc<Mutex<Vec<RegionIoEventFd>>>,
     /// Weak pointer pointing to the father address-spaces.
     space: Arc<RwLock<Weak<AddressSpace>>>,
     /// Sub-regions array, keep sorted
@@ -140,7 +142,7 @@ impl Region {
         size: u64,
         region_type: RegionType,
         mem_mapping: Option<Arc<HostMemMapping>>,
-        ops: Option<Arc<Mutex<dyn RegionOps>>>,
+        ops: Option<RegionOps>,
     ) -> Region {
         Region {
             region_type,
@@ -149,6 +151,7 @@ impl Region {
             size: Arc::new(AtomicU64::new(size)),
             mem_mapping,
             ops,
+            io_evtfds: Arc::new(Mutex::new(Vec::new())),
             space: Arc::new(RwLock::new(Weak::new())),
             subregions: Arc::new(RwLock::new(Vec::new())),
         }
@@ -169,8 +172,8 @@ impl Region {
     ///
     /// * `size` - Size of IO region.
     /// * `dev` - Operation of Region.
-    pub fn init_io_region(size: u64, dev: Arc<Mutex<dyn RegionOps>>) -> Region {
-        Region::init_region_internal(size, RegionType::IO, None, Some(dev))
+    pub fn init_io_region(size: u64, ops: RegionOps) -> Region {
+        Region::init_region_internal(size, RegionType::IO, None, Some(ops))
     }
 
     /// Initialize Container-type region.
@@ -313,14 +316,8 @@ impl Region {
                     return Err(ErrorKind::Overflow(count).into());
                 }
                 let mut slice = vec![0_u8; count as usize];
-                if !self
-                    .ops
-                    .as_ref()
-                    .unwrap()
-                    .lock()
-                    .unwrap()
-                    .read(&mut slice, base, offset)
-                {
+                let read_ops = self.ops.as_ref().unwrap().read.as_ref();
+                if !read_ops(&mut slice, base, offset) {
                     return Err(ErrorKind::IoAccess(offset).into());
                 }
                 dst.write_all(&slice)?;
@@ -371,14 +368,8 @@ impl Region {
                 let mut slice = vec![0_u8; count as usize];
                 src.read_exact(&mut slice)?;
 
-                if !self
-                    .ops
-                    .as_ref()
-                    .unwrap()
-                    .lock()
-                    .unwrap()
-                    .write(&slice, base, offset)
-                {
+                let write_ops = self.ops.as_ref().unwrap().write.as_ref();
+                if !write_ops(&slice, base, offset) {
                     return Err(ErrorKind::IoAccess(offset).into());
                 }
             }
@@ -390,21 +381,31 @@ impl Region {
     }
 
     /// Return the IoEvent of a `Region`.
+    pub fn set_ioeventfds(&self, new_fds: &[RegionIoEventFd]) {
+        let region_evtfds = new_fds
+            .iter()
+            .map(|e| {
+                let mut evt_cloned = e.try_clone().unwrap();
+                evt_cloned.addr_range.base.0 += self.offset().raw_value();
+                evt_cloned
+            })
+            .collect();
+        *self.io_evtfds.lock().unwrap() = region_evtfds;
+    }
+
+    /// Set the ioeventfds within this Region,
+    /// these fds will be register to `KVM` and used for guest notifier.
     pub(crate) fn ioeventfds(&self) -> Vec<RegionIoEventFd> {
-        match self.region_type {
-            RegionType::IO => {
-                let ioeventfds = self.ops.as_ref().unwrap().lock().unwrap().ioeventfds();
-                ioeventfds
-                    .iter()
-                    .map(|e| {
-                        let mut evt_cloned = e.try_clone().unwrap();
-                        evt_cloned.addr_range.base.0 += self.offset().raw_value();
-                        evt_cloned
-                    })
-                    .collect()
-            }
-            _ => Vec::new(),
-        }
+        self.io_evtfds
+            .lock()
+            .unwrap()
+            .iter()
+            .map(|e| {
+                let mut evt_cloned = e.try_clone().unwrap();
+                evt_cloned.addr_range.base.0 += self.offset().raw_value();
+                evt_cloned
+            })
+            .collect()
     }
 
     /// Add sub-region to this region.
@@ -650,7 +651,7 @@ mod test {
         head: u64,
     }
 
-    impl RegionOps for TestDevice {
+    impl TestDevice {
         fn read(&mut self, data: &mut [u8], _base: GuestAddress, _offset: u64) -> bool {
             if data.len() != std::mem::size_of::<u64>() {
                 return false;
@@ -742,8 +743,24 @@ mod test {
 
     #[test]
     fn test_io_region() {
-        let config_space = Arc::new(Mutex::new(TestDevice::default()));
-        let io_region = Region::init_io_region(16, config_space.clone());
+        let test_dev = Arc::new(Mutex::new(TestDevice::default()));
+        let test_dev_clone = test_dev.clone();
+        let read_ops = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool {
+            let mut device_locked = test_dev_clone.lock().unwrap();
+            device_locked.read(data, addr, offset)
+        };
+        let test_dev_clone = test_dev.clone();
+        let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool {
+            let mut device_locked = test_dev_clone.lock().unwrap();
+            device_locked.write(data, addr, offset)
+        };
+
+        let test_dev_ops = RegionOps {
+            read: Arc::new(read_ops),
+            write: Arc::new(write_ops),
+        };
+
+        let io_region = Region::init_io_region(16, test_dev_ops.clone());
         let data = [0x01u8; 8];
         let mut data_res = [0x0u8; 8];
         let count = data.len() as u64;
@@ -798,11 +815,13 @@ mod test {
         assert_eq!(container.region_type(), RegionType::Container);
         assert_eq!(container.priority(), 0);
 
-        // create two io region as container's sub regions
-        let dev = Arc::new(Mutex::new(TestDevice::default()));
-        let dev2 = Arc::new(Mutex::new(TestDevice::default()));
-        let io_region = Region::init_io_region(1 << 4, dev.clone());
-        let io_region2 = Region::init_io_region(1 << 4, dev2.clone());
+        let default_ops = RegionOps {
+            read: Arc::new(|_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }),
+            write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }),
+        };
+
+        let io_region = Region::init_io_region(1 << 4, default_ops.clone());
+        let io_region2 = Region::init_io_region(1 << 4, default_ops.clone());
         io_region2.set_priority(10);
 
         // add duplicate io-region or ram-region will fail
@@ -841,9 +860,10 @@ mod test {
 
     #[test]
     fn test_generate_flatview() {
-        let config_c = Arc::new(Mutex::new(TestDevice::default()));
-        let config_d = Arc::new(Mutex::new(TestDevice::default()));
-        let config_e = Arc::new(Mutex::new(TestDevice::default()));
+        let default_ops = RegionOps {
+            read: Arc::new(|_: &mut [u8], _: GuestAddress, _: u64| -> bool { true }),
+            write: Arc::new(|_: &[u8], _: GuestAddress, _: u64| -> bool { true }),
+        };
 
         // memory region layout
         //        0      1000   2000   3000   4000   5000   6000   7000   8000
@@ -859,9 +879,9 @@ mod test {
         {
             let region_a = Region::init_container_region(8000);
             let region_b = Region::init_container_region(4000);
-            let region_c = Region::init_io_region(6000, config_c.clone());
-            let region_d = Region::init_io_region(1000, config_d.clone());
-            let region_e = Region::init_io_region(1000, config_e.clone());
+            let region_c = Region::init_io_region(6000, default_ops.clone());
+            let region_d = Region::init_io_region(1000, default_ops.clone());
+            let region_e = Region::init_io_region(1000, default_ops.clone());
 
             region_b.set_priority(2);
             region_c.set_priority(1);
@@ -906,9 +926,9 @@ mod test {
         {
             let region_a = Region::init_container_region(8000);
             let region_b = Region::init_container_region(5000);
-            let region_c = Region::init_io_region(1000, config_c.clone());
-            let region_d = Region::init_io_region(3000, config_d.clone());
-            let region_e = Region::init_io_region(2000, config_e.clone());
+            let region_c = Region::init_io_region(1000, default_ops.clone());
+            let region_d = Region::init_io_region(3000, default_ops.clone());
+            let region_e = Region::init_io_region(2000, default_ops.clone());
 
             region_a.add_subregion(region_b.clone(), 2000).unwrap();
             region_a.add_subregion(region_c.clone(), 0).unwrap();
diff --git a/device_model/src/legacy/pl031.rs b/device_model/src/legacy/pl031.rs
index a662e08..623048d 100644
--- a/device_model/src/legacy/pl031.rs
+++ b/device_model/src/legacy/pl031.rs
@@ -12,13 +12,13 @@
 
 use std::time::{Instant, SystemTime, UNIX_EPOCH};
 
-use address_space::{GuestAddress, RegionOps};
+use address_space::GuestAddress;
 use byteorder::{ByteOrder, LittleEndian};
 use kvm_ioctls::VmFd;
 use vmm_sys_util::eventfd::EventFd;
 
 use super::super::mmio::errors::{Result, ResultExt};
-use super::super::mmio::{DeviceResource, DeviceType, MmioDeviceOps};
+use super::super::mmio::{DeviceOps, DeviceResource, DeviceType, MmioDeviceOps};
 
 /// Registers for pl032 from ARM PrimeCell Real Time Clock Technical Reference Manual.
 /// Data Register.
@@ -87,7 +87,7 @@ impl PL031 {
     }
 }
 
-impl RegionOps for PL031 {
+impl DeviceOps for PL031 {
     /// Read data from registers by guest.
     fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool {
         if offset >= 0xFE0 && offset < 0x1000 {
diff --git a/device_model/src/legacy/serial.rs b/device_model/src/legacy/serial.rs
index 62902ae..ba34a00 100644
--- a/device_model/src/legacy/serial.rs
+++ b/device_model/src/legacy/serial.rs
@@ -15,13 +15,13 @@ use std::io;
 use std::os::unix::io::RawFd;
 use std::sync::{Arc, Mutex};
 
-use address_space::{GuestAddress, RegionOps};
+use address_space::GuestAddress;
 use kvm_ioctls::VmFd;
 use util::epoll_context::{EventNotifier, EventNotifierHelper, NotifierOperation};
 use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, terminal::Terminal};
 
 use super::super::mmio::errors::{Result, ResultExt};
-use super::super::mmio::{DeviceResource, DeviceType, MmioDeviceOps};
+use super::super::mmio::{DeviceOps, DeviceResource, DeviceType, MmioDeviceOps};
 
 const UART_IER_RDI: u8 = 0x01;
 const UART_IER_THRI: u8 = 0x02;
@@ -290,7 +290,7 @@ impl Serial {
     }
 }
 
-impl RegionOps for Serial {
+impl DeviceOps for Serial {
     /// Read one byte data to `data` from a certain register selected by `offset`.
     ///
     /// # Arguments
@@ -324,7 +324,6 @@ impl RegionOps for Serial {
         self.write_internal(offset, data[0]).is_ok()
     }
 }
-
 impl MmioDeviceOps for Serial {
     /// Realize a serial for VM.
     /// * Create a new output component.
diff --git a/device_model/src/lib.rs b/device_model/src/lib.rs
index 52e86a7..3bde642 100644
--- a/device_model/src/lib.rs
+++ b/device_model/src/lib.rs
@@ -48,6 +48,27 @@ mod virtio;
 pub use error_chain::*;
 pub use micro_vm::{cmdline, main_loop::MainLoop, micro_syscall::register_seccomp, LightMachine};
 
+use address_space::GuestAddress;
+/// Basic device operations
+pub trait DeviceOps: Send {
+    /// Read function of device.
+    ///
+    /// # Arguments
+    ///
+    /// * `data` - A u8-type array.
+    /// * `base` - Base address of this device.
+    /// * `offset` - Offset from base address.
+    fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool;
+    /// Write function of device.
+    ///
+    /// # Arguments
+    ///
+    /// * `data` - A u8-type array.
+    /// * `base` - Base address of this device.
+    /// * `offset` - Offset from base address.
+    fn write(&mut self, data: &[u8], base: GuestAddress, offset: u64) -> bool;
+}
+
 pub mod errors {
     error_chain! {
         links {
diff --git a/device_model/src/mmio/bus.rs b/device_model/src/mmio/bus.rs
index 4690563..8f4f328 100644
--- a/device_model/src/mmio/bus.rs
+++ b/device_model/src/mmio/bus.rs
@@ -12,7 +12,7 @@
 
 use std::sync::{Arc, Mutex};
 
-use address_space::{AddressSpace, RegionOps};
+use address_space::AddressSpace;
 use kvm_ioctls::VmFd;
 use machine_manager::config::{BootSource, ConfigCheck};
 
@@ -146,7 +146,7 @@ impl Bus {
     /// # Errors
     ///
     /// Return Error if irq number exceed the limit as Arch spec defined.
-    pub fn attach_device<T: 'static + RegionOps + MmioDeviceOps>(
+    pub fn attach_device<T: 'static + MmioDeviceOps>(
         &mut self,
         device: Arc<Mutex<T>>,
     ) -> Result<MmioDevice> {
@@ -172,15 +172,11 @@ impl Bus {
             bail!("irq {} exceed max value {}", resource.irq, IRQ_RANGE.1);
         }
 
-        let device = MmioDevice {
-            device: device.clone(),
-            dev_region: device,
-            resource: Arc::new(resource),
-        };
+        let mmio_dev = MmioDevice::new(device, resource);
 
-        self.devices.push(device.clone());
+        self.devices.push(mmio_dev.clone());
 
-        Ok(device)
+        Ok(mmio_dev)
     }
 
     /// Get the information of all devices inserted in bus.
diff --git a/device_model/src/mmio/mod.rs b/device_model/src/mmio/mod.rs
index b9787be..e25cabb 100644
--- a/device_model/src/mmio/mod.rs
+++ b/device_model/src/mmio/mod.rs
@@ -34,7 +34,7 @@ mod virtio_mmio;
 pub use self::bus::Bus;
 pub use self::virtio_mmio::VirtioMmioDevice;
 
-use address_space::{AddressSpace, Region, RegionOps};
+use address_space::{AddressSpace, GuestAddress, Region, RegionIoEventFd, RegionOps};
 use error_chain::bail;
 use machine_manager::config::{BootSource, ConfigCheck, Param};
 
@@ -86,12 +86,38 @@ pub struct MmioDevice {
     /// MmioDeviceOps used to be invoked in function realize().
     device: Arc<Mutex<dyn MmioDeviceOps>>,
     /// RegionOps used to be registered into system address space.
-    dev_region: Arc<Mutex<dyn RegionOps>>,
+    region_ops: RegionOps,
     /// The DeviceResource required by this MMIO device.
     resource: Arc<DeviceResource>,
 }
 
 impl MmioDevice {
+    pub fn new<T: 'static + MmioDeviceOps>(
+        device: Arc<Mutex<T>>,
+        res: DeviceResource,
+    ) -> MmioDevice {
+        let device_clone = device.clone();
+        let read_ops = move |data: &mut [u8], addr: GuestAddress, offset: u64| -> bool {
+            let mut device_locked = device_clone.lock().unwrap();
+            device_locked.read(data, addr, offset)
+        };
+
+        let device_clone = device.clone();
+        let write_ops = move |data: &[u8], addr: GuestAddress, offset: u64| -> bool {
+            let mut device_locked = device_clone.lock().unwrap();
+            device_locked.write(data, addr, offset)
+        };
+
+        let region_ops = RegionOps {
+            read: Arc::new(read_ops),
+            write: Arc::new(write_ops),
+        };
+        MmioDevice {
+            device,
+            region_ops,
+            resource: Arc::new(res),
+        }
+    }
     /// Realize this MMIO device for VM.
     ///
     /// # Arguments
@@ -108,19 +134,15 @@ impl MmioDevice {
     ) -> Result<()> {
         self.device.lock().unwrap().realize(vm_fd, *self.resource)?;
 
+        let region = Region::init_io_region(self.resource.size, self.region_ops.clone());
+        region.set_ioeventfds(&self.device.lock().unwrap().ioeventfds());
         match self.resource.dev_type {
             DeviceType::SERIAL if cfg!(target_arch = "x86_64") => {
                 #[cfg(target_arch = "x86_64")]
-                sys_io.root().add_subregion(
-                    Region::init_io_region(self.resource.size, self.dev_region.clone()),
-                    self.resource.addr,
-                )?;
+                sys_io.root().add_subregion(region, self.resource.addr)?;
             }
             _ => {
-                sys_mem.root().add_subregion(
-                    Region::init_io_region(self.resource.size, self.dev_region.clone()),
-                    self.resource.addr,
-                )?;
+                sys_mem.root().add_subregion(region, self.resource.addr)?;
             }
         }
 
@@ -165,7 +187,7 @@ impl MmioDevice {
 }
 
 /// Trait for MMIO device.
-pub trait MmioDeviceOps: Send {
+pub trait MmioDeviceOps: Send + DeviceOps {
     /// Realize this MMIO device for VM.
     fn realize(&mut self, vm_fd: &VmFd, resource: DeviceResource) -> Result<()>;
 
@@ -176,4 +198,15 @@ pub trait MmioDeviceOps: Send {
     fn update_config(&mut self, _dev_config: Option<Arc<dyn ConfigCheck>>) -> Result<()> {
         bail!("Unsupported to update configuration");
     }
+
+    /// Get IoEventFds of MMIO device.
+    fn ioeventfds(&self) -> Vec<RegionIoEventFd> {
+        Vec::new()
+    }
+}
+
+pub trait DeviceOps: Send {
+    fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool;
+
+    fn write(&mut self, data: &[u8], base: GuestAddress, offset: u64) -> bool;
 }
diff --git a/device_model/src/mmio/virtio_mmio.rs b/device_model/src/mmio/virtio_mmio.rs
index d642fc0..90877f8 100644
--- a/device_model/src/mmio/virtio_mmio.rs
+++ b/device_model/src/mmio/virtio_mmio.rs
@@ -13,7 +13,7 @@
 use std::sync::atomic::{AtomicU32, Ordering};
 use std::sync::{Arc, Mutex};
 
-use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd, RegionOps};
+use address_space::{AddressRange, AddressSpace, GuestAddress, RegionIoEventFd};
 use byteorder::{ByteOrder, LittleEndian};
 use kvm_ioctls::VmFd;
 use machine_manager::config::ConfigCheck;
@@ -26,7 +26,7 @@ use super::super::virtio::{
 };
 
 use super::errors::{ErrorKind, Result, ResultExt};
-use super::{DeviceResource, DeviceType, MmioDeviceOps};
+use super::{DeviceOps, DeviceResource, DeviceType, MmioDeviceOps};
 
 /// Registers of virtio-mmio device refer to Virtio Spec.
 /// Magic value - Read Only.
@@ -353,7 +353,7 @@ impl VirtioMmioDevice {
     }
 }
 
-impl RegionOps for VirtioMmioDevice {
+impl DeviceOps for VirtioMmioDevice {
     /// Read data by virtio driver from VM.
     fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool {
         match offset {
@@ -453,30 +453,6 @@ impl RegionOps for VirtioMmioDevice {
         }
         true
     }
-
-    /// Return the ioeventfds of device,
-    /// these fds will be register to `KVM` and used for guest notifier.
-    fn ioeventfds(&self) -> Vec<RegionIoEventFd> {
-        let mut ret = Vec::new();
-        for (index, eventfd) in self.host_notify_info.events.iter().enumerate() {
-            let addr = u64::from(NOTIFY_REG_OFFSET);
-            let eventfd_clone = match eventfd.try_clone() {
-                Err(e) => {
-                    error!("Failed to clone ioeventfd, error is {}", e);
-                    continue;
-                }
-                Ok(fd) => fd,
-            };
-            ret.push(RegionIoEventFd {
-                fd: eventfd_clone,
-                addr_range: AddressRange::from((addr, std::mem::size_of::<u32>() as u64)),
-                data_match: true,
-                data: index as u64,
-            })
-        }
-
-        ret
-    }
 }
 
 impl MmioDeviceOps for VirtioMmioDevice {
@@ -513,6 +489,28 @@ impl MmioDeviceOps for VirtioMmioDevice {
             .chain_err(|| "Failed to update configuration")?;
         Ok(())
     }
+
+    fn ioeventfds(&self) -> Vec<RegionIoEventFd> {
+        let mut ret = Vec::new();
+        for (index, eventfd) in self.host_notify_info.events.iter().enumerate() {
+            let addr = u64::from(NOTIFY_REG_OFFSET);
+            let eventfd_clone = match eventfd.try_clone() {
+                Err(e) => {
+                    error!("Failed to clone ioeventfd, error is {}", e);
+                    continue;
+                }
+                Ok(fd) => fd,
+            };
+            ret.push(RegionIoEventFd {
+                fd: eventfd_clone,
+                addr_range: AddressRange::from((addr, std::mem::size_of::<u32>() as u64)),
+                data_match: true,
+                data: index as u64,
+            })
+        }
+
+        ret
+    }
 }
 
 #[cfg(test)]
-- 
2.27.0

