Skip to content

Commit e532386

Browse files
committed
refactor(its): cleanup ITS save/restore logic
We should squash that to previous commit Signed-off-by: Babis Chalios <[email protected]>
1 parent 97483a1 commit e532386

File tree

4 files changed

+142
-168
lines changed

4 files changed

+142
-168
lines changed

src/vmm/src/arch/aarch64/gic/gicv3/mod.rs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
mod regs;
4+
pub mod regs;
55

66
use kvm_ioctls::{DeviceFd, VmFd};
77

@@ -234,7 +234,7 @@ impl GICv3 {
234234
/// RDIST pending tables into guest RAM.
235235
///
236236
/// The tables get flushed to guest RAM whenever the VM gets stopped.
237-
fn save_pending_tables(gic_device: &DeviceFd, its_device: &DeviceFd) -> Result<(), GicError> {
237+
fn save_pending_tables(gic_device: &DeviceFd) -> Result<(), GicError> {
238238
let init_gic_attr = kvm_bindings::kvm_device_attr {
239239
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
240240
attr: u64::from(kvm_bindings::KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES),
@@ -243,18 +243,7 @@ fn save_pending_tables(gic_device: &DeviceFd, its_device: &DeviceFd) -> Result<(
243243
};
244244
gic_device.set_device_attr(&init_gic_attr).map_err(|err| {
245245
GicError::DeviceAttribute(err, true, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL)
246-
})?;
247-
let save_its_tables_attr = kvm_bindings::kvm_device_attr {
248-
group: kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
249-
attr: u64::from(kvm_bindings::KVM_DEV_ARM_ITS_SAVE_TABLES),
250-
addr: 0,
251-
flags: 0,
252-
};
253-
its_device
254-
.set_device_attr(&save_its_tables_attr)
255-
.map_err(|err| {
256-
GicError::DeviceAttribute(err, true, kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL)
257-
})
246+
})
258247
}
259248

260249
#[cfg(test)]
@@ -272,11 +261,11 @@ mod tests {
272261
let kvm = Kvm::new().unwrap();
273262
let vm = kvm.create_vm().unwrap();
274263
let gic = create_gic(&vm, 1, Some(GICVersion::GICV3)).expect("Cannot create gic");
275-
save_pending_tables(gic.device_fd(), gic.its_fd().unwrap()).unwrap();
264+
save_pending_tables(gic.device_fd()).unwrap();
276265

277266
unsafe { libc::close(gic.device_fd().as_raw_fd()) };
278267

279-
let res = save_pending_tables(gic.device_fd(), gic.its_fd().unwrap());
268+
let res = save_pending_tables(gic.device_fd());
280269
assert_eq!(
281270
format!("{:?}", res.unwrap_err()),
282271
"DeviceAttribute(Error(9), true, 4)"
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use kvm_bindings::{
5+
KVM_DEV_ARM_ITS_RESTORE_TABLES, KVM_DEV_ARM_ITS_SAVE_TABLES, KVM_DEV_ARM_VGIC_GRP_CTRL,
6+
KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
7+
};
8+
use kvm_ioctls::DeviceFd;
9+
use serde::{Deserialize, Serialize};
10+
11+
use crate::arch::aarch64::gic::GicError;
12+
13+
// ITS registers that we want to preserve across snapshots
14+
const GITS_CTLR: u32 = 0x0000;
15+
const GITS_IIDR: u32 = 0x0004;
16+
const GITS_CBASER: u32 = 0x0080;
17+
const GITS_CWRITER: u32 = 0x0088;
18+
const GITS_CREADR: u32 = 0x0090;
19+
const GITS_BASER: u32 = 0x0100;
20+
21+
fn set_device_attribute(
22+
its_device: &DeviceFd,
23+
group: u32,
24+
attr: u32,
25+
val: u64,
26+
) -> Result<(), GicError> {
27+
let gicv3_its_attr = kvm_bindings::kvm_device_attr {
28+
group,
29+
attr: attr as u64,
30+
addr: &val as *const u64 as u64,
31+
flags: 0,
32+
};
33+
34+
its_device
35+
.set_device_attr(&gicv3_its_attr)
36+
.map_err(|err| GicError::DeviceAttribute(err, true, group))
37+
}
38+
39+
fn get_device_attribute(its_device: &DeviceFd, group: u32, attr: u32) -> Result<u64, GicError> {
40+
let mut val = 0;
41+
42+
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
43+
group,
44+
attr: attr as u64,
45+
addr: &mut val as *mut u64 as u64,
46+
flags: 0,
47+
};
48+
49+
// SAFETY: gicv3_its_attr.addr is safe to write to.
50+
unsafe { its_device.get_device_attr(&mut gicv3_its_attr) }
51+
.map_err(|err| GicError::DeviceAttribute(err, false, group))?;
52+
53+
Ok(val)
54+
}
55+
56+
fn its_read_register(its_fd: &DeviceFd, attr: u32) -> Result<u64, GicError> {
57+
get_device_attribute(its_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, attr)
58+
}
59+
60+
fn its_set_register(its_fd: &DeviceFd, attr: u32, val: u64) -> Result<(), GicError> {
61+
set_device_attribute(its_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, attr, val)
62+
}
63+
64+
pub fn its_save_tables(its_fd: &DeviceFd) -> Result<(), GicError> {
65+
set_device_attribute(
66+
its_fd,
67+
KVM_DEV_ARM_VGIC_GRP_CTRL,
68+
KVM_DEV_ARM_ITS_SAVE_TABLES,
69+
0,
70+
)
71+
}
72+
73+
pub fn its_restore_tables(its_fd: &DeviceFd) -> Result<(), GicError> {
74+
set_device_attribute(
75+
its_fd,
76+
KVM_DEV_ARM_VGIC_GRP_CTRL,
77+
KVM_DEV_ARM_ITS_RESTORE_TABLES,
78+
0,
79+
)
80+
}
81+
82+
/// ITS registers that we save/restore during snapshot
83+
#[derive(Debug, Default, Serialize, Deserialize)]
84+
pub struct ItsRegisterState {
85+
iidr: u64,
86+
cbaser: u64,
87+
creadr: u64,
88+
cwriter: u64,
89+
baser: [u64; 8],
90+
ctlr: u64,
91+
}
92+
93+
impl ItsRegisterState {
94+
/// Save ITS state
95+
pub fn save(its_fd: &DeviceFd) -> Result<Self, GicError> {
96+
let mut state = ItsRegisterState::default();
97+
98+
for i in 0..8 {
99+
state.baser[i as usize] = its_read_register(its_fd, GITS_BASER + i * 8)?;
100+
}
101+
state.ctlr = its_read_register(its_fd, GITS_CTLR)?;
102+
state.cbaser = its_read_register(its_fd, GITS_CBASER)?;
103+
state.creadr = its_read_register(its_fd, GITS_CREADR)?;
104+
state.cwriter = its_read_register(its_fd, GITS_CWRITER)?;
105+
state.iidr = its_read_register(its_fd, GITS_IIDR)?;
106+
107+
Ok(state)
108+
}
109+
110+
/// Restore ITS state
111+
pub fn restore(&self, its_fd: &DeviceFd) -> Result<(), GicError> {
112+
its_set_register(its_fd, GITS_IIDR, self.iidr)?;
113+
its_set_register(its_fd, GITS_CBASER, self.cbaser)?;
114+
its_set_register(its_fd, GITS_CREADR, self.creadr)?;
115+
its_set_register(its_fd, GITS_CWRITER, self.cwriter)?;
116+
for i in 0..8 {
117+
its_set_register(its_fd, GITS_BASER + i * 8, self.baser[i as usize])?;
118+
}
119+
120+
// We need to restore saved ITS tables before restoring GITS_CTLR
121+
its_restore_tables(its_fd)?;
122+
its_set_register(its_fd, GITS_CTLR, self.ctlr)
123+
}
124+
}

src/vmm/src/arch/aarch64/gic/gicv3/regs/mod.rs

Lines changed: 10 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -3,54 +3,14 @@
33

44
mod dist_regs;
55
mod icc_regs;
6+
pub mod its_regs;
67
mod redist_regs;
78

89
use kvm_ioctls::DeviceFd;
910

1011
use crate::arch::aarch64::gic::GicError;
1112
use crate::arch::aarch64::gic::regs::{GicState, GicVcpuState};
12-
13-
const GITS_CTLR: u32 = 0x0000;
14-
const GITS_IIDR: u32 = 0x0004;
15-
const GITS_CBASER: u32 = 0x0080;
16-
const GITS_CWRITER: u32 = 0x0088;
17-
const GITS_CREADR: u32 = 0x0090;
18-
const GITS_BASER: u32 = 0x0100;
19-
20-
pub fn gicv3_its_attr_set(
21-
its_device: &DeviceFd,
22-
group: u32,
23-
attr: u32,
24-
val: u64,
25-
) -> Result<(), GicError> {
26-
let gicv3_its_attr = kvm_bindings::kvm_device_attr {
27-
group,
28-
attr: attr as u64,
29-
addr: &val as *const u64 as u64,
30-
flags: 0,
31-
};
32-
33-
its_device
34-
.set_device_attr(&gicv3_its_attr)
35-
.map_err(|err| GicError::DeviceAttribute(err, true, group))
36-
}
37-
38-
pub fn gicv3_its_attr_get(its_device: &DeviceFd, group: u32, attr: u32) -> Result<u64, GicError> {
39-
let mut val = 0;
40-
41-
let mut gicv3_its_attr = kvm_bindings::kvm_device_attr {
42-
group,
43-
attr: attr as u64,
44-
addr: &mut val as *mut u64 as u64,
45-
flags: 0,
46-
};
47-
48-
// SAFETY: gicv3_its_attr.addr is safe to write to.
49-
unsafe { its_device.get_device_attr(&mut gicv3_its_attr) }
50-
.map_err(|err| GicError::DeviceAttribute(err, false, group))?;
51-
52-
Ok(val)
53-
}
13+
use its_regs::{ItsRegisterState, its_save_tables};
5414

5515
/// Save the state of the GIC device.
5616
pub fn save_state(
@@ -59,7 +19,9 @@ pub fn save_state(
5919
mpidrs: &[u64],
6020
) -> Result<GicState, GicError> {
6121
// Flush redistributors pending tables to guest RAM.
62-
super::save_pending_tables(gic_device, its_device)?;
22+
super::save_pending_tables(gic_device)?;
23+
// Flush ITS tables into guest memory.
24+
its_save_tables(its_device)?;
6325

6426
let mut vcpu_states = Vec::with_capacity(mpidrs.len());
6527
for mpidr in mpidrs {
@@ -69,54 +31,12 @@ pub fn save_state(
6931
})
7032
}
7133

72-
let mut its_baser: [u64; 8] = [0; 8];
73-
for i in 0..8 {
74-
its_baser[i as usize] = gicv3_its_attr_get(
75-
its_device,
76-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
77-
GITS_BASER + i * 8,
78-
)?;
79-
}
80-
81-
let its_ctlr = gicv3_its_attr_get(
82-
its_device,
83-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
84-
GITS_CTLR,
85-
)?;
86-
87-
let its_cbaser = gicv3_its_attr_get(
88-
its_device,
89-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
90-
GITS_CBASER,
91-
)?;
92-
93-
let its_creadr = gicv3_its_attr_get(
94-
its_device,
95-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
96-
GITS_CREADR,
97-
)?;
98-
99-
let its_cwriter = gicv3_its_attr_get(
100-
its_device,
101-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
102-
GITS_CWRITER,
103-
)?;
104-
105-
let its_iidr = gicv3_its_attr_get(
106-
its_device,
107-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
108-
GITS_IIDR,
109-
)?;
34+
let its_state = ItsRegisterState::save(its_device)?;
11035

11136
Ok(GicState {
11237
dist: dist_regs::get_dist_regs(gic_device)?,
11338
gic_vcpu_states: vcpu_states,
114-
its_ctlr,
115-
its_cbaser,
116-
its_creadr,
117-
its_cwriter,
118-
its_iidr,
119-
its_baser,
39+
its_state: Some(its_state),
12040
})
12141
}
12242

@@ -137,59 +57,9 @@ pub fn restore_state(
13757
icc_regs::set_icc_regs(gic_device, *mpidr, &vcpu_state.icc)?;
13858
}
13959

140-
// Restore ITS registers
141-
gicv3_its_attr_set(
142-
its_device,
143-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
144-
GITS_IIDR,
145-
state.its_iidr,
146-
)?;
147-
148-
gicv3_its_attr_set(
149-
its_device,
150-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
151-
GITS_CBASER,
152-
state.its_cbaser,
153-
)?;
154-
155-
gicv3_its_attr_set(
156-
its_device,
157-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
158-
GITS_CREADR,
159-
state.its_creadr,
160-
)?;
161-
162-
gicv3_its_attr_set(
163-
its_device,
164-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
165-
GITS_CWRITER,
166-
state.its_cwriter,
167-
)?;
168-
169-
for i in 0..8 {
170-
gicv3_its_attr_set(
171-
its_device,
172-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
173-
GITS_BASER + i * 8,
174-
state.its_baser[i as usize],
175-
)?;
176-
}
177-
178-
gicv3_its_attr_set(
179-
its_device,
180-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_CTRL,
181-
kvm_bindings::KVM_DEV_ARM_ITS_RESTORE_TABLES,
182-
0,
183-
)?;
184-
185-
gicv3_its_attr_set(
186-
its_device,
187-
kvm_bindings::KVM_DEV_ARM_VGIC_GRP_ITS_REGS,
188-
GITS_CTLR,
189-
state.its_ctlr,
190-
)?;
191-
192-
Ok(())
60+
// Safe to unwrap here, as we know we support an ITS device, so `its_state.is_some()` is always
61+
// `true`.
62+
state.its_state.as_ref().unwrap().restore(its_device)
19363
}
19464

19565
#[cfg(test)]

src/vmm/src/arch/aarch64/gic/regs.rs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use kvm_ioctls::DeviceFd;
1010
use serde::{Deserialize, Serialize};
1111

1212
use crate::arch::aarch64::gic::GicError;
13+
use crate::arch::aarch64::gic::gicv3::regs::its_regs::ItsRegisterState;
1314

1415
#[derive(Debug, Serialize, Deserialize)]
1516
pub struct GicRegState<T> {
@@ -30,18 +31,8 @@ pub struct GicState {
3031
pub dist: Vec<GicRegState<u32>>,
3132
/// The state of the vcpu interfaces.
3233
pub gic_vcpu_states: Vec<GicVcpuState>,
33-
/// ITS control register
34-
pub its_ctlr: u64,
35-
/// ITS IID register
36-
pub its_iidr: u64,
37-
/// ITS CBASE register
38-
pub its_cbaser: u64,
39-
/// ITS CWRITE register
40-
pub its_cwriter: u64,
41-
/// ITS CREAD register
42-
pub its_creadr: u64,
43-
/// ITS BASE registers
44-
pub its_baser: [u64; 8],
34+
/// The state of the ITS device. Only present with GICv3.
35+
pub its_state: Option<ItsRegisterState>,
4536
}
4637

4738
/// Structure used for serializing the state of the GIC registers for a specific vCPU.

0 commit comments

Comments
 (0)