// Copyright (c) 2020 Huawei Technologies Co.,Ltd. All rights reserved. // // StratoVirt is licensed under Mulan PSL v2. // You can use this software according to the terms and conditions of the Mulan // PSL v2. // You may obtain a copy of Mulan PSL v2 at: // http://license.coscl.org.cn/MulanPSL2 // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. // See the Mulan PSL v2 for more details. use std::sync::{Arc, Mutex}; use anyhow::{anyhow, bail, Context, Result}; #[cfg(target_arch = "x86_64")] use byteorder::LittleEndian; use byteorder::{BigEndian, ByteOrder}; use log::{error, warn}; use crate::legacy::error::LegacyError; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType, SysRes}; use crate::{Device, DeviceBase}; use acpi::{ AmlBuilder, AmlDevice, AmlInteger, AmlNameDecl, AmlResTemplate, AmlScopeBuilder, AmlString, }; #[cfg(target_arch = "x86_64")] use acpi::{AmlIoDecode, AmlIoResource}; #[cfg(target_arch = "aarch64")] use acpi::{AmlMemory32Fixed, AmlReadAndWrite}; use address_space::{AddressSpace, GuestAddress}; use util::byte_code::ByteCode; use util::num_ops::extract_u64; use util::offset_of; #[cfg(target_arch = "x86_64")] const FW_CFG_IO_BASE: u64 = 0x510; // Size of ioports including control/data registers and DMA. #[cfg(target_arch = "x86_64")] const FW_CFG_IO_SIZE: u64 = 0x12; // FwCfg Signature const FW_CFG_DMA_SIGNATURE: u128 = 0x51454d5520434647; /// FwCfg version bits const FW_CFG_VERSION: u16 = 0x01; const FW_CFG_VERSION_DMA: u16 = 0x02; // FwCfg related constants const FW_CFG_FILE_SLOTS_DFLT: u16 = 0x20; const FW_CFG_FILE_FIRST: u16 = 0x20; 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; /// FwCfg 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; /// Define the Firmware Configuration Entry Type #[repr(u16)] pub enum FwCfgEntryType { Signature = 0x00, Id, Uuid, RamSize, NoGraphic, NbCpus, MachineId, KernelAddr, KernelSize, KernelCmdline, InitrdAddr, InitrdSize, BootDevice, Numa, BootMenu, MaxCpus, KernelEntry, KernelData, InitrdData, CmdlineAddr, CmdlineSize, CmdlineData, SetupAddr, SetupSize, SetupData, FileDir, #[cfg(target_arch = "x86_64")] Irq0Override = 0x8002, #[cfg(target_arch = "x86_64")] E820Table = 0x8003, } /// Get the FwCfg entry name of a given key fn get_key_name(key: usize) -> &'static str { static FW_CFG_KEYS: [&str; 26] = [ "signature", "id", "uuid", "ram_size", "nographic", "nb_cpus", "machine_id", "kernel_addr", "kernel_size", "kernel_cmdline", "initrd_addr", "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", ]; if key < FW_CFG_FILE_FIRST as usize { FW_CFG_KEYS[key] } else { "unknown" } } /// FwCfg select callback and write callback type definition type FwCfgCallbackType = Arc<dyn FwCfgCallback + Send + Sync>; type FwCfgWriteCallbackType = Arc<Mutex<dyn FwCfgWriteCallback + Send + Sync>>; /// FwCfg select callback pub trait FwCfgCallback { fn select_callback(&self); } /// FwCfg write callback pub trait FwCfgWriteCallback { fn write_callback(&mut self, data: Vec<u8>, start: u64, len: usize); } /// The FwCfgEntry type which holds the firmware item #[derive(Clone, Default)] struct FwCfgEntry { data: Vec<u8>, select_cb: Option<FwCfgCallbackType>, write_cb: Option<FwCfgWriteCallbackType>, allow_write: bool, } impl FwCfgEntry { 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, } } } /// The FwCfgFile entry used to retrieve firwmware files by os loader #[repr(C, packed)] #[derive(Copy, Clone)] struct FwCfgFile { size: u32, select: u16, reserved: u16, name: [u8; 56], } impl FwCfgFile { fn new(size: u32, select: u16, name: &str) -> Self { let len = std::cmp::min(56, name.len()); let mut bytes = [0; 56]; bytes[..len].copy_from_slice(&name.as_bytes()[..len]); FwCfgFile { size, select, reserved: 0_u16, name: bytes, } } /// Convert FwCfgFile item into a big endian format data array fn as_be_bytes(&self) -> Vec<u8> { let mut bytes = vec![0; 64_usize]; let mut curr_offset = 0_usize; let mut next_size = std::mem::size_of::<u32>(); BigEndian::write_u32( &mut bytes[curr_offset..(curr_offset + next_size)], self.size, ); curr_offset += next_size; next_size = std::mem::size_of::<u16>(); BigEndian::write_u16( &mut bytes[curr_offset..(curr_offset + next_size)], self.select, ); curr_offset += next_size; next_size = std::mem::size_of::<u16>(); BigEndian::write_u16( &mut bytes[curr_offset..(curr_offset + next_size)], self.reserved, ); curr_offset += next_size; bytes[curr_offset..].copy_from_slice(&self.name); bytes } } /// The FwCfgDmaAccess entry used as DMA descriptor in memory for operation #[repr(C, packed)] #[derive(Copy, Clone, Default)] struct FwCfgDmaAccess { control: u32, length: u32, address: u64, } impl ByteCode for FwCfgDmaAccess {} /// write data to DMA memory zone fn write_dma_memory( addr_space: &Arc<AddressSpace>, addr: GuestAddress, mut buf: &[u8], len: u64, ) -> Result<()> { addr_space.write(&mut buf, addr, len).with_context(|| { format!( "Failed to write dma memory of fwcfg at gpa=0x{:x} len=0x{:x}", addr.0, len ) })?; Ok(()) } /// read data form DMA memory zone fn read_dma_memory( addr_space: &Arc<AddressSpace>, addr: GuestAddress, mut buf: &mut [u8], len: u64, ) -> Result<()> { addr_space.read(&mut buf, addr, len).with_context(|| { format!( "Failed to read dma memory of fwcfg at gpa=0x{:x} len=0x{:x}", addr.0, len ) })?; Ok(()) } /// Write DMA result to DMA response zone fn write_dma_result(addr_space: &Arc<AddressSpace>, addr: GuestAddress, value: u32) -> Result<()> { let mut dma_buf: [u8; 4] = [0; 4]; BigEndian::write_u32(&mut dma_buf, value); write_dma_memory( addr_space, addr.unchecked_add(offset_of!(FwCfgDmaAccess, control) as u64), &dma_buf, dma_buf.len() as u64, ) .with_context(|| { format!( "Failed to write dma result of fwcfg at gpa=0x{:x} value=0x{:x}", addr.0, value ) })?; Ok(()) } pub struct FwCfgCommon { // Firmware file slot count file_slots: u16, // Arch related firmware entry arch_entries: Vec<FwCfgEntry>, // Arch independent firmware entry entries: Vec<FwCfgEntry>, // Firmware configuration files files: Vec<FwCfgFile>, // The current entry index selected cur_entry: u16, // The current entry data offset of the entry selected cur_offset: u32, // DMA enable flag dma_enabled: bool, // DMA guest address dma_addr: GuestAddress, // System memory address space mem_space: Arc<AddressSpace>, } impl FwCfgCommon { fn new(sys_mem: Arc<AddressSpace>) -> Self { FwCfgCommon { file_slots: FW_CFG_FILE_SLOTS_DFLT, arch_entries: vec![ FwCfgEntry::default(); (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS_DFLT) as usize ], entries: vec![ FwCfgEntry::default(); (FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS_DFLT) as usize ], files: Vec::new(), cur_entry: 0, cur_offset: 0, dma_enabled: true, dma_addr: GuestAddress(0), mem_space: sys_mem, } } /// Check whether the selected entry has a arch_local flag fn is_arch_local(&self) -> bool { (self.cur_entry & FW_CFG_ARCH_LOCAL) > 0 } /// Get the max entry size fn max_entry(&self) -> u16 { FW_CFG_FILE_FIRST + self.file_slots } /// Get a mutable reference of the current selected entry fn get_entry_mut(&mut self) -> Result<&mut FwCfgEntry> { let key = self.cur_entry & FW_CFG_ENTRY_MASK; if key >= self.max_entry() || self.cur_entry == FW_CFG_INVALID { return Err(anyhow!(LegacyError::EntryNotFound( get_key_name(self.cur_entry as usize).to_owned() ))); }; // unwrap is safe because the count of arch_entries and entries is initialized // as `FW_CFG_FILE_FIRST + FW_CFG_FILE_SLOTS_DFLT`, which is equal to the return // value of `max_entry` function. if self.is_arch_local() { Ok(self.arch_entries.get_mut(key as usize).unwrap()) } else { Ok(self.entries.get_mut(key as usize).unwrap()) } } /// Select the entry by the key specified fn select_entry(&mut self, key: u16) { self.cur_offset = 0; if (key & FW_CFG_ENTRY_MASK) >= self.max_entry() { self.cur_entry = FW_CFG_INVALID; } else { self.cur_entry = key; // unwrap() is safe because we have checked the range of `key`. let selected_entry = self.get_entry_mut().unwrap(); if let Some(ref mut cb) = selected_entry.select_cb { cb.select_callback(); } } } fn add_entry( &mut self, key: FwCfgEntryType, select_cb: Option<FwCfgCallbackType>, write_cb: Option<FwCfgWriteCallbackType>, data: Vec<u8>, allow_write: bool, ) -> Result<()> { let key = (key as u16) & FW_CFG_ENTRY_MASK; if key >= self.max_entry() || data.len() >= std::u32::MAX as usize { return Err(anyhow!(LegacyError::InvalidFwCfgEntry(key))); } let entry = if self.is_arch_local() { self.arch_entries.get_mut(key as usize) } else { self.entries.get_mut(key as usize) }; if entry.is_none() { warn!("entry not initialized in construction function"); } let entry = entry.unwrap(); if !entry.data.is_empty() { warn!("Entry not empty, will override"); } entry.data = data; entry.select_cb = select_cb; entry.allow_write = allow_write; entry.write_cb = write_cb; Ok(()) } // Update a FwCfgEntry fn update_entry_data(&mut self, key: u16, mut data: Vec<u8>) -> Result<()> { if key >= self.max_entry() || data.len() >= std::u32::MAX as usize { return Err(anyhow!(LegacyError::InvalidFwCfgEntry(key))); } let entry = if self.is_arch_local() { self.arch_entries.get_mut(key as usize) } else { self.entries.get_mut(key as usize) }; if let Some(e) = entry { e.data.clear(); e.data.append(&mut data); Ok(()) } else { Err(anyhow!(LegacyError::EntryNotFound( get_key_name(key as usize).to_owned() ))) } } fn add_file_callback( &mut self, filename: &str, data: Vec<u8>, select_cb: Option<FwCfgCallbackType>, write_cb: Option<FwCfgWriteCallbackType>, allow_write: bool, ) -> Result<()> { if self.files.len() >= self.file_slots as usize { return Err(anyhow!(LegacyError::FileSlotsNotAvailable( filename.to_owned() ))); } let file_name_bytes = filename.to_string().as_bytes().to_vec(); // Check against duplicate file here if self .files .iter() .any(|f| f.name[0..file_name_bytes.len()].to_vec() == file_name_bytes) { return Err(anyhow!(LegacyError::DuplicateFile(filename.to_owned()))); } let mut index = self.files.len(); for (i, file_entry) in self.files.iter().enumerate() { if file_name_bytes < file_entry.name.to_vec() { index = i; break; } } let file = FwCfgFile::new( data.len() as u32, FW_CFG_FILE_FIRST + index as u16, filename, ); self.files.insert(index, file); self.files.iter_mut().skip(index + 1).for_each(|f| { f.select += 1; }); let mut bytes = Vec::new(); let file_length_be = BigEndian::read_u32((self.files.len() as u32).as_bytes()); bytes.append(&mut file_length_be.as_bytes().to_vec()); for value in self.files.iter() { bytes.append(&mut value.as_be_bytes()); } self.update_entry_data(FwCfgEntryType::FileDir as u16, bytes)?; self.entries.insert( FW_CFG_FILE_FIRST as usize + index, FwCfgEntry::new(data, select_cb, write_cb, allow_write), ); Ok(()) } fn modify_file_callback( &mut self, filename: &str, data: Vec<u8>, select_cb: Option<FwCfgCallbackType>, write_cb: Option<FwCfgWriteCallbackType>, allow_write: bool, ) -> Result<()> { if self.files.len() >= self.file_slots as usize { return Err(anyhow!(LegacyError::FileSlotsNotAvailable( filename.to_owned() ))); } let file_name_bytes = filename.to_string().as_bytes().to_vec(); // Make sure file entry is existed. let index = self .files .iter() .position(|f| f.name[0..file_name_bytes.len()].to_vec() == file_name_bytes) .with_context(|| LegacyError::EntryNotFound(filename.to_owned()))?; self.files[index].size = data.len() as u32; // Update FileDir entry let mut bytes = Vec::new(); let file_length_be = BigEndian::read_u32((self.files.len() as u32).as_bytes()); bytes.append(&mut file_length_be.as_bytes().to_vec()); for value in self.files.iter() { bytes.append(&mut value.as_be_bytes()); } self.update_entry_data(FwCfgEntryType::FileDir as u16, bytes)?; // Update File entry self.entries[FW_CFG_FILE_FIRST as usize + index] = FwCfgEntry::new(data, select_cb, write_cb, allow_write); Ok(()) } // Fetch FwCfgDma request from guest and handle it fn handle_dma_request(&mut self) -> Result<()> { let dma_addr = self.dma_addr; let mem_space = self.mem_space.clone(); let cur_entry = self.cur_entry; // Reset dma_addr address before next dma access self.dma_addr = GuestAddress(0); // Read DMA request from guest let mut dma_req = FwCfgDmaAccess::default(); let dma_request = dma_req.as_mut_bytes(); let size = std::mem::size_of::<FwCfgDmaAccess>() as u64; if let Err(_e) = read_dma_memory(&self.mem_space, dma_addr, dma_request, size) { write_dma_result(&self.mem_space, dma_addr, FW_CFG_DMA_CTL_ERROR)?; return Err(anyhow!(LegacyError::ReadDmaRequest(dma_addr.0, size))); } // Build `FwCfgDmaAccess` object from dma_request here let mut dma = FwCfgDmaAccess { control: BigEndian::read_u32(&dma_request[0..4]), length: BigEndian::read_u32(&dma_request[4..8]), address: BigEndian::read_u64(&dma_request[8..]), }; if dma.control & FW_CFG_DMA_CTL_SELECT > 0 { self.select_entry((dma.control >> 16) as u16); } let mut offset = self.cur_offset; let mut is_read = false; let mut is_write = false; if dma.control & FW_CFG_DMA_CTL_READ != 0 { is_read = true; } if dma.control & FW_CFG_DMA_CTL_WRITE != 0 { is_write = true; } if dma.control & FW_CFG_DMA_CTL_SKIP != 0 { dma.length = 0; } // clear dma.control here dma.control = 0; let entry = self.get_entry_mut()?; let mut len: u32; while dma.length > 0 && (dma.control & FW_CFG_DMA_CTL_ERROR) == 0 { if cur_entry == FW_CFG_INVALID || entry.data.is_empty() || offset >= entry.data.len() as u32 { len = dma.length; if is_read { let data = vec![0_u8; len as usize]; if write_dma_memory( &mem_space, GuestAddress(dma.address), data.as_slice(), len as u64, ) .is_err() { dma.control |= FW_CFG_DMA_CTL_ERROR; } } if is_write { dma.control |= FW_CFG_DMA_CTL_ERROR; } } else { if dma.length <= entry.data.len() as u32 - offset { len = dma.length; } else { len = entry.data.len() as u32 - offset; } // If it is a DMA read request if is_read && write_dma_memory( &mem_space, GuestAddress(dma.address), &entry.data[offset as usize..], len as u64, ) .is_err() { dma.control |= FW_CFG_DMA_CTL_ERROR; } if is_write { let mut dma_read_error = false; let data = &mut entry.data[offset as usize..]; if read_dma_memory(&mem_space, GuestAddress(dma.address), data, len as u64) .is_err() { dma_read_error = true; } if dma_read_error || !entry.allow_write || len != dma.length { dma.control |= FW_CFG_DMA_CTL_ERROR; } else if true { if let Some(cb) = &entry.write_cb { cb.lock().unwrap().write_callback( data.to_vec(), offset as u64, len as usize, ); } } } offset += len; } dma.length -= len; dma.address += len as u64 } self.cur_offset = offset; write_dma_result(&self.mem_space, dma_addr, dma.control)?; Ok(()) } /// Write DMA mem register /// /// # Arguments /// /// * `addr` - The address to write to /// * `value` - Value to write /// * `size` - Length of raw bytes to write /// /// # Errors /// /// Return Error if fail to add the file entry. fn dma_mem_write(&mut self, addr: u64, value: u64, size: u32) -> Result<()> { if size == 4 { if addr == 0 { self.dma_addr = GuestAddress(value << 32); } else if addr == 4 { self.dma_addr = GuestAddress(self.dma_addr.raw_value() | value); self.handle_dma_request()?; } } else if size == 8 && addr == 0 { self.dma_addr = GuestAddress(value); self.handle_dma_request()?; } else { bail!( "Failed to set DMA address, offset is 0x{:x}, size is 0x{:x}", addr, size ); } Ok(()) } /// Read DMA mem register /// /// # Arguments /// /// * `addr` - The address to read to /// * `size` - Length of raw bytes to read /// /// # Return /// /// Return the value of the register fn dma_mem_read(&self, addr: u64, size: u32) -> Result<u64> { extract_u64( FW_CFG_DMA_SIGNATURE as u64, ((8 - addr - size as u64) * 8) as u32, size * 8, ) .with_context(|| "Failed to extract bits from u64") } /// Read data register /// /// # Arguments /// /// * `_addr` - The address to read to /// * `size` - Length of raw bytes to read /// /// # Return /// /// Return the value of the register fn read_data_reg(&mut self, _addr: u64, mut size: u32) -> Result<u64> { if size == 0 || size > std::mem::size_of::<u64>() as u32 { bail!( "Failed to read from FWcfg data register, size {} overflows", size ); } let cur_entry = self.cur_entry; let mut cur_offset = self.cur_offset; let entry = self.get_entry_mut()?; let mut value: u64 = 0; if cur_entry != FW_CFG_INVALID && !entry.data.is_empty() && cur_offset < entry.data.len() as u32 { loop { value = (value << 8) | entry.data[cur_offset as usize] as u64; cur_offset += 1; size -= 1; if size == 0 || cur_offset >= entry.data.len() as u32 { break; } } value <<= 8 * size as u64; } self.cur_offset = cur_offset; Ok(value) } fn common_realize(&mut self) -> Result<()> { // Firmware configurations add Signature item let sig = &[b'Q', b'E', b'M', b'U']; self.add_entry(FwCfgEntryType::Signature, None, None, sig.to_vec(), false)?; self.add_entry( FwCfgEntryType::NoGraphic, None, None, (0_u16).as_bytes().to_vec(), false, )?; self.add_entry( FwCfgEntryType::BootMenu, None, None, (0_u16).as_bytes().to_vec(), false, )?; // Add FileDir item self.add_entry(FwCfgEntryType::FileDir, None, None, Vec::new(), false)?; // Add boot-fail-wait file item, default to 5s self.add_file_callback( "etc/boot-fail-wait", (5_u32).as_bytes().to_vec(), None, None, false, )?; // Firmware version let mut version = FW_CFG_VERSION; if self.dma_enabled { version |= FW_CFG_VERSION_DMA; } self.add_entry( FwCfgEntryType::Id, None, None, version.as_bytes().to_vec(), false, )?; Ok(()) } } fn get_io_count(data_len: usize) -> usize { if data_len % 8 == 0 { 8 } else if data_len % 4 == 0 { 4 } else if data_len % 2 == 0 { 2 } else { 1 } } fn common_read( #[cfg(target_arch = "aarch64")] fwcfg_arch: &mut FwCfgMem, #[cfg(target_arch = "x86_64")] fwcfg_arch: &mut FwCfgIO, data: &mut [u8], base: GuestAddress, offset: u64, ) -> bool { let data_len = data.len(); let io_count = get_io_count(data_len); for i in (0..data_len).step_by(io_count) { if !read_bytes(fwcfg_arch, &mut data[i..i + io_count], base, offset) { return false; } } true } /// FwCfg MMIO Device use for AArch64 platform #[cfg(target_arch = "aarch64")] pub struct FwCfgMem { base: SysBusDevBase, fwcfg: FwCfgCommon, } #[cfg(target_arch = "aarch64")] impl FwCfgMem { pub fn new(sys_mem: Arc<AddressSpace>) -> Self { FwCfgMem { base: SysBusDevBase::new(SysBusDevType::FwCfg), fwcfg: FwCfgCommon::new(sys_mem), } } pub fn realize( mut self, sysbus: &mut SysBus, region_base: u64, region_size: u64, ) -> Result<Arc<Mutex<Self>>> { self.fwcfg.common_realize()?; self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| "Failed to allocate system resource for FwCfg.")?; let dev = Arc::new(Mutex::new(self)); sysbus .attach_device(&dev, region_base, region_size, "FwCfgMem") .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) } } #[cfg(target_arch = "aarch64")] fn read_bytes( fwcfg_arch: &mut FwCfgMem, data: &mut [u8], _base: GuestAddress, offset: u64, ) -> bool { let value = match offset { 0..=7 => match fwcfg_arch.fwcfg.read_data_reg(offset, data.len() as u32) { Ok(val) => val, Err(e) => { error!("Failed to read from FwCfg data register, error is {:?}", e); return false; } }, 8..=15 => { error!("Read from FwCfg control register is not supported."); 0 } 16..=23 => { if (offset + data.len() as u64) > 0x18 { error!( "Illegal parameter: the sum of addr 0x{:x} and size 0x{:x} overflow", offset - 0x10, data.len() ); return false; } match fwcfg_arch .fwcfg .dma_mem_read(offset - 0x10, data.len() as u32) { Ok(val) => val, Err(e) => { error!("Failed to handle FwCfg DMA-read, error is {:?}", e); return false; } } } _ => { error!("Failed to read FwCfg, offset 0x{:x} is invalid", offset); return false; } }; match data.len() { 1 => data[0] = value as u8, 2 => BigEndian::write_u16(data, value as u16), 4 => BigEndian::write_u32(data, value as u32), 8 => BigEndian::write_u64(data, value), _ => {} } true } #[cfg(target_arch = "aarch64")] impl FwCfgOps for FwCfgMem { fn fw_cfg_common(&mut self) -> &mut FwCfgCommon { &mut self.fwcfg } } #[cfg(target_arch = "aarch64")] impl Device for FwCfgMem { fn device_base(&self) -> &DeviceBase { &self.base.base } fn device_base_mut(&mut self) -> &mut DeviceBase { &mut self.base.base } } #[cfg(target_arch = "aarch64")] impl SysBusDevOps for FwCfgMem { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base } fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { &mut self.base } fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { common_read(self, data, base, offset) } fn write(&mut self, data: &[u8], _base: GuestAddress, offset: u64) -> bool { let size = data.len() as u32; let value = match size { 1 => data[0] as u64, 2 => BigEndian::read_u16(data) as u64, 4 => BigEndian::read_u32(data) as u64, 8 => BigEndian::read_u64(data), _ => 0, }; match offset { 0..=7 => { error!("Write to FwCfg data register is not supported."); } 8..=15 => { // Write to FwCfg control register self.fwcfg.select_entry(value as u16); } 16..=23 => { if self .fwcfg .dma_mem_write(offset - 0x10, value, size) .is_err() { error!("Failed to write dma at offset=0x{:x}.", offset); return false; } } _ => { error!("Failed to write FwCfg, offset 0x{:x} is invalid", offset); return false; } } true } fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } fn set_sys_resource( &mut self, _sysbus: &mut SysBus, region_base: u64, region_size: u64, ) -> Result<()> { let res = self.get_sys_resource_mut().unwrap(); res.region_base = region_base; res.region_size = region_size; Ok(()) } fn reset(&mut self) -> Result<()> { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) } } #[allow(clippy::upper_case_acronyms)] #[cfg(target_arch = "x86_64")] pub struct FwCfgIO { base: SysBusDevBase, fwcfg: FwCfgCommon, } #[cfg(target_arch = "x86_64")] impl FwCfgIO { pub fn new(sys_mem: Arc<AddressSpace>) -> Self { FwCfgIO { base: SysBusDevBase { base: DeviceBase::default(), dev_type: SysBusDevType::FwCfg, res: SysRes { region_base: FW_CFG_IO_BASE, region_size: FW_CFG_IO_SIZE, irq: -1, }, ..Default::default() }, fwcfg: FwCfgCommon::new(sys_mem), } } pub fn realize(mut self, sysbus: &mut SysBus) -> Result<Arc<Mutex<Self>>> { self.fwcfg.common_realize()?; let region_base = self.base.res.region_base; let region_size = self.base.res.region_size; self.set_sys_resource(sysbus, region_base, region_size) .with_context(|| "Failed to allocate system resource for FwCfg.")?; let dev = Arc::new(Mutex::new(self)); sysbus .attach_device(&dev, region_base, region_size, "FwCfgIO") .with_context(|| "Failed to attach FwCfg device to system bus.")?; Ok(dev) } } #[cfg(target_arch = "x86_64")] fn read_bytes(fwcfg_arch: &mut FwCfgIO, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { let value: u64 = match offset { 0..=1 => match fwcfg_arch.fwcfg.read_data_reg(offset, data.len() as u32) { Err(e) => { error!("Failed to read from FwCfg data register, error is {:?}", e); return false; } Ok(val) => val, }, 4..=11 => { if (offset + data.len() as u64) > 0x14 { error!( "Illegal parameter: the sum of addr 0x{:x} and size 0x{:x} overflow", offset - 0x10, data.len() ); return false; } match fwcfg_arch.fwcfg.dma_mem_read(offset - 4, data.len() as u32) { Err(e) => { error!("Failed to handle FwCfg DMA-read, error is {:?}", e); return false; } Ok(val) => val, } } _ => { // This should never happen error!( "Failed to read FwCfg, ioport 0x{:x} is invalid", base.0 + offset ); return false; } }; match data.len() { 1 => data[0] = value as u8, 2 => BigEndian::write_u16(data, value as u16), 4 => BigEndian::write_u32(data, value as u32), 8 => BigEndian::write_u64(data, value), _ => { warn!( "Failed to read from FwCfg data register, data length {} is invalid", data.len() ); return false; } } true } #[cfg(target_arch = "x86_64")] impl FwCfgOps for FwCfgIO { fn fw_cfg_common(&mut self) -> &mut FwCfgCommon { &mut self.fwcfg } } #[cfg(target_arch = "x86_64")] impl Device for FwCfgIO { fn device_base(&self) -> &DeviceBase { &self.base.base } fn device_base_mut(&mut self) -> &mut DeviceBase { &mut self.base.base } } #[cfg(target_arch = "x86_64")] impl SysBusDevOps for FwCfgIO { fn sysbusdev_base(&self) -> &SysBusDevBase { &self.base } fn sysbusdev_base_mut(&mut self) -> &mut SysBusDevBase { &mut self.base } fn read(&mut self, data: &mut [u8], base: GuestAddress, offset: u64) -> bool { common_read(self, data, base, offset) } fn write(&mut self, data: &[u8], base: GuestAddress, offset: u64) -> bool { let size = data.len() as u32; match offset { 0..=1 => { if size != 2 { error!( "Failed to write FwCfg control register, data length {} is invalid", data.len() ); return false; } self.fwcfg.select_entry(LittleEndian::read_u16(data)); } 4..=11 => { let value = match size { 1 => data[0] as u64, 2 => BigEndian::read_u16(data) as u64, 4 => BigEndian::read_u32(data) as u64, 8 => BigEndian::read_u64(data), _ => 0, }; if let Err(e) = self.fwcfg.dma_mem_write(offset - 4, value, size) { error!("Failed to handle FwCfg DMA-write, error is {:?}", e); return false; } } _ => { // This should never happen error!( "Failed to write FwCfg, ioport 0x{:x} is invalid", base.0 + offset ); return false; } } true } fn get_sys_resource_mut(&mut self) -> Option<&mut SysRes> { Some(&mut self.base.res) } fn set_sys_resource( &mut self, _sysbus: &mut SysBus, region_base: u64, region_size: u64, ) -> Result<()> { let res = self.get_sys_resource_mut().unwrap(); res.region_base = region_base; res.region_size = region_size; Ok(()) } fn reset(&mut self) -> Result<()> { self.fwcfg.select_entry(FwCfgEntryType::Signature as u16); Ok(()) } } pub trait FwCfgOps: Send + Sync { fn fw_cfg_common(&mut self) -> &mut FwCfgCommon; /// Add an entry to FwCfg device, with Vector content. /// /// # Arguments /// /// * `key` - FwCfgEntryType /// * `data` - Raw data bytes of the entry to be added fn add_data_entry(&mut self, key: FwCfgEntryType, data: Vec<u8>) -> Result<()> { self.fw_cfg_common().add_entry(key, None, None, data, false) } /// Add an entry to FwCfg device, with String content. /// /// # Arguments /// /// * `key` - FwCfgEntryType /// * `value` - string data entry to be added fn add_string_entry(&mut self, key: FwCfgEntryType, value: &str) -> Result<()> { let mut bytes = value.as_bytes().to_vec(); bytes.push(0_u8); self.fw_cfg_common() .add_entry(key, None, None, bytes, false) } /// Add a file entry to FwCfg device, with select callback function and /// write callback function. /// /// # Arguments /// /// * `filename` - Name of the file /// * `data` - Raw data bytes of the file to be added /// * `select_cb` - Select callback /// * `write_cb` - Write callback /// * `allow_write` - Does the file allow write fn add_file_callback_entry( &mut self, filename: &str, data: Vec<u8>, select_cb: Option<FwCfgCallbackType>, write_cb: Option<FwCfgWriteCallbackType>, allow_write: bool, ) -> Result<()> { self.fw_cfg_common() .add_file_callback(filename, data, select_cb, write_cb, allow_write) } /// Add a file entry to FwCfg device, without callbacks, write-allow. /// /// # Arguments /// /// * `filename` - Name of the file /// * `data` - Raw data bytes of the file to be added fn add_file_entry(&mut self, filename: &str, data: Vec<u8>) -> Result<()> { self.fw_cfg_common() .add_file_callback(filename, data, None, None, true) } /// Modify a file entry to FwCfg device, without callbacks, write-allow. /// /// # Arguments /// /// * `filename` - Name of the file /// * `data` - Raw data bytes of the file to be modified fn modify_file_entry(&mut self, filename: &str, data: Vec<u8>) -> Result<()> { self.fw_cfg_common() .modify_file_callback(filename, data, None, None, true) } } #[cfg(target_arch = "aarch64")] impl AmlBuilder for FwCfgMem { fn aml_bytes(&self) -> Vec<u8> { let mut acpi_dev = AmlDevice::new("FWCF"); acpi_dev.append_child(AmlNameDecl::new("_HID", AmlString("QEMU0002".to_string()))); acpi_dev.append_child(AmlNameDecl::new("_STA", AmlInteger(0xB))); acpi_dev.append_child(AmlNameDecl::new("_CCA", AmlInteger(0x1))); let mut res = AmlResTemplate::new(); res.append_child(AmlMemory32Fixed::new( AmlReadAndWrite::ReadWrite, self.base.res.region_base as u32, self.base.res.region_size as u32, )); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); acpi_dev.aml_bytes() } } #[cfg(target_arch = "x86_64")] impl AmlBuilder for FwCfgIO { fn aml_bytes(&self) -> Vec<u8> { let mut acpi_dev = AmlDevice::new("FWCF"); acpi_dev.append_child(AmlNameDecl::new("_HID", AmlString("QEMU0002".to_string()))); acpi_dev.append_child(AmlNameDecl::new("_STA", AmlInteger(0xB))); let mut res = AmlResTemplate::new(); res.append_child(AmlIoResource::new( AmlIoDecode::Decode16, self.base.res.region_base as u16, self.base.res.region_base as u16, 0x01, self.base.res.region_size as u8, )); acpi_dev.append_child(AmlNameDecl::new("_CRS", res)); acpi_dev.aml_bytes() } } #[cfg(test)] mod test { use super::*; use crate::sysbus::{IRQ_BASE, IRQ_MAX}; use address_space::{AddressSpace, HostMemMapping, Region}; fn sysbus_init() -> SysBus { let sys_mem = AddressSpace::new( Region::init_container_region(u64::max_value(), "sys_mem"), "sys_mem", None, ) .unwrap(); #[cfg(target_arch = "x86_64")] let sys_io = AddressSpace::new( Region::init_container_region(1 << 16, "sys_io"), "sys_io", None, ) .unwrap(); let free_irqs: (i32, i32) = (IRQ_BASE, IRQ_MAX); let mmio_region: (u64, u64) = (0x0A00_0000, 0x1000_0000); SysBus::new( #[cfg(target_arch = "x86_64")] &sys_io, &sys_mem, free_irqs, mmio_region, ) } fn address_space_init() -> Arc<AddressSpace> { let root = Region::init_container_region(1 << 36, "root"); let sys_space = AddressSpace::new(root, "sys_space", None).unwrap(); let host_mmap = Arc::new( HostMemMapping::new( GuestAddress(0), None, 0x1000_0000, None, false, false, false, ) .unwrap(), ); sys_space .root() .add_subregion( Region::init_ram_region(host_mmap.clone(), "region_1"), host_mmap.start_address().raw_value(), ) .unwrap(); sys_space } #[test] fn test_entry_functions() { let sys_mem = address_space_init(); let mut fwcfg_common = FwCfgCommon::new(sys_mem); let ncpus: usize = 1; fwcfg_common .add_entry( FwCfgEntryType::NbCpus, None, None, ncpus.as_bytes().to_vec(), false, ) .unwrap(); assert_eq!( fwcfg_common.entries[FwCfgEntryType::NbCpus as usize].data, ncpus.as_bytes().to_vec() ); let cmdline = "console=ttyAMA0 reboot=t panic=1 tsc=reliable ipv6.disable=1 root=/dev/vda rw" .to_string(); fwcfg_common .add_entry( FwCfgEntryType::CmdlineSize, None, None, (cmdline.len() + 1).as_bytes().to_vec(), false, ) .unwrap(); assert_eq!( fwcfg_common.entries[FwCfgEntryType::CmdlineSize as usize].data, (cmdline.len() + 1).as_bytes().to_vec() ); fwcfg_common .add_entry( FwCfgEntryType::CmdlineData, None, None, cmdline.as_bytes().to_vec(), false, ) .unwrap(); assert_eq!( fwcfg_common.entries[FwCfgEntryType::CmdlineData as usize].data, cmdline.as_bytes().to_vec() ); let boot_order = Vec::<u8>::new(); fwcfg_common .add_entry(FwCfgEntryType::FileDir, None, None, boot_order, false) .unwrap(); assert_eq!( fwcfg_common.entries[FwCfgEntryType::FileDir as usize].data, Vec::<u8>::new() ); } #[test] fn test_file_entry() { let sys_mem = address_space_init(); let mut fwcfg_common = FwCfgCommon::new(sys_mem); assert_eq!(fwcfg_common.common_realize().is_ok(), true); let file_data = vec![0xcc; 200]; assert_eq!( fwcfg_common .add_file_callback("xyz", file_data.clone(), None, None, false) .is_ok(), true ); let id = fwcfg_common.files.last().unwrap().select; fwcfg_common.select_entry(id); let entry = fwcfg_common.get_entry_mut().unwrap(); assert_eq!(entry.data, file_data); let file_data = vec![0x8b; 200]; assert_eq!( fwcfg_common .add_file_callback("def", file_data.clone(), None, None, false) .is_ok(), true ); let id = fwcfg_common.files[0].select; fwcfg_common.select_entry(id); let entry = fwcfg_common.get_entry_mut().unwrap(); assert_eq!(entry.data, file_data); let file_data = Vec::new(); assert_eq!( fwcfg_common .add_file_callback("bootorder", file_data.clone(), None, None, false) .is_ok(), true ); let id = fwcfg_common.files[0].select; fwcfg_common.select_entry(id); let entry = fwcfg_common.get_entry_mut().unwrap(); assert_eq!(entry.data, file_data); let modify_file_data = "/pci@ffffffffffffffff/pci-bridge@3/scsi@0,1/disk@0,0" .as_bytes() .to_vec(); assert_eq!( fwcfg_common .modify_file_callback("bootorder", modify_file_data.clone(), None, None, true) .is_ok(), true ); let id = fwcfg_common.files[0].select; fwcfg_common.select_entry(id); let entry = fwcfg_common.get_entry_mut().unwrap(); assert_eq!(entry.data, modify_file_data); assert_eq!( fwcfg_common .modify_file_callback("abc", modify_file_data.clone(), None, None, true) .is_err(), true ); let file_data = vec![0x33; 500]; assert_eq!( fwcfg_common .add_file_callback("abc", file_data.clone(), None, None, false) .is_ok(), true ); let id = fwcfg_common.files[0].select; fwcfg_common.select_entry(id); let entry = fwcfg_common.get_entry_mut().unwrap(); assert_eq!(entry.data, file_data); } #[test] fn test_dma_functions() { let sys_mem = address_space_init(); let mut fwcfg_common = FwCfgCommon::new(sys_mem); assert_eq!(fwcfg_common.common_realize().is_ok(), true); // [1]write dma request. let mut dma_req = FwCfgDmaAccess::default(); dma_req.length = *u32::from_bytes(&4_u32.to_be_bytes()).unwrap(); dma_req.address = *u64::from_bytes(&0xffff_u64.to_be_bytes()).unwrap(); dma_req.control = *u32::from_bytes(&FW_CFG_DMA_CTL_READ.to_be_bytes()).unwrap(); let dma_request = dma_req.as_mut_bytes(); let addr = GuestAddress(0x0000); fwcfg_common .mem_space .write(&mut dma_request.as_ref(), addr, dma_request.len() as u64) .unwrap(); // [2]set dma addr. fwcfg_common.dma_addr = addr; // [3]handle dma request. assert_eq!(fwcfg_common.handle_dma_request().is_ok(), true); // [4]check dma response. assert_eq!(fwcfg_common.mem_space.read_object::<u32>(addr).unwrap(), 0); // [5]check dma write result. let mut read_dma_buf = Vec::new(); let sig_entry_data = [b'Q', b'E', b'M', b'U']; let len = sig_entry_data.len(); fwcfg_common .mem_space .read(&mut read_dma_buf, GuestAddress(0xffff), len as u64) .unwrap(); assert_eq!(read_dma_buf, sig_entry_data); // Read uninitialized entry. let mut dma_req = FwCfgDmaAccess::default(); dma_req.length = *u32::from_bytes(&8_u32.to_be_bytes()).unwrap(); dma_req.address = *u64::from_bytes(&0xffff_u64.to_be_bytes()).unwrap(); dma_req.control = *u32::from_bytes(&FW_CFG_DMA_CTL_READ.to_be_bytes()).unwrap(); let dma_request = dma_req.as_mut_bytes(); let addr = GuestAddress(0x0000); fwcfg_common .mem_space .write(&mut dma_request.as_ref(), addr, dma_request.len() as u64) .unwrap(); fwcfg_common.dma_addr = addr; assert_eq!(fwcfg_common.handle_dma_request().is_ok(), true); // Result should be all zero. assert_eq!(fwcfg_common.mem_space.read_object::<u32>(addr).unwrap(), 0); let mut read_dma_buf = Vec::new(); let all_zero = vec![0x0_u8; 4]; let len = all_zero.len(); fwcfg_common .mem_space .read(&mut read_dma_buf, GuestAddress(0xffff), len as u64) .unwrap(); assert_eq!(read_dma_buf, all_zero); } #[test] #[cfg(target_arch = "aarch64")] fn test_read_write_aarch64() { let mut sys_bus = sysbus_init(); let sys_mem = address_space_init(); let fwcfg = FwCfgMem::new(sys_mem); let fwcfg_dev = FwCfgMem::realize(fwcfg, &mut sys_bus, 0x0902_0000, 0x0000_0018).unwrap(); // Read FW_CFG_DMA_SIGNATURE entry. let base = GuestAddress(0x0000); let mut read_data = vec![0xff_u8, 0xff, 0xff, 0xff]; let offset = 0x10; let target_data = vec![0x51_u8, 0x45, 0x4d, 0x55]; fwcfg_dev.lock().unwrap().read(&mut read_data, base, offset); assert_eq!(read_data, target_data); // Select entry and read it. let write_data = vec![0x0_u8]; let offset = 0x8; let f_back = fwcfg_dev.lock().unwrap().write(&write_data, base, offset); assert_eq!(f_back, true); let mut read_data = vec![0xff_u8, 0xff, 0xff, 0xff]; let offset = 0x4; let sig_entry_data = [b'Q', b'E', b'M', b'U']; fwcfg_dev.lock().unwrap().read(&mut read_data, base, offset); assert_eq!(read_data, sig_entry_data); // Failed to write because of offset overflow. let write_data = vec![0x0_u8]; let offset = 0x18; let f_back = fwcfg_dev.lock().unwrap().write(&write_data, base, offset); assert_eq!(f_back, false); // Illegal write_data length. let write_data = vec![0x0_u8]; let offset = 0x10; let f_back = fwcfg_dev.lock().unwrap().write(&write_data, base, offset); assert_eq!(f_back, false); } #[test] #[cfg(target_arch = "x86_64")] fn test_read_write_x86_64() { let mut sys_bus = sysbus_init(); let sys_mem = address_space_init(); let fwcfg = FwCfgIO::new(sys_mem); let fwcfg_dev = FwCfgIO::realize(fwcfg, &mut sys_bus).unwrap(); // Read FW_CFG_DMA_SIGNATURE entry. let base = GuestAddress(0x0000); let mut read_data = vec![0xff_u8, 0xff, 0xff, 0xff]; let offset = 0x4; let target_data = vec![0x51_u8, 0x45, 0x4d, 0x55]; fwcfg_dev.lock().unwrap().read(&mut read_data, base, offset); assert_eq!(read_data, target_data); // Select entry and read it. let write_data = vec![0x0_u8, 0x0]; let offset = 0x0; let f_back = fwcfg_dev.lock().unwrap().write(&write_data, base, offset); assert_eq!(f_back, true); let mut read_data = vec![0xff_u8, 0xff, 0xff, 0xff]; let offset = 0x0; let sig_entry_data = [b'Q', b'E', b'M', b'U']; fwcfg_dev.lock().unwrap().read(&mut read_data, base, offset); assert_eq!(read_data, sig_entry_data); // Failed to write because of offset overflow. let write_data = vec![0x0_u8]; let offset = 0x0c; let f_back = fwcfg_dev.lock().unwrap().write(&write_data, base, offset); assert_eq!(f_back, false); // Illegal write_data length. let write_data = vec![0x0_u8]; let offset = 0x0; let f_back = fwcfg_dev.lock().unwrap().write(&write_data, base, offset); assert_eq!(f_back, false); } }