1use crate::platform::PlatformServices;
5use anyhow::{Error, anyhow};
6use fidl_fuchsia_virtualization::{
7 GuestMarker, GuestStatus, MemControllerMarker, MemControllerProxy,
8};
9use guest_cli_args as arguments;
10use std::fmt;
11use zx_status;
12
13#[derive(serde::Serialize, serde::Deserialize)]
14pub struct RequestSizeResult {
15 size: u64,
16}
17
18impl fmt::Display for RequestSizeResult {
19 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
20 write!(f, "Resizing dynamically plugged memory to {} bytes!\n", self.size)
21 }
22}
23
24#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
25pub struct VirtioMemStats {
26 block_size: u64,
27 region_size: u64,
28 usable_region_size: u64,
29 plugged_size: u64,
30 requested_size: u64,
31}
32
33impl fmt::Display for VirtioMemStats {
34 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35 write!(f, "Dynamically plugged memory stats:\n")?;
36 write!(f, " block_size: {}\n", self.block_size)?;
37 write!(f, " region_size: {}\n", self.region_size)?;
38 write!(f, " usable_region_size: {}\n", self.usable_region_size)?;
39 write!(f, " plugged_size: {}\n", self.plugged_size)?;
40 write!(f, " requested_size: {}\n", self.requested_size)
41 }
42}
43
44#[derive(serde::Serialize, serde::Deserialize)]
45pub enum GuestMemResult {
46 RequestSize(RequestSizeResult),
47 MemStats(VirtioMemStats),
48}
49
50impl fmt::Display for GuestMemResult {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 match self {
53 GuestMemResult::RequestSize(res) => write!(f, "{}", res),
54 GuestMemResult::MemStats(stats) => write!(f, "{}", stats),
55 }
56 }
57}
58
59async fn connect_to_mem_controller<P: PlatformServices>(
60 services: &P,
61 guest_type: arguments::GuestType,
62) -> Result<MemControllerProxy, Error> {
63 let manager = services.connect_to_manager(guest_type).await?;
64
65 let guest_info = manager.get_info().await?;
66 if guest_info.guest_status.expect("guest status should be set") == GuestStatus::Running {
67 let (guest_endpoint, guest_server_end) = fidl::endpoints::create_proxy::<GuestMarker>();
68 manager
69 .connect(guest_server_end)
70 .await
71 .map_err(|err| anyhow!("failed to get a connect response: {}", err))?
72 .map_err(|err| anyhow!("connect failed with: {:?}", err))?;
73
74 let (controller, server_end) = fidl::endpoints::create_proxy::<MemControllerMarker>();
75
76 guest_endpoint
77 .get_mem_controller(server_end)
78 .await?
79 .map_err(|err| anyhow!("failed to get MemController: {:?}", err))?;
80
81 Ok(controller)
82 } else {
83 Err(anyhow!(zx_status::Status::NOT_CONNECTED))
84 }
85}
86
87pub async fn handle_mem<P: PlatformServices>(
88 services: &P,
89 args: arguments::mem_args::MemArgs,
90) -> Result<GuestMemResult, Error> {
91 Ok(match args.mem_cmd {
92 arguments::mem_args::MemCommands::RequestPluggedMem(args) => {
93 GuestMemResult::RequestSize(do_request_size(
94 connect_to_mem_controller(services, args.guest_type).await?,
95 args.size,
96 )?)
97 }
98 arguments::mem_args::MemCommands::StatsMem(args) => GuestMemResult::MemStats(
99 do_stats(connect_to_mem_controller(services, args.guest_type).await?).await?,
100 ),
101 })
102}
103
104fn do_request_size(controller: MemControllerProxy, size: u64) -> Result<RequestSizeResult, Error> {
105 controller.request_size(size)?;
106 Ok(RequestSizeResult { size })
107}
108
109async fn do_stats(controller: MemControllerProxy) -> Result<VirtioMemStats, Error> {
110 let (block_size, region_size, usable_region_size, plugged_size, requested_size) =
111 controller.get_mem_size().await?;
112 Ok(VirtioMemStats { block_size, region_size, usable_region_size, plugged_size, requested_size })
113}
114
115#[cfg(test)]
116mod test {
117 use super::*;
118 use fidl::endpoints::create_proxy_and_stream;
119 use fuchsia_async as fasync;
120 use futures::StreamExt;
121
122 #[fuchsia::test(allow_stalls = false)]
123 async fn mem_valid_request_plugged_returns_ok() {
124 let (proxy, mut stream) = create_proxy_and_stream::<MemControllerMarker>();
125 let size = 12345;
126 let expected_string = format!("Resizing dynamically plugged memory to {} bytes!\n", size);
127
128 let res = do_request_size(proxy, size).unwrap();
129 let _ = stream
130 .next()
131 .await
132 .expect("Failed to read from stream")
133 .expect("Failed to parse request")
134 .into_request_size()
135 .expect("Unexpected call to Mem Controller");
136
137 assert_eq!(res.size, size);
138 assert_eq!(format!("{}", res), expected_string);
139 }
140
141 #[fuchsia::test(allow_stalls = false)]
142 async fn mem_valid_stats_returns_ok() {
143 let (proxy, mut stream) = create_proxy_and_stream::<MemControllerMarker>();
144 let (block_size, region_size, usable_region_size, plugged_size, requested_size) =
145 (1, 2, 3, 4, 5);
146
147 let _task = fasync::Task::spawn(async move {
148 let get_mem_size_responder = stream
149 .next()
150 .await
151 .expect("Failed to read from stream")
152 .expect("Failed to parse request")
153 .into_get_mem_size()
154 .expect("Unexpected call to Mem Controller");
155 get_mem_size_responder
156 .send(block_size, region_size, usable_region_size, plugged_size, requested_size)
157 .expect("Failed to send request to proxy");
158 });
159 let res = do_stats(proxy).await.unwrap();
160 assert_eq!(
161 res,
162 VirtioMemStats {
163 block_size,
164 region_size,
165 usable_region_size,
166 plugged_size,
167 requested_size
168 }
169 );
170
171 assert_eq!(
172 format!("{}", res),
173 concat!(
174 "Dynamically plugged memory stats:\n",
175 " block_size: 1\n",
176 " region_size: 2\n",
177 " usable_region_size: 3\n",
178 " plugged_size: 4\n",
179 " requested_size: 5\n",
180 )
181 );
182 }
183}