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