@@ -4,20 +4,24 @@ use std::collections::HashMap;
44
55use displaydoc:: Display ;
66use kvm_bindings:: { KVM_ARM_VCPU_PVTIME_CTRL , KVM_ARM_VCPU_PVTIME_IPA } ;
7- use log :: { debug , info , warn } ;
7+ use serde :: { Deserialize , Serialize } ;
88use thiserror:: Error ;
99use vm_memory:: GuestAddress ;
1010
1111use crate :: device_manager:: resources:: ResourceAllocator ;
12+ use crate :: snapshot:: Persist ;
1213
1314/// 64 bytes due to alignment requirement in 3.1 of https://www.kernel.org/doc/html/v5.8/virt/kvm/devices/vcpu.html#attribute-kvm-arm-vcpu-pvtime-ipa
1415pub const STEALTIME_STRUCT_MEM_SIZE : u64 = 64 ;
1516
1617/// Represent PVTime device for ARM
18+ /// TODO: Decide whether we want to keep the hashmap OR the base IPA
1719#[ derive( Debug ) ]
1820pub struct PVTime {
1921 /// Maps vCPU index to IPA location of stolen_time struct as defined in DEN0057A
2022 steal_time_regions : HashMap < u8 , u64 > ,
23+ /// The base IPA of the shared memory region
24+ base_ipa : u64 ,
2125}
2226
2327/// Errors associated with PVTime operations
@@ -32,31 +36,42 @@ pub enum PVTimeError {
3236}
3337
3438impl PVTime {
35- /// Creates a new PVTime device by allocating system memory for all vCPUs
36- pub fn new (
37- resource_allocator : & mut ResourceAllocator ,
38- vcpu_count : u8 ,
39- ) -> Result < Self , PVTimeError > {
40-
41- // This returns the IPA of the start of our shared memory region for all vCPUs.
42- let base_addr: GuestAddress = GuestAddress ( resource_allocator
43- . allocate_system_memory (
44- STEALTIME_STRUCT_MEM_SIZE * vcpu_count as u64 ,
45- 64 ,
46- vm_allocator:: AllocPolicy :: LastMatch ,
47- )
48- . map_err ( |e| PVTimeError :: AllocationFailed ( e. to_string ( ) ) ) ?) ;
49-
39+ /// Create a new PVTime device given a base addr
40+ /// - Assumes total shared memory region from base addr is already allocated
41+ fn from_base ( base_addr : GuestAddress , vcpu_count : u8 ) -> Self {
42+ let base_ipa: u64 = base_addr. 0 ;
5043
5144 // Now we need to store the base IPA for each vCPU's steal_time struct.
5245 let mut steal_time_regions = HashMap :: new ( ) ;
5346 for i in 0 ..vcpu_count {
54- let ipa = base_addr . 0 + ( i as u64 * STEALTIME_STRUCT_MEM_SIZE ) ;
47+ let ipa = base_ipa + ( i as u64 * STEALTIME_STRUCT_MEM_SIZE ) ;
5548 steal_time_regions. insert ( i, ipa) ;
5649 }
5750
5851 // Return the PVTime device with the steal_time region IPAs mapped to vCPU indices.
59- Ok ( PVTime { steal_time_regions } )
52+ PVTime {
53+ steal_time_regions,
54+ base_ipa,
55+ }
56+ }
57+
58+ /// Creates a new PVTime device by allocating new system memory for all vCPUs
59+ pub fn new (
60+ resource_allocator : & mut ResourceAllocator ,
61+ vcpu_count : u8 ,
62+ ) -> Result < Self , PVTimeError > {
63+ // This returns the IPA of the start of our shared memory region for all vCPUs.
64+ let base_ipa: GuestAddress = GuestAddress (
65+ resource_allocator
66+ . allocate_system_memory (
67+ STEALTIME_STRUCT_MEM_SIZE * vcpu_count as u64 ,
68+ 64 ,
69+ vm_allocator:: AllocPolicy :: LastMatch ,
70+ )
71+ . map_err ( |e| PVTimeError :: AllocationFailed ( e. to_string ( ) ) ) ?,
72+ ) ;
73+
74+ Ok ( Self :: from_base ( base_ipa, vcpu_count) )
6075 }
6176
6277 /// Register a vCPU with its pre-allocated steal time region
@@ -65,7 +80,6 @@ impl PVTime {
6580 vcpu_index : u8 ,
6681 vcpu_fd : & kvm_ioctls:: VcpuFd ,
6782 ) -> Result < ( ) , PVTimeError > {
68-
6983 // Get IPA of the steal_time region for this vCPU
7084 let ipa = self
7185 . steal_time_regions
@@ -80,15 +94,55 @@ impl PVTime {
8094 flags : 0 ,
8195 } ;
8296
83- vcpu_fd. set_device_attr ( & vcpu_device_attr ) . map_err ( |err| {
84- PVTimeError :: DeviceAttribute ( err , true , KVM_ARM_VCPU_PVTIME_CTRL )
85- } ) ?;
97+ vcpu_fd
98+ . set_device_attr ( & vcpu_device_attr )
99+ . map_err ( |err| PVTimeError :: DeviceAttribute ( err , true , KVM_ARM_VCPU_PVTIME_CTRL ) ) ?;
86100
87101 Ok ( ( ) )
88102 }
89103}
90104
91- // TODO/Q: Would we be correct in implementing Persist for PVTime? Some sort of persistence is
92- // needed for snapshot capability. We would only need to store base_addr IPA of shared memory region
93- // assuming the # of vCPUs is constant across snapshots. Also assuming we are correct that
94- // kvm_set_device_attr takes an IPA and we get an IPA back from resource_allocator.
105+ /// Logic to save/restore the state of a PVTime device
106+ #[ derive( Default , Debug , Clone , Serialize , Deserialize ) ]
107+ pub struct PVTimeState {
108+ /// base IPA of the total shared memory region
109+ pub base_ipa : u64 ,
110+ }
111+
112+ #[ derive( Debug ) ]
113+ pub struct PVTimeConstructorArgs < ' a > {
114+ pub resource_allocator : & ' a mut ResourceAllocator ,
115+ pub vcpu_count : u8 ,
116+ }
117+
118+ impl < ' a > Persist < ' a > for PVTime {
119+ type State = PVTimeState ;
120+ type ConstructorArgs = PVTimeConstructorArgs < ' a > ;
121+ type Error = PVTimeError ;
122+
123+ /// Save base IPA of PVTime device for persistence
124+ fn save ( & self ) -> Self :: State {
125+ PVTimeState {
126+ base_ipa : self . base_ipa ,
127+ }
128+ }
129+
130+ /// Restore state of PVTime device from given base IPA
131+ fn restore (
132+ constructor_args : Self :: ConstructorArgs ,
133+ state : & Self :: State ,
134+ ) -> std:: result:: Result < Self , PVTimeError > {
135+ constructor_args
136+ . resource_allocator
137+ . allocate_system_memory (
138+ STEALTIME_STRUCT_MEM_SIZE * constructor_args. vcpu_count as u64 ,
139+ 64 ,
140+ vm_allocator:: AllocPolicy :: ExactMatch ( state. base_ipa ) ,
141+ )
142+ . map_err ( |e| PVTimeError :: AllocationFailed ( e. to_string ( ) ) ) ?;
143+ Ok ( Self :: from_base (
144+ GuestAddress ( state. base_ipa ) ,
145+ constructor_args. vcpu_count ,
146+ ) )
147+ }
148+ }
0 commit comments