1 /*
2  * Copyright (c) 2024 Google Inc. All rights reserved
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files
6  * (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge,
8  * publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so,
10  * subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 use log::error;
25 
26 use num_integer::Integer;
27 
28 use hypervisor_backends::get_mem_sharer;
29 use hypervisor_backends::Error;
30 use hypervisor_backends::KvmError;
31 
32 use spin::Once;
33 
34 /// Result type with kvm error.
35 pub type KvmResult<T> = Result<T, KvmError>;
36 
37 /// The granule size used by the hypervisor
38 static GRANULE: Once<KvmResult<usize>> = Once::new();
39 
get_granule() -> KvmResult<usize>40 fn get_granule() -> KvmResult<usize> {
41     *GRANULE.call_once(|| {
42         let hypervisor = get_mem_sharer()
43             .ok_or(KvmError::NotSupported)
44             .inspect_err(|_| error!("failed to get hypervisor"))?;
45         let granule = hypervisor
46             .granule()
47             .inspect_err(|e| error!("failed to get granule: {e:?}"))
48             .map_err(|_| KvmError::NotSupported)?;
49         if !granule.is_power_of_two() {
50             error!("invalid memory protection granule");
51             return Err(KvmError::InvalidParameter);
52         }
53         Ok(granule)
54     })
55 }
56 
share_pages(paddr: usize, size: usize) -> KvmResult<()>57 pub(crate) fn share_pages(paddr: usize, size: usize) -> KvmResult<()> {
58     let hypervisor = get_mem_sharer()
59         .ok_or(KvmError::NotSupported)
60         .inspect_err(|_| error!("failed to get hypervisor"))?;
61     let hypervisor_page_size = get_granule()?;
62 
63     if !paddr.is_multiple_of(&hypervisor_page_size) {
64         error!("paddr not aligned");
65         return Err(KvmError::InvalidParameter);
66     }
67 
68     if !size.is_multiple_of(&hypervisor_page_size) {
69         error!("size ({size}) not aligned to page size ({hypervisor_page_size})");
70         return Err(KvmError::InvalidParameter);
71     }
72 
73     for page in (paddr..paddr + size).step_by(hypervisor_page_size) {
74         hypervisor.share(page as u64).map_err(|err| {
75             error!("failed to share page 0x{page:x}: {err}");
76 
77             // unmap any previously shared pages on error
78             // if sharing fail on the first page, the half-open range below is empty
79             for prev in (paddr..page).step_by(hypervisor_page_size) {
80                 // keep going even if we fail
81                 let _ = hypervisor.unshare(prev as u64);
82             }
83 
84             match err {
85                 Error::KvmError(e, _) => e,
86                 _ => panic!("unexpected share error: {err:?}"),
87             }
88         })?;
89     }
90 
91     Ok(())
92 }
93 
unshare_pages(paddr: usize, size: usize) -> KvmResult<()>94 pub(crate) fn unshare_pages(paddr: usize, size: usize) -> KvmResult<()> {
95     let hypervisor = get_mem_sharer()
96         .ok_or(KvmError::NotSupported)
97         .inspect_err(|_| error!("failed to get hypervisor"))?;
98 
99     let hypervisor_page_size = get_granule()?;
100 
101     if !hypervisor_page_size.is_power_of_two() {
102         error!("invalid memory protection granule");
103         return Err(KvmError::InvalidParameter);
104     }
105 
106     if !paddr.is_multiple_of(&hypervisor_page_size) {
107         error!("paddr not aligned");
108         return Err(KvmError::InvalidParameter);
109     }
110 
111     if !size.is_multiple_of(&hypervisor_page_size) {
112         error!("size ({size}) not aligned to page size ({hypervisor_page_size})");
113         return Err(KvmError::InvalidParameter);
114     }
115 
116     for page in (paddr..paddr + size).step_by(hypervisor_page_size) {
117         hypervisor.unshare(page as u64).map_err(|err| {
118             error!("failed to unshare page 0x{page:x}: {err:?}");
119 
120             match err {
121                 Error::KvmError(e, _) => e,
122                 _ => panic!("unexpected unshare error: {err:?}"),
123             }
124         })?;
125     }
126 
127     Ok(())
128 }
129