1 // Copyright (C) 2024 Intel Corporation. 2 // Author(s): Zhao Liu <zhai1.liu@intel.com> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 use std::ptr::addr_of_mut; 6 7 use qemu_api::{cell::bql_locked, impl_zeroable, zeroable::Zeroable}; 8 9 /// Each `HPETState` represents a Event Timer Block. The v1 spec supports 10 /// up to 8 blocks. QEMU only uses 1 block (in PC machine). 11 const HPET_MAX_NUM_EVENT_TIMER_BLOCK: usize = 8; 12 13 #[repr(C, packed)] 14 #[derive(Copy, Clone, Default)] 15 pub struct HPETFwEntry { 16 pub event_timer_block_id: u32, 17 pub address: u64, 18 pub min_tick: u16, 19 pub page_prot: u8, 20 } 21 impl_zeroable!(HPETFwEntry); 22 23 #[repr(C, packed)] 24 #[derive(Copy, Clone, Default)] 25 pub struct HPETFwConfig { 26 pub count: u8, 27 pub hpet: [HPETFwEntry; HPET_MAX_NUM_EVENT_TIMER_BLOCK], 28 } 29 impl_zeroable!(HPETFwConfig); 30 31 #[allow(non_upper_case_globals)] 32 #[no_mangle] 33 pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig { 34 count: u8::MAX, 35 ..Zeroable::ZERO 36 }; 37 38 impl HPETFwConfig { 39 pub(crate) fn assign_hpet_id() -> usize { 40 assert!(bql_locked()); 41 // SAFETY: all accesses go through these methods, which guarantee 42 // that the accesses are protected by the BQL. 43 let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; 44 45 if fw_cfg.count == u8::MAX { 46 // first instance 47 fw_cfg.count = 0; 48 } 49 50 if fw_cfg.count == 8 { 51 // TODO: Add error binding: error_setg() 52 panic!("Only 8 instances of HPET is allowed"); 53 } 54 55 let id: usize = fw_cfg.count.into(); 56 fw_cfg.count += 1; 57 id 58 } 59 60 pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64) { 61 assert!(bql_locked()); 62 // SAFETY: all accesses go through these methods, which guarantee 63 // that the accesses are protected by the BQL. 64 let mut fw_cfg = unsafe { *addr_of_mut!(hpet_fw_cfg) }; 65 66 fw_cfg.hpet[hpet_id].event_timer_block_id = timer_block_id; 67 fw_cfg.hpet[hpet_id].address = address; 68 } 69 } 70