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