From nobody Thu Jan 8 13:19:46 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1766827147; cv=none; d=zohomail.com; s=zohoarc; b=hR8HBzP86hM1jY9l1eicPmvOU1B1mAf4bGEH3teGkSDqawbMV5ZYER8eQ3HhHC9gV7h0vn2VeeYQ/MoKxeylvPCdNEcXOD3HWz8SyWtu2BVUYuhy7R6LCwi+84HTJVTtIwWgWTOd12YBKsD0ldyQu3G3BihAvrH4KjKolbXgrNw= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1766827147; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=1Uq86M4XvjQn415+OGKJuuGSPMQ2/3Qzq3ETHRXpqV0=; b=WIJm6w6HXIj5F1EP10l224J95ObXBJtlset6GMPzkiojBOgFO0PNU1y/0KquDK/pje+SZ84BnAokXpRzR+IK40qsbpztT4MY8Ipdtfv1a0tUYG2ku94f3AD0NPj4sIL2NZGIXZ2drYcr0gO8QL5LvN9LG2q/VmQEDRQHNfeQsIE= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1766827147003304.620544433594; Sat, 27 Dec 2025 01:19:07 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vZQRf-0007xC-Hj; Sat, 27 Dec 2025 04:18:45 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vZQRR-00071l-4p for qemu-devel@nongnu.org; Sat, 27 Dec 2025 04:18:29 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vZQRO-0003tb-Rw for qemu-devel@nongnu.org; Sat, 27 Dec 2025 04:18:28 -0500 Received: from mail-wm1-f70.google.com (mail-wm1-f70.google.com [209.85.128.70]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-490-iQK6Cn2vPpOYGgkyUK6Itw-1; Sat, 27 Dec 2025 04:18:24 -0500 Received: by mail-wm1-f70.google.com with SMTP id 5b1f17b1804b1-47d40e8a588so11256205e9.3 for ; Sat, 27 Dec 2025 01:18:24 -0800 (PST) Received: from [192.168.10.48] ([151.95.145.106]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-47d193d4f09sm442684595e9.12.2025.12.27.01.18.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 27 Dec 2025 01:18:21 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1766827106; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=1Uq86M4XvjQn415+OGKJuuGSPMQ2/3Qzq3ETHRXpqV0=; b=QoKifPe3YagwPMrqBXVTQnNqWIy60kKqeuefMqIML9RVvgo16F+wGoxOnwyAeSYHaDZlIr 1+n924TkhDqsCvk6RvwzE9e6CS4QHDeWyvulOIdCerH4kTr4iI4yoLgz0XKhdQrmgZhZ28 ioHFG/K2SU6XMLaSuVQ5zbVYpHoRTwQ= X-MC-Unique: iQK6Cn2vPpOYGgkyUK6Itw-1 X-Mimecast-MFC-AGG-ID: iQK6Cn2vPpOYGgkyUK6Itw_1766827103 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=google; t=1766827102; x=1767431902; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=1Uq86M4XvjQn415+OGKJuuGSPMQ2/3Qzq3ETHRXpqV0=; b=GYGfirU7wPBvIr3qvUzhro2bXwgWwP7m09VeywSrsOqbvHoKJk3mFY5FK43SSt2SjU eeT/NIi08RgbiVHPcmowWVAxVLkh4uG2zIzaqnMQnE0RarRG4wu7h+fJsMPSKvmzPX4s o8Z/Y8MJKJqfgBTiFHS1HNGYygUmwiiluIic7HRIQ8Ya1epJkBXPhAEHYLubVuysPD0O lXwB7PDC44pwAAmyq+RmgqPmxb5pAtpvtpy672JT8BB+j4CkLi5YTKEDGtfpK0861xqM WHVUkvXA8z8ppcTxG7teC/lZhgFMvfJt2yl1jTQSrk1bA7SryhwyFpfzwWOaTMbuPFVf a0VA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1766827102; x=1767431902; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=1Uq86M4XvjQn415+OGKJuuGSPMQ2/3Qzq3ETHRXpqV0=; b=Sbzn8CA07Yq8NSb4yiij0CgvbN87COVISUfE2uElpxKNEdjomEmUw7Yq1txm8CABeg g4eb7OpfzBPWw8kUhL750S83YrVvejXEdeaVpi8eGslkZFDBpgnNwzhthNu35RzgQL1m fNFKM6r3xJ7WUq+hGBly5WryyJXoydne67J/GgPuZwHRNmK7s3y4YX95BUkPcOUgSjrD YPMvyOcAmSa4Lt15qyxvhHRdhb3ljU8qgDF9Cty9SIMWObYm/MMhmTn51p3IbWeNyMh/ mTbClxm5sfulILpLUyvw57oGD+JD7bhqfoHTpxVn8vTs+TXY/PbvSxZuBygk0m1pvj3H vNbQ== X-Gm-Message-State: AOJu0YyHOT+lioVzK/39kuZArC5icD4+1xZ+mUwHSW1cPYyGxUjnMwNZ XJlc/161FxVdcqjptuK1q7p4ffxVz43OG3QAlrX8YRxWiU4ZFkFSVrmTuA3TyGqACH+lMW3OxU7 trW7B5i63LYvfLBIVBMftrvRLPo+7n7o3ZxYNDt/3LYkreY2cwYldkNgdCarK7CBPg22AD2iGAb ynK70HhUtsASujFZxhp0olXQZip5lnH8GgMMp5E4g7 X-Gm-Gg: AY/fxX78E6HypHv9y7YXRcJC5hktfL7O9g0Wwubb7AeEt9/p3hx6JuLpf+kK7DeK8Md CC6EPIGA+SWEs85OqjdI/zOdNTvxfdVyIPsEFa3X5L9snXSNDx59kWc0Lc06puHsyTn5tpJemR+ cfNRX6zVBBcoPLrgVVVMGFeAGe64DGAUftoJtIvG2GWLOLyia4qTFqSN1FWJ5kLDwv0CjlADzfk 75IXIoZgAKZXVsL2TDlxNQqNz93+D2rtdns3Ul2+QsaL1LxOi8mP68NMj2sD4OXU/NbUe0ZkgYR lIcso/A7o3O6dLu0A+fc/c7yijjkT248B0BljGTgda54/ZuQbyoPvqcDBfsG/20FgiyD1q7Ntpv 1d32ZyWxacuOncPLgoqES3A2fzTHdF6iivtqL8uC2vlKZO7j/G6gXZb1NrFtoknN6bZpxzDy0p+ PzTsU3/v75xzmB2Q4= X-Received: by 2002:a05:600c:1d0b:b0:479:2a0b:180d with SMTP id 5b1f17b1804b1-47d1954a5f7mr273164285e9.11.1766827102452; Sat, 27 Dec 2025 01:18:22 -0800 (PST) X-Google-Smtp-Source: AGHT+IHivJ8xaNlju7I0KZ5kVb6YandZ626sG0lgpCoPcmyYnMz0ayyBeGkWM9fUpLkr83TSKOCp3Q== X-Received: by 2002:a05:600c:1d0b:b0:479:2a0b:180d with SMTP id 5b1f17b1804b1-47d1954a5f7mr273163955e9.11.1766827101878; Sat, 27 Dec 2025 01:18:21 -0800 (PST) From: Paolo Bonzini To: qemu-devel@nongnu.org Cc: Zhao Liu Subject: [PULL 061/153] rust/hpet: Maintain HPETTimerRegisters in HPETRegisters Date: Sat, 27 Dec 2025 10:14:48 +0100 Message-ID: <20251227091622.20725-62-pbonzini@redhat.com> X-Mailer: git-send-email 2.52.0 In-Reply-To: <20251227091622.20725-1-pbonzini@redhat.com> References: <20251227091622.20725-1-pbonzini@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=pbonzini@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H3=0.001, RCVD_IN_MSPIKE_WL=0.001, RCVD_IN_VALIDITY_CERTIFIED_BLOCKED=0.001, RCVD_IN_VALIDITY_RPBL_BLOCKED=0.001, SPF_HELO_PASS=-0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1766827147550158500 Content-Type: text/plain; charset="utf-8" From: Zhao Liu Lockless IO requires holding a single lock during MMIO access, so that it's necessary to maintain timer N's registers (HPETTimerRegisters) with global register in one place. Therefore, move HPETTimerRegisters to HPETRegisters from HPETTimer, and access timer registers from HPETRegisters struct for the whole HPET code. This changes HPETTimer and HPETRegisters, and the layout of VMState has changed, which makes it incompatible to migrate with previous versions. Thus, bump up the version IDs in VMStates of HPETState and HPETTimer. The VMState version IDs of HPETRegisters doesn't need to change since it's a newly added struct and its version IDs doesn't affect the compatibility of HPETState's VMState. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20251113051937.4017675-18-zhao1.liu@intel.c= om Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/device.rs | 163 ++++++++++++++++++------------- 1 file changed, 96 insertions(+), 67 deletions(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/devi= ce.rs index 0e076a7f1d8..f9cdced5406 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -235,7 +235,6 @@ pub struct HPETTimer { /// timer block abstraction containing this timer state: NonNull, =20 - regs: HPETTimerRegisters, // Hidden register state /// comparator (extended to counter width) cmp64: u64, @@ -260,7 +259,6 @@ fn new(index: u8, state: *const HPETState) -> HPETTimer= { // is initialized below. qemu_timer: unsafe { Timer::new() }, state: NonNull::new(state.cast_mut()).unwrap(), - regs: Default::default(), cmp64: 0, period: 0, wrap_flag: 0, @@ -289,8 +287,14 @@ fn is_int_active(&self, hpet_regs: &BqlRefCell) -> bool { /// calculate next value of the general counter that matches the /// target (either entirely, or the low 32-bit only depending on /// the timer mode). - fn calculate_cmp64(&self, cur_tick: u64, target: u64) -> u64 { - if self.regs.is_32bit_mod() { + fn calculate_cmp64( + &self, + hpet_regs: &BqlRefCell, + cur_tick: u64, + target: u64, + ) -> u64 { + let tn_regs =3D &hpet_regs.borrow().tn_regs[self.index as usize]; + if tn_regs.is_32bit_mod() { let mut result: u64 =3D cur_tick.deposit(0, 32, target); if result < cur_tick { result +=3D 0x100000000; @@ -322,32 +326,33 @@ fn get_int_route(&self, regs: &HPETRegisters) -> usiz= e { // ... // If the LegacyReplacement Route bit is not set, the individu= al // routing bits for each of the timers are used. - self.regs.get_individual_route() + regs.tn_regs[self.index as usize].get_individual_route() } } =20 fn set_irq(&self, regs: &HPETRegisters, set: bool) { + let tn_regs =3D ®s.tn_regs[self.index as usize]; let route =3D self.get_int_route(regs); =20 - if set && self.regs.is_int_enabled() && regs.is_hpet_enabled() { - if self.regs.is_fsb_route_enabled() { + if set && tn_regs.is_int_enabled() && regs.is_hpet_enabled() { + if tn_regs.is_fsb_route_enabled() { // SAFETY: // the parameters are valid. unsafe { address_space_stl_le( addr_of_mut!(address_space_memory), - self.regs.fsb >> 32, // Timer N FSB int addr - self.regs.fsb as u32, // Timer N FSB int value, tr= uncate! + tn_regs.fsb >> 32, // Timer N FSB int addr + tn_regs.fsb as u32, // Timer N FSB int value, trun= cate! MEMTXATTRS_UNSPECIFIED, null_mut(), ); } - } else if self.regs.is_int_level_triggered() { + } else if tn_regs.is_int_level_triggered() { self.get_state().irqs[route].raise(); } else { self.get_state().irqs[route].pulse(); } - } else if !self.regs.is_fsb_route_enabled() { + } else if !tn_regs.is_fsb_route_enabled() { self.get_state().irqs[route].lower(); } } @@ -360,16 +365,17 @@ fn update_irq(&self, hpet_regs: &BqlRefCell, set: bool) { regs.int_status =3D regs.int_status.deposit( self.index.into(), 1, - u64::from(set && self.regs.is_int_level_triggered()), + u64::from(set && regs.tn_regs[self.index as usize].is_int_leve= l_triggered()), ); self.set_irq(®s, set); } =20 - fn arm_timer(&mut self, tick: u64) { + fn arm_timer(&mut self, hpet_regs: &BqlRefCell, tick: u= 64) { + let tn_regs =3D &hpet_regs.borrow().tn_regs[self.index as usize]; let mut ns =3D self.get_state().get_ns(tick); =20 // Clamp period to reasonable min value (1 us) - if self.regs.is_periodic() && ns - self.last < 1000 { + if tn_regs.is_periodic() && ns - self.last < 1000 { ns =3D self.last + 1000; } =20 @@ -377,21 +383,22 @@ fn arm_timer(&mut self, tick: u64) { self.qemu_timer.modify(self.last); } =20 - fn set_timer(&mut self) { + fn set_timer(&mut self, hpet_regs: &BqlRefCell) { + let tn_regs =3D &hpet_regs.borrow().tn_regs[self.index as usize]; let cur_tick: u64 =3D self.get_state().get_ticks(); =20 self.wrap_flag =3D 0; - self.cmp64 =3D self.calculate_cmp64(cur_tick, self.regs.cmp); - if self.regs.is_32bit_mod() { + self.cmp64 =3D self.calculate_cmp64(hpet_regs, cur_tick, tn_regs.c= mp); + if tn_regs.is_32bit_mod() { // HPET spec says in one-shot 32-bit mode, generate an interru= pt when // counter wraps in addition to an interrupt with comparator m= atch. - if !self.regs.is_periodic() && self.cmp64 > hpet_next_wrap(cur= _tick) { + if !tn_regs.is_periodic() && self.cmp64 > hpet_next_wrap(cur_t= ick) { self.wrap_flag =3D 1; - self.arm_timer(hpet_next_wrap(cur_tick)); + self.arm_timer(hpet_regs, hpet_next_wrap(cur_tick)); return; } } - self.arm_timer(self.cmp64); + self.arm_timer(hpet_regs, self.cmp64); } =20 fn del_timer(&self, hpet_regs: &BqlRefCell) { @@ -406,16 +413,16 @@ fn del_timer(&self, hpet_regs: &BqlRefCell) { } } =20 - /// Configuration and Capability Register - fn set_tn_cfg_reg( - &mut self, + fn prepare_tn_cfg_reg_new( + &self, hpet_regs: &BqlRefCell, shift: u32, len: u32, val: u64, - ) { + ) -> (u64, u64) { trace::trace_hpet_ram_write_tn_cfg((shift / 8).try_into().unwrap()= ); - let old_val: u64 =3D self.regs.config; + let tn_regs =3D &hpet_regs.borrow().tn_regs[self.index as usize]; + let old_val: u64 =3D tn_regs.config; let mut new_val: u64 =3D old_val.deposit(shift, len, val); new_val =3D hpet_fixup_reg(new_val, old_val, HPET_TN_CFG_WRITE_MAS= K); =20 @@ -426,7 +433,21 @@ fn set_tn_cfg_reg( self.update_irq(hpet_regs, false); } =20 - self.regs.config =3D new_val; + (new_val, old_val) + } + + /// Configuration and Capability Register + fn set_tn_cfg_reg( + &mut self, + hpet_regs: &BqlRefCell, + shift: u32, + len: u32, + val: u64, + ) { + // Factor out a prepare_tn_cfg_reg_new() to better handle immutabl= e scope. + let (new_val, old_val) =3D self.prepare_tn_cfg_reg_new(hpet_regs, = shift, len, val); + let tn_regs =3D &mut hpet_regs.borrow_mut().tn_regs[self.index as = usize]; + tn_regs.config =3D new_val; =20 if activating_bit(old_val, new_val, HPET_TN_CFG_INT_ENABLE_SHIFT) && self.is_int_active(hpet_regs) @@ -434,13 +455,13 @@ fn set_tn_cfg_reg( self.update_irq(hpet_regs, true); } =20 - if self.regs.is_32bit_mod() { - self.regs.cmp =3D u64::from(self.regs.cmp as u32); // truncate! + if tn_regs.is_32bit_mod() { + tn_regs.cmp =3D u64::from(tn_regs.cmp as u32); // truncate! self.period =3D u64::from(self.period as u32); // truncate! } =20 if hpet_regs.borrow().is_hpet_enabled() { - self.set_timer(); + self.set_timer(hpet_regs); } } =20 @@ -452,10 +473,11 @@ fn set_tn_cmp_reg( len: u32, val: u64, ) { + let tn_regs =3D &mut hpet_regs.borrow_mut().tn_regs[self.index as = usize]; let mut length =3D len; let mut value =3D val; =20 - if self.regs.is_32bit_mod() { + if tn_regs.is_32bit_mod() { // High 32-bits are zero, leave them untouched. if shift !=3D 0 { trace::trace_hpet_ram_write_invalid_tn_cmp(); @@ -467,41 +489,43 @@ fn set_tn_cmp_reg( =20 trace::trace_hpet_ram_write_tn_cmp((shift / 8).try_into().unwrap()= ); =20 - if !self.regs.is_periodic() || self.regs.is_valset_enabled() { - self.regs.cmp =3D self.regs.cmp.deposit(shift, length, value); + if !tn_regs.is_periodic() || tn_regs.is_valset_enabled() { + tn_regs.cmp =3D tn_regs.cmp.deposit(shift, length, value); } =20 - if self.regs.is_periodic() { + if tn_regs.is_periodic() { self.period =3D self.period.deposit(shift, length, value); } =20 - self.regs.clear_valset(); + tn_regs.clear_valset(); if hpet_regs.borrow().is_hpet_enabled() { - self.set_timer(); + self.set_timer(hpet_regs); } } =20 /// FSB Interrupt Route Register fn set_tn_fsb_route_reg( - &mut self, - _hpet_regs: &BqlRefCell, + &self, + hpet_regs: &BqlRefCell, shift: u32, len: u32, val: u64, ) { - self.regs.fsb =3D self.regs.fsb.deposit(shift, len, val); + let tn_regs =3D &mut hpet_regs.borrow_mut().tn_regs[self.index as = usize]; + tn_regs.fsb =3D tn_regs.fsb.deposit(shift, len, val); } =20 fn reset(&mut self, hpet_regs: &BqlRefCell) { self.del_timer(hpet_regs); - self.regs.cmp =3D u64::MAX; // Comparator Match Registers reset to= all 1's. - self.regs.config =3D - (1 << HPET_TN_CFG_PERIODIC_CAP_SHIFT) | (1 << HPET_TN_CFG_SIZE= _CAP_SHIFT); + + let tn_regs =3D &mut hpet_regs.borrow_mut().tn_regs[self.index as = usize]; + tn_regs.cmp =3D u64::MAX; // Comparator Match Registers reset to a= ll 1's. + tn_regs.config =3D (1 << HPET_TN_CFG_PERIODIC_CAP_SHIFT) | (1 << H= PET_TN_CFG_SIZE_CAP_SHIFT); if self.get_state().has_msi_flag() { - self.regs.config |=3D 1 << HPET_TN_CFG_FSB_CAP_SHIFT; + tn_regs.config |=3D 1 << HPET_TN_CFG_FSB_CAP_SHIFT; } // advertise availability of ioapic int - self.regs.config |=3D + tn_regs.config |=3D (u64::from(self.get_state().int_route_cap)) << HPET_TN_CFG_INT= _ROUTE_CAP_SHIFT; self.period =3D 0; self.wrap_flag =3D 0; @@ -509,32 +533,35 @@ fn reset(&mut self, hpet_regs: &BqlRefCell) { =20 /// timer expiration callback fn callback(&mut self, hpet_regs: &BqlRefCell) { + let tn_regs =3D &mut hpet_regs.borrow_mut().tn_regs[self.index as = usize]; let period: u64 =3D self.period; let cur_tick: u64 =3D self.get_state().get_ticks(); =20 - if self.regs.is_periodic() && period !=3D 0 { + if tn_regs.is_periodic() && period !=3D 0 { while hpet_time_after(cur_tick, self.cmp64) { self.cmp64 +=3D period; } - if self.regs.is_32bit_mod() { - self.regs.cmp =3D u64::from(self.cmp64 as u32); // truncat= e! + if tn_regs.is_32bit_mod() { + tn_regs.cmp =3D u64::from(self.cmp64 as u32); // truncate! } else { - self.regs.cmp =3D self.cmp64; + tn_regs.cmp =3D self.cmp64; } - self.arm_timer(self.cmp64); + self.arm_timer(hpet_regs, self.cmp64); } else if self.wrap_flag !=3D 0 { self.wrap_flag =3D 0; - self.arm_timer(self.cmp64); + self.arm_timer(hpet_regs, self.cmp64); } self.update_irq(hpet_regs, true); } =20 - const fn read(&self, target: TimerRegister, _hpet_regs: &BqlRefCell) -> u64 { + fn read(&self, target: TimerRegister, hpet_regs: &BqlRefCell) -> u64 { + let tn_regs =3D &hpet_regs.borrow().tn_regs[self.index as usize]; + use TimerRegister::*; match target { - CFG =3D> self.regs.config, // including interrupt capabilities - CMP =3D> self.regs.cmp, // comparator register - ROUTE =3D> self.regs.fsb, + CFG =3D> tn_regs.config, // including interrupt capabilities + CMP =3D> tn_regs.cmp, // comparator register + ROUTE =3D> tn_regs.fsb, } } =20 @@ -571,6 +598,9 @@ pub struct HPETRegisters { /// Main Counter Value Register #[doc(alias =3D "hpet_counter")] counter: u64, + + /// HPET Timer N Registers + tn_regs: [HPETTimerRegisters; HPET_MAX_TIMERS], } =20 impl HPETRegisters { @@ -686,11 +716,13 @@ fn set_cfg_reg(&self, regs: &BqlRefCell, shift: u32, len: u32, va =20 for timer in self.timers.iter().take(self.num_timers) { let mut t =3D timer.borrow_mut(); + let id =3D t.index as usize; + let tn_regs =3D ®s.borrow().tn_regs[id]; =20 - if t.regs.is_int_enabled() && t.is_int_active(regs) { + if tn_regs.is_int_enabled() && t.is_int_active(regs) { t.update_irq(regs, true); } - t.set_timer(); + t.set_timer(regs); } } else if deactivating_bit(old_val, new_val, HPET_CFG_ENABLE_SHIFT= ) { // Halt main counter and disable interrupt generation. @@ -932,8 +964,9 @@ fn post_load(&self, _version_id: u8) -> Result<(), migr= ation::Infallible> { for timer in self.timers.iter().take(self.num_timers) { let mut t =3D timer.borrow_mut(); let cnt =3D regs.counter; + let cmp =3D regs.tn_regs[t.index as usize].cmp; =20 - t.cmp64 =3D t.calculate_cmp64(cnt, t.regs.cmp); + t.cmp64 =3D t.calculate_cmp64(&self.regs, cnt, cmp); t.last =3D CLOCK_VIRTUAL.get_ns() - NANOSECONDS_PER_SECOND; } =20 @@ -997,8 +1030,6 @@ impl ObjectImpl for HPETState { }) .build(); =20 -// In fact, version_id and minimum_version_id for HPETTimerRegisters are -// unrelated to HPETTimer's version IDs. Does not affect compatibility. impl_vmstate_struct!( HPETTimerRegisters, VMStateDescriptionBuilder::::new() @@ -1016,11 +1047,10 @@ impl ObjectImpl for HPETState { const VMSTATE_HPET_TIMER: VMStateDescription =3D VMStateDescriptionBuilder::::new() .name(c"hpet_timer") - .version_id(1) - .minimum_version_id(1) + .version_id(2) + .minimum_version_id(2) .fields(vmstate_fields! { vmstate_of!(HPETTimer, index), - vmstate_of!(HPETTimer, regs), vmstate_of!(HPETTimer, period), vmstate_of!(HPETTimer, wrap_flag), vmstate_of!(HPETTimer, qemu_timer), @@ -1031,18 +1061,17 @@ impl ObjectImpl for HPETState { =20 const VALIDATE_TIMERS_NAME: &CStr =3D c"num_timers must match"; =20 -// In fact, version_id and minimum_version_id for HPETRegisters are -// unrelated to HPETState's version IDs. Does not affect compatibility. impl_vmstate_struct!( HPETRegisters, VMStateDescriptionBuilder::::new() .name(c"hpet/regs") - .version_id(1) - .minimum_version_id(1) + .version_id(2) + .minimum_version_id(2) .fields(vmstate_fields! { vmstate_of!(HPETRegisters, config), vmstate_of!(HPETRegisters, int_status), vmstate_of!(HPETRegisters, counter), + vmstate_of!(HPETRegisters, tn_regs), }) .build() ); @@ -1050,8 +1079,8 @@ impl ObjectImpl for HPETState { const VMSTATE_HPET: VMStateDescription =3D VMStateDescriptionBuilder::::new() .name(c"hpet") - .version_id(2) - .minimum_version_id(2) + .version_id(3) + .minimum_version_id(3) .pre_save(&HPETState::pre_save) .post_load(&HPETState::post_load) .fields(vmstate_fields! { --=20 2.52.0