Skip to main content

starnix_kernel_runner/
serve_protocols.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::Container;
6use anyhow::{Context as _, Error};
7use fidl::endpoints::{RequestStream, ServerEnd};
8use fidl_fuchsia_component_runner as frunner;
9use fidl_fuchsia_element as felement;
10use fidl_fuchsia_io as fio;
11use fidl_fuchsia_memory_attribution as fattribution;
12use fidl_fuchsia_posix as fposix;
13use fidl_fuchsia_starnix_binder as fbinder;
14use fidl_fuchsia_starnix_container as fstarcontainer;
15use fuchsia_async::{
16    DurationExt, {self as fasync},
17};
18use fuchsia_rcu::RcuReadScope;
19use futures::channel::oneshot;
20use futures::{
21    AsyncReadExt, AsyncWriteExt, Future, FutureExt, StreamExt, TryFutureExt, TryStreamExt, pin_mut,
22    select,
23};
24use starnix_core::execution::{create_init_child_process, execute_task_with_prerun_result};
25use starnix_core::fs::fuchsia::create_fuchsia_pipe;
26use starnix_core::task::dynamic_thread_spawner::SpawnRequestBuilder;
27use starnix_core::task::{CurrentTask, ExitStatus, Kernel, ProcessEntryRef};
28use starnix_core::vfs::buffers::{VecInputBuffer, VecOutputBuffer};
29use starnix_core::vfs::file_server::serve_file_at;
30use starnix_core::vfs::socket::VsockSocket;
31use starnix_core::vfs::{FdFlags, FileHandle};
32use starnix_logging::{log_error, log_warn};
33use starnix_modules::{Framebuffer, create_main_and_replica};
34use starnix_task_command::TaskCommand;
35use starnix_uapi::auth::Credentials;
36use starnix_uapi::errors::Errno;
37use starnix_uapi::open_flags::OpenFlags;
38use starnix_uapi::signals::UncheckedSignal;
39use starnix_uapi::{errno, error, uapi};
40use std::ffi::CString;
41
42use super::start_component;
43
44pub fn expose_root(
45    system_task: &CurrentTask,
46    server_end: ServerEnd<fio::DirectoryMarker>,
47) -> Result<(), Error> {
48    let root_file = system_task.open_file("/".into(), OpenFlags::RDONLY)?;
49    serve_file_at(server_end.into_channel().into(), system_task, &root_file, Credentials::root())?;
50    Ok(())
51}
52
53pub async fn serve_component_runner(
54    request_stream: frunner::ComponentRunnerRequestStream,
55    system_task: &CurrentTask,
56) -> Result<(), Error> {
57    request_stream
58        .try_for_each_concurrent(None, |event| async {
59            match event {
60                frunner::ComponentRunnerRequest::Start { start_info, controller, .. } => {
61                    if let Err(e) = start_component(start_info, controller, system_task).await {
62                        log_error!("failed to start component: {:?}", e);
63                    }
64                }
65                frunner::ComponentRunnerRequest::_UnknownMethod { ordinal, .. } => {
66                    log_warn!("Unknown ComponentRunner request: {ordinal}");
67                }
68            }
69            Ok(())
70        })
71        .await
72        .map_err(Error::from)
73}
74
75fn to_winsize(window_size: Option<fstarcontainer::ConsoleWindowSize>) -> uapi::winsize {
76    window_size
77        .map(|window_size| uapi::winsize {
78            ws_row: window_size.rows,
79            ws_col: window_size.cols,
80            ws_xpixel: window_size.x_pixels,
81            ws_ypixel: window_size.y_pixels,
82        })
83        .unwrap_or(uapi::winsize::default())
84}
85
86async fn spawn_console(
87    kernel: &Kernel,
88    payload: fstarcontainer::ControllerSpawnConsoleRequest,
89) -> Result<Result<u8, fstarcontainer::SpawnConsoleError>, Error> {
90    if let (Some(console_in), Some(console_out), Some(binary_path)) =
91        (payload.console_in, payload.console_out, payload.binary_path)
92    {
93        let binary_path = CString::new(binary_path)?;
94        let argv = payload
95            .argv
96            .unwrap_or(vec![])
97            .into_iter()
98            .map(CString::new)
99            .collect::<Result<Vec<_>, _>>()?;
100        let environ = payload
101            .environ
102            .unwrap_or(vec![])
103            .into_iter()
104            .map(CString::new)
105            .collect::<Result<Vec<_>, _>>()?;
106        let window_size = to_winsize(payload.window_size);
107        let current_task = create_init_child_process(
108            &kernel.weak_self.upgrade().expect("Kernel must still be alive"),
109            TaskCommand::new(binary_path.as_bytes()),
110            Credentials::with_ids(0, 0),
111            None,
112        )?;
113        let (sender, receiver) = oneshot::channel();
114        let pty = execute_task_with_prerun_result(
115            current_task,
116            move |current_task| {
117                let executable = current_task.open_file_for_exec(
118                    starnix_core::vfs::FdNumber::AT_FDCWD,
119                    binary_path.as_bytes().into(),
120                    OpenFlags::empty(),
121                )?;
122                current_task.exec(executable, binary_path, argv, environ)?;
123                let (pty, pts) = create_main_and_replica(&current_task, window_size)?;
124                let fd_flags = FdFlags::empty();
125                assert_eq!(0, current_task.add_file(pts.clone(), fd_flags)?.raw());
126                assert_eq!(1, current_task.add_file(pts.clone(), fd_flags)?.raw());
127                assert_eq!(2, current_task.add_file(pts, fd_flags)?.raw());
128                Ok(pty)
129            },
130            move |result| {
131                let _ = match result {
132                    Ok(ExitStatus::Exit(exit_code)) => sender.send(Ok(exit_code)),
133                    _ => sender.send(Err(fstarcontainer::SpawnConsoleError::Canceled)),
134                };
135            },
136            None,
137        )?;
138        let _ = forward_to_pty(kernel, console_in, console_out, pty).map_err(|e| {
139            log_error!("failed to forward to terminal {:?}", e);
140        });
141
142        Ok(receiver.await?)
143    } else {
144        Ok(Err(fstarcontainer::SpawnConsoleError::InvalidArgs))
145    }
146}
147
148pub async fn serve_container_controller(
149    request_stream: fstarcontainer::ControllerRequestStream,
150    system_task: &CurrentTask,
151) -> Result<(), Error> {
152    request_stream
153        .map_err(Error::from)
154        .try_for_each_concurrent(None, |event| async {
155            match event {
156                fstarcontainer::ControllerRequest::VsockConnect {
157                    payload:
158                        fstarcontainer::ControllerVsockConnectRequest { port, bridge_socket, .. },
159                    ..
160                } => {
161                    let Some(port) = port else {
162                        log_warn!("vsock connection missing port");
163                        return Ok(());
164                    };
165                    let Some(bridge_socket) = bridge_socket else {
166                        log_warn!("vsock connection missing bridge_socket");
167                        return Ok(());
168                    };
169                    connect_to_vsock(port, bridge_socket, system_task).await.unwrap_or_else(|e| {
170                        log_error!("failed to connect to vsock {:?}", e);
171                    });
172                }
173                fstarcontainer::ControllerRequest::SpawnConsole { payload, responder } => {
174                    responder.send(spawn_console(system_task.kernel(), payload).await?)?;
175                }
176                fstarcontainer::ControllerRequest::GetVmoReferences { payload, responder } => {
177                    if let Some(koid) = payload.koid {
178                        let mut results = vec![];
179                        for thread_group in
180                            system_task.kernel().pids.get_thread_groups(&RcuReadScope::new())
181                        {
182                            if let Ok(leader) = thread_group.leader.get_task() {
183                                if let Ok(files) = leader.files() {
184                                    let fds = files.get_all_fds();
185                                    for fd in fds {
186                                        if let Ok(file) = files.get(fd) {
187                                            if let Ok(memory) = file.get_memory(
188                                                system_task,
189                                                None,
190                                                starnix_core::mm::ProtectionFlags::READ,
191                                            ) {
192                                                let memory_koid = memory
193                                                    .info()
194                                                    .expect("Failed to get memory info")
195                                                    .koid;
196                                                if memory_koid.raw_koid() == koid {
197                                                    let process_name = thread_group
198                                                        .process
199                                                        .get_name()
200                                                        .unwrap_or_default();
201                                                    results.push(fstarcontainer::VmoReference {
202                                                        process_name: Some(
203                                                            process_name.to_string(),
204                                                        ),
205                                                        pid: Some(leader.get_pid() as u64),
206                                                        fd: Some(fd.raw()),
207                                                        koid: Some(koid),
208                                                        ..Default::default()
209                                                    });
210                                                }
211                                            }
212                                        }
213                                    }
214                                }
215                            }
216                        }
217                        let _ =
218                            responder.send(&fstarcontainer::ControllerGetVmoReferencesResponse {
219                                references: Some(results),
220                                ..Default::default()
221                            });
222                    }
223                }
224                fstarcontainer::ControllerRequest::GetJobHandle { responder } => {
225                    let _result = responder.send(fstarcontainer::ControllerGetJobHandleResponse {
226                        job: Some(
227                            fuchsia_runtime::job_default()
228                                .duplicate_handle(zx::Rights::SAME_RIGHTS)
229                                .expect("Failed to dup handle"),
230                        ),
231                        ..Default::default()
232                    });
233                }
234                fstarcontainer::ControllerRequest::SendSignal {
235                    payload:
236                        fstarcontainer::ControllerSendSignalRequest {
237                            pid: Some(pid),
238                            signal: Some(signal),
239                            ..
240                        },
241                    responder,
242                } => {
243                    let pids = &system_task.kernel().pids;
244                    if let Some(ProcessEntryRef::Process(target_thread_group)) =
245                        pids.get(pid).ok().and_then(|p| p.get_process())
246                    {
247                        #[allow(
248                            clippy::undocumented_unsafe_blocks,
249                            reason = "Force documented unsafe blocks in Starnix"
250                        )]
251                        match unsafe {
252                            target_thread_group.send_signal_unchecked_debug(
253                                system_task,
254                                UncheckedSignal::new(signal),
255                            )
256                        } {
257                            Ok(_) => {
258                                let _result = responder.send(Ok(()));
259                            }
260                            Err(_) => {
261                                let _result =
262                                    responder.send(Err(fstarcontainer::SignalError::InvalidSignal));
263                            }
264                        };
265                    } else {
266                        let _result =
267                            responder.send(Err(fstarcontainer::SignalError::InvalidTarget));
268                    };
269                }
270                // The request did not contain both a signal and a target pid.
271                fstarcontainer::ControllerRequest::SendSignal { responder, .. } => {
272                    log_error!("malformed SendSignal request");
273                    let _result = responder.send(Err(fstarcontainer::SignalError::InvalidTarget));
274                }
275                fstarcontainer::ControllerRequest::SetSyscallLogFilter { payload, responder } => {
276                    if let Some(process_name) = payload.process_name {
277                        system_task.kernel().add_syscall_log_filter(&process_name);
278                        let _ = responder.send(Ok(()));
279                    } else {
280                        let _ = responder.send(Err(
281                            fstarcontainer::SetSyscallLogFilterError::MissingProcessName,
282                        ));
283                    }
284                }
285                fstarcontainer::ControllerRequest::ClearSyscallLogFilters { responder } => {
286                    system_task.kernel().clear_syscall_log_filters();
287                    let _ = responder.send();
288                }
289                fstarcontainer::ControllerRequest::_UnknownMethod { .. } => (),
290            }
291            Ok(())
292        })
293        .await
294}
295
296async fn connect_to_vsock(
297    port: u32,
298    bridge_socket: fidl::Socket,
299    system_task: &CurrentTask,
300) -> Result<(), Error> {
301    let socket = loop {
302        if let Ok(socket) = system_task.kernel().default_abstract_vsock_namespace.lookup(&port) {
303            break socket;
304        };
305        fasync::Timer::new(fasync::MonotonicDuration::from_millis(100).after_now()).await;
306    };
307
308    let pipe =
309        create_fuchsia_pipe(system_task, bridge_socket, OpenFlags::RDWR | OpenFlags::NONBLOCK)?;
310    socket.downcast_socket::<VsockSocket>().unwrap().remote_connection(
311        &socket,
312        system_task,
313        pipe,
314    )?;
315
316    Ok(())
317}
318
319fn forward_to_pty(
320    kernel: &Kernel,
321    console_in: fidl::Socket,
322    console_out: fidl::Socket,
323    pty: FileHandle,
324) -> Result<(), Error> {
325    // Matches fuchsia.io.Transfer capacity, somewhat arbitrarily.
326    const BUFFER_CAPACITY: usize = 8192;
327
328    let mut rx = fuchsia_async::Socket::from_socket(console_in);
329    let mut tx = fuchsia_async::Socket::from_socket(console_out);
330    let pty_sink = pty.clone();
331    let closure = async move |current_task: &CurrentTask| {
332        let _result: Result<(), Error> = (async || {
333            let mut buffer = vec![0u8; BUFFER_CAPACITY];
334            loop {
335                let bytes = rx.read(&mut buffer[..]).await?;
336                if bytes == 0 {
337                    return Ok(());
338                }
339                pty_sink.write(current_task, &mut VecInputBuffer::new(&buffer[..bytes]))?;
340            }
341        })()
342        .await;
343    };
344    let req = SpawnRequestBuilder::new()
345        .with_debug_name("forward-to-pty-in")
346        .with_async_closure(closure)
347        .build();
348    kernel.kthreads.spawner().spawn_from_request(req);
349
350    let pty_source = pty;
351    let closure = move |current_task: &CurrentTask| {
352        let _result: Result<(), Error> =
353            fasync::LocalExecutor::default().run_singlethreaded(async {
354                let mut buffer = VecOutputBuffer::new(BUFFER_CAPACITY);
355                loop {
356                    buffer.reset();
357                    let bytes = pty_source.read(current_task, &mut buffer)?;
358                    if bytes == 0 {
359                        return Ok(());
360                    }
361                    tx.write_all(buffer.data()).await?;
362                }
363            });
364    };
365    let req = SpawnRequestBuilder::new()
366        .with_debug_name("forward-to-pty-out")
367        .with_sync_closure(closure)
368        .build();
369    kernel.kthreads.spawner().spawn_from_request(req);
370
371    Ok(())
372}
373
374pub async fn serve_container_info(
375    request_stream: fstarcontainer::InfoRequestStream,
376) -> Result<(), Error> {
377    request_stream
378        .map_err(Error::from)
379        .try_for_each_concurrent(None, |event| async {
380            match event {
381                fstarcontainer::InfoRequest::GetJobKoid { responder } => {
382                    let koid = fuchsia_runtime::job_default()
383                        .koid()
384                        .unwrap_or(zx::Koid::from_raw(zx::sys::ZX_KOID_INVALID));
385                    responder.send(&fstarcontainer::InfoGetJobKoidResponse {
386                        koid: Some(koid.raw_koid()),
387                        ..Default::default()
388                    })?;
389                }
390                fstarcontainer::InfoRequest::_UnknownMethod { ordinal, .. } => {
391                    starnix_logging::log_warn!("Unknown InfoRequest method: {}", ordinal);
392                }
393            }
394            Ok(())
395        })
396        .await
397}
398
399pub async fn serve_graphical_presenter(
400    mut request_stream: felement::GraphicalPresenterRequestStream,
401    kernel: &Kernel,
402) -> Result<(), Error> {
403    while let Some(request) = request_stream.next().await {
404        match request.context("reading graphical presenter request")? {
405            felement::GraphicalPresenterRequest::PresentView {
406                view_spec,
407                annotation_controller: _,
408                view_controller_request: _,
409                responder,
410            } => match view_spec.viewport_creation_token {
411                Some(token) => {
412                    let fb = Framebuffer::get(kernel).context("getting framebuffer from kernel")?;
413                    fb.present_view(token);
414                    let _ = responder.send(Ok(()));
415                }
416                None => {
417                    let _ = responder.send(Err(felement::PresentViewError::InvalidArgs));
418                }
419            },
420        }
421    }
422    Ok(())
423}
424
425/// Serves the memory attribution provider for the Kernel ELF component.
426pub fn serve_memory_attribution_provider_elfkernel(
427    mut request_stream: fattribution::ProviderRequestStream,
428    container: &Container,
429) -> impl Future<Output = Result<(), Error>> {
430    let observer = container.new_memory_attribution_observer(request_stream.control_handle());
431    async move {
432        while let Some(event) = request_stream.try_next().await? {
433            match event {
434                fattribution::ProviderRequest::Get { responder } => {
435                    observer.next(responder);
436                }
437                fattribution::ProviderRequest::_UnknownMethod {
438                    ordinal, control_handle, ..
439                } => {
440                    log_error!("Invalid request to AttributionProvider: {ordinal}");
441                    control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
442                }
443            }
444        }
445        Ok(())
446    }
447}
448
449/// Serves the memory attribution provider for the Container component.
450pub fn serve_memory_attribution_provider_container(
451    mut request_stream: fattribution::ProviderRequestStream,
452    kernel: &Kernel,
453) -> impl Future<Output = ()> + use<> {
454    let observer = kernel.new_memory_attribution_observer(request_stream.control_handle());
455    async move {
456        while let Some(event) = request_stream
457            .try_next()
458            .await
459            .inspect_err(|err| {
460                log_warn!("Error while serving container memory attribution: {:?}", err)
461            })
462            .ok()
463            .flatten()
464        {
465            match event {
466                fattribution::ProviderRequest::Get { responder } => {
467                    observer.next(responder);
468                }
469                fattribution::ProviderRequest::_UnknownMethod {
470                    ordinal, control_handle, ..
471                } => {
472                    log_error!("Invalid request to AttributionProvider: {ordinal}");
473                    control_handle.shutdown_with_epitaph(zx::Status::INVALID_ARGS);
474                }
475            }
476        }
477    }
478}
479
480async fn select_first<O>(f1: impl Future<Output = O>, f2: impl Future<Output = O>) -> O {
481    let f1 = f1.fuse();
482    let f2 = f2.fuse();
483    pin_mut!(f1, f2);
484    select! {
485        f1 = f1 => f1,
486        f2 = f2 => f2,
487    }
488}
489
490/// Serve the LutexController protocol.
491pub async fn serve_lutex_controller(
492    request_stream: fbinder::LutexControllerRequestStream,
493    current_task: &CurrentTask,
494) -> Result<(), Error> {
495    let kernel = current_task.kernel();
496    request_stream
497        .map_err(Error::from)
498        .try_for_each_concurrent(None, |event| async move {
499            match event {
500                fbinder::LutexControllerRequest::WaitBitset { payload, responder } => {
501                    let deadline_and_receiver = (|| {
502                        let vmo = payload.vmo.ok_or_else(|| errno!(EINVAL))?;
503                        let offset = payload.offset.ok_or_else(|| errno!(EINVAL))?;
504                        let value = payload.value.ok_or_else(|| errno!(EINVAL))?;
505                        let mask = payload.mask.unwrap_or(u32::MAX);
506                        let deadline = payload.deadline.map(zx::MonotonicInstant::from_nanos);
507                        kernel
508                            .shared_futexes
509                            .external_wait(vmo.into(), offset, value, mask)
510                            .map(|(event, receiver)| (deadline, event, receiver))
511                    })();
512                    let result = match deadline_and_receiver {
513                        Ok((deadline, event, receiver)) => {
514                            // We construct a specific `wait_fut` to explicitly bind the lifecycle
515                            // of the `event` to the duration of this wait operation. If the FIDL
516                            // client disconnects (or the future is otherwise dropped/cancelled),
517                            // the `event` is immediately dropped. This zeros the strong reference
518                            // count on the `InterruptibleEvent`, signaling to the `FutexTable`
519                            // that this external waiter is now stale and should be garbage
520                            // collected on its next cleanup pass.
521                            let wait_fut = async move {
522                                let _event = event;
523                                let receiver = receiver.map_err(|_| errno!(EINTR));
524                                if let Some(deadline) = deadline {
525                                    let timer =
526                                        fasync::Timer::new(deadline).map(|_| error!(ETIMEDOUT));
527                                    select_first(timer, receiver).await
528                                } else {
529                                    receiver.await
530                                }
531                            };
532                            wait_fut.await
533                        }
534                        Err(e) => Err(e),
535                    };
536                    let result = result.map_err(|e: Errno| {
537                        fposix::Errno::from_primitive(e.code.error_code() as i32)
538                            .unwrap_or(fposix::Errno::Einval)
539                    });
540                    responder
541                        .send(result)
542                        .context("Unable to send LutexControllerRequest::WaitBitset response")?;
543                }
544                fbinder::LutexControllerRequest::WakeBitset { payload, responder } => {
545                    let result = (|| {
546                        let vmo = payload.vmo.ok_or_else(|| errno!(EINVAL))?;
547                        let offset = payload.offset.ok_or_else(|| errno!(EINVAL))?;
548                        let count = payload.count.ok_or_else(|| errno!(EINVAL))?;
549                        let mask = payload.mask.unwrap_or(u32::MAX);
550                        kernel.shared_futexes.external_wake(
551                            vmo.into(),
552                            offset,
553                            count as usize,
554                            mask,
555                        )
556                    })();
557                    let result = result
558                        .map(|count| fbinder::WakeResponse {
559                            count: Some(count as u64),
560                            ..fbinder::WakeResponse::default()
561                        })
562                        .map_err(|e: Errno| {
563                            fposix::Errno::from_primitive(e.code.error_code() as i32)
564                                .unwrap_or(fposix::Errno::Einval)
565                        });
566                    responder
567                        .send(result)
568                        .context("Unable to send LutexControllerRequest::WakeBitset response")?;
569                }
570                fbinder::LutexControllerRequest::CmpRequeue { payload, responder } => {
571                    let result = (|| {
572                        let first_vmo = payload.first_vmo.ok_or_else(|| errno!(EINVAL))?;
573                        let first_offset = payload.first_offset.ok_or_else(|| errno!(EINVAL))?;
574                        let second_vmo = payload.second_vmo;
575                        let second_offset = payload.second_offset.ok_or_else(|| errno!(EINVAL))?;
576                        let wake_count = payload.wake_count.ok_or_else(|| errno!(EINVAL))?;
577                        let requeue_count = payload.requeue_count.ok_or_else(|| errno!(EINVAL))?;
578                        let cmp_val = payload.cmp_val;
579                        kernel.shared_futexes.external_requeue(
580                            first_vmo.into(),
581                            first_offset,
582                            second_vmo.map(Into::into),
583                            second_offset,
584                            wake_count as usize,
585                            requeue_count as usize,
586                            cmp_val,
587                        )
588                    })();
589                    let result = result
590                        .map(|count| fbinder::CmpRequeueResponse {
591                            count: Some(count as u64),
592                            ..fbinder::CmpRequeueResponse::default()
593                        })
594                        .map_err(|e: Errno| {
595                            fposix::Errno::from_primitive(e.code.error_code() as i32)
596                                .unwrap_or(fposix::Errno::Einval)
597                        });
598                    responder
599                        .send(result)
600                        .context("Unable to send LutexControllerRequest::Requeue response")?;
601                }
602                fbinder::LutexControllerRequest::_UnknownMethod { ordinal, .. } => {
603                    log_warn!("Unknown LutexController ordinal: {}", ordinal);
604                }
605            }
606            Ok(())
607        })
608        .await
609        .context("failed fbinder::LutexController request")
610}
611
612#[cfg(test)]
613mod tests {
614    use super::*;
615    use assert_matches::assert_matches;
616    use fidl_fuchsia_posix as fposix;
617    use starnix_core::testing::*;
618    use zx;
619
620    #[fuchsia::test]
621    async fn lutex_controller_test() {
622        spawn_kernel_and_run(async |current_task| {
623            let (sender, receiver) = oneshot::channel::<()>();
624            current_task.kernel.kthreads.spawn_future(
625                {
626                    let kernel = current_task.kernel.clone();
627                    move || async move {
628                        let (lutex_controller, stream) = fidl::endpoints::create_proxy_and_stream::<
629                            fbinder::LutexControllerMarker,
630                        >();
631
632                        // Spawn the server
633                        let server_fut =
634                            serve_lutex_controller(stream, kernel.kthreads.system_task());
635
636                        let client_fut = async move {
637                            const VMO_SIZE: usize = 4 * 1024;
638                            let vmo = zx::Vmo::create(VMO_SIZE as u64).expect("Vmo::create");
639                            // Wait on an incorrect value.
640                            let wait = lutex_controller
641                                .wait_bitset(fbinder::WaitBitsetRequest {
642                                    vmo: Some(
643                                        vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
644                                            .expect("duplicate vmo"),
645                                    ),
646                                    offset: Some(0),
647                                    value: Some(1),
648                                    ..Default::default()
649                                })
650                                .await
651                                .expect("got_answer");
652                            assert_matches!(wait, Err(fposix::Errno::Eagain));
653
654                            // Wait with a timeout
655                            let wait = lutex_controller
656                                .wait_bitset(fbinder::WaitBitsetRequest {
657                                    vmo: Some(
658                                        vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
659                                            .expect("duplicate vmo"),
660                                    ),
661                                    offset: Some(0),
662                                    value: Some(0),
663                                    deadline: Some(0),
664                                    ..Default::default()
665                                })
666                                .await
667                                .expect("got_answer");
668                            assert_matches!(wait, Err(fposix::Errno::Etimedout));
669
670                            let mut wait = Box::pin(
671                                lutex_controller.wait_bitset(fbinder::WaitBitsetRequest {
672                                    vmo: Some(
673                                        vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
674                                            .expect("duplicate vmo"),
675                                    ),
676                                    offset: Some(0),
677                                    value: Some(0),
678                                    deadline: None,
679                                    ..Default::default()
680                                }),
681                            );
682                            // The wait is correct, the future should stay pending until a wake.
683                            assert!(futures::poll!(&mut wait).is_pending());
684
685                            let waken_up: fbinder::WakeResponse = lutex_controller
686                                .wake_bitset(fbinder::WakeBitsetRequest {
687                                    vmo: Some(
688                                        vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
689                                            .expect("duplicate vmo"),
690                                    ),
691                                    offset: Some(0),
692                                    count: Some(1),
693                                    mask: None,
694                                    ..Default::default()
695                                })
696                                .await
697                                .expect("wake_answer")
698                                .expect("wake_response");
699                            assert_eq!(waken_up.count, Some(1));
700
701                            // The wait should now return.
702                            assert!(wait.await.expect("await_answer").is_ok());
703
704                            // test cmp_requeue
705                            {
706                                let vmo2 = zx::Vmo::create(VMO_SIZE as u64).expect("Vmo::create");
707                                let mut wait1 = Box::pin(
708                                    lutex_controller.wait_bitset(fbinder::WaitBitsetRequest {
709                                        vmo: Some(
710                                            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
711                                                .expect("duplicate vmo"),
712                                        ),
713                                        offset: Some(4),
714                                        value: Some(0),
715                                        deadline: None,
716                                        ..Default::default()
717                                    }),
718                                );
719                                let mut wait2 = Box::pin(
720                                    lutex_controller.wait_bitset(fbinder::WaitBitsetRequest {
721                                        vmo: Some(
722                                            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
723                                                .expect("duplicate vmo"),
724                                        ),
725                                        offset: Some(4),
726                                        value: Some(0),
727                                        deadline: None,
728                                        ..Default::default()
729                                    }),
730                                );
731                                let cmp_requeue_response = lutex_controller
732                                    .cmp_requeue(fbinder::CmpRequeueRequest {
733                                        first_vmo: Some(
734                                            vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
735                                                .expect("duplicate vmo"),
736                                        ),
737                                        first_offset: Some(4),
738                                        second_vmo: Some(
739                                            vmo2.duplicate_handle(zx::Rights::SAME_RIGHTS)
740                                                .expect("duplicate vmo"),
741                                        ),
742                                        second_offset: Some(8),
743                                        wake_count: Some(1),
744                                        requeue_count: Some(1),
745                                        cmp_val: Some(0xBAD),
746                                        ..Default::default()
747                                    })
748                                    .await
749                                    .expect("got_answer")
750                                    .unwrap_err();
751                                // 0xBAD is not 0x0, so EAGAIN expected
752                                assert_matches!(cmp_requeue_response, fposix::Errno::Eagain);
753                                let cmp_requeue_response2: fbinder::CmpRequeueResponse =
754                                    lutex_controller
755                                        .cmp_requeue(fbinder::CmpRequeueRequest {
756                                            first_vmo: Some(
757                                                vmo.duplicate_handle(zx::Rights::SAME_RIGHTS)
758                                                    .expect("duplicate vmo"),
759                                            ),
760                                            first_offset: Some(4),
761                                            second_vmo: Some(
762                                                vmo2.duplicate_handle(zx::Rights::SAME_RIGHTS)
763                                                    .expect("duplicate vmo"),
764                                            ),
765                                            second_offset: Some(8),
766                                            wake_count: Some(1),
767                                            requeue_count: Some(1),
768                                            cmp_val: Some(0x0),
769                                            ..Default::default()
770                                        })
771                                        .await
772                                        .expect("got_answer")
773                                        .expect("got_response");
774                                // 1 woken, 1 re-queued
775                                assert_matches!(cmp_requeue_response2.count, Some(2));
776                                // give wait1 and wait2 a chance to complete; we don't rely on this
777                                // being long enough however
778                                fasync::Timer::new(
779                                    fasync::MonotonicDuration::from_millis(100).after_now(),
780                                )
781                                .await;
782                                let mut still_pending_count: u32 = 0;
783                                // Up to one of wait1 and wait2 can be completed by this point, so at
784                                // least one must still be pending.
785                                let wait1_poll = futures::poll!(&mut wait1);
786                                if wait1_poll.is_pending() {
787                                    still_pending_count += 1;
788                                }
789                                let wait2_poll = futures::poll!(&mut wait2);
790                                if wait2_poll.is_pending() {
791                                    still_pending_count += 1;
792                                }
793                                assert!(still_pending_count >= 1);
794                                // at this point exactly one of wait1 or wait2 will remain pending until
795                                // we wake it via vmo2 - we don't know which of wait1 or wait2 we're
796                                // waking here (which is fine)
797                                let vmo2_wake_response: fbinder::WakeResponse = lutex_controller
798                                    .wake_bitset(fbinder::WakeBitsetRequest {
799                                        vmo: Some(
800                                            vmo2.duplicate_handle(zx::Rights::SAME_RIGHTS)
801                                                .expect("duplicate vmo"),
802                                        ),
803                                        offset: Some(8),
804                                        count: Some(1),
805                                        mask: None,
806                                        ..Default::default()
807                                    })
808                                    .await
809                                    .expect("wake_answer")
810                                    .expect("wake_response");
811                                assert_eq!(vmo2_wake_response.count, Some(1));
812                                // Now we know that both wait1 and wait2 are unblocked, so we can
813                                // wait on both of them.
814                                if wait1_poll.is_pending() {
815                                    assert!(wait1.await.expect("await_answer").is_ok());
816                                }
817                                if wait2_poll.is_pending() {
818                                    assert!(wait2.await.expect("await_answer").is_ok());
819                                }
820                            }
821                        };
822
823                        let (server_res, _) = futures::join!(server_fut, client_fut);
824                        server_res.expect("server failed");
825                        let _ = sender.send(());
826                    }
827                },
828                "lutex_controller_test",
829            );
830            receiver.await.expect("test failed");
831        })
832        .await;
833    }
834
835    #[fuchsia::test]
836    async fn container_info_test() {
837        let (info_proxy, stream) =
838            fidl::endpoints::create_proxy_and_stream::<fstarcontainer::InfoMarker>();
839
840        let server_fut = serve_container_info(stream);
841        let client_fut = async move {
842            let response = info_proxy.get_job_koid().await.expect("get_job_koid failed");
843            let expected_koid = fuchsia_runtime::job_default()
844                .koid()
845                .unwrap_or(zx::Koid::from_raw(zx::sys::ZX_KOID_INVALID));
846            assert_eq!(response.koid, Some(expected_koid.raw_koid()));
847            assert_ne!(response.koid, Some(zx::sys::ZX_KOID_INVALID));
848            drop(info_proxy);
849        };
850
851        let (server_res, ()) = futures::join!(server_fut, client_fut);
852        server_res.expect("server failed");
853    }
854}