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