1use crate::device::mem::DevNull;
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, FsNode, FsNodeFlags, FsNodeHandle,
24 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 let null_file =
152 Anon::new_file(current_task, Box::new(DevNull), OpenFlags::empty(), "[null]")
153 .expect("create null file for selinuxfs");
154 security::selinuxfs_init_null(current_task, &null_file);
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 init_task = create_test_init_task(&kernel, fs_factory);
202 fasync::unblock(move || {
203 let (sender, receiver) = mpsc::sync_channel(1);
204 let error = execute_task_with_prerun_result(
205 init_task,
206 move |current_task| -> Result<(), Errno> {
207 let result = callback(current_task);
208 current_task.write().set_exit_status_if_not_already(ExitStatus::Exit(0));
209 sender.send(result).map_err(|e| errno!(EIO, e))?;
210 error!(EHWPOISON)
211 },
212 |_| {},
213 )
214 .unwrap_err();
215 assert_eq!(error, errno!(EHWPOISON));
217 receiver.recv().expect("recv")
218 })
219}
220
221fn create_test_kernel(
222 security_server: Option<Arc<SecurityServer>>,
223 features: KernelFeatures,
224 scheduler: SchedulerManager,
225) -> Arc<Kernel> {
226 Kernel::new(
227 b"".into(),
228 features,
229 SystemLimits::default(),
230 ContainerNamespace::new(),
231 scheduler,
232 None,
233 fuchsia_inspect::Node::default(),
234 security::testing::kernel_state(security_server),
235 None,
236 None,
237 )
238 .expect("failed to create kernel")
239}
240
241fn create_test_mm(task: &Task) -> Result<Arc<MemoryManager>, Errno> {
243 let arch_width = ArchWidth::Arch64;
244 let mm =
245 MemoryManager::new_for_test(task.thread_group().root_vmar.unowned(), ArchWidth::Arch64);
246 let fake_executable_addr = mm.get_random_base_for_executable(arch_width, 0)?;
247 mm.initialize_brk_origin(arch_width, fake_executable_addr)?;
248 task.running_state()?.mm.update(Some(mm.clone()));
249 Ok(mm)
250}
251
252fn create_test_init_task(
253 kernel: &Arc<Kernel>,
254 create_fs: impl FnOnce(&Kernel) -> FileSystemHandle,
255) -> TaskBuilder {
256 let init_pid = kernel.pids.lock().allocate_pid().expect("allocating init pid");
257 assert_eq!(init_pid.id, 1);
258
259 let system_task = create_system_task(kernel).expect("create system task");
260 kernel.kthreads.init(system_task).expect("failed to initialize kthreads");
261
262 let system_task = kernel.kthreads.system_task();
263 system_task.fs().replace_bootstrap_namespace(system_task, Namespace::new(create_fs(kernel)));
264
265 let init_task = create_init_process(
266 kernel,
267 init_pid,
268 TaskCommand::new(b"test-task"),
269 system_task.fs().fork(),
270 &[],
271 )
272 .expect("failed to create first task");
273 create_test_mm(&init_task).expect("failed to create MM");
274
275 kernel.hrtimer_manager.init(&system_task).expect("init hrtimer manager worker thread");
276
277 {
280 let _l1 = init_task.thread_group().read();
281 let _l2 = init_task.read();
282 }
283 init_task
284}
285
286pub fn create_task(kernel: &Kernel, task_name: &str) -> AutoReleasableTask {
296 create_task_with_security_context(kernel, task_name, &CString::new("#kernel").unwrap())
297}
298
299pub fn create_task_with_security_context(
303 kernel: &Kernel,
304 task_name: &str,
305 security_context: &CString,
306) -> AutoReleasableTask {
307 let task = create_init_child_process(
308 &kernel.weak_self.upgrade().unwrap(),
309 TaskCommand::new(task_name.as_bytes()),
310 Credentials::with_ids(0, 0),
311 Some(security_context),
312 )
313 .expect("failed to create second task");
314 create_test_mm(&task).expect("failed to create MM");
315
316 {
319 let _l1 = task.thread_group().read();
320 let _l2 = task.read();
321 }
322
323 task.into()
324}
325
326pub fn map_memory_anywhere(current_task: &CurrentTask, len: u64) -> UserAddress {
329 map_memory(current_task, UserAddress::NULL, len)
330}
331
332pub fn map_object_anywhere<T>(current_task: &CurrentTask, object: &T) -> UserAddress
337where
338 T: IntoBytes + Immutable,
339{
340 let addr = map_memory_anywhere(current_task, std::mem::size_of::<T>() as u64);
341 current_task.write_object(addr.into(), object).expect("could not write object");
342 addr
343}
344
345pub fn map_memory(current_task: &CurrentTask, address: UserAddress, length: u64) -> UserAddress {
349 map_memory_with_flags(current_task, address, length, MAP_ANONYMOUS | MAP_PRIVATE)
350}
351
352pub fn map_memory_with_flags(
356 current_task: &CurrentTask,
357 address: UserAddress,
358 length: u64,
359 flags: u32,
360) -> UserAddress {
361 do_mmap(
362 current_task,
363 address,
364 length as usize,
365 PROT_READ | PROT_WRITE,
366 flags,
367 FdNumber::from_raw(-1),
368 0,
369 )
370 .expect("Could not map memory")
371}
372
373pub fn remap_memory(
376 current_task: &CurrentTask,
377 old_addr: UserAddress,
378 old_length: u64,
379 new_length: u64,
380 flags: u32,
381 new_addr: UserAddress,
382) -> Result<UserAddress, Errno> {
383 sys_mremap(current_task, old_addr, old_length as usize, new_length as usize, flags, new_addr)
384}
385
386#[track_caller]
392pub fn fill_page(current_task: &CurrentTask, addr: UserAddress, data: char) {
393 let data = [data as u8].repeat(*PAGE_SIZE as usize);
394 if let Err(err) = current_task.write_memory(addr, &data) {
395 panic!("write page: failed to fill page @ {addr:?} with {data:?}: {err:?}");
396 }
397}
398
399#[track_caller]
405pub fn check_page_eq(current_task: &CurrentTask, addr: UserAddress, data: char) {
406 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
407 Ok(b) => b,
408 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
409 };
410 assert!(
411 buf.into_iter().all(|c| c == data as u8),
412 "unexpected payload: page @ {addr:?} should be filled with {data:?}"
413 );
414}
415
416#[track_caller]
422pub fn check_page_ne(current_task: &CurrentTask, addr: UserAddress, data: char) {
423 let buf = match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
424 Ok(b) => b,
425 Err(err) => panic!("read page: failed to read page @ {addr:?}: {err:?}"),
426 };
427 assert!(
428 !buf.into_iter().all(|c| c == data as u8),
429 "unexpected payload: page @ {addr:?} should not be filled with {data:?}"
430 );
431}
432
433#[track_caller]
439pub fn check_unmapped(current_task: &CurrentTask, addr: UserAddress) {
440 match current_task.read_memory_to_vec(addr, *PAGE_SIZE as usize) {
441 Ok(_) => panic!("read page: page @ {addr:?} should be unmapped"),
442 Err(err) if err == starnix_uapi::errors::EFAULT => {}
443 Err(err) => {
444 panic!("read page: expected EFAULT reading page @ {addr:?} but got {err:?} instead")
445 }
446 }
447}
448
449pub struct PanickingFsNode;
452
453impl FsNodeOps for PanickingFsNode {
454 fs_node_impl_not_dir!();
455
456 fn create_file_ops(
457 &self,
458 _node: &FsNode,
459 _current_task: &CurrentTask,
460 _flags: OpenFlags,
461 ) -> Result<Box<dyn FileOps>, Errno> {
462 panic!("should not be called")
463 }
464}
465
466pub struct PanickingFile;
468
469impl PanickingFile {
470 pub fn new_file(current_task: &CurrentTask) -> FileHandle {
472 anon_test_file(current_task, Box::new(PanickingFile), OpenFlags::RDWR)
473 }
474}
475
476impl FileOps for PanickingFile {
477 fileops_impl_nonseekable!();
478 fileops_impl_noop_sync!();
479
480 fn write(
481 &self,
482 _file: &FileObject,
483 _current_task: &CurrentTask,
484 _offset: usize,
485 _data: &mut dyn InputBuffer,
486 ) -> Result<usize, Errno> {
487 panic!("write called on TestFile")
488 }
489
490 fn read(
491 &self,
492 _file: &FileObject,
493 _current_task: &CurrentTask,
494 _offset: usize,
495 _data: &mut dyn OutputBuffer,
496 ) -> Result<usize, Errno> {
497 panic!("read called on TestFile")
498 }
499
500 fn ioctl(
501 &self,
502 _file: &FileObject,
503 _current_task: &CurrentTask,
504 _request: u32,
505 _arg: SyscallArg,
506 ) -> Result<SyscallResult, Errno> {
507 panic!("ioctl called on TestFile")
508 }
509}
510
511pub fn anon_test_file(
513 current_task: &CurrentTask,
514 ops: Box<dyn FileOps>,
515 flags: OpenFlags,
516) -> FileHandle {
517 Anon::new_private_file(current_task, ops, flags, "[fuchsia:test_file]")
519}
520
521pub struct UserMemoryWriter<'a> {
523 mm: &'a Task,
525 current_addr: UserAddress,
527}
528
529impl<'a> UserMemoryWriter<'a> {
530 pub fn new(task: &'a Task, addr: UserAddress) -> Self {
532 Self { mm: task, current_addr: addr }
533 }
534
535 pub fn write(&mut self, data: &[u8]) -> UserAddress {
539 let bytes_written = self.mm.write_memory(self.current_addr, data).unwrap();
540 assert_eq!(bytes_written, data.len());
541 let start_addr = self.current_addr;
542 self.current_addr = (self.current_addr + bytes_written).unwrap();
543 start_addr
544 }
545
546 pub fn write_object<T: IntoBytes + Immutable>(&mut self, object: &T) -> UserAddress {
550 self.write(object.as_bytes())
551 }
552
553 pub fn current_address(&self) -> UserAddress {
555 self.current_addr
556 }
557}
558
559#[derive(Debug)]
560pub struct AutoReleasableTask(Option<CurrentTask>);
561
562impl AutoReleasableTask {
563 fn as_ref(this: &Self) -> &CurrentTask {
564 this.0.as_ref().unwrap()
565 }
566
567 fn as_mut(this: &mut Self) -> &mut CurrentTask {
568 this.0.as_mut().unwrap()
569 }
570}
571
572impl From<CurrentTask> for AutoReleasableTask {
573 fn from(task: CurrentTask) -> Self {
574 Self(Some(task))
575 }
576}
577
578impl From<TaskBuilder> for AutoReleasableTask {
579 fn from(builder: TaskBuilder) -> Self {
580 CurrentTask::from(builder).into()
581 }
582}
583
584impl Drop for AutoReleasableTask {
585 fn drop(&mut self) {
586 self.0.take().unwrap().release(());
587 }
588}
589
590impl std::ops::Deref for AutoReleasableTask {
591 type Target = CurrentTask;
592
593 fn deref(&self) -> &Self::Target {
594 AutoReleasableTask::as_ref(self)
595 }
596}
597
598impl std::ops::DerefMut for AutoReleasableTask {
599 fn deref_mut(&mut self) -> &mut Self::Target {
600 AutoReleasableTask::as_mut(self)
601 }
602}
603
604impl std::borrow::Borrow<CurrentTask> for AutoReleasableTask {
605 fn borrow(&self) -> &CurrentTask {
606 AutoReleasableTask::as_ref(self)
607 }
608}
609
610impl std::convert::AsRef<CurrentTask> for AutoReleasableTask {
611 fn as_ref(&self) -> &CurrentTask {
612 AutoReleasableTask::as_ref(self)
613 }
614}
615
616impl ArchSpecific for AutoReleasableTask {
617 fn is_arch32(&self) -> bool {
618 self.deref().is_arch32()
619 }
620}
621
622impl MemoryAccessor for AutoReleasableTask {
623 fn read_memory<'a>(
624 &self,
625 addr: UserAddress,
626 bytes: &'a mut [MaybeUninit<u8>],
627 ) -> Result<&'a mut [u8], Errno> {
628 (**self).read_memory(addr, bytes)
629 }
630 fn read_memory_partial_until_null_byte<'a>(
631 &self,
632 addr: UserAddress,
633 bytes: &'a mut [MaybeUninit<u8>],
634 ) -> Result<&'a mut [u8], Errno> {
635 (**self).read_memory_partial_until_null_byte(addr, bytes)
636 }
637 fn read_memory_partial<'a>(
638 &self,
639 addr: UserAddress,
640 bytes: &'a mut [MaybeUninit<u8>],
641 ) -> Result<&'a mut [u8], Errno> {
642 (**self).read_memory_partial(addr, bytes)
643 }
644 fn write_memory(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
645 (**self).write_memory(addr, bytes)
646 }
647 fn write_memory_partial(&self, addr: UserAddress, bytes: &[u8]) -> Result<usize, Errno> {
648 (**self).write_memory_partial(addr, bytes)
649 }
650 fn zero(&self, addr: UserAddress, length: usize) -> Result<usize, Errno> {
651 (**self).zero(addr, length)
652 }
653}
654
655struct TestFs;
656impl FileSystemOps for TestFs {
657 fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
658 Ok(default_statfs(0))
659 }
660 fn name(&self) -> &'static FsStr {
661 "test".into()
662 }
663}
664
665pub fn create_testfs(kernel: &Kernel) -> FileSystemHandle {
666 FileSystem::new(&kernel, CacheMode::Uncached, TestFs, Default::default())
667 .expect("testfs constructed with valid options")
668}
669
670pub fn create_testfs_with_root(kernel: &Kernel, ops: impl FsNodeOps) -> FileSystemHandle {
671 let test_fs = create_testfs(kernel);
672 let root_ino = test_fs.allocate_ino();
673 test_fs.create_root(root_ino, ops);
674 test_fs
675}
676
677pub fn create_fs_node_for_testing(fs: &FileSystemHandle, ops: impl FsNodeOps) -> FsNodeHandle {
678 let ino = fs.allocate_ino();
679 let info = FsNodeInfo::new(mode!(IFDIR, 0o777), FsCred::root());
680 FsNode::new_uncached(ino, ops, fs, info, FsNodeFlags::empty())
681}
682
683pub fn create_namespace_node_for_testing(
684 fs: &FileSystemHandle,
685 ops: impl FsNodeOps,
686) -> NamespaceNode {
687 let node = create_fs_node_for_testing(fs, ops);
688 NamespaceNode::new_anonymous(DirEntry::new_unrooted(node))
689}