1#![cfg(target_os = "linux")]
8#![expect(missing_docs)]
9#![forbid(unsafe_code)]
10
11mod diag;
12mod dispatch;
13mod emuplat;
14mod get_tracing;
15mod hibernate;
16mod inspect_internal;
17mod inspect_proc;
18mod livedump;
19mod loader;
20#[cfg(feature = "product_policy")]
21mod measured_product_policy;
22mod nvme_manager;
23mod options;
24mod reference_time;
25mod servicing;
26mod threadpool_vm_task_backend;
27mod vmbus_relay_unit;
28mod vmgs_logger;
29mod vp;
30mod vpci;
31mod worker;
32mod wrapped_partition;
33
34pub use options::Options;
37
38use crate::diag::DiagWorker;
39use crate::dispatch::UhVmRpc;
40use crate::worker::UnderhillEnvCfg;
41use crate::worker::UnderhillRemoteConsoleCfg;
42use crate::worker::UnderhillVmWorker;
43use crate::worker::UnderhillWorkerParameters;
44use anyhow::Context;
45use bootloader_fdt_parser::BootTimes;
46use cvm_tracing::CVM_ALLOWED;
47use framebuffer::FRAMEBUFFER_SIZE;
48use framebuffer::FramebufferAccess;
49use futures::StreamExt;
50use futures_concurrency::stream::Merge;
51use get_tracing::init_tracing;
52use get_tracing::init_tracing_backend;
53use inspect::Inspect;
54use inspect::SensitivityLevel;
55use mesh::CancelContext;
56use mesh::CancelReason;
57use mesh::MeshPayload;
58use mesh::error::RemoteError;
59use mesh::rpc::Rpc;
60use mesh::rpc::RpcSend;
61use mesh_process::Mesh;
62use mesh_process::ProcessConfig;
63use mesh_process::try_run_mesh_host;
64use mesh_tracing::RemoteTracer;
65use mesh_tracing::TracingBackend;
66use mesh_worker::RegisteredWorkers;
67use mesh_worker::WorkerEvent;
68use mesh_worker::WorkerHandle;
69use mesh_worker::WorkerHost;
70use mesh_worker::WorkerHostRunner;
71use mesh_worker::launch_local_worker;
72use mesh_worker::register_workers;
73use pal_async::DefaultDriver;
74use pal_async::DefaultPool;
75use pal_async::task::Spawn;
76#[cfg(feature = "profiler")]
77use profiler_worker::ProfilerWorker;
78#[cfg(feature = "profiler")]
79use profiler_worker::ProfilerWorkerParameters;
80use std::time::Duration;
81use vmsocket::VmAddress;
82use vmsocket::VmListener;
83use vnc_worker_defs::VncParameters;
84
85fn new_underhill_remote_console_cfg(
86 framebuffer_gpa_base: Option<u64>,
87) -> anyhow::Result<(
88 UnderhillRemoteConsoleCfg,
89 Option<FramebufferAccess>,
90 Option<mesh::Receiver<Vec<video_core::DirtyRect>>>,
91)> {
92 if let Some(framebuffer_gpa_base) = framebuffer_gpa_base {
93 let gpa_fd = fs_err::OpenOptions::new()
103 .read(true)
104 .write(true)
105 .open("/dev/mshv_vtl_low")
106 .context("failed to open gpa device")?;
107
108 let vram = sparse_mmap::new_mappable_from_file(gpa_fd.file(), true, false)?;
109 let (fb, fba) = framebuffer::framebuffer(vram, FRAMEBUFFER_SIZE, framebuffer_gpa_base)
110 .context("allocating framebuffer")?;
111 tracing::debug!("framebuffer_gpa_base: {:#x}", framebuffer_gpa_base);
112
113 let (dirt_send, dirt_recv) = mesh::channel();
114
115 Ok((
116 UnderhillRemoteConsoleCfg {
117 synth_keyboard: true,
118 synth_mouse: true,
119 synth_video: true,
120 input: mesh::Receiver::new(),
121 framebuffer: Some(fb),
122 dirt_send: Some(dirt_send),
123 },
124 Some(fba),
125 Some(dirt_recv),
126 ))
127 } else {
128 Ok((
129 UnderhillRemoteConsoleCfg {
130 synth_keyboard: false,
131 synth_mouse: false,
132 synth_video: false,
133 input: mesh::Receiver::new(),
134 framebuffer: None,
135 dirt_send: None,
136 },
137 None,
138 None,
139 ))
140 }
141}
142
143pub fn main() -> anyhow::Result<()> {
144 install_task_name_panic_hook();
147
148 if let Some(path) = std::env::var_os("OPENVMM_WRITE_SAVED_STATE_PROTO") {
149 if cfg!(debug_assertions) {
150 mesh::payload::protofile::DescriptorWriter::new(
151 vmcore::save_restore::saved_state_roots(),
152 )
153 .write_to_path(path)
154 .context("failed to write protobuf descriptors")?;
155 return Ok(());
156 } else {
157 anyhow::bail!(".proto output only supported in debug builds");
159 }
160 }
161
162 let (_, tracing_driver) = DefaultPool::spawn_on_thread("tracing");
164
165 try_run_mesh_host("underhill", {
172 let tracing_driver = tracing_driver.clone();
173 async |params: MeshHostParams| {
174 if let Some(remote_tracer) = params.tracer {
175 init_tracing(tracing_driver, remote_tracer).context("failed to init tracing")?;
176 }
177 params.runner.run(RegisteredWorkers).await;
178 Ok(())
179 }
180 })?;
181
182 let mut tracing = init_tracing_backend(tracing_driver.clone())?;
184 init_tracing(tracing_driver, tracing.tracer()).context("failed to init tracing")?;
186 DefaultPool::run_with(|driver| do_main(driver, tracing))
187}
188
189fn install_task_name_panic_hook() {
190 use std::io::Write;
191
192 let panic_hook = std::panic::take_hook();
193 std::panic::set_hook(Box::new(move |info| {
194 pal_async::task::with_current_task_metadata(|metadata| {
195 if let Some(metadata) = metadata {
196 let _ = write!(std::io::stderr(), "task '{}', ", metadata.name());
197 }
198 });
199 panic_hook(info);
201 }));
202}
203
204async fn do_main(driver: DefaultDriver, mut tracing: TracingBackend) -> anyhow::Result<()> {
205 let opt = Options::parse(Vec::new(), Vec::new())?;
206
207 let crate_name = build_info::get().crate_name();
208 let crate_revision = build_info::get().scm_revision();
209 let openhcl_version = build_info::get().openhcl_version();
210 tracing::info!(
211 CVM_ALLOWED,
212 ?crate_name,
213 ?crate_revision,
214 ?openhcl_version,
215 "VMM process"
216 );
217 log_boot_times().context("failure logging boot times")?;
218
219 if let Some(pid_path) = &opt.pid {
221 std::fs::write(pid_path, std::process::id().to_string())
222 .with_context(|| format!("failed to write pid to {}", pid_path.display()))?;
223 }
224
225 let mesh = Mesh::new("underhill".to_string()).context("failed to create mesh")?;
226
227 let r = run_control(driver, &mesh, opt, &mut tracing).await;
228 if let Err(err) = &r {
229 tracing::error!(
230 CVM_ALLOWED,
231 error = err.as_ref() as &dyn std::error::Error,
232 "VM failure"
233 );
234 }
235
236 CancelContext::new()
238 .with_timeout(Duration::from_secs(10))
239 .until_cancelled(async {
240 mesh.shutdown().await;
241 tracing.shutdown().await;
242 })
243 .await
244 .ok();
245
246 r
247}
248
249fn log_boot_times() -> anyhow::Result<()> {
250 fn diff(start: Option<u64>, end: Option<u64>) -> Option<tracing::field::DebugValue<Duration>> {
251 use reference_time::ReferenceTime;
252 Some(tracing::field::debug(
253 ReferenceTime::new(end?).since(ReferenceTime::new(start?))?,
254 ))
255 }
256
257 let BootTimes {
259 start,
260 end,
261 sidecar_start,
262 sidecar_end,
263 } = BootTimes::new().context("failed to parse boot times")?;
264 tracing::info!(
265 CVM_ALLOWED,
266 start,
267 end,
268 sidecar_start,
269 sidecar_end,
270 elapsed = diff(start, end),
271 sidecar_elapsed = diff(sidecar_start, sidecar_end),
272 "boot loader times"
273 );
274 Ok(())
275}
276
277struct DiagState {
278 _worker: WorkerHandle,
279 request_recv: mesh::Receiver<diag_server::DiagRequest>,
280}
281
282impl DiagState {
283 async fn new() -> anyhow::Result<Self> {
284 let (request_send, request_recv) = mesh::channel();
286 let worker = launch_local_worker::<DiagWorker>(diag::DiagWorkerParameters { request_send })
287 .await
288 .context("failed to launch diagnostics worker")?;
289 Ok(Self {
290 _worker: worker,
291 request_recv,
292 })
293 }
294}
295
296#[derive(Inspect)]
297struct Workers {
298 #[inspect(safe)]
299 vm: WorkerHandle,
300 #[inspect(skip)]
301 vm_rpc: mesh::Sender<UhVmRpc>,
302 vnc: Option<WorkerHandle>,
303 #[cfg(feature = "gdb")]
304 gdb: Option<WorkerHandle>,
305}
306
307#[derive(MeshPayload)]
308struct MeshHostParams {
309 tracer: Option<RemoteTracer>,
310 runner: WorkerHostRunner,
311}
312
313async fn launch_mesh_host(
314 mesh: &Mesh,
315 name: &str,
316 tracer: Option<RemoteTracer>,
317) -> anyhow::Result<WorkerHost> {
318 let (host, runner) = mesh_worker::worker_host();
319 mesh.launch_host(ProcessConfig::new(name), MeshHostParams { tracer, runner })
320 .await?;
321 Ok(host)
322}
323
324async fn launch_workers(
325 mesh: &Mesh,
326 tracing: &mut TracingBackend,
327 control_send: mesh::Sender<ControlRequest>,
328 opt: Options,
329) -> anyhow::Result<Workers> {
330 let env_cfg = UnderhillEnvCfg {
331 vmbus_max_version: opt.vmbus_max_version,
332 vmbus_enable_mnf: opt.vmbus_enable_mnf,
333 vmbus_force_confidential_external_memory: opt.vmbus_force_confidential_external_memory,
334 vmbus_force_gpa_pinning: opt.vmbus_force_gpa_pinning,
335 vmbus_channel_unstick_delay: (opt.vmbus_channel_unstick_delay_ms != 0)
336 .then(|| Duration::from_millis(opt.vmbus_channel_unstick_delay_ms)),
337 cmdline_append: opt.cmdline_append.clone(),
338 reformat_vmgs: opt.reformat_vmgs,
339 vtl0_starts_paused: opt.vtl0_starts_paused,
340 emulated_serial_wait_for_rts: opt.serial_wait_for_rts,
341 force_load_vtl0_image: opt.force_load_vtl0_image,
342 nvme_vfio: opt.nvme_vfio,
343 halt_on_guest_halt: opt.halt_on_guest_halt,
344 no_sidecar_hotplug: opt.no_sidecar_hotplug,
345 gdbstub: opt.gdbstub,
346 hide_isolation: opt.hide_isolation,
347 nvme_keep_alive: opt.nvme_keep_alive,
348 mana_keep_alive: opt.mana_keep_alive,
349 nvme_always_flr: opt.nvme_always_flr,
350 test_configuration: opt.test_configuration,
351 disable_uefi_frontpage: opt.disable_uefi_frontpage,
352 default_boot_always_attempt: opt.default_boot_always_attempt,
353 guest_state_lifetime: opt.guest_state_lifetime,
354 guest_state_encryption_policy: opt.guest_state_encryption_policy,
355 hardware_sealing_policy: opt.hardware_sealing_policy,
356 efi_diagnostics_log_level: opt.efi_diagnostics_log_level,
357 efi_diagnostics_rate_limit: opt.efi_diagnostics_rate_limit,
358 strict_encryption_policy: opt.strict_encryption_policy,
359 attempt_ak_cert_callback: opt.attempt_ak_cert_callback,
360 enable_vpci_relay: opt.enable_vpci_relay,
361 disable_proxy_redirect: opt.disable_proxy_redirect,
362 disable_lower_vtl_timer_virt: opt.disable_lower_vtl_timer_virt,
363 config_timeout_in_seconds: opt.config_timeout_in_seconds,
364 servicing_timeout_dump_collection_in_ms: opt.servicing_timeout_dump_collection_in_ms,
365 };
366
367 let (mut remote_console_cfg, framebuffer_access, dirty_rect_recv) =
368 new_underhill_remote_console_cfg(opt.framebuffer_gpa_base)?;
369
370 let mut vnc_worker = None;
371 if let Some(framebuffer) = framebuffer_access {
372 let listener = VmListener::bind(VmAddress::vsock_any(opt.vnc_port))
373 .context("failed to bind socket")?;
374
375 let input_send = remote_console_cfg.input.sender();
376
377 let vnc_host = launch_mesh_host(mesh, "vnc", Some(tracing.tracer()))
378 .await
379 .context("spawning vnc process failed")?;
380
381 vnc_worker = Some(
382 vnc_host
383 .launch_worker(
384 vnc_worker_defs::VNC_WORKER_VMSOCKET,
385 VncParameters {
386 listener,
387 framebuffer,
388 input_send,
389 dirty_recv: dirty_rect_recv,
390 max_clients: 16,
391 evict_oldest: false,
392 },
393 )
394 .await?,
395 )
396 }
397
398 #[cfg(feature = "gdb")]
399 let mut gdbstub_worker = None;
400 #[cfg_attr(not(feature = "gdb"), expect(unused_mut))]
401 let mut debugger_rpc = None;
402 #[cfg(feature = "gdb")]
403 if opt.gdbstub {
404 let listener = VmListener::bind(VmAddress::vsock_any(opt.gdbstub_port))
405 .context("failed to bind socket")?;
406
407 let gdb_host = launch_mesh_host(mesh, "gdb", Some(tracing.tracer()))
408 .await
409 .context("failed to spawn gdb host process")?;
410
411 let vp_count =
415 pal::unix::affinity::max_present_cpu().context("failed to get max present cpu")? + 1;
416
417 let (send, recv) = mesh::channel();
418 debugger_rpc = Some(recv);
419 gdbstub_worker = Some(
420 gdb_host
421 .launch_worker(
422 debug_worker_defs::DEBUGGER_VSOCK_WORKER,
423 debug_worker_defs::DebuggerParameters {
424 listener,
425 req_chan: send,
426 vp_count,
427 target_arch: if cfg!(guest_arch = "x86_64") {
428 debug_worker_defs::TargetArch::X86_64
429 } else {
430 debug_worker_defs::TargetArch::Aarch64
431 },
432 },
433 )
434 .await?,
435 );
436 }
437 let (vm_rpc, vm_rpc_rx) = mesh::channel();
438
439 let host = launch_mesh_host(mesh, "vm", Some(tracing.tracer()))
443 .await
444 .context("failed to launch worker process")?;
445
446 let vm_worker = host
447 .start_worker(
448 worker::UNDERHILL_WORKER,
449 UnderhillWorkerParameters {
450 env_cfg,
451 remote_console_cfg,
452 debugger_rpc,
453 vm_rpc: vm_rpc_rx,
454 control_send,
455 },
456 )
457 .context("failed to launch worker")?;
458
459 Ok(Workers {
460 vm: vm_worker,
461 vm_rpc,
462 vnc: vnc_worker,
463 #[cfg(feature = "gdb")]
464 gdb: gdbstub_worker,
465 })
466}
467
468#[derive(Inspect)]
470enum ControlState {
471 WaitingForStart,
472 Starting,
473 Started,
474 Restarting,
475}
476
477#[derive(MeshPayload)]
478pub enum ControlRequest {
479 FlushLogs(Rpc<CancelContext, Result<(), CancelReason>>),
480 MakeWorker(Rpc<String, Result<WorkerHost, RemoteError>>),
481}
482
483async fn run_control(
484 driver: DefaultDriver,
485 mesh: &Mesh,
486 opt: Options,
487 mut tracing: &mut TracingBackend,
488) -> anyhow::Result<()> {
489 let (control_send, mut control_recv) = mesh::channel();
490 let mut control_send = Some(control_send);
491
492 if opt.signal_vtl0_started {
493 signal_vtl0_started(&driver)
494 .await
495 .context("failed to signal vtl0 started")?;
496 }
497
498 let mut diag = DiagState::new().await?;
499
500 let (diag_reinspect_send, mut diag_reinspect_recv) = mesh::channel();
501 #[cfg(feature = "profiler")]
502 let mut profiler_host = None;
503 let mut state;
504 let mut workers = if opt.wait_for_start {
505 state = ControlState::WaitingForStart;
506 None
507 } else {
508 state = ControlState::Starting;
509 let workers = launch_workers(mesh, tracing, control_send.take().unwrap(), opt)
510 .await
511 .context("failed to launch workers")?;
512 Some(workers)
513 };
514
515 enum Event {
516 Diag(diag_server::DiagRequest),
517 Worker(WorkerEvent),
518 Control(ControlRequest),
519 }
520
521 let mut restart_rpc = None;
522 #[cfg(feature = "mem-profile-tracing")]
523 let mut profiler = mem_profile_tracing::HeapProfiler::new();
524 loop {
525 let event = {
526 let mut stream = (
527 (&mut diag.request_recv).map(Event::Diag),
528 (&mut diag_reinspect_recv)
529 .map(|req| Event::Diag(diag_server::DiagRequest::Inspect(req))),
530 (&mut control_recv).map(Event::Control),
531 futures::stream::select_all(workers.as_mut().map(|w| &mut w.vm)).map(Event::Worker),
532 )
533 .merge();
534
535 let Some(event) = stream.next().await else {
536 break;
537 };
538 event
539 };
540
541 match event {
542 Event::Diag(request) => {
543 match request {
544 diag_server::DiagRequest::Start(rpc) => {
545 rpc.handle_failable(async |params| {
546 if workers.is_some() {
547 Err(anyhow::anyhow!("workers have already been started"))?;
548 }
549 let new_opt = Options::parse(params.args, params.env)
550 .context("failed to parse new options")?;
551
552 workers = Some(
553 launch_workers(
554 mesh,
555 tracing,
556 control_send.take().unwrap(),
557 new_opt,
558 )
559 .await?,
560 );
561 state = ControlState::Starting;
562 anyhow::Ok(())
563 })
564 .await
565 }
566 diag_server::DiagRequest::Inspect(deferred) => deferred.respond(|resp| {
567 resp.sensitivity_field("mesh", SensitivityLevel::Safe, mesh)
568 .sensitivity_field_mut("trace", SensitivityLevel::Safe, &mut tracing)
569 .sensitivity_field(
570 "build_info",
571 SensitivityLevel::Safe,
572 build_info::get(),
573 )
574 .sensitivity_child(
575 "proc",
576 SensitivityLevel::Safe,
577 inspect_proc::inspect_proc,
578 )
579 .sensitivity_field("control_state", SensitivityLevel::Safe, &state)
580 .sensitivity_child("uhdiag", SensitivityLevel::Safe, |req| {
583 inspect_internal::inspect_internal_diagnostics(
584 req,
585 &diag_reinspect_send,
586 &driver,
587 )
588 });
589
590 resp.merge(&workers);
591 }),
592 diag_server::DiagRequest::Crash(pid) => {
593 mesh.crash(pid);
594 }
595 diag_server::DiagRequest::Restart(rpc) => {
596 let Some(workers) = &mut workers else {
597 rpc.complete(Err(RemoteError::new(anyhow::anyhow!(
598 "worker has not been started yet"
599 ))));
600 continue;
601 };
602
603 let r = async {
604 if restart_rpc.is_some() {
605 anyhow::bail!("previous restart still in progress");
606 }
607
608 let host = launch_mesh_host(mesh, "vm", Some(tracing.tracer()))
609 .await
610 .context("failed to launch worker process")?;
611
612 workers.vm.restart(&host);
613 Ok(())
614 }
615 .await;
616
617 if r.is_err() {
618 rpc.complete(r.map_err(RemoteError::new));
619 } else {
620 state = ControlState::Restarting;
621 restart_rpc = Some(rpc);
622 }
623 }
624 diag_server::DiagRequest::Pause(rpc) => {
625 let Some(workers) = &mut workers else {
626 rpc.complete(Err(RemoteError::new(anyhow::anyhow!(
627 "worker has not been started yet"
628 ))));
629 continue;
630 };
631
632 let req = workers.vm_rpc.call(UhVmRpc::Pause, ());
635
636 driver
638 .spawn("diag-pause", async move {
639 let was_paused = req.await.expect("failed to pause VM");
640 rpc.handle_failable_sync(|_| {
641 if !was_paused {
642 Err(anyhow::anyhow!("VM is already paused"))
643 } else {
644 Ok(())
645 }
646 });
647 })
648 .detach();
649 }
650 diag_server::DiagRequest::PacketCapture(rpc) => {
651 let Some(workers) = &mut workers else {
652 rpc.complete(Err(RemoteError::new(anyhow::anyhow!(
653 "worker has not been started yet"
654 ))));
655 continue;
656 };
657
658 workers.vm_rpc.send(UhVmRpc::PacketCapture(rpc));
659 }
660 #[cfg(feature = "mem-profile-tracing")]
661 diag_server::DiagRequest::MemoryProfileTrace(rpc) => {
662 rpc.handle_failable(async |pid| {
663 if pid == std::process::id() as i32 {
664 anyhow::Ok(profiler.capture_and_restart())
665 } else {
666 let Some(workers) = &mut workers else {
667 anyhow::bail!("workers have not been started yet");
668 };
669
670 let result = workers
671 .vm_rpc
672 .call(UhVmRpc::MemoryProfileTrace, pid)
673 .await
674 .context("failed to get memory profile from worker process")?;
675 Ok(result?)
676 }
677 })
678 .await
679 }
680 diag_server::DiagRequest::Resume(rpc) => {
681 let Some(workers) = &mut workers else {
682 rpc.complete(Err(RemoteError::new(anyhow::anyhow!(
683 "worker has not been started yet"
684 ))));
685 continue;
686 };
687
688 let was_resumed = workers
689 .vm_rpc
690 .call(UhVmRpc::Resume, ())
691 .await
692 .context("failed to resumed VM")?;
693
694 let was_halted = workers
695 .vm_rpc
696 .call(UhVmRpc::ClearHalt, ())
697 .await
698 .context("failed to clear halt from VPs")?;
699
700 rpc.handle_sync(|_| {
701 if was_resumed || was_halted {
702 Ok(())
703 } else {
704 Err(RemoteError::new(anyhow::anyhow!("VM is currently running")))
705 }
706 });
707 }
708 diag_server::DiagRequest::Save(rpc) => {
709 let Some(workers) = &mut workers else {
710 rpc.complete(Err(RemoteError::new(anyhow::anyhow!(
711 "worker has not been started yet"
712 ))));
713 continue;
714 };
715
716 workers.vm_rpc.send(UhVmRpc::Save(rpc));
717 }
718 #[cfg(feature = "profiler")]
719 diag_server::DiagRequest::Profile(rpc) => {
720 let (rpc_params, rpc_sender) = rpc.split();
721 if profiler_host.is_none() {
723 match launch_mesh_host(mesh, "profiler", Some(tracing.tracer()))
724 .await
725 .context("failed to launch profiler host")
726 {
727 Ok(host) => {
728 profiler_host = Some(host);
729 }
730 Err(e) => {
731 rpc_sender.complete(Err(RemoteError::new(e)));
732 continue;
733 }
734 }
735 }
736
737 let profiling_duration = rpc_params.duration;
738 let host = profiler_host.as_ref().unwrap();
739 let mut profiler_worker;
740 match host
741 .launch_worker(
742 profiler_worker::PROFILER_WORKER,
743 ProfilerWorkerParameters {
744 profiler_request: rpc_params,
745 },
746 )
747 .await
748 {
749 Ok(worker) => {
750 profiler_worker = worker;
751 }
752 Err(e) => {
753 rpc_sender.complete(Err(RemoteError::new(e)));
754 continue;
755 }
756 }
757
758 driver
759 .spawn("profiler_worker", async move {
760 let result = CancelContext::new()
761 .with_timeout(Duration::from_secs(profiling_duration + 30))
762 .until_cancelled(profiler_worker.join())
763 .await
764 .context("profiler worker cancelled")
765 .and_then(|result| result.context("profiler worker failed"))
766 .map_err(RemoteError::new);
767
768 rpc_sender.complete(result);
769 })
770 .detach();
771 }
772 }
773 }
774 Event::Worker(event) => match event {
775 WorkerEvent::Started => {
776 if let Some(response) = restart_rpc.take() {
777 tracing::info!(CVM_ALLOWED, "restart complete");
778 response.complete(Ok(()));
779 } else {
780 tracing::info!(CVM_ALLOWED, "vm worker started");
781 }
782 state = ControlState::Started;
783 }
784 WorkerEvent::Stopped => {
785 anyhow::bail!("worker unexpectedly stopped");
786 }
787 WorkerEvent::Failed(err) => {
788 return Err(anyhow::Error::from(err)).context("vm worker failed");
789 }
790 WorkerEvent::RestartFailed(err) => {
791 tracing::error!(
792 CVM_ALLOWED,
793 error = &err as &dyn std::error::Error,
794 "restart failed"
795 );
796 restart_rpc.take().unwrap().complete(Err(err));
797 state = ControlState::Started;
798 }
799 },
800 Event::Control(req) => match req {
801 ControlRequest::FlushLogs(rpc) => {
802 rpc.handle(async |mut ctx| {
803 tracing::info!(CVM_ALLOWED, "flushing logs");
804 ctx.until_cancelled(tracing.flush()).await?;
805 Ok(())
806 })
807 .await
808 }
809 ControlRequest::MakeWorker(rpc) => {
810 rpc.handle_failable(async |name| {
811 launch_mesh_host(mesh, &name, Some(tracing.tracer())).await
812 })
813 .await
814 }
815 },
816 }
817 }
818
819 Ok(())
820}
821
822async fn signal_vtl0_started(driver: &DefaultDriver) -> anyhow::Result<()> {
823 tracing::info!(CVM_ALLOWED, "signaling vtl0 started early");
824 let (client, task) = guest_emulation_transport::spawn_get_worker(driver.clone())
825 .await
826 .context("failed to spawn GET")?;
827 client.complete_start_vtl0(None).await;
828 drop(client);
830 task.await.unwrap();
831 tracing::info!(CVM_ALLOWED, "signaled vtl0 start");
832 Ok(())
833}
834
835register_workers! {
841 UnderhillVmWorker,
842 DiagWorker,
843 #[cfg(feature = "profiler")]
844 ProfilerWorker,
845}