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