The fw_cfg device is a hardware interface allows the guest to retrieve various data items (blobs) that can influence how the firmware configures itself, or may contain tables to be installed for the guest OS.
Examples include device boot order, ACPI and SMBIOS tables, virtual machine UUID, SMP and NUMA information, kernel/initrd images for direct (Linux) kernel booting, etc.
StratoVirt will reuse the fw_cfg device model as firmware loader for the our standard virtual machine.
Signed-off-by: Ying Fang fangying1@huawei.com --- device_model/src/legacy/fw_cfg.rs | 781 ++++++++++++++++++++++++++++++ device_model/src/legacy/mod.rs | 11 + device_model/src/micro_vm/mod.rs | 24 + device_model/src/mmio/bus.rs | 9 + device_model/src/mmio/mod.rs | 1 + 5 files changed, 826 insertions(+) create mode 100644 device_model/src/legacy/fw_cfg.rs
diff --git a/device_model/src/legacy/fw_cfg.rs b/device_model/src/legacy/fw_cfg.rs new file mode 100644 index 0000000..7422d4b --- /dev/null +++ b/device_model/src/legacy/fw_cfg.rs @@ -0,0 +1,781 @@ +// 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 super::super::mmio::errors::{Result}; +use super::super::mmio::{DeviceResource, DeviceType, MmioDeviceOps}; +use super::super::DeviceOps; +use address_space::{AddressSpace, GuestAddress}; +use byteorder::{ByteOrder, BigEndian, LittleEndian}; +use serde::{Deserialize, Serialize}; +use kvm_ioctls::VmFd; +use std::default::Default; +use std::cmp::Ordering; +use std::sync::{Arc}; +use util::byte_code::ByteCode; + +const FW_CFG_ACPI_DEVICE_ID: &str = "QEMU0002"; +/// QEMU CFG Signature +const FW_CFG_DMA_SIGNATURE: u128 = 0x51454d5520434647; + +/// FW_CFG_VERSION bits +const FW_CFG_VERSION: u32 = 0x01; +const FW_CFG_VERSION_DMA: u32 = 0x02; +const FW_CFG_FILE_SLOTS_DFLT: u32 = 0x20; + +enum FWCfgEntryType { + FW_CFG_SIGNATURE = 0x00, + FW_CFG_ID, + FW_CFG_UUID, + FW_CFG_RAM_SIZE, + FW_CFG_NOGRAPHIC, + FW_CFG_NB_CPUS, + FW_CFG_MACHINE_ID, + FW_CFG_KERNEL_ADDR, + FW_CFG_KERNEL_SIZE, + FW_CFG_KERNEL_CMDLINE, + FW_CFG_INITRD_ADDR, + FW_CFG_INITRD_SIZE, + FW_CFG_BOOT_DEVICE, + FW_CFG_NUMA, + FW_CFG_BOOT_MENU, + FW_CFG_MAX_CPUS, + FW_CFG_KERNEL_ENTRY, + FW_CFG_KERNEL_DATA, + FW_CFG_INITRD_DATA, + FW_CFG_CMDLINE_ADDR, + FW_CFG_CMDLINE_SIZE, + FW_CFG_SETUP_ADDR, + FW_CFG_SETUP_SIZE, + FW_CFG_FILE_DIR, +} + +const FW_CFG_FILE_FIRST: u16 = 0x20; +const FW_CFG_FILE_SLOTS_MIN: u16 = 0x10; +const FW_CFG_WRITE_CHANNEL: u16 = 0x4000; +const FW_CFG_ARCH_LOCAL: u16 = 0x8000; +const FW_CFG_ENTRY_MASK: u16 = !(FW_CFG_WRITE_CHANNEL | FW_CFG_ARCH_LOCAL); + +const FW_CFG_INVALID: u16 = 0xffff; +const FW_CFG_CTL_SIZE: u32 = 0x02; +const FW_CFG_MAX_FILE_PATH: u32 = 56; +const FW_CFG_SIG_SIZE: u32 = 4; + +type FWCfgCallbackType = Box<dyn FWCfgCallback + Send + Sync>; +type FWCfgWriteCallbackType = Box<dyn FWCfgWriteCallback + Send + Sync>; + +pub struct FWCfgEntry { + data: Vec<u8>, + pub select_cb: Option<FWCfgCallbackType>, + write_cb: Option<FWCfgWriteCallbackType>, + allow_write: bool, +} + +impl FWCfgEntry { + pub fn new( + data: Vec<u8>, + select_cb: Option<FWCfgCallbackType>, + write_cb: Option<FWCfgWriteCallbackType>, + allow_write: bool, + ) -> Self { + FWCfgEntry { + data, + select_cb, + write_cb, + allow_write, + } + } +} + +pub trait FWCfgCallback { + fn select_callback(&mut self); +} + +pub trait FWCfgWriteCallback { + fn write_callback(&mut self); +} + +pub struct FWCfgFile { + size: u32, + select: u16, + reserved: u16, + name: String, +} + +impl FWCfgFile { + pub fn new(size: u32, select: u16, reserved: u16, name: String) -> Self { + FWCfgFile { + size, + select, + reserved, + name, + } + } +} + +impl PartialEq for FWCfgFile { + fn eq(&self, other: &Self) -> bool { + self.name.to_string() == other.name.to_string() + } +} + +impl Eq for FWCfgFile {} + +impl Ord for FWCfgFile { + fn cmp(&self, other: &Self) -> Ordering { + self.name.cmp(&other.name) + } +} + +impl PartialOrd for FWCfgFile { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +const FW_CFG_VMCOREINFO_FILENAME: &str = "etc/vmcoreinfo"; +const FW_CFG_VMCOREINFO_FORMAT_NONE: u8 = 0x0; +const FW_CFG_VMCOREINFO_FORMAT_ELF: u8 = 0x1; + +pub struct FWCfgVmcoreInfo { + host_format: u16, + guest_format: u16, + size: u32, + paddr: u64, +} + +/// FW_CFG_DMA_CONTROL bits +const FW_CFG_DMA_CTL_ERROR: u32 = 0x01; +const FW_CFG_DMA_CTL_READ: u32 = 0x02; +const FW_CFG_DMA_CTL_SKIP: u32 = 0x04; +const FW_CFG_DMA_CTL_SELECT: u32 = 0x08; +const FW_CFG_DMA_CTL_WRITE: u32 = 0x10; + +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct FWCfgDmaAccess { + control: u32, + length: u32, + address: u64, +} + +impl ByteCode for FWCfgDmaAccess {} + +impl FWCfgDmaAccess { + pub fn new(control: u32, length: u32, address: u64) -> Self { + FWCfgDmaAccess { + control, + length, + address, + } + } +} + +pub fn dma_memory_set( + addr_space: &Arc<AddressSpace>, + addr: GuestAddress, + char: u8, + len: u64, +) -> Result<()> { + const FILLBUF_SIZE: usize = 512; + let fillbuf: &[u8; FILLBUF_SIZE] = &[char; FILLBUF_SIZE]; + + addr_space.write(&mut fillbuf.as_ref(), addr, len)?; + Ok(()) +} + +pub fn dma_memory_write( + addr_space: &Arc<AddressSpace>, + addr: GuestAddress, + mut buf: &[u8], + len: u64, +) -> Result<()> { + addr_space.write(&mut buf, addr, len)?; + Ok(()) +} + +pub fn dma_memory_read( + addr_space: &Arc<AddressSpace>, + addr: GuestAddress, + mut buf: &mut [u8], + len: u64, +) -> Result<()> { + addr_space.read(&mut buf, addr, len)?; + Ok(()) +} + +pub fn dma_result_write( + addr_space: &Arc<AddressSpace>, + addr: GuestAddress, + value: u32, +) -> Result<()> { + let mut dma_buf: [u8; 4] = [0; 4]; + BigEndian::write_u32(&mut dma_buf, value); + dma_memory_write( + addr_space, + addr.unchecked_add(offset_of!(FWCfgDmaAccess, control) as u64), + &dma_buf, + dma_buf.len() as u64, + )?; + Ok(()) +} + +fn extract64(value: u64, start: i32, length: i32) -> u64 { + assert!(start >= 0 && length > 0 && length < 64 - start); + + (value >> start as u64) & (!(0 as u64) >> (64 - length) as u64) +} + +fn get_key_name(key: usize) -> &'static str { + static fw_cfg_keys: [&str; 25] = [ + "signature", + "id", + "uuid", + "ram_size", + "nographic", + "nb_cpus", + "machine_id", + "kernel_addr", + "kernel_size", + "kernel_cmdline", + "initrd_size", + "boot_device", + "numa", + "boot_menu", + "max_cpus", + "kernel_entry", + "kernel_data", + "initrd_data", + "cmdline_addr", + "cmdline_size", + "cmdline_data", + "setup_addr", + "setup_size", + "setup_data", + "file_dir", + ]; + return fw_cfg_keys[key]; +} + +pub struct FWCfgCommon { + file_slots: u16, + local_entries: Vec<FWCfgEntry>, + entries: Vec<FWCfgEntry>, + files: Vec<FWCfgFile>, + cur_entry: u16, + cur_offset: u32, + + dma_enabled: bool, + dma_addr: GuestAddress, + + /// System address space. + mem_space: Arc<AddressSpace>, +} + +impl FWCfgCommon { + pub fn new(sys_mem: Arc<AddressSpace>) -> Self { + FWCfgCommon { + file_slots: FW_CFG_FILE_SLOTS_DFLT as u16, + local_entries: Vec::new(), + entries: Vec::new(), + files: Vec::new(), + cur_entry: 0, + cur_offset: 0, + dma_enabled: true, + dma_addr: GuestAddress(0), + mem_space: sys_mem, + } + } + + fn fw_cfg_max_entry(&self) -> u16 { + FW_CFG_FILE_FIRST + self.file_slots + } + + /// QEMU v2.4+ does not support write anymore + fn fw_cfg_write(&self, _value: u8) {} + + pub fn fw_cfg_get_entry(&mut self) -> Option<&mut FWCfgEntry> { + let is_local = (self.cur_entry & FW_CFG_ARCH_LOCAL as u16) > 0; + + let mut entry = None; + if self.cur_entry != FW_CFG_INVALID { + if is_local { + entry = self + .local_entries + .get_mut((self.cur_entry & FW_CFG_ENTRY_MASK) as usize) + } else { + entry = self + .entries + .get_mut((self.cur_entry & FW_CFG_ENTRY_MASK) as usize) + } + } + entry + } + + pub fn fw_cfg_select(&mut self, key: u16) -> i32 { + let mut ret = 0; + + self.cur_offset = 0; + if (key & FW_CFG_ENTRY_MASK) >= self.fw_cfg_max_entry() { + self.cur_entry = FW_CFG_INVALID; + ret = 0; + } else { + self.cur_entry = key; + ret = 1; + + // TODO Invoke callback here + let entry = self.fw_cfg_get_entry().unwrap(); + entry.select_cb.as_mut().unwrap().select_callback(); + } + + debug!("fw_cfg_select key {} ret {}", key, ret); + ret + } + + fn fw_cfg_dma_transfer(&mut self) { + let dma_addr = self.dma_addr; + let mem_space = self.mem_space.clone(); + let mut cur_offset = self.cur_offset; + let cur_entry = self.cur_entry; + + // Reset dma_addr address before next dma access + self.dma_addr = GuestAddress(0); + + let mut default = FWCfgDmaAccess::default(); + let mut dma_buf = default.as_mut_bytes(); + + // Read DMA request from guest + let size = std::mem::size_of::<FWCfgDmaAccess>() as u64; + if let Err(_) = dma_memory_read(&self.mem_space, dma_addr, dma_buf, size) { + dma_result_write(&self.mem_space, dma_addr, FW_CFG_DMA_CTL_ERROR); + } + + // Build FWCfgDmaAccess object here + let dma = FWCfgDmaAccess::from_mut_bytes(&mut dma_buf).unwrap(); + + if dma.control & FW_CFG_DMA_CTL_SELECT > 0 { + self.fw_cfg_select((dma.control >> 16) as u16); + } + + // Note a mutable borrow here + let entry = self.fw_cfg_get_entry().unwrap(); + let mut read = false; + let mut write = false; + + if dma.control & FW_CFG_DMA_CTL_READ != 0 { + read = true; + write = false; + } else if dma.control & FW_CFG_DMA_CTL_WRITE != 0 { + read = false; + write = true; + } else if dma.control & FW_CFG_DMA_CTL_SKIP != 0 { + dma.length = 0; + } + + dma.control = 0; + let mut len: u32 = 0; + + while dma.length > 0 && (dma.control & FW_CFG_DMA_CTL_ERROR) != 0 { + if cur_entry == FW_CFG_INVALID + || !entry.data.is_empty() + || cur_offset >= entry.data.len() as u32 + { + len = dma.length; + } + + if read { + if let Err(_) = dma_memory_set(&mem_space, GuestAddress(dma.address), 0, len as u64) + { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } + + if write { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } + } else { + if dma.length <= entry.data.len() as u32 - cur_offset { + len = dma.length; + } else { + len = entry.data.len() as u32 - cur_offset; + } + + if read { + if let Err(_) = dma_memory_write( + &mem_space, + GuestAddress(dma.address), + &entry.data[cur_offset as usize..], + len as u64, + ) { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } + } + + if write { + let mut dma_read_error = false; + let data = &mut entry.data[cur_offset as usize..]; + if let Err(_) = + dma_memory_read(&mem_space, GuestAddress(dma.address), data, len as u64) + { + dma_read_error = true; + } + + if dma_read_error || !entry.allow_write || len != dma.length { + dma.control |= FW_CFG_DMA_CTL_ERROR; + } else if true { + // TODO invoke write callback + } + } + cur_offset += len; + } + + dma.length += len; + dma.address += len as u64 + } + dma_result_write(&self.mem_space, dma_addr, dma.control); + debug!("fw_cfg dma tranfer"); + } + + pub fn fw_cfg_add_entry( + &mut self, + key: u16, + select_cb: Option<FWCfgCallbackType>, + write_cb: Option<FWCfgWriteCallbackType>, + data: Vec<u8>, + allow_write: bool, + ) { + let is_local = key & FW_CFG_ARCH_LOCAL > 0; + + assert!(key < self.fw_cfg_max_entry() && data.len() < std::u32::MAX as usize); + + let mut entry; + if is_local { + entry = &mut self.local_entries[key as usize]; + } else { + entry = &mut self.entries[key as usize]; + } + + entry.data = data; + entry.select_cb = select_cb; + entry.write_cb = write_cb; + entry.allow_write = allow_write; + } + + pub fn fw_cfg_update_entry_data(&mut self, key: u16, data: Vec<u8>) { + let is_local = key & FW_CFG_ARCH_LOCAL > 0; + + assert!(key < self.fw_cfg_max_entry() && data.len() < std::u32::MAX as usize); + + let mut entry; + if is_local { + entry = &mut self.local_entries[key as usize]; + } else { + entry = &mut self.entries[key as usize]; + } + + entry.data = data; + entry.allow_write = false; + } + + pub fn fw_cfg_add_entry_data(&mut self, key: u16, data: Vec<u8>) { + debug!("fw_cfg_add_data key {} ", get_key_name(key as usize)); + + self.fw_cfg_add_entry(key, None, None, data, true); + } + + pub fn fw_cfg_entry_add_string(&mut self, key: u16, value: &str) { + self.fw_cfg_update_entry_data(key, value.as_bytes().to_vec()); + } + + pub fn fw_cfg_add_file_callback(&mut self, + filename: &str, + data: Vec<u8>, + select_cb: Option<FWCfgCallbackType>, + write_cb: Option<FWCfgWriteCallbackType>, + allow_write: bool) -> Result<()> { + let len = self.files.len(); + assert!(len < self.file_slots as usize); + let mut index = len; + loop { + if filename.to_string() < self.files[index - 1].name.to_string() { + index -= 1; + } else { + break; + } + } + + let mut i = 0; + // check duplicate file + for f in self.files.iter() { + if f.name == filename && i != index { + bail!("duplicate fw_cfg file name {}", filename); + } + i += 1; + } + + let entry = FWCfgEntry::new(data, select_cb, write_cb, allow_write); + self.local_entries.insert(index + FW_CFG_FILE_FIRST as usize, entry); + let file = FWCfgFile::new(BigEndian::read_u32((len as u32).as_bytes()), + BigEndian::read_u16(((index + FW_CFG_FILE_FIRST as usize) as u16).as_bytes()), + 0, filename.to_string()); + self.files.push(file); + self.files.sort(); + Ok(()) + } + + pub fn fw_cfg_add_file(&mut self, filename: &str, data: Vec<u8>) { + self.fw_cfg_add_file_callback(filename, data, None, None, false); + } + + pub fn fw_cfg_dma_mem_write(&mut self, addr: GuestAddress, value: u64, size: u32) { + if size == 4 { + if addr == GuestAddress(0) { + self.dma_addr = GuestAddress(value << 32); + } else if addr == GuestAddress(4) { + self.dma_addr = GuestAddress(self.dma_addr.raw_value() | size as u64); + self.fw_cfg_dma_transfer(); + } + } else if size == 8 && addr == GuestAddress(0) { + self.dma_addr = GuestAddress(value); + self.fw_cfg_dma_transfer(); + } + } + + pub fn fw_cfg_dma_mem_read(&self, addr: GuestAddress, size: u32) -> u64 { + extract64( + FW_CFG_DMA_SIGNATURE as u64, + ((8 - addr.raw_value() - size as u64) * 8) as i32, + (size * 8) as i32, + ) + } + + pub fn fw_cfg_data_read(&mut self, addr: GuestAddress, mut size: u32) -> u64 { + let mut value: u64 = 0; + let cur_entry = self.cur_entry; + let cur_offset = self.cur_offset; + + let entry = self.fw_cfg_get_entry().unwrap(); + let SIZE_U64 = std::mem::size_of::<u64>() as u32; + assert!(size > 0 && size < SIZE_U64); + if cur_entry != FW_CFG_INVALID + && !entry.data.is_empty() + && cur_offset < entry.data.len() as u32 + { + let mut offset = cur_offset; + loop { + value = (value << 8) | entry.data[offset as usize] as u64; + offset += 1; + size -= 1; + + if size < 0 || offset >= entry.data.len() as u32 { + break; + } + } + value = 8 * size as u64; + } + + debug!( + "fw_cfg_data_read 0x{:x} size {} value 0x{:x}", + addr.raw_value(), + size, + value + ); + value + } + + pub fn fw_cfg_common_realize(&mut self) { + self.local_entries = Vec::with_capacity(self.fw_cfg_max_entry() as usize); + self.entries = Vec::with_capacity(self.fw_cfg_max_entry() as usize); + + self.fw_cfg_add_entry_data( + FWCfgEntryType::FW_CFG_SIGNATURE as u16, + "QEMU".as_bytes().to_vec(), + ); + + // TODO add real uuid here + self.fw_cfg_add_entry_data( + FWCfgEntryType::FW_CFG_UUID as u16, + "49c9bedd6459b52a".as_bytes().to_vec(), + ); + + self.fw_cfg_add_entry_data( + FWCfgEntryType::FW_CFG_NOGRAPHIC as u16, + (0 as u16).as_bytes().to_vec(), + ); + + self.fw_cfg_add_entry_data( + FWCfgEntryType::FW_CFG_BOOT_MENU as u16, + (0 as u16).as_bytes().to_vec(), + ); + + let version = FW_CFG_VERSION & FW_CFG_VERSION_DMA; + self.fw_cfg_add_entry_data( + FWCfgEntryType::FW_CFG_ID as u16, + version.as_bytes().to_vec(), + ); + } +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct FWCfgConfig {} + +/// Emulate FWCfg MMIO Device +pub struct FWCfgMem { + fwcfg: FWCfgCommon, + data_width: u32, +} + + +impl FWCfgMem { + pub fn new(sys_mem: Arc<AddressSpace>) -> Self { + FWCfgMem { + fwcfg: FWCfgCommon::new(sys_mem), + data_width: 8, + } + } + + pub fn fw_cfg_data_mem_write(&self, _addr: GuestAddress, value: u64, size: u32) { + let mut i = size; + loop { + i -= 1; + self.fwcfg.fw_cfg_write((value >> (8 * i) as u64) as u8); + if i == 0 { + break; + } + } + } + + pub fn fw_cfg_ctl_mem_read(&self, _addr: GuestAddress, _size: u32) -> u64 { + 0 + } + + pub fn fw_cfg_ctl_mem_write(&mut self, _addr: GuestAddress, value: u64, _size: u32) { + self.fwcfg.fw_cfg_select(value as u16); + } +} + +impl DeviceOps for FWCfgMem { + fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { + let mut value = 0; + match offset { + 0..=7 => { + value = self.fwcfg.fw_cfg_data_read(base, data.len() as u32); + } + 8..=15 => { + value = self.fw_cfg_ctl_mem_read(base, data.len() as u32); + } + _ => { + self.fwcfg.fw_cfg_dma_mem_read(base, data.len() as u32); + } + } + + BigEndian::write_u64(data, value); + true + } + + fn write(&mut self, data: &[u8], base: GuestAddress, offset: u64) -> bool { + let mut value = 0; + let size = data.len() as u32; + value = BigEndian::read_u64(data); + match offset { + 0..=7 => { + self.fw_cfg_data_mem_write(base, value, size); + } + 8..=15 => { + self.fw_cfg_ctl_mem_write(base, value, size); + } + _ => { + self.fwcfg.fw_cfg_dma_mem_write(base, value, size); + } + } + true + } +} + +impl MmioDeviceOps for FWCfgMem { + /// Realize FWCfgMem device when VM started. + fn realize(&mut self, _vm_fd: &VmFd, _resource: DeviceResource) -> Result<()> { + self.fwcfg.fw_cfg_common_realize(); + Ok(()) + } + + /// Get device type. + fn get_type(&self) -> DeviceType { + DeviceType::FWCFG + } +} + +/// Emulate FWCfg IO device +pub struct FWCfgIO { + fwcfg: FWCfgCommon, +} + +impl FWCfgIO { + pub fn new(sys_mem: Arc<AddressSpace>) -> Self { + FWCfgIO { + fwcfg: FWCfgCommon { + file_slots: FW_CFG_FILE_SLOTS_DFLT as u16, + local_entries: Vec::new(), + entries: Vec::new(), + files: Vec::new(), + cur_entry: 0, + cur_offset: 0, + dma_enabled: true, + dma_addr: GuestAddress(0), + mem_space: sys_mem, + }, + } + } + + pub fn fw_cfg_comb_write(&mut self, _addr: GuestAddress, value: u64, size: u32) { + match size { + 1 => { + self.fwcfg.fw_cfg_write(value as u8); + } + 2 => { + self.fwcfg.fw_cfg_select(value as u16); + } + _ => return, + } + } +} + +impl DeviceOps for FWCfgIO { + fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { + let mut value = 0; + match offset { + 1..=2 => { + value = self.fwcfg.fw_cfg_data_read(base, data.len() as u32); + } + 15..=23 => { + value = self.fwcfg.fw_cfg_dma_mem_read(base, data.len() as u32); + } + _ => error!("invalid read of set in FWCfgIO"), + } + LittleEndian::write_u64(data, value); + true + } + + fn write(&mut self, data: &[u8], base: GuestAddress, offset: u64) -> bool { + let mut value = 0; + let size = data.len() as u32; + value = LittleEndian::read_u64(data); + match offset { + 1..=2 => { + self.fw_cfg_comb_write(base, value, size); + } + 15..=23 => { + self.fwcfg.fw_cfg_dma_mem_write(base, value, size); + } + _ => {} + } + true + } +} diff --git a/device_model/src/legacy/mod.rs b/device_model/src/legacy/mod.rs index f840656..bd8970e 100644 --- a/device_model/src/legacy/mod.rs +++ b/device_model/src/legacy/mod.rs @@ -25,9 +25,20 @@ //! - `x86_64` //! - `aarch64` mod serial; + pub use self::serial::Serial;
#[cfg(target_arch = "aarch64")] mod pl031; + #[cfg(target_arch = "aarch64")] pub use self::pl031::PL031; + +#[cfg(target_arch = "aarch64")] +mod fw_cfg; + +#[cfg(target_arch = "aarch64")] +pub use self::fw_cfg::FWCfgMem; +#[cfg(target_arch = "aarch64")] +pub use self::fw_cfg::FWCfgConfig; + diff --git a/device_model/src/micro_vm/mod.rs b/device_model/src/micro_vm/mod.rs index 94a3e3f..6751f8d 100644 --- a/device_model/src/micro_vm/mod.rs +++ b/device_model/src/micro_vm/mod.rs @@ -983,6 +983,26 @@ impl MainLoopManager for LightMachine { } }
+/// Function that helps to generate fw-cfg node in device-tree. +/// +/// # Arguments +/// +/// * `dev_info` - Device resource info of fw-cfg device. +/// * `fdt` - Flatted device-tree blob where fw-cfg node will be filled into. +#[cfg(target_arch = "aarch64")] +fn generate_fwcfg_device_node( + dev_info: &DeviceResource, + fdt: &mut Vec<u8>, +) -> util::errors::Result<()> { + let node = format!("/fw-cfg@{:x}", dev_info.addr); + device_tree::add_sub_node(fdt, &node)?; + device_tree::set_property_string(fdt, &node, "compatible", "qemu,fw-cfg-mmio")?; + device_tree::set_property_array_u64(fdt, &node, "reg", &[dev_info.addr, dev_info.size])?; + + Ok(()) +} + + /// Function that helps to generate serial node in device-tree. /// /// # Arguments @@ -1216,6 +1236,7 @@ impl CompileFDTHelper for LightMachine { device_tree::set_property_string(fdt, node, "compatible", "arm,psci-0.2")?; device_tree::set_property_string(fdt, node, "method", "hvc")?;
+ for dev_info in self.bus.get_devices_info().iter().rev() { match dev_info.dev_type { DeviceType::SERIAL => { @@ -1224,6 +1245,9 @@ impl CompileFDTHelper for LightMachine { DeviceType::RTC => { generate_rtc_device_node(dev_info, fdt)?; } + DeviceType::FWCFG => { + generate_fwcfg_device_node(dev_info, fdt)?; + } _ => { generate_virtio_devices_node(dev_info, fdt)?; } diff --git a/device_model/src/mmio/bus.rs b/device_model/src/mmio/bus.rs index 79132b9..3fe8c7c 100644 --- a/device_model/src/mmio/bus.rs +++ b/device_model/src/mmio/bus.rs @@ -184,6 +184,15 @@ impl Bus { } } } + DeviceType::FWCFG => { + #[cfg(target_arch = "aarch64")] + DeviceResource { + addr: MEM_LAYOUT[LayoutEntryType::FwCfg as usize].0, + size: MEM_LAYOUT[LayoutEntryType::FwCfg as usize].1, + irq: 0, + dev_type: device_type, + } + } _ => DeviceResource { addr: MMIO_BASE + index as u64 * MMIO_LEN, size: MMIO_LEN, diff --git a/device_model/src/mmio/mod.rs b/device_model/src/mmio/mod.rs index d2f6295..4ac532f 100644 --- a/device_model/src/mmio/mod.rs +++ b/device_model/src/mmio/mod.rs @@ -65,6 +65,7 @@ pub enum DeviceType { SERIAL, #[cfg(target_arch = "aarch64")] RTC, + FWCFG, OTHER, }
Signed-off-by: Ying Fang fangying1@huawei.com --- boot_loader/src/aarch64/mod.rs | 5 ++++- device_model/src/micro_vm/mod.rs | 21 ++++++++++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index 190abe2..b9fa3d3 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -73,6 +73,7 @@ pub fn linux_bootloader( config: &AArch64BootLoaderConfig, sys_mem: &Arc<AddressSpace>, ) -> Result<AArch64BootLoader> { + let dtb_addr = if sys_mem.memory_end_address().raw_value() > u64::from(device_tree::FDT_MAX_SIZE) { if let Some(addr) = sys_mem @@ -96,6 +97,8 @@ pub fn linux_bootloader( return Err(ErrorKind::DTBOverflow(sys_mem.memory_end_address().raw_value()).into()); }
+ let dbt_addr: u64 = 1 << 30; + let mut initrd_addr = 0; if config.initrd_size > 0 { initrd_addr = if let Some(addr) = dtb_addr.checked_sub(u64::from(config.initrd_size)) { @@ -112,7 +115,7 @@ pub fn linux_bootloader( }
Ok(AArch64BootLoader { - kernel_start: config.mem_start + AARCH64_KERNEL_OFFSET, + kernel_start: 0x00, vmlinux_start: config.mem_start + AARCH64_KERNEL_OFFSET, initrd_start: initrd_addr, dtb_start: dtb_addr, diff --git a/device_model/src/micro_vm/mod.rs b/device_model/src/micro_vm/mod.rs index 6751f8d..e99d8fb 100644 --- a/device_model/src/micro_vm/mod.rs +++ b/device_model/src/micro_vm/mod.rs @@ -51,7 +51,7 @@ use vmm_sys_util::terminal::Terminal;
#[cfg(target_arch = "x86_64")] use address_space::KvmIoListener; -use address_space::{create_host_mmaps, AddressSpace, GuestAddress, KvmMemoryListener, Region}; +use address_space::{create_host_mmaps, AddressSpace, GuestAddress, KvmMemoryListener, Region, FileBackend, HostMemMapping}; use boot_loader::{load_kernel, BootLoaderConfig}; use machine_manager::config::{ BootSource, ConsoleConfig, DriveConfig, NetworkInterfaceConfig, SerialConfig, VmConfig, @@ -61,6 +61,7 @@ use machine_manager::machine::{ DeviceInterface, KvmVmState, MachineAddressInterface, MachineExternalInterface, MachineInterface, MachineLifecycle, }; + #[cfg(feature = "qmp")] use machine_manager::{qmp, qmp::qmp_schema as schema, qmp::QmpChannel}; #[cfg(target_arch = "aarch64")] @@ -88,6 +89,9 @@ use crate::{
use crate::{LayoutEntryType, MEM_LAYOUT};
+#[cfg(target_arch = "aarch64")] +pub const UEFI_ROM_BASE: u64 = 0x0; + /// Every type of devices depends on this configure-related trait to perform /// initialization. pub trait ConfigDevBuilder { @@ -223,6 +227,21 @@ impl LightMachine { )?; }
+ // Init uefi rom + let path = Some("/home/QEMU_EFI.raw"); + let fb = FileBackend::new(path.unwrap(), 0x04000000)?; + let mapping = Arc::new(HostMemMapping::new( + GuestAddress(UEFI_ROM_BASE), + 0x04000000, + fb.file.as_raw_fd(), + 0 as u64, + false, + false)?); + sys_mem.root().add_subregion( + Region::init_ram_region(mapping.clone()), + mapping.start_address().raw_value(), + )?; + #[cfg(target_arch = "x86_64")] Self::arch_init(&vm_fd)?;
The aarch64 platform uses a virt machine type similar with Qemu does. UEFI requires this pl011 device, so let's add it into legacy.
Signed-off-by: Ying Fang fangying1@huawei.com --- boot_loader/src/aarch64/mod.rs | 7 +- device_model/src/legacy/mod.rs | 3 + device_model/src/legacy/pl011.rs | 464 +++++++++++++++++++++++++++++++ device_model/src/micro_vm/mod.rs | 30 +- device_model/src/mmio/mod.rs | 2 +- 5 files changed, 495 insertions(+), 11 deletions(-) create mode 100644 device_model/src/legacy/pl011.rs
diff --git a/boot_loader/src/aarch64/mod.rs b/boot_loader/src/aarch64/mod.rs index b9fa3d3..1dc3c95 100644 --- a/boot_loader/src/aarch64/mod.rs +++ b/boot_loader/src/aarch64/mod.rs @@ -74,6 +74,7 @@ pub fn linux_bootloader( sys_mem: &Arc<AddressSpace>, ) -> Result<AArch64BootLoader> {
+ /* let dtb_addr = if sys_mem.memory_end_address().raw_value() > u64::from(device_tree::FDT_MAX_SIZE) { if let Some(addr) = sys_mem @@ -95,9 +96,9 @@ pub fn linux_bootloader(
if dtb_addr == 0 { return Err(ErrorKind::DTBOverflow(sys_mem.memory_end_address().raw_value()).into()); - } + }*/
- let dbt_addr: u64 = 1 << 30; + let dtb_addr: u64 = 1 << 30;
let mut initrd_addr = 0; if config.initrd_size > 0 { @@ -115,7 +116,7 @@ pub fn linux_bootloader( }
Ok(AArch64BootLoader { - kernel_start: 0x00, + kernel_start: 0x0000, vmlinux_start: config.mem_start + AARCH64_KERNEL_OFFSET, initrd_start: initrd_addr, dtb_start: dtb_addr, diff --git a/device_model/src/legacy/mod.rs b/device_model/src/legacy/mod.rs index bd8970e..0df5ae3 100644 --- a/device_model/src/legacy/mod.rs +++ b/device_model/src/legacy/mod.rs @@ -42,3 +42,6 @@ pub use self::fw_cfg::FWCfgMem; #[cfg(target_arch = "aarch64")] pub use self::fw_cfg::FWCfgConfig;
+mod pl011; +#[cfg(target_arch = "aarch64")] +pub use self::pl011::PL011; diff --git a/device_model/src/legacy/pl011.rs b/device_model/src/legacy/pl011.rs new file mode 100644 index 0000000..9f435c7 --- /dev/null +++ b/device_model/src/legacy/pl011.rs @@ -0,0 +1,464 @@ +// 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::io; +use std::os::unix::io::RawFd; +use std::sync::{Arc, Mutex}; + +use kvm_ioctls::VmFd; +use address_space::GuestAddress; +use byteorder::{ByteOrder, LittleEndian}; + +use util::epoll_context::{EventNotifier, EventNotifierHelper, NotifierOperation}; +use vmm_sys_util::{epoll::EventSet, eventfd::EventFd, terminal::Terminal}; + +use super::super::mmio::{DeviceResource, DeviceType, MmioDeviceOps}; +use super::super::DeviceOps; +use super::super::mmio::errors::{Result, ResultExt}; + + +const PL011_INT_TX: u8 = 0x20; +const PL011_INT_RX: u8 = 0x10; + +const PL011_FLAG_RXFF: u8 = 0x40; +const PL011_FLAG_RXFE: u8 = 0x10; + + +/// Interrupt status bits in UARTRIS, UARTMIS and UARTIMSC +const INT_OE: u32 = 1 << 10; +const INT_BE: u32 = 1 << 9; +const INT_PE: u32 = 1 << 8; +const INT_FE: u32 = 1 << 7; +const INT_RT: u32 = 1 << 6; +const INT_TX: u32 = 1 << 5; +const INT_RX: u32 = 1 << 4; +const INT_DSR: u32 = 1 << 3; +const INT_DCD: u32 = 1 << 2; +const INT_CTS: u32 = 1 << 1; +const INT_RI: u32 = 1 << 0; +const INT_E: u32 = INT_OE | INT_BE | INT_PE | INT_FE; +const INT_MS: u32 = INT_RI | INT_DSR | INT_DCD | INT_CTS; + +pub struct PL011 { + /// Read FIFO. + rfifo: Vec<u32>, + /// 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, + /// Interrut FIFO Level Select Register. + ifl: u32, + /// Identifier Register. + id: Vec<u8>, + /// FIFO Status. + read_pos: i32, + read_count: i32, + read_trigger: i32, + /// Raw Interrupt Status Register. + int_level: u32, + /// Interrupt Mask Set/Clear Register. + int_enabled: u32, + /// Interrupt event file descriptor. + interrupt_evt: Option<EventFd>, + /// Operation methods. + output: Option<Box<dyn io::Write + Send + Sync>>, +} + +impl PL011 { + /// Create a new `PL011` instance with default parameters. + pub fn new() -> Self { + PL011 { + rfifo: vec![0; 16], + flags: 0x90, + lcr: 0, + rsr: 0, + cr: 0x300, + dmacr: 0, + ilpr: 0, + ibrd: 0, + fbrd: 0, + ifl: 0x12, + id: vec![0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1], + read_pos: 0, + read_count: 0, + read_trigger: 1, + int_level: 0, + int_enabled: 0, + interrupt_evt: None, + output: None, + } + } + + /// Set EventFd for PL011 + /// + /// # Errors + /// + /// Return Error if + /// * fail to write EventFd. + /// * fail to get an interrupt event fd. + fn interrupt(&self) -> Result<()> { + info!("interrupt once"); + match &self.interrupt_evt { + Some(evt) => evt.write(1).chain_err(|| "Failed to write fd")?, + None => bail!("Failed to get an interrupt event fd"), + }; + + Ok(()) + } + + /// Update interrupt + /// Call this function to send irq to guest. + fn update(&self) -> Result<()> { + let irq_mask = [INT_E | INT_MS | INT_RT | INT_TX | INT_RX, + INT_RX, INT_TX, INT_RT, INT_MS, INT_E]; + let flags = self.int_level & self.int_enabled; + debug!("update int_level 0x{:x} int_enabled 0x{:x}", self.int_level, self.int_enabled); + debug!("pl011_irq_state irq state {}", flags != 0); + for i in 0..irq_mask.len() { + if flags & irq_mask[i as usize] != 0 { + self.interrupt()?; + } + } + Ok(()) + } + /// Append `data` to receiver buffer register, and update interrupt. + /// + /// # Arguments + /// + /// * `value` - A u8-type array. + pub fn receive(&mut self, data: &[u8]) -> Result<()> { + debug!("pl011 receive read_pos 0x{:x} read_count 0x{:x}", self.read_pos, self.read_count); + let value = data[0] as u32; + let mut slot = self.read_pos + self.read_count; + if slot >= 16 { + slot -= 16; + } + self.rfifo[slot as usize] = value; + self.read_count += 1; + self.flags &= !PL011_FLAG_RXFE as u32; + if ((self.lcr & 0x10) == 0) || (self.read_count == 16) { + self.flags |= PL011_FLAG_RXFF as u32; + } + debug!("slot is {} read_count {} read_trigger {}", slot, self.read_count, self.read_trigger); + if self.read_count == self.read_trigger { + self.int_level |= PL011_INT_RX as u32; + if self.update().is_err() { + error!("Failed to update interrupt."); + } + } + + Ok(()) + } +} + +impl DeviceOps for PL011 { + /// Read data from a certain register selected by `offset`. + /// + /// # Arguments + /// + /// * `offset` - Used to select a register. + /// + /// # Errors + /// + /// Return Error if fail to update interrupt. + fn read(&mut self, data: &mut [u8], _base: GuestAddress, offset: u64) -> bool { + let mut ret: u32 = 0; + + match offset >> 2 { + 0 => { + self.flags &= !(PL011_FLAG_RXFF as u32); + let c = self.rfifo[self.read_pos as usize]; + + if self.read_count > 0 { + self.read_count -= 1; + self.read_pos += 1; + if self.read_pos == 16 { + self.read_pos = 0; + } + } + if self.read_count == 0 { + self.flags |= PL011_FLAG_RXFE as u32; + } + if self.read_count == self.read_trigger - 1 { + self.int_level &= !(PL011_INT_RX as u32); + } + self.rsr = c >> 8; + if self.update().is_err() { + error!("Failed to update interrupt."); + } + ret = c; + } + 1 => { // UARTRSR + ret = self.rsr; + } + 6 => { // UARTFR + ret = self.flags; + } + 8 => { // UARTILPR + ret = self.ilpr; + } + 9 => { // UARTIBRD + ret = self.ibrd; + } + 10 => { // UARTFBRD + ret = self.fbrd; + } + 11 => { // UARTLCR_H + ret = self.lcr; + } + 12 => { // UARTCR + ret = self.cr; + } + 13 => { // UARTIFLS + ret = self.ifl; + } + 14 => { // UARTIMSC + ret = self.int_enabled; + } + 15 => { // UARTRIS + ret = self.int_level; + } + 16 => { // UARTMIS + ret = self.int_level & self.int_enabled; + } + 18 => { // UARTDMACR + ret = self.dmacr; + } + 0x3f8..=0x400 => { + ret = *self.id.get(((offset - 0xfe0) >> 2) as usize).unwrap() as u32; + } + _ => { + ret = 0; + debug!("read bad ofset 0x{:x}", offset) + } + } + debug!("pl011 read addr 0x{:x} value 0x{:x}", offset, ret); + use crate::util::byte_code::ByteCode; + data.copy_from_slice(&ret.as_bytes()[0..data.len()]); + + true + } + + /// Write one byte data to a certain register selected by `offset`. + /// + /// # Arguments + /// + /// * `offset` - Used to select a register. + /// * `data` - A u8-type data, which will be written to the register. + /// + /// # Errors + /// + /// Return Error if + /// * fail to get output file descriptor. + /// * fail to write pl011. + /// * fail to flush pl011. + fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { + let value = if data.len() == 1 { + data[0] as u16 + } else if data.len() == 2 { + LittleEndian::read_u16(data) + } else { 0 } as u32; + + debug!("pl011 write data.len={} data=0x{:x}", data.len(), data[0]); + debug!("pl011 write addr 0x{:x} value=0x{:x}", offset, value); + match offset >> 2 { + 0 => { + let ch = value as u8; + + let output = match &mut self.output { + Some(output_) => output_, + None => { + error!("Failed to get output fd."); + return true; + } + }; + output.write_all(&[ch]).unwrap(); + output.flush().unwrap(); + + self.int_level |= PL011_INT_TX as u32; + if self.update().is_err() { + error!("Failed to update interrupt."); + } + } + 1 => { // UARTRSR/UARTECR + self.rsr = 0; + } + 6 => { // UARTFR: write to flag register are ignored + } + 8 => { // UARTUARTILPR + self.ilpr = value; + } + 9 => { // UARTIBRD + self.ibrd = value; + } + 10 => { // UARTFBRD + self.fbrd = value; + } + 11 => { // UARTLCR_H + if (self.lcr ^ value) & 0x10 != 0 { + self.read_count = 0; + self.read_pos = 0; + } + self.lcr = value; + self.read_trigger = 1; + info!("write lcr 0x{:x}", self.lcr); + } + 12 => { // UARTCR + self.cr = value; + } + 13 => { // UARTIFS + self.ifl = value; + self.read_trigger = 1; + } + 14 => { // UARTIMSC + self.int_enabled = value; + if self.update().is_err() { + error!("Failed to update interrupt."); + } + } + 17 => { // UARTICR + self.int_level &= !value; + if self.update().is_err() { + error!("Failed to update interrupt."); + } + } + 18 => { // UARTDMACR + self.dmacr = value; + if value & 3 != 0 { + error!("pl011: DMA not implemented"); + } + } + _ => { + error!("pl011_write: Bad offset 0x{:x}", offset); + } + } + + true + } +} + +impl MmioDeviceOps for PL011 { + /// Realize a PL011 serial port for VM. + /// * Create a new output component. + /// * Register DeviceResource IRQ to VM. + /// * Set interrupt_evt component. + /// + /// # Arguments + /// + /// * `vm_fd` - File descriptor of VM. + /// * `resource` - Device resource. + /// + /// # Errors + /// + /// Return Error if + /// * fail to register. + /// * fail to create a new EventFd. + fn realize(&mut self, vm_fd: &VmFd, resource: DeviceResource) -> Result<()> { + self.output = Some(Box::new(std::io::stdout())); + + match EventFd::new(libc::EFD_NONBLOCK) { + Ok(evt) => { + vm_fd + .register_irqfd(&evt, resource.irq) + .chain_err(|| "Failed to register irqfd")?; + self.interrupt_evt = Some(evt); + + Ok(()) + } + Err(_) => Err("Failed to create new EventFd".into()), + } + } + + /// Get type of Device. + fn get_type(&self) -> DeviceType { + DeviceType::SERIAL + } +} + + +impl EventNotifierHelper for PL011 { + /// Add PL011 to `EventNotifier`. + /// + /// # Arguments + /// + /// * `pl011` - PL011 Serial instance. + fn internal_notifiers(pl011: Arc<Mutex<Self>>) -> Vec<EventNotifier> { + let mut notifiers = Vec::new(); + + let mut handlers = Vec::new(); + let handler: Box<dyn Fn(EventSet, RawFd) -> Option<Vec<EventNotifier>>> = + Box::new(move |_, _| { + let mut out = [0_u8; 64]; + if let Ok(count) = std::io::stdin().lock().read_raw(&mut out) { + info!("read {} bytes from stdin", count); + let _ = pl011.lock().unwrap().receive(&out[..count]); + info!("receive done here"); + } + None + }); + + handlers.push(Arc::new(Mutex::new(handler))); + + let notifier = EventNotifier::new( + NotifierOperation::AddShared, + libc::STDIN_FILENO, + None, + EventSet::IN, + handlers, + ); + + notifiers.push(notifier); + notifiers + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_methods_of_pl011() { + // test new method + let mut pl011 = PL011::new(); + assert_eq!(pl011.ifl, 0x12); + assert_eq!(pl011.cr, 1); + assert_eq!(pl011.flags, 0x90); + assert_eq!(pl011.read_trigger, 1); + + // test interrupt method + // for interrupt method to work, + // you need to set interrupt_evt at first + assert!(pl011.interrupt().is_err()); + + let evt = EventFd::new(libc::EFD_NONBLOCK).unwrap(); + pl011.interrupt_evt = Some(evt); + assert!(pl011.interrupt().is_ok()); + + // test receive method + let data = [0x01, 0x02]; + assert!(pl011.receive(&data).is_ok()); + assert!(pl011.read_count, 1); + assert!(pl011.flags, 0x90 & !PL011_FLAG_RXFE); + } +} diff --git a/device_model/src/micro_vm/mod.rs b/device_model/src/micro_vm/mod.rs index e99d8fb..4905513 100644 --- a/device_model/src/micro_vm/mod.rs +++ b/device_model/src/micro_vm/mod.rs @@ -81,8 +81,11 @@ use crate::legacy::PL031; #[cfg(target_arch = "aarch64")] use crate::mmio::DeviceResource; use crate::MainLoop; +#[cfg(target_arh = "x86_64")] +use crate::legacy::Serial; +#[cfg(target_arch = "aarch64")] +use crate::{legacy::PL011, legacy::FWCfgMem}; use crate::{ - legacy::Serial, mmio::{Bus, DeviceType, VirtioMmioDevice}, virtio::{vhost, Console}, }; @@ -154,7 +157,7 @@ impl ConfigDevBuilder for VsockConfig {
impl ConfigDevBuilder for SerialConfig { fn build_dev(&self, _sys_mem: Arc<AddressSpace>, bus: &mut Bus) -> Result<()> { - let serial = Arc::new(Mutex::new(Serial::new())); + let serial = Arc::new(Mutex::new(PL011::new())); bus.attach_device(serial.clone()) .chain_err(|| "build dev from config failed")?;
@@ -558,6 +561,11 @@ impl LightMachine { self.bus .attach_device(rtc) .chain_err(|| "add rtc to bus failed")?; + + let fwcfg = Arc::new(Mutex::new(FWCfgMem::new(self.sys_mem.clone()))); + self.bus + .attach_device(fwcfg) + .chain_err(|| "add fwcfg to bus failed")?; }
if let Some(serial) = vm_config.serial { @@ -1021,7 +1029,6 @@ fn generate_fwcfg_device_node( Ok(()) }
- /// Function that helps to generate serial node in device-tree. /// /// # Arguments @@ -1033,11 +1040,11 @@ fn generate_serial_device_node( dev_info: &DeviceResource, fdt: &mut Vec<u8>, ) -> util::errors::Result<()> { - let node = format!("/uart@{:x}", dev_info.addr); + let node = format!("/pl011@{:x}", dev_info.addr); device_tree::add_sub_node(fdt, &node)?; - device_tree::set_property_string(fdt, &node, "compatible", "ns16550a")?; - device_tree::set_property_string(fdt, &node, "clock-names", "apb_pclk")?; - device_tree::set_property_u32(fdt, &node, "clocks", device_tree::CLK_PHANDLE)?; + device_tree::set_property_string(fdt, &node, "compatible", "arm,pl011\0arm,primecell")?; + device_tree::set_property_string(fdt, &node, "clock-names", "uartclk\0apb_pclk")?; + device_tree::set_property_array_u32(fdt, &node, "clocks", &[device_tree::CLK_PHANDLE, device_tree::CLK_PHANDLE])?; device_tree::set_property_array_u64(fdt, &node, "reg", &[dev_info.addr, dev_info.size])?; device_tree::set_property_array_u32( fdt, @@ -1285,6 +1292,14 @@ impl CompileFDTHelper for LightMachine { let cmdline = &boot_source.kernel_cmdline.to_string(); device_tree::set_property_string(fdt, node, "bootargs", cmdline.as_str())?;
+ let mut prop_string = String::new(); + for dev_info in self.bus.get_devices_info().iter().rev() { + if dev_info.dev_type == DeviceType::SERIAL { + prop_string = format!("/pl011@{:x}", dev_info.addr); + } + } + device_tree::set_property_string(fdt, node, "stdout-path", &prop_string)?; + match &boot_source.initrd { Some(initrd) => { device_tree::set_property_u64( @@ -1323,6 +1338,7 @@ impl device_tree::CompileFDT for LightMachine { self.generate_chosen_node(fdt)?; self.irq_chip.generate_fdt_node(fdt)?;
+ util::device_tree::dump_dtb(fdt, "/tmp/dtb"); Ok(()) } } diff --git a/device_model/src/mmio/mod.rs b/device_model/src/mmio/mod.rs index 4ac532f..c29412b 100644 --- a/device_model/src/mmio/mod.rs +++ b/device_model/src/mmio/mod.rs @@ -154,7 +154,7 @@ impl MmioDevice { #[cfg(target_arch = "aarch64")] cmdline.push(Param { param_type: "earlycon".to_string(), - value: format!("uart,mmio,0x{:08x}", self.resource.addr), + value: format!("pl011,mmio,0x{:08x}", self.resource.addr), }); } else { #[cfg(target_arch = "x86_64")]
Signed-off-by: Ying Fang fangying1@huawei.com --- device_model/src/legacy/fw_cfg.rs | 238 +++++++++++++++++------------- device_model/src/legacy/mod.rs | 2 - device_model/src/micro_vm/mod.rs | 2 +- 3 files changed, 137 insertions(+), 105 deletions(-)
diff --git a/device_model/src/legacy/fw_cfg.rs b/device_model/src/legacy/fw_cfg.rs index 7422d4b..09396cc 100644 --- a/device_model/src/legacy/fw_cfg.rs +++ b/device_model/src/legacy/fw_cfg.rs @@ -10,18 +10,18 @@ // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details.
-use super::super::mmio::errors::{Result}; +use super::super::mmio::errors::Result; use super::super::mmio::{DeviceResource, DeviceType, MmioDeviceOps}; use super::super::DeviceOps; use address_space::{AddressSpace, GuestAddress}; -use byteorder::{ByteOrder, BigEndian, LittleEndian}; -use serde::{Deserialize, Serialize}; +use byteorder::{BigEndian, ByteOrder, LittleEndian}; use kvm_ioctls::VmFd; -use std::default::Default; use std::cmp::Ordering; -use std::sync::{Arc}; +use std::default::Default; +use std::sync::Arc; use util::byte_code::ByteCode;
+/// FWCfg ACPI Device ID const FW_CFG_ACPI_DEVICE_ID: &str = "QEMU0002"; /// QEMU CFG Signature const FW_CFG_DMA_SIGNATURE: u128 = 0x51454d5520434647; @@ -32,30 +32,30 @@ const FW_CFG_VERSION_DMA: u32 = 0x02; const FW_CFG_FILE_SLOTS_DFLT: u32 = 0x20;
enum FWCfgEntryType { - FW_CFG_SIGNATURE = 0x00, - FW_CFG_ID, - FW_CFG_UUID, - FW_CFG_RAM_SIZE, - FW_CFG_NOGRAPHIC, - FW_CFG_NB_CPUS, - FW_CFG_MACHINE_ID, - FW_CFG_KERNEL_ADDR, - FW_CFG_KERNEL_SIZE, - FW_CFG_KERNEL_CMDLINE, - FW_CFG_INITRD_ADDR, - FW_CFG_INITRD_SIZE, - FW_CFG_BOOT_DEVICE, - FW_CFG_NUMA, - FW_CFG_BOOT_MENU, - FW_CFG_MAX_CPUS, - FW_CFG_KERNEL_ENTRY, - FW_CFG_KERNEL_DATA, - FW_CFG_INITRD_DATA, - FW_CFG_CMDLINE_ADDR, - FW_CFG_CMDLINE_SIZE, - FW_CFG_SETUP_ADDR, - FW_CFG_SETUP_SIZE, - FW_CFG_FILE_DIR, + FWCfgSignature = 0x00, + FWCfgId, + FWCfgUUID, + FWCfgRamSize, + FWCfgNoGraphic, + FWCfgNbCpus, + FWCfgMachineId, + FWCfgKernelAddr, + FWCfgKernelSize, + FWCfgKernelCmdline, + FWCfgInitrdAddr, + FWCfgInitrdSize, + FWCfgBootDevice, + FWCfgNuma, + FWCfgBootMenu, + FWCfgMaxCpus, + FWCfgKernelEntry, + FWCfgKernelData, + FWCfgInitrdData, + FWCfgCmdlineAddr, + FWCfgCmdlineSize, + FWCfgSetupAddr, + FWCfgSetupSize, + FWCfgFileDir, }
const FW_CFG_FILE_FIRST: u16 = 0x20; @@ -72,9 +72,10 @@ const FW_CFG_SIG_SIZE: u32 = 4; type FWCfgCallbackType = Box<dyn FWCfgCallback + Send + Sync>; type FWCfgWriteCallbackType = Box<dyn FWCfgWriteCallback + Send + Sync>;
+#[derive(Default)] pub struct FWCfgEntry { data: Vec<u8>, - pub select_cb: Option<FWCfgCallbackType>, + select_cb: Option<FWCfgCallbackType>, write_cb: Option<FWCfgWriteCallbackType>, allow_write: bool, } @@ -229,13 +230,13 @@ pub fn dma_result_write( }
fn extract64(value: u64, start: i32, length: i32) -> u64 { - assert!(start >= 0 && length > 0 && length < 64 - start); + assert!(start >= 0 && length > 0 && length <= 64 - start);
(value >> start as u64) & (!(0 as u64) >> (64 - length) as u64) }
fn get_key_name(key: usize) -> &'static str { - static fw_cfg_keys: [&str; 25] = [ + static FW_CFG_KEYS: [&str; 25] = [ "signature", "id", "uuid", @@ -262,7 +263,7 @@ fn get_key_name(key: usize) -> &'static str { "setup_data", "file_dir", ]; - return fw_cfg_keys[key]; + return FW_CFG_KEYS[key]; }
pub struct FWCfgCommon { @@ -303,6 +304,7 @@ impl FWCfgCommon { fn fw_cfg_write(&self, _value: u8) {}
pub fn fw_cfg_get_entry(&mut self) -> Option<&mut FWCfgEntry> { + info!("fw_cfg_get_entry"); let is_local = (self.cur_entry & FW_CFG_ARCH_LOCAL as u16) > 0;
let mut entry = None; @@ -321,6 +323,7 @@ impl FWCfgCommon { }
pub fn fw_cfg_select(&mut self, key: u16) -> i32 { + info!("fw_cfg_select"); let mut ret = 0;
self.cur_offset = 0; @@ -336,11 +339,12 @@ impl FWCfgCommon { entry.select_cb.as_mut().unwrap().select_callback(); }
- debug!("fw_cfg_select key {} ret {}", key, ret); + info!("fw_cfg_select key {} ret {}", key, ret); ret }
fn fw_cfg_dma_transfer(&mut self) { + info!("fw_cfg_dma_transfer"); let dma_addr = self.dma_addr; let mem_space = self.mem_space.clone(); let mut cur_offset = self.cur_offset; @@ -356,6 +360,7 @@ impl FWCfgCommon { let size = std::mem::size_of::<FWCfgDmaAccess>() as u64; if let Err(_) = dma_memory_read(&self.mem_space, dma_addr, dma_buf, size) { dma_result_write(&self.mem_space, dma_addr, FW_CFG_DMA_CTL_ERROR); + return; }
// Build FWCfgDmaAccess object here @@ -385,7 +390,7 @@ impl FWCfgCommon {
while dma.length > 0 && (dma.control & FW_CFG_DMA_CTL_ERROR) != 0 { if cur_entry == FW_CFG_INVALID - || !entry.data.is_empty() + || entry.data.is_empty() || cur_offset >= entry.data.len() as u32 { len = dma.length; @@ -422,7 +427,7 @@ impl FWCfgCommon { let mut dma_read_error = false; let data = &mut entry.data[cur_offset as usize..]; if let Err(_) = - dma_memory_read(&mem_space, GuestAddress(dma.address), data, len as u64) + dma_memory_read(&mem_space, GuestAddress(dma.address), data, len as u64) { dma_read_error = true; } @@ -440,35 +445,46 @@ impl FWCfgCommon { dma.address += len as u64 } dma_result_write(&self.mem_space, dma_addr, dma.control); - debug!("fw_cfg dma tranfer"); + info!("fw_cfg dma tranfer"); }
pub fn fw_cfg_add_entry( &mut self, - key: u16, + mut key: u16, select_cb: Option<FWCfgCallbackType>, write_cb: Option<FWCfgWriteCallbackType>, data: Vec<u8>, allow_write: bool, ) { + info!("fw_cfg_add_entry {}", get_key_name(key as usize)); let is_local = key & FW_CFG_ARCH_LOCAL > 0;
assert!(key < self.fw_cfg_max_entry() && data.len() < std::u32::MAX as usize);
+ key &= FW_CFG_ENTRY_MASK; + let mut entry; if is_local { - entry = &mut self.local_entries[key as usize]; + entry = self.local_entries.get_mut(key as usize); } else { - entry = &mut self.entries[key as usize]; + entry = self.entries.get_mut(key as usize); }
- entry.data = data; - entry.select_cb = select_cb; - entry.write_cb = write_cb; - entry.allow_write = allow_write; + if let Some(ref mut e) = entry { + e.data = data; + e.select_cb = select_cb; + e.write_cb = write_cb; + e.allow_write = allow_write; + } else { + // *e = FWCfgEntry::new(data, select_cb, write_cb, allow_write); + } }
pub fn fw_cfg_update_entry_data(&mut self, key: u16, data: Vec<u8>) { + info!( + "fw_cfg_update_entry_data key {}", + get_key_name(key as usize) + ); let is_local = key & FW_CFG_ARCH_LOCAL > 0;
assert!(key < self.fw_cfg_max_entry() && data.len() < std::u32::MAX as usize); @@ -485,21 +501,25 @@ impl FWCfgCommon { }
pub fn fw_cfg_add_entry_data(&mut self, key: u16, data: Vec<u8>) { - debug!("fw_cfg_add_data key {} ", get_key_name(key as usize)); + info!("fw_cfg_add_data key {} ", get_key_name(key as usize));
- self.fw_cfg_add_entry(key, None, None, data, true); + self.fw_cfg_add_entry(key, None, None, data, false); }
pub fn fw_cfg_entry_add_string(&mut self, key: u16, value: &str) { - self.fw_cfg_update_entry_data(key, value.as_bytes().to_vec()); + info!("fw_cfg_entry_add_string {}", value); + self.fw_cfg_add_entry_data(key, value.as_bytes().to_vec()); }
- pub fn fw_cfg_add_file_callback(&mut self, - filename: &str, - data: Vec<u8>, - select_cb: Option<FWCfgCallbackType>, - write_cb: Option<FWCfgWriteCallbackType>, - allow_write: bool) -> Result<()> { + pub fn fw_cfg_add_file_callback( + &mut self, + filename: &str, + data: Vec<u8>, + select_cb: Option<FWCfgCallbackType>, + write_cb: Option<FWCfgWriteCallbackType>, + allow_write: bool, + ) -> Result<()> { + info!("fw_cfg_add_file_callback filename {}", filename); let len = self.files.len(); assert!(len < self.file_slots as usize); let mut index = len; @@ -521,10 +541,14 @@ impl FWCfgCommon { }
let entry = FWCfgEntry::new(data, select_cb, write_cb, allow_write); - self.local_entries.insert(index + FW_CFG_FILE_FIRST as usize, entry); - let file = FWCfgFile::new(BigEndian::read_u32((len as u32).as_bytes()), - BigEndian::read_u16(((index + FW_CFG_FILE_FIRST as usize) as u16).as_bytes()), - 0, filename.to_string()); + self.local_entries + .insert(index + FW_CFG_FILE_FIRST as usize, entry); + let file = FWCfgFile::new( + BigEndian::read_u32((len as u32).as_bytes()), + BigEndian::read_u16(((index + FW_CFG_FILE_FIRST as usize) as u16).as_bytes()), + 0, + filename.to_string(), + ); self.files.push(file); self.files.sort(); Ok(()) @@ -534,36 +558,41 @@ impl FWCfgCommon { self.fw_cfg_add_file_callback(filename, data, None, None, false); }
- pub fn fw_cfg_dma_mem_write(&mut self, addr: GuestAddress, value: u64, size: u32) { + pub fn fw_cfg_dma_mem_write(&mut self, addr: u64, value: u64, size: u32) { + info!( + "fw_cfg_dma_mem_write addr 0x{:x} value 0x{:x} size 0x{:x}", + addr, value, size + ); if size == 4 { - if addr == GuestAddress(0) { + if addr == 0 { self.dma_addr = GuestAddress(value << 32); - } else if addr == GuestAddress(4) { - self.dma_addr = GuestAddress(self.dma_addr.raw_value() | size as u64); + } else if addr == 4 { + self.dma_addr = GuestAddress(self.dma_addr.raw_value() | value as u64); self.fw_cfg_dma_transfer(); } - } else if size == 8 && addr == GuestAddress(0) { + } else if size == 8 && addr == 0 { self.dma_addr = GuestAddress(value); self.fw_cfg_dma_transfer(); } }
- pub fn fw_cfg_dma_mem_read(&self, addr: GuestAddress, size: u32) -> u64 { + pub fn fw_cfg_dma_mem_read(&self, addr: u64, size: u32) -> u64 { + info!("fw_cfg_dma_mem_read addr 0x{:x} size 0x{:x}", addr, size); extract64( FW_CFG_DMA_SIGNATURE as u64, - ((8 - addr.raw_value() - size as u64) * 8) as i32, + ((8 - addr - size as u64) * 8) as i32, (size * 8) as i32, ) }
- pub fn fw_cfg_data_read(&mut self, addr: GuestAddress, mut size: u32) -> u64 { + pub fn fw_cfg_data_read(&mut self, addr: u64, mut size: u32) -> u64 { let mut value: u64 = 0; let cur_entry = self.cur_entry; let cur_offset = self.cur_offset;
let entry = self.fw_cfg_get_entry().unwrap(); - let SIZE_U64 = std::mem::size_of::<u64>() as u32; - assert!(size > 0 && size < SIZE_U64); + let size_u64 = std::mem::size_of::<u64>() as u32; + assert!(size > 0 && size < size_u64); if cur_entry != FW_CFG_INVALID && !entry.data.is_empty() && cur_offset < entry.data.len() as u32 @@ -574,65 +603,54 @@ impl FWCfgCommon { offset += 1; size -= 1;
- if size < 0 || offset >= entry.data.len() as u32 { + if size <= 0 || offset >= entry.data.len() as u32 { break; } } value = 8 * size as u64; }
- debug!( + info!( "fw_cfg_data_read 0x{:x} size {} value 0x{:x}", - addr.raw_value(), - size, - value + addr, size, value ); value }
pub fn fw_cfg_common_realize(&mut self) { - self.local_entries = Vec::with_capacity(self.fw_cfg_max_entry() as usize); - self.entries = Vec::with_capacity(self.fw_cfg_max_entry() as usize);
self.fw_cfg_add_entry_data( - FWCfgEntryType::FW_CFG_SIGNATURE as u16, + FWCfgEntryType::FWCfgSignature as u16, "QEMU".as_bytes().to_vec(), );
// TODO add real uuid here self.fw_cfg_add_entry_data( - FWCfgEntryType::FW_CFG_UUID as u16, + FWCfgEntryType::FWCfgUUID as u16, "49c9bedd6459b52a".as_bytes().to_vec(), );
self.fw_cfg_add_entry_data( - FWCfgEntryType::FW_CFG_NOGRAPHIC as u16, + FWCfgEntryType::FWCfgNoGraphic as u16, (0 as u16).as_bytes().to_vec(), );
self.fw_cfg_add_entry_data( - FWCfgEntryType::FW_CFG_BOOT_MENU as u16, + FWCfgEntryType::FWCfgBootMenu as u16, (0 as u16).as_bytes().to_vec(), );
let version = FW_CFG_VERSION & FW_CFG_VERSION_DMA; - self.fw_cfg_add_entry_data( - FWCfgEntryType::FW_CFG_ID as u16, - version.as_bytes().to_vec(), - ); + self.fw_cfg_add_entry_data(FWCfgEntryType::FWCfgId as u16, version.as_bytes().to_vec()); } }
-#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct FWCfgConfig {} - /// Emulate FWCfg MMIO Device pub struct FWCfgMem { fwcfg: FWCfgCommon, data_width: u32, }
- impl FWCfgMem { pub fn new(sys_mem: Arc<AddressSpace>) -> Self { FWCfgMem { @@ -641,7 +659,11 @@ impl FWCfgMem { } }
- pub fn fw_cfg_data_mem_write(&self, _addr: GuestAddress, value: u64, size: u32) { + pub fn fw_cfg_data_mem_write(&self, _addr: u64, value: u64, size: u32) { + info!( + "fw_cfg_data_mem_write addr 0x{:x} value 0x{:x} size 0x{:x}", + _addr, value, size + ); let mut i = size; loop { i -= 1; @@ -652,27 +674,35 @@ impl FWCfgMem { } }
- pub fn fw_cfg_ctl_mem_read(&self, _addr: GuestAddress, _size: u32) -> u64 { + pub fn fw_cfg_ctl_mem_read(&self, addr: u64, _size: u32) -> u64 { 0 }
- pub fn fw_cfg_ctl_mem_write(&mut self, _addr: GuestAddress, value: u64, _size: u32) { + pub fn fw_cfg_ctl_mem_write(&mut self, addr: u64, value: u64, _size: u32) { self.fwcfg.fw_cfg_select(value as u16); } }
impl DeviceOps for FWCfgMem { fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { + info!( + "FWCfgMem read offset 0x{:x} size 0x{:x}", + offset, + data.len() + ); let mut value = 0; match offset { 0..=7 => { - value = self.fwcfg.fw_cfg_data_read(base, data.len() as u32); + value = self.fwcfg.fw_cfg_data_read(offset, data.len() as u32); } 8..=15 => { - value = self.fw_cfg_ctl_mem_read(base, data.len() as u32); + value = self.fw_cfg_ctl_mem_read(offset, data.len() as u32); + } + 16..=23 => { + self.fwcfg.fw_cfg_dma_mem_read(offset, data.len() as u32); } _ => { - self.fwcfg.fw_cfg_dma_mem_read(base, data.len() as u32); + error!("write overflow"); } }
@@ -681,18 +711,22 @@ impl DeviceOps for FWCfgMem { }
fn write(&mut self, data: &[u8], base: GuestAddress, offset: u64) -> bool { - let mut value = 0; let size = data.len() as u32; - value = BigEndian::read_u64(data); + let mut value = match size { + 2 => BigEndian::read_u16(data) as u64, + 4 => BigEndian::read_u32(data) as u64, + _ => 0, + }; + info!("FWCfgMem write value 0x{:x} size 0x{:x}", value, size); match offset { 0..=7 => { - self.fw_cfg_data_mem_write(base, value, size); + self.fw_cfg_data_mem_write(offset, value, size); } 8..=15 => { - self.fw_cfg_ctl_mem_write(base, value, size); + self.fw_cfg_ctl_mem_write(offset, value, size); } _ => { - self.fwcfg.fw_cfg_dma_mem_write(base, value, size); + self.fwcfg.fw_cfg_dma_mem_write(offset, value, size); } } true @@ -734,7 +768,7 @@ impl FWCfgIO { } }
- pub fn fw_cfg_comb_write(&mut self, _addr: GuestAddress, value: u64, size: u32) { + pub fn fw_cfg_comb_write(&mut self, _addr: u64, value: u64, size: u32) { match size { 1 => { self.fwcfg.fw_cfg_write(value as u8); @@ -752,10 +786,10 @@ impl DeviceOps for FWCfgIO { let mut value = 0; match offset { 1..=2 => { - value = self.fwcfg.fw_cfg_data_read(base, data.len() as u32); + value = self.fwcfg.fw_cfg_data_read(offset, data.len() as u32); } 15..=23 => { - value = self.fwcfg.fw_cfg_dma_mem_read(base, data.len() as u32); + value = self.fwcfg.fw_cfg_dma_mem_read(offset, data.len() as u32); } _ => error!("invalid read of set in FWCfgIO"), } @@ -769,10 +803,10 @@ impl DeviceOps for FWCfgIO { value = LittleEndian::read_u64(data); match offset { 1..=2 => { - self.fw_cfg_comb_write(base, value, size); + self.fw_cfg_comb_write(offset, value, size); } 15..=23 => { - self.fwcfg.fw_cfg_dma_mem_write(base, value, size); + self.fwcfg.fw_cfg_dma_mem_write(offset, value, size); } _ => {} } diff --git a/device_model/src/legacy/mod.rs b/device_model/src/legacy/mod.rs index 0df5ae3..0cb4ccd 100644 --- a/device_model/src/legacy/mod.rs +++ b/device_model/src/legacy/mod.rs @@ -39,8 +39,6 @@ mod fw_cfg;
#[cfg(target_arch = "aarch64")] pub use self::fw_cfg::FWCfgMem; -#[cfg(target_arch = "aarch64")] -pub use self::fw_cfg::FWCfgConfig;
mod pl011; #[cfg(target_arch = "aarch64")] diff --git a/device_model/src/micro_vm/mod.rs b/device_model/src/micro_vm/mod.rs index 4905513..19cebdc 100644 --- a/device_model/src/micro_vm/mod.rs +++ b/device_model/src/micro_vm/mod.rs @@ -470,7 +470,7 @@ impl LightMachine { pml4_start: layout.boot_pml4_addr, };
- for cpu_index in 0..self.cpu_topo.max_cpus { + for cpu_index in 0. .self.cpu_topo.max_cpus { self.cpus.lock().unwrap()[cpu_index as usize].realize(&boot_config)?; }
Signed-off-by: Ying Fang fangying1@huawei.com --- device_model/src/legacy/fw_cfg.rs | 160 ++++++++++++++++++------------ device_model/src/legacy/pl011.rs | 8 +- 2 files changed, 99 insertions(+), 69 deletions(-)
diff --git a/device_model/src/legacy/fw_cfg.rs b/device_model/src/legacy/fw_cfg.rs index 09396cc..04e4862 100644 --- a/device_model/src/legacy/fw_cfg.rs +++ b/device_model/src/legacy/fw_cfg.rs @@ -14,9 +14,11 @@ use super::super::mmio::errors::Result; use super::super::mmio::{DeviceResource, DeviceType, MmioDeviceOps}; use super::super::DeviceOps; use address_space::{AddressSpace, GuestAddress}; -use byteorder::{BigEndian, ByteOrder, LittleEndian}; use kvm_ioctls::VmFd; + +use byteorder::{BigEndian, ByteOrder, LittleEndian}; use std::cmp::Ordering; +use std::collections::BTreeMap; use std::default::Default; use std::sync::Arc; use util::byte_code::ByteCode; @@ -268,9 +270,9 @@ fn get_key_name(key: usize) -> &'static str {
pub struct FWCfgCommon { file_slots: u16, - local_entries: Vec<FWCfgEntry>, - entries: Vec<FWCfgEntry>, - files: Vec<FWCfgFile>, + local_entries: BTreeMap<u16, FWCfgEntry>, + entries: BTreeMap<u16, FWCfgEntry>, + files: BTreeMap<String, FWCfgFile>, cur_entry: u16, cur_offset: u32,
@@ -285,9 +287,9 @@ impl FWCfgCommon { pub fn new(sys_mem: Arc<AddressSpace>) -> Self { FWCfgCommon { file_slots: FW_CFG_FILE_SLOTS_DFLT as u16, - local_entries: Vec::new(), - entries: Vec::new(), - files: Vec::new(), + local_entries: BTreeMap::new(), + entries: BTreeMap::new(), + files: BTreeMap::new(), cur_entry: 0, cur_offset: 0, dma_enabled: true, @@ -304,26 +306,24 @@ impl FWCfgCommon { fn fw_cfg_write(&self, _value: u8) {}
pub fn fw_cfg_get_entry(&mut self) -> Option<&mut FWCfgEntry> { - info!("fw_cfg_get_entry"); - let is_local = (self.cur_entry & FW_CFG_ARCH_LOCAL as u16) > 0; + let key = self.cur_entry & FW_CFG_ENTRY_MASK; + info!("fw_cfg_get_entry 0x{:x}", key); + let is_local = (self.cur_entry & FW_CFG_ARCH_LOCAL) == 0;
let mut entry = None; + if self.cur_entry != FW_CFG_INVALID { if is_local { - entry = self - .local_entries - .get_mut((self.cur_entry & FW_CFG_ENTRY_MASK) as usize) + entry = self.local_entries.get_mut(&key) } else { - entry = self - .entries - .get_mut((self.cur_entry & FW_CFG_ENTRY_MASK) as usize) + entry = self.entries.get_mut(&key); } } entry }
pub fn fw_cfg_select(&mut self, key: u16) -> i32 { - info!("fw_cfg_select"); + info!("fw_cfg_select {}", get_key_name(key as usize)); let mut ret = 0;
self.cur_offset = 0; @@ -336,7 +336,11 @@ impl FWCfgCommon {
// TODO Invoke callback here let entry = self.fw_cfg_get_entry().unwrap(); - entry.select_cb.as_mut().unwrap().select_callback(); + if let Some(ref mut cb) = &mut entry.select_cb { + cb.select_callback(); + } else { + info!("no callback func found!"); + } }
info!("fw_cfg_select key {} ret {}", key, ret); @@ -456,28 +460,29 @@ impl FWCfgCommon { data: Vec<u8>, allow_write: bool, ) { - info!("fw_cfg_add_entry {}", get_key_name(key as usize)); - let is_local = key & FW_CFG_ARCH_LOCAL > 0; - - assert!(key < self.fw_cfg_max_entry() && data.len() < std::u32::MAX as usize); + let is_local = key & FW_CFG_ARCH_LOCAL == 0;
key &= FW_CFG_ENTRY_MASK; + assert!(key < self.fw_cfg_max_entry() && data.len() < std::u32::MAX as usize);
let mut entry; if is_local { - entry = self.local_entries.get_mut(key as usize); + entry = self.local_entries.get_mut(&key); } else { - entry = self.entries.get_mut(key as usize); + entry = self.entries.get_mut(&key); }
if let Some(ref mut e) = entry { - e.data = data; - e.select_cb = select_cb; - e.write_cb = write_cb; - e.allow_write = allow_write; + error!("entry {} is not empty!", get_key_name(key as usize)); + } + + let entry = FWCfgEntry::new(data, select_cb, write_cb, allow_write); + if is_local { + self.local_entries.insert(key, entry); } else { - // *e = FWCfgEntry::new(data, select_cb, write_cb, allow_write); + self.entries.insert(key, entry); } + info!("fw_cfg_add_entry index 0x{:x} done!", key); }
pub fn fw_cfg_update_entry_data(&mut self, key: u16, data: Vec<u8>) { @@ -491,18 +496,17 @@ impl FWCfgCommon {
let mut entry; if is_local { - entry = &mut self.local_entries[key as usize]; + entry = &mut self.local_entries.get_mut(&key); } else { - entry = &mut self.entries[key as usize]; + entry = &mut self.entries.get_mut(&key); }
- entry.data = data; - entry.allow_write = false; + // entry.data = data; + // entry.allow_write = false; }
pub fn fw_cfg_add_entry_data(&mut self, key: u16, data: Vec<u8>) { - info!("fw_cfg_add_data key {} ", get_key_name(key as usize)); - + info!("fw_cfg_add_entry_data key {}", get_key_name(key as usize)); self.fw_cfg_add_entry(key, None, None, data, false); }
@@ -520,42 +524,50 @@ impl FWCfgCommon { allow_write: bool, ) -> Result<()> { info!("fw_cfg_add_file_callback filename {}", filename); - let len = self.files.len(); - assert!(len < self.file_slots as usize); - let mut index = len; - loop { - if filename.to_string() < self.files[index - 1].name.to_string() { - index -= 1; - } else { - break; - } + + assert!(self.files.len() < self.file_slots as usize); + + // check against duplicate file + if self.files.contains_key(&filename.to_string()) { + bail!("duplicate fw_cfg file name {}", filename); }
- let mut i = 0; - // check duplicate file - for f in self.files.iter() { - if f.name == filename && i != index { - bail!("duplicate fw_cfg file name {}", filename); + let tmp = FWCfgFile::new( + BigEndian::read_u32((data.len() as u32).as_bytes()), + BigEndian::read_u16(FW_CFG_FILE_FIRST.as_bytes()), + 0, + filename.to_string(), + ); + self.files.insert(filename.to_string(), tmp); + + let mut index = 0; + for (key, value) in self.files.iter() { + if *key == filename.to_string() { + break; } - i += 1; + index += 1; }
- let entry = FWCfgEntry::new(data, select_cb, write_cb, allow_write); - self.local_entries - .insert(index + FW_CFG_FILE_FIRST as usize, entry); let file = FWCfgFile::new( - BigEndian::read_u32((len as u32).as_bytes()), - BigEndian::read_u16(((index + FW_CFG_FILE_FIRST as usize) as u16).as_bytes()), + BigEndian::read_u32((data.len() as u32).as_bytes()), + BigEndian::read_u16((FW_CFG_FILE_FIRST + index).as_bytes()), 0, filename.to_string(), ); - self.files.push(file); - self.files.sort(); + self.files.insert(filename.to_string(), file); + + self.fw_cfg_add_entry( + FW_CFG_FILE_FIRST + index, + select_cb, + write_cb, + data, + allow_write, + ); Ok(()) }
pub fn fw_cfg_add_file(&mut self, filename: &str, data: Vec<u8>) { - self.fw_cfg_add_file_callback(filename, data, None, None, false); + self.fw_cfg_add_file_callback(filename, data, None, None, true); }
pub fn fw_cfg_dma_mem_write(&mut self, addr: u64, value: u64, size: u32) { @@ -593,6 +605,10 @@ impl FWCfgCommon { let entry = self.fw_cfg_get_entry().unwrap(); let size_u64 = std::mem::size_of::<u64>() as u32; assert!(size > 0 && size < size_u64); + + info!("entry data len is {} cur_offset {}", entry.data.len(), cur_offset); + info!("entry data is {:?}", entry.data); + if cur_entry != FW_CFG_INVALID && !entry.data.is_empty() && cur_offset < entry.data.len() as u32 @@ -607,7 +623,7 @@ impl FWCfgCommon { break; } } - value = 8 * size as u64; + value <<= 8 * size as u64; }
info!( @@ -618,7 +634,6 @@ impl FWCfgCommon { }
pub fn fw_cfg_common_realize(&mut self) { - self.fw_cfg_add_entry_data( FWCfgEntryType::FWCfgSignature as u16, "QEMU".as_bytes().to_vec(), @@ -640,7 +655,13 @@ impl FWCfgCommon { (0 as u16).as_bytes().to_vec(), );
- let version = FW_CFG_VERSION & FW_CFG_VERSION_DMA; + // reboot boot menu timeout + self.fw_cfg_add_file("etc/boot-fail-wait", (5 as u32).as_bytes().to_vec()); + + let mut version = 0; + if self.dma_enabled { + version = FW_CFG_VERSION | FW_CFG_VERSION_DMA; + } self.fw_cfg_add_entry_data(FWCfgEntryType::FWCfgId as u16, version.as_bytes().to_vec()); } } @@ -706,7 +727,12 @@ impl DeviceOps for FWCfgMem { } }
- BigEndian::write_u64(data, value); + match data.len() { + 2 => BigEndian::write_u16(data, value as u16), + 4 => BigEndian::write_u32(data, value as u32), + 8 => BigEndian::write_u64(data, value as u64), + _ => info!("Make sure read size right ?"), + } true }
@@ -715,9 +741,13 @@ impl DeviceOps for FWCfgMem { let mut value = match size { 2 => BigEndian::read_u16(data) as u64, 4 => BigEndian::read_u32(data) as u64, + 8 => BigEndian::read_u64(data) as u64, _ => 0, }; - info!("FWCfgMem write value 0x{:x} size 0x{:x}", value, size); + info!( + "FWCfgMem write value 0x{:x} offset 0x{:x} size 0x{:x}", + value, offset, size + ); match offset { 0..=7 => { self.fw_cfg_data_mem_write(offset, value, size); @@ -756,9 +786,9 @@ impl FWCfgIO { FWCfgIO { fwcfg: FWCfgCommon { file_slots: FW_CFG_FILE_SLOTS_DFLT as u16, - local_entries: Vec::new(), - entries: Vec::new(), - files: Vec::new(), + local_entries: BTreeMap::new(), + entries: BTreeMap::new(), + files: BTreeMap::new(), cur_entry: 0, cur_offset: 0, dma_enabled: true, diff --git a/device_model/src/legacy/pl011.rs b/device_model/src/legacy/pl011.rs index 9f435c7..4634227 100644 --- a/device_model/src/legacy/pl011.rs +++ b/device_model/src/legacy/pl011.rs @@ -118,7 +118,7 @@ impl PL011 { /// * fail to write EventFd. /// * fail to get an interrupt event fd. fn interrupt(&self) -> Result<()> { - info!("interrupt once"); + debug!("interrupt once"); match &self.interrupt_evt { Some(evt) => evt.write(1).chain_err(|| "Failed to write fd")?, None => bail!("Failed to get an interrupt event fd"), @@ -322,7 +322,7 @@ impl DeviceOps for PL011 { } self.lcr = value; self.read_trigger = 1; - info!("write lcr 0x{:x}", self.lcr); + debug!("write lcr 0x{:x}", self.lcr); } 12 => { // UARTCR self.cr = value; @@ -411,9 +411,9 @@ impl EventNotifierHelper for PL011 { Box::new(move |_, _| { let mut out = [0_u8; 64]; if let Ok(count) = std::io::stdin().lock().read_raw(&mut out) { - info!("read {} bytes from stdin", count); + debug!("read {} bytes from stdin", count); let _ = pl011.lock().unwrap().receive(&out[..count]); - info!("receive done here"); + debug!("receive done here"); } None });