1pub mod channel;
2pub mod fs9p;
3pub mod pci;
4pub mod vbd;
5pub mod vif;
6
7use crate::{
8 devalloc::DeviceIdAllocator,
9 error::{Error, Result},
10};
11use std::{collections::HashMap, sync::Arc};
12use tokio::sync::Mutex;
13use xenplatform::domain::{PlatformDomainConfig, PlatformDomainInfo};
14use xenstore::{
15 XsPermission, XsdClient, XsdInterface, XsdTransaction, XS_PERM_NONE, XS_PERM_READ,
16 XS_PERM_READ_WRITE,
17};
18
19pub struct XenTransaction {
20 frontend_domid: u32,
21 frontend_dom_path: String,
22 backend_domid: u32,
23 backend_dom_path: String,
24 blkalloc: Arc<Mutex<DeviceIdAllocator>>,
25 devalloc: Arc<Mutex<DeviceIdAllocator>>,
26 tx: XsdTransaction,
27 abort: bool,
28}
29
30impl XenTransaction {
31 pub async fn new(store: &XsdClient, frontend_domid: u32, backend_domid: u32) -> Result<Self> {
32 let frontend_dom_path = store.get_domain_path(frontend_domid).await?;
33 let backend_dom_path = store.get_domain_path(backend_domid).await?;
34 let tx = store.transaction().await?;
35
36 let devalloc = XenTransaction::load_id_allocator(&tx, "devid", &frontend_dom_path).await?;
37 let blkalloc = XenTransaction::load_id_allocator(&tx, "blkid", &frontend_dom_path).await?;
38
39 Ok(XenTransaction {
40 frontend_domid,
41 frontend_dom_path,
42 backend_domid,
43 backend_dom_path,
44 tx,
45 devalloc: Arc::new(Mutex::new(devalloc)),
46 blkalloc: Arc::new(Mutex::new(blkalloc)),
47 abort: true,
48 })
49 }
50
51 async fn load_id_allocator(
52 tx: &XsdTransaction,
53 allocator_type: &str,
54 frontend_dom_path: &str,
55 ) -> Result<DeviceIdAllocator> {
56 let state = tx
57 .read(format!(
58 "{}/{}-alloc-state",
59 frontend_dom_path, allocator_type
60 ))
61 .await?;
62 let allocator = state
63 .and_then(|state| DeviceIdAllocator::deserialize(&state))
64 .unwrap_or_else(DeviceIdAllocator::new);
65 Ok(allocator)
66 }
67
68 pub async fn assign_next_devid(&self) -> Result<u64> {
69 self.devalloc
70 .lock()
71 .await
72 .allocate()
73 .ok_or(Error::DevIdExhausted)
74 .map(|x| x as u64)
75 }
76
77 pub async fn assign_next_blkidx(&self) -> Result<u32> {
78 self.blkalloc
79 .lock()
80 .await
81 .allocate()
82 .ok_or(Error::DevIdExhausted)
83 }
84
85 pub async fn release_devid(&self, devid: u64) -> Result<()> {
86 self.devalloc.lock().await.release(devid as u32);
87 Ok(())
88 }
89
90 pub async fn release_blkid(&self, blkid: u32) -> Result<()> {
91 self.blkalloc.lock().await.release(blkid);
92 Ok(())
93 }
94
95 pub async fn write(
96 &self,
97 key: impl AsRef<str>,
98 value: impl AsRef<str>,
99 perms: Option<&[XsPermission]>,
100 ) -> Result<()> {
101 let path = format!("{}/{}", self.frontend_dom_path, key.as_ref());
102 if let Some(perms) = perms {
103 self.tx.mknod(&path, perms).await?;
104 }
105
106 if perms.is_none() || perms.is_some() && !value.as_ref().is_empty() {
108 self.tx.write_string(path, value.as_ref()).await?;
109 }
110 Ok(())
111 }
112
113 pub async fn add_domain_declaration(
114 &self,
115 name: Option<impl AsRef<str>>,
116 platform: &PlatformDomainConfig,
117 created: &PlatformDomainInfo,
118 ) -> Result<()> {
119 let vm_path = format!("/vm/{}", platform.uuid);
120 let ro_perm = &[
121 XsPermission {
122 id: 0,
123 perms: XS_PERM_NONE,
124 },
125 XsPermission {
126 id: self.frontend_domid,
127 perms: XS_PERM_READ,
128 },
129 ];
130
131 let no_perm = &[XsPermission {
132 id: 0,
133 perms: XS_PERM_NONE,
134 }];
135
136 let rw_perm = &[XsPermission {
137 id: self.frontend_domid,
138 perms: XS_PERM_READ_WRITE,
139 }];
140
141 self.tx.rm(&self.frontend_dom_path).await?;
142 self.tx.mknod(&self.frontend_dom_path, ro_perm).await?;
143
144 self.tx.rm(&vm_path).await?;
145 self.tx.mknod(&vm_path, no_perm).await?;
146 self.tx
147 .write_string(format!("{}/uuid", vm_path), &platform.uuid.to_string())
148 .await?;
149
150 self.write("vm", &vm_path, None).await?;
151 self.write("cpu", "", Some(ro_perm)).await?;
152 self.write("memory", "", Some(ro_perm)).await?;
153 self.write("control", "", Some(ro_perm)).await?;
154 self.write("control/shutdown", "", Some(rw_perm)).await?;
155 self.write("control/feature-poweroff", "", Some(rw_perm))
156 .await?;
157 self.write("control/feature-reboot", "", Some(rw_perm))
158 .await?;
159 self.write("control/feature-suspend", "", Some(rw_perm))
160 .await?;
161 self.write("control/sysrq", "", Some(rw_perm)).await?;
162 self.write("data", "", Some(rw_perm)).await?;
163 self.write("drivers", "", Some(rw_perm)).await?;
164 self.write("feature", "", Some(rw_perm)).await?;
165 self.write("attr", "", Some(rw_perm)).await?;
166 self.write("error", "", Some(rw_perm)).await?;
167 self.write("uuid", platform.uuid.to_string(), Some(ro_perm))
168 .await?;
169 if let Some(name) = name {
170 self.write("name", name.as_ref(), Some(ro_perm)).await?;
171 }
172 self.write(
173 "memory/static-max",
174 (platform.resources.max_memory_mb * 1024).to_string(),
175 None,
176 )
177 .await?;
178 self.write(
179 "memory/target",
180 (platform.resources.assigned_memory_mb * 1024).to_string(),
181 None,
182 )
183 .await?;
184 self.write("memory/videoram", "0", None).await?;
185 self.write("domid", self.frontend_domid.to_string(), None)
186 .await?;
187 self.write("type", "PV", None).await?;
188 self.write("store/port", created.store_evtchn.to_string(), None)
189 .await?;
190 self.write("store/ring-ref", created.store_mfn.to_string(), None)
191 .await?;
192 for i in 0..platform.resources.max_vcpus {
193 let path = format!("{}/cpu/{}", self.frontend_dom_path, i);
194 self.tx.mkdir(&path).await?;
195 self.tx.set_perms(&path, ro_perm).await?;
196 let path = format!("{}/cpu/{}/availability", self.frontend_dom_path, i);
197 self.tx
198 .write_string(
199 &path,
200 if i < platform.resources.assigned_vcpus {
201 "online"
202 } else {
203 "offline"
204 },
205 )
206 .await?;
207 self.tx.set_perms(&path, ro_perm).await?;
208 }
209 Ok(())
210 }
211
212 pub async fn add_device(&self, id: u64, device: DeviceDescription) -> Result<()> {
213 let frontend_path = if let Some(ref special_frontend_path) = device.special_frontend_path {
214 format!("{}/{}", self.frontend_dom_path, special_frontend_path)
215 } else {
216 format!(
217 "{}/device/{}/{}",
218 self.frontend_dom_path, device.frontend_type, id
219 )
220 };
221 let backend_path = format!(
222 "{}/backend/{}/{}/{}",
223 self.backend_dom_path, device.backend_type, self.frontend_domid, id
224 );
225
226 let frontend_perms = &[
227 XsPermission {
228 id: self.frontend_domid,
229 perms: XS_PERM_READ_WRITE,
230 },
231 XsPermission {
232 id: self.backend_domid,
233 perms: XS_PERM_READ,
234 },
235 ];
236
237 let backend_perms = &[
238 XsPermission {
239 id: self.backend_domid,
240 perms: XS_PERM_READ_WRITE,
241 },
242 XsPermission {
243 id: self.frontend_domid,
244 perms: XS_PERM_READ,
245 },
246 ];
247
248 self.tx.mknod(&frontend_path, frontend_perms).await?;
249 self.tx.mknod(&backend_path, backend_perms).await?;
250
251 for (key, value) in &device.backend_items {
252 let path = format!("{}/{}", backend_path, key);
253 self.tx.write_string(&path, value).await?;
254 }
255
256 self.tx
257 .write_string(format!("{}/frontend", backend_path), &frontend_path)
258 .await?;
259 self.tx
260 .write_string(
261 format!("{}/frontend-id", backend_path),
262 &self.frontend_domid.to_string(),
263 )
264 .await?;
265 for (key, value) in &device.frontend_items {
266 let path = format!("{}/{}", frontend_path, key);
267 self.tx.write_string(&path, value).await?;
268 if device.special_frontend_path.is_none() {
269 self.tx.set_perms(&path, frontend_perms).await?;
270 }
271 }
272 self.tx
273 .write_string(format!("{}/backend", frontend_path), &backend_path)
274 .await?;
275 self.tx
276 .write_string(
277 format!("{}/backend-id", frontend_path),
278 &self.backend_domid.to_string(),
279 )
280 .await?;
281 Ok(())
282 }
283
284 pub async fn add_rw_path(&self, key: impl AsRef<str>) -> Result<()> {
285 let rw_perm = &[XsPermission {
286 id: self.frontend_domid,
287 perms: XS_PERM_READ_WRITE,
288 }];
289
290 self.tx
291 .mknod(
292 &format!("{}/{}", self.frontend_dom_path, key.as_ref()),
293 rw_perm,
294 )
295 .await?;
296 Ok(())
297 }
298
299 async fn before_commit(&self) -> Result<()> {
300 let devid_allocator_state = self.devalloc.lock().await.serialize();
301 let blkid_allocator_state = self.blkalloc.lock().await.serialize();
302 self.tx
303 .write(
304 format!("{}/devid-alloc-state", self.frontend_dom_path),
305 devid_allocator_state,
306 )
307 .await?;
308 self.tx
309 .write(
310 format!("{}/blkid-alloc-state", self.frontend_dom_path),
311 blkid_allocator_state,
312 )
313 .await?;
314 Ok(())
315 }
316
317 pub async fn maybe_commit(mut self) -> Result<bool> {
318 self.abort = false;
319 self.before_commit().await?;
320 Ok(self.tx.maybe_commit().await?)
321 }
322
323 pub async fn commit(mut self) -> Result<()> {
324 self.abort = false;
325 self.before_commit().await?;
326 self.tx.commit().await?;
327 Ok(())
328 }
329}
330
331impl Drop for XenTransaction {
332 fn drop(&mut self) {
333 if !self.abort {
334 return;
335 }
336 let tx = self.tx.clone();
337 tokio::task::spawn(async move {
338 let _ = tx.abort().await;
339 });
340 }
341}
342
343pub struct DeviceDescription {
344 frontend_type: String,
345 backend_type: String,
346 special_frontend_path: Option<String>,
347 frontend_items: HashMap<String, String>,
348 backend_items: HashMap<String, String>,
349}
350
351impl DeviceDescription {
352 pub fn new(frontend_type: impl AsRef<str>, backend_type: impl AsRef<str>) -> Self {
353 Self {
354 frontend_type: frontend_type.as_ref().to_string(),
355 backend_type: backend_type.as_ref().to_string(),
356 special_frontend_path: None,
357 frontend_items: HashMap::new(),
358 backend_items: HashMap::new(),
359 }
360 }
361
362 pub fn special_frontend_path(&mut self, path: impl AsRef<str>) -> &mut Self {
363 self.special_frontend_path = Some(path.as_ref().to_string());
364 self
365 }
366
367 pub fn add_frontend_item(&mut self, key: impl AsRef<str>, value: impl ToString) -> &mut Self {
368 self.frontend_items
369 .insert(key.as_ref().to_string(), value.to_string());
370 self
371 }
372
373 pub fn add_backend_item(&mut self, key: impl AsRef<str>, value: impl ToString) -> &mut Self {
374 self.backend_items
375 .insert(key.as_ref().to_string(), value.to_string());
376 self
377 }
378
379 pub fn add_frontend_bool(&mut self, key: impl AsRef<str>, value: bool) -> &mut Self {
380 self.add_frontend_item(key, if value { "1" } else { "0" })
381 }
382
383 pub fn add_backend_bool(&mut self, key: impl AsRef<str>, value: bool) -> &mut Self {
384 self.add_backend_item(key, if value { "1" } else { "0" })
385 }
386
387 pub fn done(self) -> Self {
388 self
389 }
390}
391
392#[derive(Clone, Debug)]
393pub struct DeviceResult {
394 pub id: u64,
395}
396
397#[derive(Clone, Debug)]
398pub struct BlockDeviceResult {
399 pub id: u64,
400 pub idx: u32,
401}
402
403#[async_trait::async_trait]
404pub trait DeviceConfig {
405 type Result;
406
407 async fn add_to_transaction(&self, tx: &XenTransaction) -> Result<Self::Result>;
408}
409
410#[derive(Clone, Debug)]
411pub struct BlockDeviceRef {
412 pub path: String,
413 pub major: u32,
414 pub minor: u32,
415}
416
417impl BlockDeviceRef {
418 pub fn new(path: impl AsRef<str>, major: u32, minor: u32) -> Self {
419 Self {
420 path: path.as_ref().to_string(),
421 major,
422 minor,
423 }
424 }
425}