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 Vec::new(),
216 None,
217 None,
218 )
219 .expect("failed to create kernel")
220}
221
222fn create_test_fs_context(
223 locked: &mut Locked<Unlocked>,
224 kernel: &Kernel,
225 create_fs: impl FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
226) -> Arc<FsContext> {
227 FsContext::new(Namespace::new(create_fs(locked, kernel)))
228}
229
230fn create_test_init_task(
231 locked: &mut Locked<Unlocked>,
232 kernel: &Kernel,
233 fs: Arc<FsContext>,
234) -> TaskBuilder {
235 let init_pid = kernel.pids.write().allocate_pid();
236 assert_eq!(init_pid, 1);
237 let init_task = create_init_process(
238 locked,
239 &kernel.weak_self.upgrade().unwrap(),
240 init_pid,
241 TaskCommand::new(b"test-task"),
242 fs.fork(),
243 &[],
244 )
245 .expect("failed to create first task");
246 init_task.mm().unwrap().initialize_mmap_layout_for_test(ArchWidth::Arch64);
247
248 let system_task = create_system_task(locked, &kernel.weak_self.upgrade().unwrap(), fs)
249 .expect("create system task");
250 kernel.kthreads.init(system_task).expect("failed to initialize kthreads");
251
252 let system_task = kernel.kthreads.system_task();
253 kernel.hrtimer_manager.init(&system_task).expect("init hrtimer manager worker thread");
254
255 {
258 let _l1 = init_task.thread_group().read();
259 let _l2 = init_task.read();
260 }
261 init_task
262}
263
264fn create_kernel_task_and_unlocked_with_fs(
265 create_fs: impl FnOnce(&mut Locked<Unlocked>, &Kernel) -> FileSystemHandle,
266) -> (Arc<Kernel>, AutoReleasableTask, &'static mut Locked<Unlocked>) {
267 #[allow(
268 clippy::undocumented_unsafe_blocks,
269 reason = "Force documented unsafe blocks in Starnix"
270 )]
271 let locked = unsafe { Unlocked::new() };
272 let kernel = create_test_kernel(locked, None);
273 let fs = create_fs(locked, &kernel);
274 let fs_context = create_test_fs_context(locked, &kernel, |_, _| fs.clone());
275 let init_task = create_test_init_task(locked, &kernel, fs_context);
276 (kernel, init_task.into(), locked)
277}
278
279pub fn create_task(
289 locked: &mut Locked<Unlocked>,
290 kernel: &Kernel,
291 task_name: &str,
292) -> AutoReleasableTask {
293 let task = create_init_child_process(
294 locked,
295 &kernel.weak_self.upgrade().unwrap(),
296 TaskCommand::new(task_name.as_bytes()),
297 Some(&CString::new("#kernel").unwrap()),
298 )
299 .expect("failed to create second task");
300 task.mm().unwrap().initialize_mmap_layout_for_test(ArchWidth::Arch64);
301
302 {
305 let _l1 = task.thread_group().read();
306 let _l2 = task.read();
307 }
308
309 task.into()
310}
311
312pub fn map_memory_anywhere<L>(
315 locked: &mut Locked<L>,
316 current_task: &CurrentTask,
317 len: u64,
318) -> UserAddress
319where
320 L: LockEqualOrBefore<FileOpsCore>,
321{
322 map_memory(locked, current_task, UserAddress::NULL, len)
323}
324
325pub fn map_object_anywhere<L, T>(
330 locked: &mut Locked<L>,
331 current_task: &CurrentTask,
332 object: &T,
333) -> UserAddress
334where
335 L: LockEqualOrBefore<FileOpsCore>,
336 T: IntoBytes + Immutable,
337{
338 let addr = map_memory_anywhere(locked, current_task, std::mem::size_of::<T>() as u64);
339 current_task.write_object(addr.into(), object).expect("could not write object");
340 addr
341}
342
343pub fn map_memory<L>(
347 locked: &mut Locked<L>,
348 current_task: &CurrentTask,
349 address: UserAddress,
350 length: u64,
351) -> UserAddress
352where
353 L: LockEqualOrBefore<FileOpsCore>,
354{
355 map_memory_with_flags(locked, current_task, address, length, MAP_ANONYMOUS | MAP_PRIVATE)
356}
357
358pub fn map_memory_with_flags<L>(
362 locked: &mut Locked<L>,
363 current_task: &CurrentTask,
364 address: UserAddress,
365 length: u64,
366 flags: u32,
367) -> UserAddress
368where
369 L: LockEqualOrBefore<FileOpsCore>,
370{
371 do_mmap(
372 locked,
373 current_task,
374 address,
375 length as usize,
376 PROT_READ | PROT_WRITE,
377 flags,
378 FdNumber::from_raw(-1),
379 0,
380 )
381 .expect("Could not map memory")
382}
383
384pub fn remap_memory(
387 locked: &mut Locked<Unlocked>,
388 current_task: &CurrentTask,
389 old_addr: UserAddress,
390 old_length: u64,
391 new_length: u64,
392 flags: u32,
393 new_addr: UserAddress,
394) -> Result<UserAddress, Errno> {
395 sys_mremap(
396 locked,
397 current_task,
398 old_addr,
399 old_length as usize,
400 new_length as usize,
401 flags,
402 new_addr,
403 )
404}
405
406#[track_caller]
412pub fn fill_page(current_task: &CurrentTask, addr: UserAddress, data: char) {
413 let data = [data as u8].repeat(*PAGE_SIZE as usize);
414 if let Err(err) = current_task.write_memory(addr, &data) {
415 panic!("write page: failed to fill page @ {addr:?} with {data:?}: {err:?}");
416 }
417}
418
419#[track_caller]
425pub fn check_page_eq(current_task: &CurrentTask, addr: UserAddress, data: char) {
426 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
427 Ok(b) => b,
428 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
429 };
430 assert!(
431 buf.into_iter().all(|c| c == data as u8),
432 "unexpected payload: page @ {addr:?} should be filled with {data:?}"
433 );
434}
435
436#[track_caller]
442pub fn check_page_ne(current_task: &CurrentTask, addr: UserAddress, data: char) {
443 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
444 Ok(b) => b,
445 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
446 };
447 assert!(
448 !buf.into_iter().all(|c| c == data as u8),
449 "unexpected payload: page @ {addr:?} should not be filled with {data:?}"
450 );
451}
452
453#[track_caller]
459pub fn check_unmapped(current_task: &CurrentTask, addr: UserAddress) {
460 match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
461 Ok(_) => panic!("read page: page @ {addr:?} should be unmapped"),
462 Err(err) if err == starnix_uapi::errors::EFAULT => {}
463 Err(err) => {
464 panic!("read page: expected EFAULT reading page @ {addr:?} but got {err:?} instead")
465 }
466 }
467}
468
469pub struct PanickingFsNode;
472
473impl FsNodeOps for PanickingFsNode {
474 fs_node_impl_not_dir!();
475
476 fn create_file_ops(
477 &self,
478 _locked: &mut Locked<FileOpsCore>,
479 _node: &FsNode,
480 _current_task: &CurrentTask,
481 _flags: OpenFlags,
482 ) -> Result<Box<dyn FileOps>, Errno> {
483 panic!("should not be called")
484 }
485}
486
487pub struct PanickingFile;
489
490impl PanickingFile {
491 pub fn new_file(locked: &mut Locked<Unlocked>, current_task: &CurrentTask) -> FileHandle {
493 anon_test_file(locked, current_task, Box::new(PanickingFile), OpenFlags::RDWR)
494 }
495}
496
497impl FileOps for PanickingFile {
498 fileops_impl_nonseekable!();
499 fileops_impl_noop_sync!();
500
501 fn write(
502 &self,
503 _locked: &mut Locked<FileOpsCore>,
504 _file: &FileObject,
505 _current_task: &CurrentTask,
506 _offset: usize,
507 _data: &mut dyn InputBuffer,
508 ) -> Result<usize, Errno> {
509 panic!("write called on TestFile")
510 }
511
512 fn read(
513 &self,
514 _locked: &mut Locked<FileOpsCore>,
515 _file: &FileObject,
516 _current_task: &CurrentTask,
517 _offset: usize,
518 _data: &mut dyn OutputBuffer,
519 ) -> Result<usize, Errno> {
520 panic!("read called on TestFile")
521 }
522
523 fn ioctl(
524 &self,
525 _locked: &mut Locked<Unlocked>,
526 _file: &FileObject,
527 _current_task: &CurrentTask,
528 _request: u32,
529 _arg: SyscallArg,
530 ) -> Result<SyscallResult, Errno> {
531 panic!("ioctl called on TestFile")
532 }
533}
534
535pub fn anon_test_file<L>(
537 locked: &mut Locked<L>,
538 current_task: &CurrentTask,
539 ops: Box<dyn FileOps>,
540 flags: OpenFlags,
541) -> FileHandle
542where
543 L: LockEqualOrBefore<FileOpsCore>,
544{
545 Anon::new_private_file(locked, current_task, ops, flags, "[fuchsia:test_file]")
547}
548
549pub struct UserMemoryWriter<'a> {
551 mm: &'a Task,
553 current_addr: UserAddress,
555}
556
557impl<'a> UserMemoryWriter<'a> {
558 pub fn new(task: &'a Task, addr: UserAddress) -> Self {
560 Self { mm: task, current_addr: addr }
561 }
562
563 pub fn write(&mut self, data: &[u8]) -> UserAddress {
567 let bytes_written = self.mm.write_memory(self.current_addr, data).unwrap();
568 assert_eq!(bytes_written, data.len());
569 let start_addr = self.current_addr;
570 self.current_addr = (self.current_addr + bytes_written).unwrap();
571 start_addr
572 }
573
574 pub fn write_object<T: IntoBytes + Immutable>(&mut self, object: &T) -> UserAddress {
578 self.write(object.as_bytes())
579 }
580
581 pub fn current_address(&self) -> UserAddress {
583 self.current_addr
584 }
585}
586
587#[derive(Debug)]
588pub struct AutoReleasableTask(Option<CurrentTask>);
589
590impl AutoReleasableTask {
591 fn as_ref(this: &Self) -> &CurrentTask {
592 this.0.as_ref().unwrap()
593 }
594
595 fn as_mut(this: &mut Self) -> &mut CurrentTask {
596 this.0.as_mut().unwrap()
597 }
598}
599
600impl From<CurrentTask> for AutoReleasableTask {
601 fn from(task: CurrentTask) -> Self {
602 Self(Some(task))
603 }
604}
605
606impl From<TaskBuilder> for AutoReleasableTask {
607 fn from(builder: TaskBuilder) -> Self {
608 CurrentTask::from(builder).into()
609 }
610}
611
612impl<'a> KernelOrTask<'a> for &'a AutoReleasableTask {
613 fn kernel(&self) -> &'a Kernel {
614 (self as &Task).kernel()
615 }
616 fn maybe_task(&self) -> Option<&'a CurrentTask> {
617 Some(&self)
618 }
619}
620
621impl Drop for AutoReleasableTask {
622 fn drop(&mut self) {
623 #[allow(
625 clippy::undocumented_unsafe_blocks,
626 reason = "Force documented unsafe blocks in Starnix"
627 )]
628 let locked = unsafe { Unlocked::new() };
629 self.0.take().unwrap().release(locked);
630 }
631}
632
633impl std::ops::Deref for AutoReleasableTask {
634 type Target = CurrentTask;
635
636 fn deref(&self) -> &Self::Target {
637 AutoReleasableTask::as_ref(self)
638 }
639}
640
641impl std::ops::DerefMut for AutoReleasableTask {
642 fn deref_mut(&mut self) -> &mut Self::Target {
643 AutoReleasableTask::as_mut(self)
644 }
645}
646
647impl std::borrow::Borrow<CurrentTask> for AutoReleasableTask {
648 fn borrow(&self) -> &CurrentTask {
649 AutoReleasableTask::as_ref(self)
650 }
651}
652
653impl std::convert::AsRef<CurrentTask> for AutoReleasableTask {
654 fn as_ref(&self) -> &CurrentTask {
655 AutoReleasableTask::as_ref(self)
656 }
657}
658
659impl ArchSpecific for AutoReleasableTask {
660 fn is_arch32(&self) -> bool {
661 self.deref().is_arch32()
662 }
663}
664
665impl MemoryAccessor for AutoReleasableTask {
666 fn read_memory<'a>(
667 &self,
668 addr: UserAddress,
669 bytes: &'a mut [MaybeUninit<u8>],
670 ) -> Result<&'a mut [u8], Errno> {
671 (**self).read_memory(addr, bytes)
672 }
673 fn read_memory_partial_until_null_byte<'a>(
674 &self,
675 addr: UserAddress,
676 bytes: &'a mut [MaybeUninit<u8>],
677 ) -> Result<&'a mut [u8], Errno> {
678 (**self).read_memory_partial_until_null_byte(addr, bytes)
679 }
680 fn read_memory_partial<'a>(
681 &self,
682 addr: UserAddress,
683 bytes: &'a mut [MaybeUninit<u8>],
684 ) -> Result<&'a mut [u8], Errno> {
685 (**self).read_memory_partial(addr, bytes)
686 }
687 fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
688 (**self).write_memory(addr, bytes)
689 }
690 fn write_memory_partial(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
691 (**self).write_memory_partial(addr, bytes)
692 }
693 fn zero(&self, addr: UserAddress, length: usize) -> Result<usize, Errno> {
694 (**self).zero(addr, length)
695 }
696}
697
698struct TestFs;
699impl FileSystemOps for TestFs {
700 fn statfs(
701 &self,
702 _locked: &mut Locked<FileOpsCore>,
703 _fs: &FileSystem,
704 _current_task: &CurrentTask,
705 ) -> Result<statfs, Errno> {
706 Ok(default_statfs(0))
707 }
708 fn name(&self) -> &'static FsStr {
709 "test".into()
710 }
711}
712
713pub fn create_testfs(locked: &mut Locked<Unlocked>, kernel: &Kernel) -> FileSystemHandle {
714 FileSystem::new(locked, &kernel, CacheMode::Uncached, TestFs, Default::default())
715 .expect("testfs constructed with valid options")
716}
717
718pub fn create_testfs_with_root(
719 locked: &mut Locked<Unlocked>,
720 kernel: &Kernel,
721 ops: impl FsNodeOps,
722) -> FileSystemHandle {
723 let test_fs = create_testfs(locked, kernel);
724 let root_ino = test_fs.allocate_ino();
725 test_fs.create_root(root_ino, ops);
726 test_fs
727}
728
729pub fn create_fs_node_for_testing(fs: &FileSystemHandle, ops: impl FsNodeOps) -> FsNodeHandle {
730 let ino = fs.allocate_ino();
731 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
732 FsNode::new_uncached(ino, ops, fs, info)
733}
734
735pub fn create_namespace_node_for_testing(
736 fs: &FileSystemHandle,
737 ops: impl FsNodeOps,
738) -> NamespaceNode {
739 let node = create_fs_node_for_testing(fs, ops);
740 NamespaceNode::new_anonymous(DirEntry::new_unrooted(node))
741}