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::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)
78}
79
80pub fn spawn_kernel_and_run_sync<F, R>(callback: F) -> impl Future<Output = R>
85where
86 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
87 R: Send + Sync + 'static,
88{
89 spawn_kernel_and_run_internal_sync(callback, None, TmpFs::new_fs)
90}
91
92pub fn spawn_kernel_and_run_with_pkgfs<F, R>(callback: F) -> impl Future<Output = R>
98where
99 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
100 R: Send + Sync + 'static,
101{
102 spawn_kernel_and_run_internal(callback, None, create_pkgfs)
103}
104
105pub async fn spawn_kernel_with_selinux_and_run<F, R>(callback: F) -> R
111where
112 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask, &Arc<SecurityServer>) -> R
113 + Send
114 + Sync
115 + 'static,
116 R: Send + Sync + 'static,
117{
118 let security_server = SecurityServer::new_default();
119 let security_server_for_callback = security_server.clone();
120 spawn_kernel_and_run_internal(
121 async move |unlocked, current_task| {
122 security::selinuxfs_init_null(
123 current_task,
124 &new_null_file(unlocked, current_task, OpenFlags::empty()),
125 );
126 callback(unlocked, current_task, &security_server_for_callback).await
127 },
128 Some(security_server),
129 TmpFs::new_fs,
130 )
131 .await
132}
133
134fn spawn_kernel_and_run_internal<F, FS, R>(
137 callback: F,
138 security_server: Option<Arc<SecurityServer>>,
139 fs_factory: FS,
140) -> impl Future<Output = R>
141where
142 R: Send + Sync + 'static,
143 F: AsyncFnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
144 FS: FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
145{
146 spawn_kernel_and_run_internal_sync(
147 move |locked, current_task| {
148 LocalExecutor::default().run_singlethreaded(callback(locked, current_task))
149 },
150 security_server,
151 fs_factory,
152 )
153}
154
155fn spawn_kernel_and_run_internal_sync<F, FS, R>(
158 callback: F,
159 security_server: Option<Arc<SecurityServer>>,
160 fs_factory: FS,
161) -> impl Future<Output = R>
162where
163 R: Send + Sync + 'static,
164 F: FnOnce(&mut Locked<Unlocked>, &mut CurrentTask) -> R + Send + Sync + 'static,
165 FS: FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
166{
167 #[allow(
168 clippy::undocumented_unsafe_blocks,
169 reason = "Force documented unsafe blocks in Starnix"
170 )]
171 let locked = unsafe { Unlocked::new() };
172 let kernel = create_test_kernel(locked, security_server);
173 let fs = create_test_fs_context(locked, &kernel, fs_factory);
174 let init_task = create_test_init_task(locked, &kernel, fs);
175 fasync::unblock(move || {
176 let (sender, receiver) = mpsc::sync_channel(1);
177 let error = execute_task_with_prerun_result(
178 locked,
179 init_task,
180 move |locked, current_task| -> Result<(), Errno> {
181 let result = callback(locked, current_task);
182 current_task.write().set_exit_status_if_not_already(ExitStatus::Exit(0));
183 sender.send(result).map_err(|e| errno!(EIO, e))?;
184 error!(EHWPOISON)
185 },
186 |_| {},
187 None,
188 )
189 .unwrap_err();
190 assert_eq!(error, errno!(EHWPOISON));
192 receiver.recv().expect("recv")
193 })
194}
195
196#[deprecated = "Do not add new callers, use spawn_kernel_and_run() instead."]
197pub fn create_kernel_task_and_unlocked()
198-> (Arc<Kernel>, AutoReleasableTask, &'static mut Locked<Unlocked>) {
199 create_kernel_task_and_unlocked_with_fs(TmpFs::new_fs)
200}
201
202fn create_test_kernel(
203 _locked: &mut Locked<Unlocked>,
204 security_server: Option<Arc<SecurityServer>>,
205) -> Arc<Kernel> {
206 Kernel::new(
207 b"".into(),
208 KernelFeatures::default(),
209 SystemLimits::default(),
210 ContainerNamespace::new(),
211 SchedulerManager::empty_for_tests(),
212 None,
213 fuchsia_inspect::Node::default(),
214 security::testing::kernel_state(security_server),
215 None,
216 None,
217 )
218 .expect("failed to create kernel")
219}
220
221fn create_test_fs_context(
222 locked: &mut Locked<Unlocked>,
223 kernel: &Kernel,
224 create_fs: impl FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
225) -> Arc<FsContext> {
226 FsContext::new(Namespace::new(create_fs(locked, kernel)))
227}
228
229fn create_test_init_task(
230 locked: &mut Locked<Unlocked>,
231 kernel: &Kernel,
232 fs: Arc<FsContext>,
233) -> TaskBuilder {
234 let init_pid = kernel.pids.write().allocate_pid();
235 assert_eq!(init_pid, 1);
236 let init_task = create_init_process(
237 locked,
238 &kernel.weak_self.upgrade().unwrap(),
239 init_pid,
240 TaskCommand::new(b"test-task"),
241 fs.fork(),
242 &[],
243 )
244 .expect("failed to create first task");
245 init_task.mm().unwrap().initialize_mmap_layout_for_test(ArchWidth::Arch64);
246
247 let system_task = create_system_task(locked, &kernel.weak_self.upgrade().unwrap(), fs)
248 .expect("create system task");
249 kernel.kthreads.init(system_task).expect("failed to initialize kthreads");
250
251 let system_task = kernel.kthreads.system_task();
252 kernel.hrtimer_manager.init(&system_task).expect("init hrtimer manager worker thread");
253
254 {
257 let _l1 = init_task.thread_group().read();
258 let _l2 = init_task.read();
259 }
260 init_task
261}
262
263fn create_kernel_task_and_unlocked_with_fs(
264 create_fs: impl FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
265) -> (Arc<Kernel>, AutoReleasableTask, &'static mut Locked<Unlocked>) {
266 #[allow(
267 clippy::undocumented_unsafe_blocks,
268 reason = "Force documented unsafe blocks in Starnix"
269 )]
270 let locked = unsafe { Unlocked::new() };
271 let kernel = create_test_kernel(locked, None);
272 let fs = create_fs(locked, &kernel);
273 let fs_context = create_test_fs_context(locked, &kernel, |_, _| fs.clone());
274 let init_task = create_test_init_task(locked, &kernel, fs_context);
275 (kernel, init_task.into(), locked)
276}
277
278pub fn create_task(
288 locked: &mut Locked<Unlocked>,
289 kernel: &Kernel,
290 task_name: &str,
291) -> AutoReleasableTask {
292 let task = create_init_child_process(
293 locked,
294 &kernel.weak_self.upgrade().unwrap(),
295 TaskCommand::new(task_name.as_bytes()),
296 Some(&CString::new("#kernel").unwrap()),
297 )
298 .expect("failed to create second task");
299 task.mm().unwrap().initialize_mmap_layout_for_test(ArchWidth::Arch64);
300
301 {
304 let _l1 = task.thread_group().read();
305 let _l2 = task.read();
306 }
307
308 task.into()
309}
310
311pub fn map_memory_anywhere<L>(
314 locked: &mut Locked<L>,
315 current_task: &CurrentTask,
316 len: u64,
317) -> UserAddress
318where
319 L: LockEqualOrBefore<FileOpsCore>,
320{
321 map_memory(locked, current_task, UserAddress::NULL, len)
322}
323
324pub fn map_object_anywhere<L, T>(
329 locked: &mut Locked<L>,
330 current_task: &CurrentTask,
331 object: &T,
332) -> UserAddress
333where
334 L: LockEqualOrBefore<FileOpsCore>,
335 T: IntoBytes + Immutable,
336{
337 let addr = map_memory_anywhere(locked, current_task, std::mem::size_of::<T>() as u64);
338 current_task.write_object(addr.into(), object).expect("could not write object");
339 addr
340}
341
342pub fn map_memory<L>(
346 locked: &mut Locked<L>,
347 current_task: &CurrentTask,
348 address: UserAddress,
349 length: u64,
350) -> UserAddress
351where
352 L: LockEqualOrBefore<FileOpsCore>,
353{
354 map_memory_with_flags(locked, current_task, address, length, MAP_ANONYMOUS | MAP_PRIVATE)
355}
356
357pub fn map_memory_with_flags<L>(
361 locked: &mut Locked<L>,
362 current_task: &CurrentTask,
363 address: UserAddress,
364 length: u64,
365 flags: u32,
366) -> UserAddress
367where
368 L: LockEqualOrBefore<FileOpsCore>,
369{
370 do_mmap(
371 locked,
372 current_task,
373 address,
374 length as usize,
375 PROT_READ | PROT_WRITE,
376 flags,
377 FdNumber::from_raw(-1),
378 0,
379 )
380 .expect("Could not map memory")
381}
382
383pub fn remap_memory(
386 locked: &mut Locked<Unlocked>,
387 current_task: &CurrentTask,
388 old_addr: UserAddress,
389 old_length: u64,
390 new_length: u64,
391 flags: u32,
392 new_addr: UserAddress,
393) -> Result<UserAddress, Errno> {
394 sys_mremap(
395 locked,
396 current_task,
397 old_addr,
398 old_length as usize,
399 new_length as usize,
400 flags,
401 new_addr,
402 )
403}
404
405#[track_caller]
411pub fn fill_page(current_task: &CurrentTask, addr: UserAddress, data: char) {
412 let data = [data as u8].repeat(*PAGE_SIZE as usize);
413 if let Err(err) = current_task.write_memory(addr, &data) {
414 panic!("write page: failed to fill page @ {addr:?} with {data:?}: {err:?}");
415 }
416}
417
418#[track_caller]
424pub fn check_page_eq(current_task: &CurrentTask, addr: UserAddress, data: char) {
425 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
426 Ok(b) => b,
427 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
428 };
429 assert!(
430 buf.into_iter().all(|c| c == data as u8),
431 "unexpected payload: page @ {addr:?} should be filled with {data:?}"
432 );
433}
434
435#[track_caller]
441pub fn check_page_ne(current_task: &CurrentTask, addr: UserAddress, data: char) {
442 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
443 Ok(b) => b,
444 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
445 };
446 assert!(
447 !buf.into_iter().all(|c| c == data as u8),
448 "unexpected payload: page @ {addr:?} should not be filled with {data:?}"
449 );
450}
451
452#[track_caller]
458pub fn check_unmapped(current_task: &CurrentTask, addr: UserAddress) {
459 match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
460 Ok(_) => panic!("read page: page @ {addr:?} should be unmapped"),
461 Err(err) if err == starnix_uapi::errors::EFAULT => {}
462 Err(err) => {
463 panic!("read page: expected EFAULT reading page @ {addr:?} but got {err:?} instead")
464 }
465 }
466}
467
468pub struct PanickingFsNode;
471
472impl FsNodeOps for PanickingFsNode {
473 fs_node_impl_not_dir!();
474
475 fn create_file_ops(
476 &self,
477 _locked: &mut Locked<FileOpsCore>,
478 _node: &FsNode,
479 _current_task: &CurrentTask,
480 _flags: OpenFlags,
481 ) -> Result<Box<dyn FileOps>, Errno> {
482 panic!("should not be called")
483 }
484}
485
486pub struct PanickingFile;
488
489impl PanickingFile {
490 pub fn new_file(locked: &mut Locked<Unlocked>, current_task: &CurrentTask) -> FileHandle {
492 anon_test_file(locked, current_task, Box::new(PanickingFile), OpenFlags::RDWR)
493 }
494}
495
496impl FileOps for PanickingFile {
497 fileops_impl_nonseekable!();
498 fileops_impl_noop_sync!();
499
500 fn write(
501 &self,
502 _locked: &mut Locked<FileOpsCore>,
503 _file: &FileObject,
504 _current_task: &CurrentTask,
505 _offset: usize,
506 _data: &mut dyn InputBuffer,
507 ) -> Result<usize, Errno> {
508 panic!("write called on TestFile")
509 }
510
511 fn read(
512 &self,
513 _locked: &mut Locked<FileOpsCore>,
514 _file: &FileObject,
515 _current_task: &CurrentTask,
516 _offset: usize,
517 _data: &mut dyn OutputBuffer,
518 ) -> Result<usize, Errno> {
519 panic!("read called on TestFile")
520 }
521
522 fn ioctl(
523 &self,
524 _locked: &mut Locked<Unlocked>,
525 _file: &FileObject,
526 _current_task: &CurrentTask,
527 _request: u32,
528 _arg: SyscallArg,
529 ) -> Result<SyscallResult, Errno> {
530 panic!("ioctl called on TestFile")
531 }
532}
533
534pub fn anon_test_file<L>(
536 locked: &mut Locked<L>,
537 current_task: &CurrentTask,
538 ops: Box<dyn FileOps>,
539 flags: OpenFlags,
540) -> FileHandle
541where
542 L: LockEqualOrBefore<FileOpsCore>,
543{
544 Anon::new_private_file(locked, current_task, ops, flags, "[fuchsia:test_file]")
546}
547
548pub struct UserMemoryWriter<'a> {
550 mm: &'a Task,
552 current_addr: UserAddress,
554}
555
556impl<'a> UserMemoryWriter<'a> {
557 pub fn new(task: &'a Task, addr: UserAddress) -> Self {
559 Self { mm: task, current_addr: addr }
560 }
561
562 pub fn write(&mut self, data: &[u8]) -> UserAddress {
566 let bytes_written = self.mm.write_memory(self.current_addr, data).unwrap();
567 assert_eq!(bytes_written, data.len());
568 let start_addr = self.current_addr;
569 self.current_addr = (self.current_addr + bytes_written).unwrap();
570 start_addr
571 }
572
573 pub fn write_object<T: IntoBytes + Immutable>(&mut self, object: &T) -> UserAddress {
577 self.write(object.as_bytes())
578 }
579
580 pub fn current_address(&self) -> UserAddress {
582 self.current_addr
583 }
584}
585
586#[derive(Debug)]
587pub struct AutoReleasableTask(Option<CurrentTask>);
588
589impl AutoReleasableTask {
590 fn as_ref(this: &Self) -> &CurrentTask {
591 this.0.as_ref().unwrap()
592 }
593
594 fn as_mut(this: &mut Self) -> &mut CurrentTask {
595 this.0.as_mut().unwrap()
596 }
597}
598
599impl From<CurrentTask> for AutoReleasableTask {
600 fn from(task: CurrentTask) -> Self {
601 Self(Some(task))
602 }
603}
604
605impl From<TaskBuilder> for AutoReleasableTask {
606 fn from(builder: TaskBuilder) -> Self {
607 CurrentTask::from(builder).into()
608 }
609}
610
611impl<'a> KernelOrTask<'a> for &'a AutoReleasableTask {
612 fn kernel(&self) -> &'a Kernel {
613 (self as &Task).kernel()
614 }
615 fn maybe_task(&self) -> Option<&'a CurrentTask> {
616 Some(&self)
617 }
618}
619
620impl Drop for AutoReleasableTask {
621 fn drop(&mut self) {
622 #[allow(
624 clippy::undocumented_unsafe_blocks,
625 reason = "Force documented unsafe blocks in Starnix"
626 )]
627 let locked = unsafe { Unlocked::new() };
628 self.0.take().unwrap().release(locked);
629 }
630}
631
632impl std::ops::Deref for AutoReleasableTask {
633 type Target = CurrentTask;
634
635 fn deref(&self) -> &Self::Target {
636 AutoReleasableTask::as_ref(self)
637 }
638}
639
640impl std::ops::DerefMut for AutoReleasableTask {
641 fn deref_mut(&mut self) -> &mut Self::Target {
642 AutoReleasableTask::as_mut(self)
643 }
644}
645
646impl std::borrow::Borrow<CurrentTask> for AutoReleasableTask {
647 fn borrow(&self) -> &CurrentTask {
648 AutoReleasableTask::as_ref(self)
649 }
650}
651
652impl std::convert::AsRef<CurrentTask> for AutoReleasableTask {
653 fn as_ref(&self) -> &CurrentTask {
654 AutoReleasableTask::as_ref(self)
655 }
656}
657
658impl ArchSpecific for AutoReleasableTask {
659 fn is_arch32(&self) -> bool {
660 self.deref().is_arch32()
661 }
662}
663
664impl MemoryAccessor for AutoReleasableTask {
665 fn read_memory<'a>(
666 &self,
667 addr: UserAddress,
668 bytes: &'a mut [MaybeUninit<u8>],
669 ) -> Result<&'a mut [u8], Errno> {
670 (**self).read_memory(addr, bytes)
671 }
672 fn read_memory_partial_until_null_byte<'a>(
673 &self,
674 addr: UserAddress,
675 bytes: &'a mut [MaybeUninit<u8>],
676 ) -> Result<&'a mut [u8], Errno> {
677 (**self).read_memory_partial_until_null_byte(addr, bytes)
678 }
679 fn read_memory_partial<'a>(
680 &self,
681 addr: UserAddress,
682 bytes: &'a mut [MaybeUninit<u8>],
683 ) -> Result<&'a mut [u8], Errno> {
684 (**self).read_memory_partial(addr, bytes)
685 }
686 fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
687 (**self).write_memory(addr, bytes)
688 }
689 fn write_memory_partial(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
690 (**self).write_memory_partial(addr, bytes)
691 }
692 fn zero(&self, addr: UserAddress, length: usize) -> Result<usize, Errno> {
693 (**self).zero(addr, length)
694 }
695}
696
697struct TestFs;
698impl FileSystemOps for TestFs {
699 fn statfs(
700 &self,
701 _locked: &mut Locked<FileOpsCore>,
702 _fs: &FileSystem,
703 _current_task: &CurrentTask,
704 ) -> Result<statfs, Errno> {
705 Ok(default_statfs(0))
706 }
707 fn name(&self) -> &'static FsStr {
708 "test".into()
709 }
710}
711
712pub fn create_testfs(locked: &mut Locked<Unlocked>, kernel: &Kernel) -> FileSystemHandle {
713 FileSystem::new(locked, &kernel, CacheMode::Uncached, TestFs, Default::default())
714 .expect("testfs constructed with valid options")
715}
716
717pub fn create_testfs_with_root(
718 locked: &mut Locked<Unlocked>,
719 kernel: &Kernel,
720 ops: impl FsNodeOps,
721) -> FileSystemHandle {
722 let test_fs = create_testfs(locked, kernel);
723 let root_ino = test_fs.allocate_ino();
724 test_fs.create_root(root_ino, ops);
725 test_fs
726}
727
728pub fn create_fs_node_for_testing(fs: &FileSystemHandle, ops: impl FsNodeOps) -> FsNodeHandle {
729 let ino = fs.allocate_ino();
730 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
731 FsNode::new_uncached(ino, ops, fs, info)
732}
733
734pub fn create_namespace_node_for_testing(
735 fs: &FileSystemHandle,
736 ops: impl FsNodeOps,
737) -> NamespaceNode {
738 let node = create_fs_node_for_testing(fs, ops);
739 NamespaceNode::new_anonymous(DirEntry::new_unrooted(node))
740}