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, MemoryManager, PAGE_SIZE};
14use crate::security;
15use crate::task::container_namespace::ContainerNamespace;
16use crate::task::{
17 CurrentTask, ExitStatus, Kernel, KernelFeatures, SchedulerManager, SystemLimits, Task,
18 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, FsNodeFlags,
24 FsNodeHandle, FsNodeInfo, FsNodeOps, FsStr, Namespace, NamespaceNode, fileops_impl_nonseekable,
25 fileops_impl_noop_sync, fs_node_impl_not_dir,
26};
27use fidl_fuchsia_io as fio;
28use fuchsia_async as fasync;
29use fuchsia_async::LocalExecutor;
30use selinux::SecurityServer;
31use starnix_syscalls::{SyscallArg, SyscallResult};
32use starnix_task_command::TaskCommand;
33use starnix_types::arch::ArchWidth;
34use starnix_types::vfs::default_statfs;
35use starnix_uapi::auth::{Credentials, FsCred};
36use starnix_uapi::errors::Errno;
37use starnix_uapi::file_mode::mode;
38use starnix_uapi::open_flags::OpenFlags;
39use starnix_uapi::user_address::{ArchSpecific, UserAddress};
40use starnix_uapi::{MAP_ANONYMOUS, MAP_PRIVATE, PROT_READ, PROT_WRITE, errno, error, statfs};
41use std::ffi::CString;
42use std::future::Future;
43use std::mem::MaybeUninit;
44use std::ops::Deref;
45use std::sync::{Arc, mpsc};
46use zerocopy::{Immutable, IntoBytes};
47
48fn create_pkgfs(kernel: &Kernel) -> FileSystemHandle {
52 let rights = fio::PERM_READABLE | fio::PERM_EXECUTABLE;
53 let (server, client) = zx::Channel::create();
54 fdio::open("/pkg", rights, server).expect("failed to open /pkg");
55 RemoteFs::new_fs(
56 kernel,
57 client,
58 FileSystemOptions { source: "/pkg".into(), ..Default::default() },
59 rights,
60 )
61 .unwrap()
62}
63
64pub fn spawn_kernel_and_run<F, R>(callback: F) -> impl Future<Output = R>
69where
70 F: AsyncFnOnce(&mut CurrentTask) -> R + Send + Sync + 'static,
71 R: Send + Sync + 'static,
72{
73 spawn_kernel_and_run_internal(callback, None, TmpFs::new_fs, KernelFeatures::default())
74}
75
76pub fn spawn_kernel_with_features_and_run<F, R>(
78 callback: F,
79 features: KernelFeatures,
80) -> impl Future<Output = R>
81where
82 F: AsyncFnOnce(&mut CurrentTask) -> R + Send + Sync + 'static,
83 R: Send + Sync + 'static,
84{
85 spawn_kernel_and_run_internal(callback, None, TmpFs::new_fs, features)
86}
87
88pub fn spawn_kernel_and_run_sync<F, R>(callback: F) -> impl Future<Output = R>
93where
94 F: FnOnce(&mut CurrentTask) -> R + Send + Sync + 'static,
95 R: Send + Sync + 'static,
96{
97 spawn_kernel_and_run_internal_sync(
98 callback,
99 None,
100 TmpFs::new_fs,
101 KernelFeatures::default(),
102 SchedulerManager::empty_for_tests(),
103 )
104}
105
106pub fn spawn_kernel_with_scheduler_and_run_sync<F, R>(
108 scheduler: SchedulerManager,
109 callback: F,
110) -> impl Future<Output = R>
111where
112 F: FnOnce(&mut CurrentTask) -> R + Send + Sync + 'static,
113 R: Send + Sync + 'static,
114{
115 spawn_kernel_and_run_internal_sync(
116 callback,
117 None,
118 TmpFs::new_fs,
119 KernelFeatures::default(),
120 scheduler,
121 )
122}
123
124pub fn spawn_kernel_and_run_with_pkgfs<F, R>(callback: F) -> impl Future<Output = R>
130where
131 F: AsyncFnOnce(&mut CurrentTask) -> R + Send + Sync + 'static,
132 R: Send + Sync + 'static,
133{
134 spawn_kernel_and_run_internal(callback, None, create_pkgfs, KernelFeatures::default())
135}
136
137pub async fn spawn_kernel_with_selinux_and_run<F, R>(callback: F) -> R
143where
144 F: AsyncFnOnce(&mut CurrentTask, &Arc<SecurityServer>) -> R + Send + Sync + 'static,
145 R: Send + Sync + 'static,
146{
147 let security_server = SecurityServer::new_default();
148 let security_server_for_callback = security_server.clone();
149 spawn_kernel_and_run_internal(
150 async move |current_task| {
151 security::selinuxfs_init_null(
152 current_task,
153 &new_null_file(current_task, OpenFlags::empty()),
154 );
155 callback(current_task, &security_server_for_callback).await
156 },
157 Some(security_server),
158 TmpFs::new_fs,
159 KernelFeatures::default(),
160 )
161 .await
162}
163
164fn spawn_kernel_and_run_internal<F, FS, R>(
167 callback: F,
168 security_server: Option<Arc<SecurityServer>>,
169 fs_factory: FS,
170 features: KernelFeatures,
171) -> impl Future<Output = R>
172where
173 R: Send + Sync + 'static,
174 F: AsyncFnOnce(&mut CurrentTask) -> R + Send + Sync + 'static,
175 FS: FnOnce(&Kernel) -> FileSystemHandle,
176{
177 spawn_kernel_and_run_internal_sync(
178 move |current_task| LocalExecutor::default().run_singlethreaded(callback(current_task)),
179 security_server,
180 fs_factory,
181 features,
182 SchedulerManager::empty_for_tests(),
183 )
184}
185
186fn spawn_kernel_and_run_internal_sync<F, FS, R>(
189 callback: F,
190 security_server: Option<Arc<SecurityServer>>,
191 fs_factory: FS,
192 features: KernelFeatures,
193 scheduler: SchedulerManager,
194) -> impl Future<Output = R>
195where
196 R: Send + Sync + 'static,
197 F: FnOnce(&mut CurrentTask) -> R + Send + Sync + 'static,
198 FS: FnOnce(&Kernel) -> FileSystemHandle,
199{
200 let kernel = create_test_kernel(security_server, features, scheduler);
201 let fs = create_test_fs_context(&kernel, fs_factory);
202 let init_task = create_test_init_task(&kernel, fs);
203 fasync::unblock(move || {
204 let (sender, receiver) = mpsc::sync_channel(1);
205 let error = execute_task_with_prerun_result(
206 init_task,
207 move |current_task| -> Result<(), Errno> {
208 let result = callback(current_task);
209 current_task.write().set_exit_status_if_not_already(ExitStatus::Exit(0));
210 sender.send(result).map_err(|e| errno!(EIO, e))?;
211 error!(EHWPOISON)
212 },
213 |_| {},
214 None,
215 )
216 .unwrap_err();
217 assert_eq!(error, errno!(EHWPOISON));
219 receiver.recv().expect("recv")
220 })
221}
222
223fn create_test_kernel(
224 security_server: Option<Arc<SecurityServer>>,
225 features: KernelFeatures,
226 scheduler: SchedulerManager,
227) -> Arc<Kernel> {
228 Kernel::new(
229 b"".into(),
230 features,
231 SystemLimits::default(),
232 ContainerNamespace::new(),
233 scheduler,
234 None,
235 fuchsia_inspect::Node::default(),
236 security::testing::kernel_state(security_server),
237 None,
238 None,
239 )
240 .expect("failed to create kernel")
241}
242
243fn create_test_fs_context(
244 kernel: &Kernel,
245 create_fs: impl FnOnce(&Kernel) -> FileSystemHandle,
246) -> Arc<FsContext> {
247 FsContext::new(Namespace::new(create_fs(kernel)))
248}
249
250fn create_test_mm(task: &Task) -> Result<Arc<MemoryManager>, Errno> {
252 let arch_width = ArchWidth::Arch64;
253 let mm =
254 MemoryManager::new_for_test(task.thread_group().root_vmar.unowned(), ArchWidth::Arch64);
255 let fake_executable_addr = mm.get_random_base_for_executable(arch_width, 0)?;
256 mm.initialize_brk_origin(arch_width, fake_executable_addr)?;
257 task.running_state()?.mm.update(Some(mm.clone()));
258 Ok(mm)
259}
260
261fn create_test_init_task(kernel: &Kernel, fs: Arc<FsContext>) -> TaskBuilder {
262 let init_pid = kernel.pids.write().allocate_pid();
263 assert_eq!(init_pid, 1);
264 let init_task = create_init_process(
265 &kernel.weak_self.upgrade().unwrap(),
266 init_pid,
267 TaskCommand::new(b"test-task"),
268 fs.fork(),
269 &[],
270 )
271 .expect("failed to create first task");
272 create_test_mm(&init_task).expect("failed to create MM");
273
274 let system_task =
275 create_system_task(&kernel.weak_self.upgrade().unwrap(), fs).expect("create system task");
276 kernel.kthreads.init(system_task).expect("failed to initialize kthreads");
277
278 let system_task = kernel.kthreads.system_task();
279 kernel.hrtimer_manager.init(&system_task).expect("init hrtimer manager worker thread");
280
281 {
284 let _l1 = init_task.thread_group().read();
285 let _l2 = init_task.read();
286 }
287 init_task
288}
289
290pub fn create_task(kernel: &Kernel, task_name: &str) -> AutoReleasableTask {
300 create_task_with_security_context(kernel, task_name, &CString::new("#kernel").unwrap())
301}
302
303pub fn create_task_with_security_context(
307 kernel: &Kernel,
308 task_name: &str,
309 security_context: &CString,
310) -> AutoReleasableTask {
311 let task = create_init_child_process(
312 &kernel.weak_self.upgrade().unwrap(),
313 TaskCommand::new(task_name.as_bytes()),
314 Credentials::with_ids(0, 0),
315 Some(security_context),
316 )
317 .expect("failed to create second task");
318 create_test_mm(&task).expect("failed to create MM");
319
320 {
323 let _l1 = task.thread_group().read();
324 let _l2 = task.read();
325 }
326
327 task.into()
328}
329
330pub fn map_memory_anywhere(current_task: &CurrentTask, len: u64) -> UserAddress {
333 map_memory(current_task, UserAddress::NULL, len)
334}
335
336pub fn map_object_anywhere<T>(current_task: &CurrentTask, object: &T) -> UserAddress
341where
342 T: IntoBytes + Immutable,
343{
344 let addr = map_memory_anywhere(current_task, std::mem::size_of::<T>() as u64);
345 current_task.write_object(addr.into(), object).expect("could not write object");
346 addr
347}
348
349pub fn map_memory(current_task: &CurrentTask, address: UserAddress, length: u64) -> UserAddress {
353 map_memory_with_flags(current_task, address, length, MAP_ANONYMOUS | MAP_PRIVATE)
354}
355
356pub fn map_memory_with_flags(
360 current_task: &CurrentTask,
361 address: UserAddress,
362 length: u64,
363 flags: u32,
364) -> UserAddress {
365 do_mmap(
366 current_task,
367 address,
368 length as usize,
369 PROT_READ | PROT_WRITE,
370 flags,
371 FdNumber::from_raw(-1),
372 0,
373 )
374 .expect("Could not map memory")
375}
376
377pub fn remap_memory(
380 current_task: &CurrentTask,
381 old_addr: UserAddress,
382 old_length: u64,
383 new_length: u64,
384 flags: u32,
385 new_addr: UserAddress,
386) -> Result<UserAddress, Errno> {
387 sys_mremap(current_task, old_addr, old_length as usize, new_length as usize, flags, new_addr)
388}
389
390#[track_caller]
396pub fn fill_page(current_task: &CurrentTask, addr: UserAddress, data: char) {
397 let data = [data as u8].repeat(*PAGE_SIZE as usize);
398 if let Err(err) = current_task.write_memory(addr, &data) {
399 panic!("write page: failed to fill page @ {addr:?} with {data:?}: {err:?}");
400 }
401}
402
403#[track_caller]
409pub fn check_page_eq(current_task: &CurrentTask, addr: UserAddress, data: char) {
410 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
411 Ok(b) => b,
412 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
413 };
414 assert!(
415 buf.into_iter().all(|c| c == data as u8),
416 "unexpected payload: page @ {addr:?} should be filled with {data:?}"
417 );
418}
419
420#[track_caller]
426pub fn check_page_ne(current_task: &CurrentTask, addr: UserAddress, data: char) {
427 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
428 Ok(b) => b,
429 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
430 };
431 assert!(
432 !buf.into_iter().all(|c| c == data as u8),
433 "unexpected payload: page @ {addr:?} should not be filled with {data:?}"
434 );
435}
436
437#[track_caller]
443pub fn check_unmapped(current_task: &CurrentTask, addr: UserAddress) {
444 match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
445 Ok(_) => panic!("read page: page @ {addr:?} should be unmapped"),
446 Err(err) if err == starnix_uapi::errors::EFAULT => {}
447 Err(err) => {
448 panic!("read page: expected EFAULT reading page @ {addr:?} but got {err:?} instead")
449 }
450 }
451}
452
453pub struct PanickingFsNode;
456
457impl FsNodeOps for PanickingFsNode {
458 fs_node_impl_not_dir!();
459
460 fn create_file_ops(
461 &self,
462 _node: &FsNode,
463 _current_task: &CurrentTask,
464 _flags: OpenFlags,
465 ) -> Result<Box<dyn FileOps>, Errno> {
466 panic!("should not be called")
467 }
468}
469
470pub struct PanickingFile;
472
473impl PanickingFile {
474 pub fn new_file(current_task: &CurrentTask) -> FileHandle {
476 anon_test_file(current_task, Box::new(PanickingFile), OpenFlags::RDWR)
477 }
478}
479
480impl FileOps for PanickingFile {
481 fileops_impl_nonseekable!();
482 fileops_impl_noop_sync!();
483
484 fn write(
485 &self,
486 _file: &FileObject,
487 _current_task: &CurrentTask,
488 _offset: usize,
489 _data: &mut dyn InputBuffer,
490 ) -> Result<usize, Errno> {
491 panic!("write called on TestFile")
492 }
493
494 fn read(
495 &self,
496 _file: &FileObject,
497 _current_task: &CurrentTask,
498 _offset: usize,
499 _data: &mut dyn OutputBuffer,
500 ) -> Result<usize, Errno> {
501 panic!("read called on TestFile")
502 }
503
504 fn ioctl(
505 &self,
506 _file: &FileObject,
507 _current_task: &CurrentTask,
508 _request: u32,
509 _arg: SyscallArg,
510 ) -> Result<SyscallResult, Errno> {
511 panic!("ioctl called on TestFile")
512 }
513}
514
515pub fn anon_test_file(
517 current_task: &CurrentTask,
518 ops: Box<dyn FileOps>,
519 flags: OpenFlags,
520) -> FileHandle {
521 Anon::new_private_file(current_task, ops, flags, "[fuchsia:test_file]")
523}
524
525pub struct UserMemoryWriter<'a> {
527 mm: &'a Task,
529 current_addr: UserAddress,
531}
532
533impl<'a> UserMemoryWriter<'a> {
534 pub fn new(task: &'a Task, addr: UserAddress) -> Self {
536 Self { mm: task, current_addr: addr }
537 }
538
539 pub fn write(&mut self, data: &[u8]) -> UserAddress {
543 let bytes_written = self.mm.write_memory(self.current_addr, data).unwrap();
544 assert_eq!(bytes_written, data.len());
545 let start_addr = self.current_addr;
546 self.current_addr = (self.current_addr + bytes_written).unwrap();
547 start_addr
548 }
549
550 pub fn write_object<T: IntoBytes + Immutable>(&mut self, object: &T) -> UserAddress {
554 self.write(object.as_bytes())
555 }
556
557 pub fn current_address(&self) -> UserAddress {
559 self.current_addr
560 }
561}
562
563#[derive(Debug)]
564pub struct AutoReleasableTask(Option<CurrentTask>);
565
566impl AutoReleasableTask {
567 fn as_ref(this: &Self) -> &CurrentTask {
568 this.0.as_ref().unwrap()
569 }
570
571 fn as_mut(this: &mut Self) -> &mut CurrentTask {
572 this.0.as_mut().unwrap()
573 }
574}
575
576impl From<CurrentTask> for AutoReleasableTask {
577 fn from(task: CurrentTask) -> Self {
578 Self(Some(task))
579 }
580}
581
582impl From<TaskBuilder> for AutoReleasableTask {
583 fn from(builder: TaskBuilder) -> Self {
584 CurrentTask::from(builder).into()
585 }
586}
587
588impl Drop for AutoReleasableTask {
589 fn drop(&mut self) {
590 self.0.take().unwrap().release(());
591 }
592}
593
594impl std::ops::Deref for AutoReleasableTask {
595 type Target = CurrentTask;
596
597 fn deref(&self) -> &Self::Target {
598 AutoReleasableTask::as_ref(self)
599 }
600}
601
602impl std::ops::DerefMut for AutoReleasableTask {
603 fn deref_mut(&mut self) -> &mut Self::Target {
604 AutoReleasableTask::as_mut(self)
605 }
606}
607
608impl std::borrow::Borrow<CurrentTask> for AutoReleasableTask {
609 fn borrow(&self) -> &CurrentTask {
610 AutoReleasableTask::as_ref(self)
611 }
612}
613
614impl std::convert::AsRef<CurrentTask> for AutoReleasableTask {
615 fn as_ref(&self) -> &CurrentTask {
616 AutoReleasableTask::as_ref(self)
617 }
618}
619
620impl ArchSpecific for AutoReleasableTask {
621 fn is_arch32(&self) -> bool {
622 self.deref().is_arch32()
623 }
624}
625
626impl MemoryAccessor for AutoReleasableTask {
627 fn read_memory<'a>(
628 &self,
629 addr: UserAddress,
630 bytes: &'a mut [MaybeUninit<u8>],
631 ) -> Result<&'a mut [u8], Errno> {
632 (**self).read_memory(addr, bytes)
633 }
634 fn read_memory_partial_until_null_byte<'a>(
635 &self,
636 addr: UserAddress,
637 bytes: &'a mut [MaybeUninit<u8>],
638 ) -> Result<&'a mut [u8], Errno> {
639 (**self).read_memory_partial_until_null_byte(addr, bytes)
640 }
641 fn read_memory_partial<'a>(
642 &self,
643 addr: UserAddress,
644 bytes: &'a mut [MaybeUninit<u8>],
645 ) -> Result<&'a mut [u8], Errno> {
646 (**self).read_memory_partial(addr, bytes)
647 }
648 fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
649 (**self).write_memory(addr, bytes)
650 }
651 fn write_memory_partial(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
652 (**self).write_memory_partial(addr, bytes)
653 }
654 fn zero(&self, addr: UserAddress, length: usize) -> Result<usize, Errno> {
655 (**self).zero(addr, length)
656 }
657}
658
659struct TestFs;
660impl FileSystemOps for TestFs {
661 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
662 Ok(default_statfs(0))
663 }
664 fn name(&self) -> &'static FsStr {
665 "test".into()
666 }
667}
668
669pub fn create_testfs(kernel: &Kernel) -> FileSystemHandle {
670 FileSystem::new(&kernel, CacheMode::Uncached, TestFs, Default::default())
671 .expect("testfs constructed with valid options")
672}
673
674pub fn create_testfs_with_root(kernel: &Kernel, ops: impl FsNodeOps) -> FileSystemHandle {
675 let test_fs = create_testfs(kernel);
676 let root_ino = test_fs.allocate_ino();
677 test_fs.create_root(root_ino, ops);
678 test_fs
679}
680
681pub fn create_fs_node_for_testing(fs: &FileSystemHandle, ops: impl FsNodeOps) -> FsNodeHandle {
682 let ino = fs.allocate_ino();
683 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
684 FsNode::new_uncached(ino, ops, fs, info, FsNodeFlags::empty())
685}
686
687pub fn create_namespace_node_for_testing(
688 fs: &FileSystemHandle,
689 ops: impl FsNodeOps,
690) -> NamespaceNode {
691 let node = create_fs_node_for_testing(fs, ops);
692 NamespaceNode::new_anonymous(DirEntry::new_unrooted(node))
693}