1use crate::device::mem::new_null_file;
6use crate::execution::{
7 create_init_child_process, create_init_process, create_system_task,
8 execute_task_with_prerun_result,
9};
10use crate::fs::fuchsia::RemoteFs;
11use crate::fs::tmpfs::TmpFs;
12use crate::mm::syscalls::{do_mmap, sys_mremap};
13use crate::mm::{MemoryAccessor, MemoryAccessorExt, PAGE_SIZE};
14use crate::security;
15use crate::task::container_namespace::ContainerNamespace;
16use crate::task::{
17 CurrentTask, ExitStatus, Kernel, KernelFeatures, KernelOrTask, SchedulerManager, SystemLimits,
18 Task, TaskBuilder,
19};
20use crate::vfs::buffers::{InputBuffer, OutputBuffer};
21use crate::vfs::{
22 Anon, CacheMode, DirEntry, FdNumber, FileHandle, FileObject, FileOps, FileSystem,
23 FileSystemHandle, FileSystemOps, FileSystemOptions, FsContext, FsNode, FsNodeHandle,
24 FsNodeInfo, FsNodeOps, FsStr, Namespace, NamespaceNode, fileops_impl_nonseekable,
25 fileops_impl_noop_sync, fs_node_impl_not_dir,
26};
27use fuchsia_async::LocalExecutor;
28use selinux::SecurityServer;
29use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Unlocked};
30use starnix_syscalls::{SyscallArg, SyscallResult};
31use starnix_task_command::TaskCommand;
32use starnix_types::arch::ArchWidth;
33use starnix_types::vfs::default_statfs;
34use starnix_uapi::auth::{Credentials, FsCred};
35use starnix_uapi::errors::Errno;
36use starnix_uapi::file_mode::mode;
37use starnix_uapi::open_flags::OpenFlags;
38use starnix_uapi::user_address::{ArchSpecific, UserAddress};
39use starnix_uapi::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE, errno, error, statfs};
40use std::ffi::CString;
41use std::future::Future;
42use std::mem::MaybeUninit;
43use std::ops::Deref;
44use std::sync::{Arc, mpsc};
45use zerocopy::{Immutable, IntoBytes};
46use {fidl_fuchsia_io as fio, fuchsia_async as fasync};
47
48fn create_pkgfs<L>(locked: &mut Locked<L>, kernel: &Kernel) -> FileSystemHandle
52where
53 L: LockEqualOrBefore<FileOpsCore>,
54{
55 let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
56 let (server, client) = zx::Channel::create();
57 fdio::open("/pkg", rights, server).expect("failed to open /pkg");
58 RemoteFs::new_fs(
59 locked,
60 kernel,
61 client,
62 FileSystemOptions { source: "/pkg".into(), ..Default::default() },
63 rights,
64 )
65 .unwrap()
66}
67
68pub fn spawn_kernel_and_run<F, R>(callback: F) -> impl Future<Output = R>
73where
74 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
75 R: Send + Sync + 'static,
76{
77 spawn_kernel_and_run_internal(callback, None, TmpFs::new_fs, KernelFeatures::default())
78}
79
80pub fn spawn_kernel_with_features_and_run<F, R>(
82 callback: F,
83 features: KernelFeatures,
84) -> impl Future<Output = R>
85where
86 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
87 R: Send + Sync + 'static,
88{
89 spawn_kernel_and_run_internal(callback, None, TmpFs::new_fs, features)
90}
91
92pub fn spawn_kernel_and_run_sync<F, R>(callback: F) -> impl Future<Output = R>
97where
98 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
99 R: Send + Sync + 'static,
100{
101 spawn_kernel_and_run_internal_sync(callback, None, TmpFs::new_fs, KernelFeatures::default())
102}
103
104pub fn spawn_kernel_and_run_with_pkgfs<F, R>(callback: F) -> impl Future<Output = R>
110where
111 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
112 R: Send + Sync + 'static,
113{
114 spawn_kernel_and_run_internal(callback, None, create_pkgfs, KernelFeatures::default())
115}
116
117pub async fn spawn_kernel_with_selinux_and_run<F, R>(callback: F) -> R
123where
124 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask, &Arc<SecurityServer>) -> R
125 + Send
126 + Sync
127 + 'static,
128 R: Send + Sync + 'static,
129{
130 let security_server = SecurityServer::new_default();
131 let security_server_for_callback = security_server.clone();
132 spawn_kernel_and_run_internal(
133 async move |unlocked, current_task| {
134 security::selinuxfs_init_null(
135 current_task,
136 &new_null_file(unlocked, current_task, OpenFlags::empty()),
137 );
138 callback(unlocked, current_task, &security_server_for_callback).await
139 },
140 Some(security_server),
141 TmpFs::new_fs,
142 KernelFeatures::default(),
143 )
144 .await
145}
146
147fn spawn_kernel_and_run_internal<F, FS, R>(
150 callback: F,
151 security_server: Option<Arc<SecurityServer>>,
152 fs_factory: FS,
153 features: KernelFeatures,
154) -> impl Future<Output = R>
155where
156 R: Send + Sync + 'static,
157 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
158 FS: FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
159{
160 spawn_kernel_and_run_internal_sync(
161 move |locked, current_task| {
162 LocalExecutor::default().run_singlethreaded(callback(locked, current_task))
163 },
164 security_server,
165 fs_factory,
166 features,
167 )
168}
169
170fn spawn_kernel_and_run_internal_sync<F, FS, R>(
173 callback: F,
174 security_server: Option<Arc<SecurityServer>>,
175 fs_factory: FS,
176 features: KernelFeatures,
177) -> impl Future<Output = R>
178where
179 R: Send + Sync + 'static,
180 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
181 FS: FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
182{
183 #[allow(
184 clippy::undocumented_unsafe_blocks,
185 reason = "Force documented unsafe blocks in Starnix"
186 )]
187 let locked = unsafe { Unlocked::new() };
188 let kernel = create_test_kernel(locked, security_server, features);
189 let fs = create_test_fs_context(locked, &kernel, fs_factory);
190 let init_task = create_test_init_task(locked, &kernel, fs);
191 fasync::unblock(move || {
192 let (sender, receiver) = mpsc::sync_channel(1);
193 let error = execute_task_with_prerun_result(
194 locked,
195 init_task,
196 move |locked, current_task| -> Result<(), Errno> {
197 let result = callback(locked, current_task);
198 current_task.write().set_exit_status_if_not_already(ExitStatus::Exit(0));
199 sender.send(result).map_err(|e| errno!(EIO, e))?;
200 error!(EHWPOISON)
201 },
202 |_| {},
203 None,
204 )
205 .unwrap_err();
206 assert_eq!(error, errno!(EHWPOISON));
208 receiver.recv().expect("recv")
209 })
210}
211
212#[deprecated = "Do not add new callers, use spawn_kernel_and_run() instead."]
213pub fn create_kernel_task_and_unlocked()
214-> (Arc<Kernel>, AutoReleasableTask, &'static mut Locked<Unlocked>) {
215 create_kernel_task_and_unlocked_with_fs(TmpFs::new_fs)
216}
217
218fn create_test_kernel(
219 _locked: &mut Locked<Unlocked>,
220 security_server: Option<Arc<SecurityServer>>,
221 features: KernelFeatures,
222) -> Arc<Kernel> {
223 Kernel::new(
224 b"".into(),
225 features,
226 SystemLimits::default(),
227 ContainerNamespace::new(),
228 SchedulerManager::empty_for_tests(),
229 None,
230 fuchsia_inspect::Node::default(),
231 security::testing::kernel_state(security_server),
232 None,
233 None,
234 )
235 .expect("failed to create kernel")
236}
237
238fn create_test_fs_context(
239 locked: &mut Locked<Unlocked>,
240 kernel: &Kernel,
241 create_fs: impl FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
242) -> Arc<FsContext> {
243 FsContext::new(Namespace::new(create_fs(locked, kernel)))
244}
245
246fn create_test_init_task(
247 locked: &mut Locked<Unlocked>,
248 kernel: &Kernel,
249 fs: Arc<FsContext>,
250) -> TaskBuilder {
251 let init_pid = kernel.pids.write().allocate_pid();
252 assert_eq!(init_pid, 1);
253 let init_task = create_init_process(
254 locked,
255 &kernel.weak_self.upgrade().unwrap(),
256 init_pid,
257 TaskCommand::new(b"test-task"),
258 fs.fork(),
259 &[],
260 )
261 .expect("failed to create first task");
262 init_task.mm().unwrap().initialize_mmap_layout_for_test(ArchWidth::Arch64);
263
264 let system_task = create_system_task(locked, &kernel.weak_self.upgrade().unwrap(), fs)
265 .expect("create system task");
266 kernel.kthreads.init(system_task).expect("failed to initialize kthreads");
267
268 let system_task = kernel.kthreads.system_task();
269 kernel.hrtimer_manager.init(&system_task).expect("init hrtimer manager worker thread");
270
271 {
274 let _l1 = init_task.thread_group().read();
275 let _l2 = init_task.read();
276 }
277 init_task
278}
279
280fn create_kernel_task_and_unlocked_with_fs(
281 create_fs: impl FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
282) -> (Arc<Kernel>, AutoReleasableTask, &'static mut Locked<Unlocked>) {
283 #[allow(
284 clippy::undocumented_unsafe_blocks,
285 reason = "Force documented unsafe blocks in Starnix"
286 )]
287 let locked = unsafe { Unlocked::new() };
288 let kernel = create_test_kernel(locked, None, KernelFeatures::default());
289 let fs = create_fs(locked, &kernel);
290 let fs_context = create_test_fs_context(locked, &kernel, |_, _| fs.clone());
291 let init_task = create_test_init_task(locked, &kernel, fs_context);
292 (kernel, init_task.into(), locked)
293}
294
295pub fn create_task(
305 locked: &mut Locked<Unlocked>,
306 kernel: &Kernel,
307 task_name: &str,
308) -> AutoReleasableTask {
309 create_task_with_security_context(locked, kernel, task_name, &CString::new("#kernel").unwrap())
310}
311
312pub fn create_task_with_security_context(
316 locked: &mut Locked<Unlocked>,
317 kernel: &Kernel,
318 task_name: &str,
319 security_context: &CString,
320) -> AutoReleasableTask {
321 let task = create_init_child_process(
322 locked,
323 &kernel.weak_self.upgrade().unwrap(),
324 TaskCommand::new(task_name.as_bytes()),
325 Credentials::with_ids(0, 0),
326 Some(security_context),
327 )
328 .expect("failed to create second task");
329 task.mm().unwrap().initialize_mmap_layout_for_test(ArchWidth::Arch64);
330
331 {
334 let _l1 = task.thread_group().read();
335 let _l2 = task.read();
336 }
337
338 task.into()
339}
340
341pub fn map_memory_anywhere<L>(
344 locked: &mut Locked<L>,
345 current_task: &CurrentTask,
346 len: u64,
347) -> UserAddress
348where
349 L: LockEqualOrBefore<FileOpsCore>,
350{
351 map_memory(locked, current_task, UserAddress::NULL, len)
352}
353
354pub fn map_object_anywhere<L, T>(
359 locked: &mut Locked<L>,
360 current_task: &CurrentTask,
361 object: &T,
362) -> UserAddress
363where
364 L: LockEqualOrBefore<FileOpsCore>,
365 T: IntoBytes + Immutable,
366{
367 let addr = map_memory_anywhere(locked, current_task, std::mem::size_of::<T>() as u64);
368 current_task.write_object(addr.into(), object).expect("could not write object");
369 addr
370}
371
372pub fn map_memory<L>(
376 locked: &mut Locked<L>,
377 current_task: &CurrentTask,
378 address: UserAddress,
379 length: u64,
380) -> UserAddress
381where
382 L: LockEqualOrBefore<FileOpsCore>,
383{
384 map_memory_with_flags(locked, current_task, address, length, MAP_ANONYMOUS | MAP_PRIVATE)
385}
386
387pub fn map_memory_with_flags<L>(
391 locked: &mut Locked<L>,
392 current_task: &CurrentTask,
393 address: UserAddress,
394 length: u64,
395 flags: u32,
396) -> UserAddress
397where
398 L: LockEqualOrBefore<FileOpsCore>,
399{
400 do_mmap(
401 locked,
402 current_task,
403 address,
404 length as usize,
405 PROT_READ | PROT_WRITE,
406 flags,
407 FdNumber::from_raw(-1),
408 0,
409 )
410 .expect("Could not map memory")
411}
412
413pub fn remap_memory(
416 locked: &mut Locked<Unlocked>,
417 current_task: &CurrentTask,
418 old_addr: UserAddress,
419 old_length: u64,
420 new_length: u64,
421 flags: u32,
422 new_addr: UserAddress,
423) -> Result<UserAddress, Errno> {
424 sys_mremap(
425 locked,
426 current_task,
427 old_addr,
428 old_length as usize,
429 new_length as usize,
430 flags,
431 new_addr,
432 )
433}
434
435#[track_caller]
441pub fn fill_page(current_task: &CurrentTask, addr: UserAddress, data: char) {
442 let data = [data as u8].repeat(*PAGE_SIZE as usize);
443 if let Err(err) = current_task.write_memory(addr, &data) {
444 panic!("write page: failed to fill page @ {addr:?} with {data:?}: {err:?}");
445 }
446}
447
448#[track_caller]
454pub fn check_page_eq(current_task: &CurrentTask, addr: UserAddress, data: char) {
455 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
456 Ok(b) => b,
457 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
458 };
459 assert!(
460 buf.into_iter().all(|c| c == data as u8),
461 "unexpected payload: page @ {addr:?} should be filled with {data:?}"
462 );
463}
464
465#[track_caller]
471pub fn check_page_ne(current_task: &CurrentTask, addr: UserAddress, data: char) {
472 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
473 Ok(b) => b,
474 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
475 };
476 assert!(
477 !buf.into_iter().all(|c| c == data as u8),
478 "unexpected payload: page @ {addr:?} should not be filled with {data:?}"
479 );
480}
481
482#[track_caller]
488pub fn check_unmapped(current_task: &CurrentTask, addr: UserAddress) {
489 match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
490 Ok(_) => panic!("read page: page @ {addr:?} should be unmapped"),
491 Err(err) if err == starnix_uapi::errors::EFAULT => {}
492 Err(err) => {
493 panic!("read page: expected EFAULT reading page @ {addr:?} but got {err:?} instead")
494 }
495 }
496}
497
498pub struct PanickingFsNode;
501
502impl FsNodeOps for PanickingFsNode {
503 fs_node_impl_not_dir!();
504
505 fn create_file_ops(
506 &self,
507 _locked: &mut Locked<FileOpsCore>,
508 _node: &FsNode,
509 _current_task: &CurrentTask,
510 _flags: OpenFlags,
511 ) -> Result<Box<dyn FileOps>, Errno> {
512 panic!("should not be called")
513 }
514}
515
516pub struct PanickingFile;
518
519impl PanickingFile {
520 pub fn new_file(locked: &mut Locked<Unlocked>, current_task: &CurrentTask) -> FileHandle {
522 anon_test_file(locked, current_task, Box::new(PanickingFile), OpenFlags::RDWR)
523 }
524}
525
526impl FileOps for PanickingFile {
527 fileops_impl_nonseekable!();
528 fileops_impl_noop_sync!();
529
530 fn write(
531 &self,
532 _locked: &mut Locked<FileOpsCore>,
533 _file: &FileObject,
534 _current_task: &CurrentTask,
535 _offset: usize,
536 _data: &mut dyn InputBuffer,
537 ) -> Result<usize, Errno> {
538 panic!("write called on TestFile")
539 }
540
541 fn read(
542 &self,
543 _locked: &mut Locked<FileOpsCore>,
544 _file: &FileObject,
545 _current_task: &CurrentTask,
546 _offset: usize,
547 _data: &mut dyn OutputBuffer,
548 ) -> Result<usize, Errno> {
549 panic!("read called on TestFile")
550 }
551
552 fn ioctl(
553 &self,
554 _locked: &mut Locked<Unlocked>,
555 _file: &FileObject,
556 _current_task: &CurrentTask,
557 _request: u32,
558 _arg: SyscallArg,
559 ) -> Result<SyscallResult, Errno> {
560 panic!("ioctl called on TestFile")
561 }
562}
563
564pub fn anon_test_file<L>(
566 locked: &mut Locked<L>,
567 current_task: &CurrentTask,
568 ops: Box<dyn FileOps>,
569 flags: OpenFlags,
570) -> FileHandle
571where
572 L: LockEqualOrBefore<FileOpsCore>,
573{
574 Anon::new_private_file(locked, current_task, ops, flags, "[fuchsia:test_file]")
576}
577
578pub struct UserMemoryWriter<'a> {
580 mm: &'a Task,
582 current_addr: UserAddress,
584}
585
586impl<'a> UserMemoryWriter<'a> {
587 pub fn new(task: &'a Task, addr: UserAddress) -> Self {
589 Self { mm: task, current_addr: addr }
590 }
591
592 pub fn write(&mut self, data: &[u8]) -> UserAddress {
596 let bytes_written = self.mm.write_memory(self.current_addr, data).unwrap();
597 assert_eq!(bytes_written, data.len());
598 let start_addr = self.current_addr;
599 self.current_addr = (self.current_addr + bytes_written).unwrap();
600 start_addr
601 }
602
603 pub fn write_object<T: IntoBytes + Immutable>(&mut self, object: &T) -> UserAddress {
607 self.write(object.as_bytes())
608 }
609
610 pub fn current_address(&self) -> UserAddress {
612 self.current_addr
613 }
614}
615
616#[derive(Debug)]
617pub struct AutoReleasableTask(Option<CurrentTask>);
618
619impl AutoReleasableTask {
620 fn as_ref(this: &Self) -> &CurrentTask {
621 this.0.as_ref().unwrap()
622 }
623
624 fn as_mut(this: &mut Self) -> &mut CurrentTask {
625 this.0.as_mut().unwrap()
626 }
627}
628
629impl From<CurrentTask> for AutoReleasableTask {
630 fn from(task: CurrentTask) -> Self {
631 Self(Some(task))
632 }
633}
634
635impl From<TaskBuilder> for AutoReleasableTask {
636 fn from(builder: TaskBuilder) -> Self {
637 CurrentTask::from(builder).into()
638 }
639}
640
641impl<'a> KernelOrTask<'a> for &'a AutoReleasableTask {
642 fn kernel(&self) -> &'a Kernel {
643 (self as &Task).kernel()
644 }
645 fn maybe_task(&self) -> Option<&'a CurrentTask> {
646 Some(&self)
647 }
648}
649
650impl Drop for AutoReleasableTask {
651 fn drop(&mut self) {
652 #[allow(
654 clippy::undocumented_unsafe_blocks,
655 reason = "Force documented unsafe blocks in Starnix"
656 )]
657 let locked = unsafe { Unlocked::new() };
658 self.0.take().unwrap().release(locked);
659 }
660}
661
662impl std::ops::Deref for AutoReleasableTask {
663 type Target = CurrentTask;
664
665 fn deref(&self) -> &Self::Target {
666 AutoReleasableTask::as_ref(self)
667 }
668}
669
670impl std::ops::DerefMut for AutoReleasableTask {
671 fn deref_mut(&mut self) -> &mut Self::Target {
672 AutoReleasableTask::as_mut(self)
673 }
674}
675
676impl std::borrow::Borrow<CurrentTask> for AutoReleasableTask {
677 fn borrow(&self) -> &CurrentTask {
678 AutoReleasableTask::as_ref(self)
679 }
680}
681
682impl std::convert::AsRef<CurrentTask> for AutoReleasableTask {
683 fn as_ref(&self) -> &CurrentTask {
684 AutoReleasableTask::as_ref(self)
685 }
686}
687
688impl ArchSpecific for AutoReleasableTask {
689 fn is_arch32(&self) -> bool {
690 self.deref().is_arch32()
691 }
692}
693
694impl MemoryAccessor for AutoReleasableTask {
695 fn read_memory<'a>(
696 &self,
697 addr: UserAddress,
698 bytes: &'a mut [MaybeUninit<u8>],
699 ) -> Result<&'a mut [u8], Errno> {
700 (**self).read_memory(addr, bytes)
701 }
702 fn read_memory_partial_until_null_byte<'a>(
703 &self,
704 addr: UserAddress,
705 bytes: &'a mut [MaybeUninit<u8>],
706 ) -> Result<&'a mut [u8], Errno> {
707 (**self).read_memory_partial_until_null_byte(addr, bytes)
708 }
709 fn read_memory_partial<'a>(
710 &self,
711 addr: UserAddress,
712 bytes: &'a mut [MaybeUninit<u8>],
713 ) -> Result<&'a mut [u8], Errno> {
714 (**self).read_memory_partial(addr, bytes)
715 }
716 fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
717 (**self).write_memory(addr, bytes)
718 }
719 fn write_memory_partial(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
720 (**self).write_memory_partial(addr, bytes)
721 }
722 fn zero(&self, addr: UserAddress, length: usize) -> Result<usize, Errno> {
723 (**self).zero(addr, length)
724 }
725}
726
727struct TestFs;
728impl FileSystemOps for TestFs {
729 fn statfs(
730 &self,
731 _locked: &mut Locked<FileOpsCore>,
732 _fs: &FileSystem,
733 _current_task: &CurrentTask,
734 ) -> Result<statfs, Errno> {
735 Ok(default_statfs(0))
736 }
737 fn name(&self) -> &'static FsStr {
738 "test".into()
739 }
740}
741
742pub fn create_testfs(locked: &mut Locked<Unlocked>, kernel: &Kernel) -> FileSystemHandle {
743 FileSystem::new(locked, &kernel, CacheMode::Uncached, TestFs, Default::default())
744 .expect("testfs constructed with valid options")
745}
746
747pub fn create_testfs_with_root(
748 locked: &mut Locked<Unlocked>,
749 kernel: &Kernel,
750 ops: impl FsNodeOps,
751) -> FileSystemHandle {
752 let test_fs = create_testfs(locked, kernel);
753 let root_ino = test_fs.allocate_ino();
754 test_fs.create_root(root_ino, ops);
755 test_fs
756}
757
758pub fn create_fs_node_for_testing(fs: &FileSystemHandle, ops: impl FsNodeOps) -> FsNodeHandle {
759 let ino = fs.allocate_ino();
760 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
761 FsNode::new_uncached(ino, ops, fs, info)
762}
763
764pub fn create_namespace_node_for_testing(
765 fs: &FileSystemHandle,
766 ops: impl FsNodeOps,
767) -> NamespaceNode {
768 let node = create_fs_node_for_testing(fs, ops);
769 NamespaceNode::new_anonymous(DirEntry::new_unrooted(node))
770}