// Copyright (c) 2022 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::mem::size_of; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, Weak}; use std::time::Duration; use anyhow::{Context, Result}; use drm_fourcc::DrmFourcc; use log::error; use super::fwcfg::{FwCfgOps, FwCfgWriteCallback}; use crate::sysbus::{SysBus, SysBusDevBase, SysBusDevOps, SysBusDevType}; use crate::{Device, DeviceBase}; use acpi::AmlBuilder; use address_space::{AddressSpace, GuestAddress}; use machine_manager::event_loop::EventLoop; use ui::console::{ console_init, display_graphic_update, display_replace_surface, ConsoleType, DisplayConsole, DisplaySurface, HardWareOperations, }; use ui::input::{key_event, KEYCODE_RET}; use util::pixman::{pixman_format_bpp, pixman_format_code_t, pixman_image_create_bits}; const BYTES_PER_PIXELS: u32 = 8; const WIDTH_MAX: u32 = 16_000; const HEIGHT_MAX: u32 = 12_000; const INSTALL_CHECK_INTERVEL_MS: u64 = 500; const INSTALL_RELEASE_INTERVEL_MS: u64 = 200; const INSTALL_PRESS_INTERVEL_MS: u64 = 100; #[repr(packed)] struct RamfbCfg { _addr: u64, _fourcc: u32, _flags: u32, _width: u32, _height: u32, _stride: u32, } #[derive(Clone)] pub struct RamfbState { pub surface: Option<DisplaySurface>, pub con: Option<Weak<Mutex<DisplayConsole>>>, sys_mem: Arc<AddressSpace>, install: Arc<AtomicBool>, } // SAFETY: The type of image, the field of the struct DisplaySurface // is the raw pointer. create_display_surface() method will create // image object. The memory that the image pointer refers to is // modified by guest OS and accessed by vnc. So implement Sync and // Send is safe. unsafe impl Sync for RamfbState {} // SAFETY: The reason is same as above. unsafe impl Send for RamfbState {} impl RamfbState { pub fn new(sys_mem: Arc<AddressSpace>, install: bool) -> Self { let ramfb_opts = Arc::new(RamfbInterface {}); let con = console_init("ramfb".to_string(), ConsoleType::Graphic, ramfb_opts); Self { surface: None, con, sys_mem, install: Arc::new(AtomicBool::new(install)), } } pub fn setup(&mut self, fw_cfg: &Arc<Mutex<dyn FwCfgOps>>) -> Result<()> { let mut locked_fw_cfg = fw_cfg.lock().unwrap(); let ramfb_state_cb = self.clone(); let cfg: Vec<u8> = [0; size_of::<RamfbCfg>()].to_vec(); locked_fw_cfg .add_file_callback_entry( "etc/ramfb", cfg, None, Some(Arc::new(Mutex::new(ramfb_state_cb))), true, ) .with_context(|| "Failed to set fwcfg")?; Ok(()) } fn create_display_surface( &mut self, width: u32, height: u32, format: pixman_format_code_t, mut stride: u32, addr: u64, ) { if width < 16 || height < 16 || width > WIDTH_MAX || height > HEIGHT_MAX { error!("The resolution: {}x{} is unsupported.", width, height); } if stride == 0 { let linesize = width * pixman_format_bpp(format as u32) as u32 / BYTES_PER_PIXELS; stride = linesize; } let fb_addr = match self.sys_mem.addr_cache_init(GuestAddress(addr)) { Some((hva, len)) => { if len < stride as u64 { error!("Insufficient contiguous memory length"); return; } hva } None => { error!("Failed to get the host address of the framebuffer"); return; } }; let mut ds = DisplaySurface { format, ..Default::default() }; // SAFETY: pixman_image_create_bits() is C function. All // parameters passed of the function have been checked. // It returns a raw pointer. unsafe { ds.image = pixman_image_create_bits( format, width as i32, height as i32, fb_addr as *mut u32, stride as i32, ); } if ds.image.is_null() { error!("Failed to create the surface of Ramfb!"); return; } self.surface = Some(ds); set_press_event(self.install.clone(), fb_addr as *const u8); } fn reset_ramfb_state(&mut self) { self.surface = None; } } impl FwCfgWriteCallback for RamfbState { fn write_callback(&mut self, data: Vec<u8>, _start: u64, _len: usize) { if data.len() < 28 { error!("RamfbCfg data format is incorrect"); return; } let addr = u64::from_be_bytes( data.as_slice() .split_at(size_of::<u64>()) .0 .try_into() .unwrap(), ); let fourcc = u32::from_be_bytes( data.as_slice()[8..] .split_at(size_of::<u32>()) .0 .try_into() .unwrap(), ); let width = u32::from_be_bytes( data.as_slice()[16..] .split_at(size_of::<u32>()) .0 .try_into() .unwrap(), ); let height = u32::from_be_bytes( data.as_slice()[20..] .split_at(size_of::<u32>()) .0 .try_into() .unwrap(), ); let stride = u32::from_be_bytes( data.as_slice()[24..] .split_at(size_of::<u32>()) .0 .try_into() .unwrap(), ); let format: pixman_format_code_t = if fourcc == DrmFourcc::Xrgb8888 as u32 { pixman_format_code_t::PIXMAN_x8r8g8b8 } else { error!("Unsupported drm format: {}", fourcc); return; }; self.create_display_surface(width, height, format, stride, addr); display_replace_surface(&self.con, self.surface) .unwrap_or_else(|e| error!("Error occurs during surface switching: {:?}", e)); } } pub struct RamfbInterface {} impl HardWareOperations for RamfbInterface { fn hw_update(&self, con: Arc<Mutex<DisplayConsole>>) { let locked_con = con.lock().unwrap(); let width = locked_con.width; let height = locked_con.height; drop(locked_con); display_graphic_update(&Some(Arc::downgrade(&con)), 0, 0, width, height) .unwrap_or_else(|e| error!("Error occurs during graphic updating: {:?}", e)); } } pub struct Ramfb { base: SysBusDevBase, pub ramfb_state: RamfbState, } impl Ramfb { pub fn new(sys_mem: Arc<AddressSpace>, install: bool) -> Self { Ramfb { base: SysBusDevBase::new(SysBusDevType::Ramfb), ramfb_state: RamfbState::new(sys_mem, install), } } pub fn realize(self, sysbus: &mut SysBus) -> Result<()> { let dev = Arc::new(Mutex::new(self)); sysbus.attach_dynamic_device(&dev)?; Ok(()) } } impl Device for Ramfb { fn device_base(&self) -> &DeviceBase { &self.base.base } fn device_base_mut(&mut self) -> &mut DeviceBase { &mut self.base.base } } impl SysBusDevOps for Ramfb { 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 { error!("Ramfb can not be read!"); false } fn write(&mut self, _data: &[u8], _base: GuestAddress, _offset: u64) -> bool { error!("Ramfb can not be written!"); false } fn reset(&mut self) -> Result<()> { self.ramfb_state.reset_ramfb_state(); Ok(()) } } impl AmlBuilder for Ramfb { fn aml_bytes(&self) -> Vec<u8> { Vec::new() } } fn set_press_event(install: Arc<AtomicBool>, data: *const u8) { let black_screen = // SAFETY: data is the raw pointer of framebuffer. EDKII has malloc the memory of // the framebuffer. So dereference the data is safe. unsafe { !data.is_null() && *data == 0 && *data.offset(1) == 0 && *data.offset(2) == 0 }; if install.load(Ordering::Acquire) && black_screen { let set_press_func = Box::new(move || { set_press_event(install.clone(), data); }); let press_func = Box::new(move || { key_event(KEYCODE_RET, true) .unwrap_or_else(|e| error!("Ramfb couldn't press return key: {:?}", e)); }); let release_func = Box::new(move || { key_event(KEYCODE_RET, false) .unwrap_or_else(|e| error!("Ramfb couldn't release return key: {:?}.", e)); }); let ctx = EventLoop::get_ctx(None).unwrap(); ctx.timer_add( set_press_func, Duration::from_millis(INSTALL_CHECK_INTERVEL_MS), ); ctx.timer_add(press_func, Duration::from_millis(INSTALL_PRESS_INTERVEL_MS)); ctx.timer_add( release_func, Duration::from_millis(INSTALL_RELEASE_INTERVEL_MS), ); } else { install.store(false, Ordering::Release); } }