1use crate::device::kobject::DeviceMetadata;
6use crate::device::{DeviceMode, simple_device_ops};
7use crate::mm::{
8 DesiredAddress, MappingName, MappingOptions, MemoryAccessorExt, ProtectionFlags,
9 create_anonymous_mapping_memory,
10};
11use crate::task::syslog::{self, KmsgLevel};
12use crate::task::{
13 CurrentTask, EventHandler, KernelOrTask, LogSubscription, Syslog, SyslogAccess, WaitCanceler,
14 Waiter,
15};
16use crate::vfs::buffers::{InputBuffer, InputBufferExt as _, OutputBuffer};
17use crate::vfs::{
18 Anon, FileHandle, FileObject, FileOps, FsNodeInfo, NamespaceNode, SeekTarget,
19 fileops_impl_noop_sync, fileops_impl_seekless,
20};
21use starnix_logging::{Level, track_stub};
22use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex, Unlocked};
23use starnix_uapi::auth::FsCred;
24use starnix_uapi::device_id::DeviceId;
25use starnix_uapi::error;
26use starnix_uapi::errors::Errno;
27use starnix_uapi::file_mode::FileMode;
28use starnix_uapi::open_flags::OpenFlags;
29use starnix_uapi::user_address::UserAddress;
30use starnix_uapi::vfs::FdEvents;
31use std::mem::MaybeUninit;
32
33#[derive(Default)]
34pub struct DevNull;
35
36pub fn new_null_file<L>(
37 locked: &mut Locked<L>,
38 current_task: &CurrentTask,
39 flags: OpenFlags,
40) -> FileHandle
41where
42 L: LockEqualOrBefore<FileOpsCore>,
43{
44 Anon::new_private_file_extended(
45 locked,
46 current_task,
47 Box::new(DevNull),
48 flags,
49 "[fuchsia:null]",
50 FsNodeInfo::new(FileMode::from_bits(0o666), FsCred::root()),
51 )
52}
53
54impl FileOps for DevNull {
55 fileops_impl_seekless!();
56 fileops_impl_noop_sync!();
57
58 fn write(
59 &self,
60 _locked: &mut Locked<FileOpsCore>,
61 _file: &FileObject,
62 _current_task: &CurrentTask,
63 _offset: usize,
64 data: &mut dyn InputBuffer,
65 ) -> Result<usize, Errno> {
66 let bytes_logged = match data.read_to_vec_limited(data.available()) {
72 Ok(bytes) => bytes.len(),
73 Err(_) => 0,
74 };
75
76 Ok(bytes_logged + data.drain())
77 }
78
79 fn read(
80 &self,
81 _locked: &mut Locked<FileOpsCore>,
82 _file: &FileObject,
83 _current_task: &CurrentTask,
84 _offset: usize,
85 _data: &mut dyn OutputBuffer,
86 ) -> Result<usize, Errno> {
87 Ok(0)
88 }
89
90 fn to_handle(
91 &self,
92 _file: &FileObject,
93 _current_task: &CurrentTask,
94 ) -> Result<Option<zx::NullableHandle>, Errno> {
95 Ok(None)
96 }
97}
98
99#[derive(Default)]
100struct DevZero;
101impl FileOps for DevZero {
102 fileops_impl_seekless!();
103 fileops_impl_noop_sync!();
104
105 fn mmap(
106 &self,
107 _locked: &mut Locked<FileOpsCore>,
108 file: &FileObject,
109 current_task: &CurrentTask,
110 addr: DesiredAddress,
111 memory_offset: u64,
112 length: usize,
113 prot_flags: ProtectionFlags,
114 mut options: MappingOptions,
115 filename: NamespaceNode,
116 ) -> Result<UserAddress, Errno> {
117 let memory = create_anonymous_mapping_memory(length as u64)?;
127
128 options |= MappingOptions::ANONYMOUS;
129
130 current_task.mm()?.map_memory(
131 addr,
132 memory,
133 memory_offset,
134 length,
135 prot_flags,
136 file.max_access_for_memory_mapping(),
137 options,
138 MappingName::File(filename.into_mapping(None)?),
143 )
144 }
145
146 fn write(
147 &self,
148 _locked: &mut Locked<FileOpsCore>,
149 _file: &FileObject,
150 _current_task: &CurrentTask,
151 _offset: usize,
152 data: &mut dyn InputBuffer,
153 ) -> Result<usize, Errno> {
154 Ok(data.drain())
155 }
156
157 fn read(
158 &self,
159 _locked: &mut Locked<FileOpsCore>,
160 _file: &FileObject,
161 _current_task: &CurrentTask,
162 _offset: usize,
163 data: &mut dyn OutputBuffer,
164 ) -> Result<usize, Errno> {
165 data.zero()
166 }
167}
168
169#[derive(Default)]
170struct DevFull;
171impl FileOps for DevFull {
172 fileops_impl_seekless!();
173 fileops_impl_noop_sync!();
174
175 fn write(
176 &self,
177 _locked: &mut Locked<FileOpsCore>,
178 _file: &FileObject,
179 _current_task: &CurrentTask,
180 _offset: usize,
181 _data: &mut dyn InputBuffer,
182 ) -> Result<usize, Errno> {
183 error!(ENOSPC)
184 }
185
186 fn read(
187 &self,
188 _locked: &mut Locked<FileOpsCore>,
189 _file: &FileObject,
190 _current_task: &CurrentTask,
191 _offset: usize,
192 data: &mut dyn OutputBuffer,
193 ) -> Result<usize, Errno> {
194 data.write_each(&mut |bytes| {
195 bytes.fill(MaybeUninit::new(0));
196 Ok(bytes.len())
197 })
198 }
199}
200
201#[derive(Default)]
202pub struct DevRandom;
203impl FileOps for DevRandom {
204 fileops_impl_seekless!();
205 fileops_impl_noop_sync!();
206
207 fn write(
208 &self,
209 _locked: &mut Locked<FileOpsCore>,
210 _file: &FileObject,
211 _current_task: &CurrentTask,
212 _offset: usize,
213 data: &mut dyn InputBuffer,
214 ) -> Result<usize, Errno> {
215 Ok(data.drain())
216 }
217
218 fn read(
219 &self,
220 _locked: &mut Locked<FileOpsCore>,
221 _file: &FileObject,
222 _current_task: &CurrentTask,
223 _offset: usize,
224 data: &mut dyn OutputBuffer,
225 ) -> Result<usize, Errno> {
226 let mut rdm = vec![0u8; data.available()];
227 starnix_crypto::cprng_draw(&mut rdm);
228 data.write(&rdm)
229 }
230
231 fn ioctl(
232 &self,
233 locked: &mut Locked<Unlocked>,
234 file: &FileObject,
235 current_task: &CurrentTask,
236 request: u32,
237 arg: starnix_syscalls::SyscallArg,
238 ) -> Result<starnix_syscalls::SyscallResult, Errno> {
239 match request {
240 starnix_uapi::RNDGETENTCNT => {
241 let addr = starnix_uapi::user_address::UserRef::<i32>::new(UserAddress::from(arg));
242 let result = 256;
244 current_task.write_object(addr, &result).map(|_| starnix_syscalls::SUCCESS)
245 }
246 _ => crate::vfs::default_ioctl(file, locked, current_task, request, arg),
247 }
248 }
249}
250
251pub fn open_kmsg(
252 _locked: &mut Locked<FileOpsCore>,
253 current_task: &CurrentTask,
254 _id: DeviceId,
255 _node: &NamespaceNode,
256 flags: OpenFlags,
257) -> Result<Box<dyn FileOps>, Errno> {
258 if flags.can_read() {
259 Syslog::validate_access(current_task, SyslogAccess::DevKmsgRead)?;
260 }
261 let subscription = if flags.can_read() {
262 Some(Mutex::new(Syslog::snapshot_then_subscribe(¤t_task)?))
263 } else {
264 None
265 };
266 Ok(Box::new(DevKmsg(subscription)))
267}
268
269struct DevKmsg(Option<Mutex<LogSubscription>>);
270
271impl FileOps for DevKmsg {
272 fileops_impl_noop_sync!();
273
274 fn has_persistent_offsets(&self) -> bool {
275 false
276 }
277
278 fn is_seekable(&self) -> bool {
279 true
280 }
281
282 fn seek(
283 &self,
284 _locked: &mut Locked<FileOpsCore>,
285 _file: &crate::vfs::FileObject,
286 current_task: &crate::task::CurrentTask,
287 _current_offset: starnix_uapi::off_t,
288 target: crate::vfs::SeekTarget,
289 ) -> Result<starnix_uapi::off_t, starnix_uapi::errors::Errno> {
290 match target {
291 SeekTarget::Set(0) => {
292 let Some(ref subscription) = self.0 else {
293 return Ok(0);
294 };
295 let mut guard = subscription.lock();
296 *guard = Syslog::snapshot_then_subscribe(current_task)?;
297 Ok(0)
298 }
299 SeekTarget::End(0) => {
300 let Some(ref subscription) = self.0 else {
301 return Ok(0);
302 };
303 let mut guard = subscription.lock();
304 *guard = Syslog::subscribe(current_task)?;
305 Ok(0)
306 }
307 SeekTarget::Data(0) => {
308 track_stub!(TODO("https://fxbug.dev/322874315"), "/dev/kmsg: SEEK_DATA");
309 Ok(0)
310 }
311 SeekTarget::End(_) | SeekTarget::Set(_) | SeekTarget::Data(_) => {
316 error!(ESPIPE, "Unsupported offset")
317 }
318 SeekTarget::Cur(_) => error!(ESPIPE),
321 SeekTarget::Hole(_) => error!(EINVAL, "Unsupported seek target"),
322 }
323 }
324
325 fn wait_async(
326 &self,
327 _locked: &mut Locked<FileOpsCore>,
328 _file: &FileObject,
329 _current_task: &CurrentTask,
330 waiter: &Waiter,
331 events: FdEvents,
332 handler: EventHandler,
333 ) -> Option<WaitCanceler> {
334 self.0.as_ref().map(|subscription| subscription.lock().wait(waiter, events, handler))
335 }
336
337 fn query_events(
338 &self,
339 _locked: &mut Locked<FileOpsCore>,
340 _file: &FileObject,
341 _current_task: &CurrentTask,
342 ) -> Result<FdEvents, Errno> {
343 let mut events = FdEvents::empty();
344 if let Some(subscription) = self.0.as_ref() {
345 if subscription.lock().available()? > 0 {
346 events |= FdEvents::POLLIN;
347 }
348 }
349 Ok(events)
350 }
351
352 fn read(
353 &self,
354 locked: &mut Locked<FileOpsCore>,
355 file: &FileObject,
356 current_task: &CurrentTask,
357 _offset: usize,
358 data: &mut dyn OutputBuffer,
359 ) -> Result<usize, Errno> {
360 file.blocking_op(locked, current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, |_| {
361 match self.0.as_ref().unwrap().lock().next() {
362 Some(Ok(log)) => data.write(&log),
363 Some(Err(err)) => Err(err),
364 None => Ok(0),
365 }
366 })
367 }
368
369 fn write(
370 &self,
371 _locked: &mut Locked<FileOpsCore>,
372 _file: &FileObject,
373 _current_task: &CurrentTask,
374 _offset: usize,
375 data: &mut dyn InputBuffer,
376 ) -> Result<usize, Errno> {
377 let bytes = data.read_all()?;
378 let extract_result = syslog::extract_level(&bytes);
379 let (level, msg_bytes) = match extract_result {
380 None => (Level::Info, bytes.as_slice()),
381 Some((level, bytes_after_level)) => match level {
382 KmsgLevel::Emergency | KmsgLevel::Alert | KmsgLevel::Critical => {
384 (Level::Error, bytes.as_slice())
385 }
386 KmsgLevel::Error => (Level::Error, bytes_after_level),
387 KmsgLevel::Warning => (Level::Warn, bytes_after_level),
388 KmsgLevel::Notice => (Level::Info, bytes.as_slice()),
390 KmsgLevel::Info => (Level::Info, bytes_after_level),
391 KmsgLevel::Debug => (Level::Debug, bytes_after_level),
392 },
393 };
394
395 starnix_logging::with_current_task_info(|info| {
400 starnix_logging::logger().log(
401 &starnix_logging::Record::builder()
406 .level(level)
407 .key_values(&[
408 ("tag", LogOutputTag::Str("kmsg")),
409 ("tag", LogOutputTag::Display(info)),
410 ])
411 .args(format_args!(
412 "{}",
413 String::from_utf8_lossy(msg_bytes).trim_end_matches('\n')
414 ))
415 .build(),
416 );
417 });
418 Ok(bytes.len())
419 }
420}
421
422enum LogOutputTag<'a> {
423 Str(&'a str),
424 Display(&'a dyn std::fmt::Display),
425}
426
427impl<'a> starnix_logging::ToValue for LogOutputTag<'a> {
428 fn to_value(&self) -> starnix_logging::Value<'_> {
429 match self {
430 Self::Str(s) => starnix_logging::Value::from_display(s),
431 Self::Display(d) => starnix_logging::Value::from_dyn_display(d),
432 }
433 }
434}
435
436pub fn mem_device_init<'a, L>(
437 locked: &mut Locked<L>,
438 kernel_or_task: impl KernelOrTask<'a>,
439) -> Result<(), Errno>
440where
441 L: LockEqualOrBefore<FileOpsCore>,
442{
443 let kernel = kernel_or_task.kernel();
444 let registry = &kernel.device_registry;
445
446 let mem_class = registry.objects.mem_class();
447 registry.register_device(
448 locked,
449 kernel_or_task,
450 "null".into(),
451 DeviceMetadata::new("null".into(), DeviceId::NULL, DeviceMode::Char),
452 mem_class.clone(),
453 simple_device_ops::<DevNull>,
454 )?;
455 registry.register_device(
456 locked,
457 kernel_or_task,
458 "zero".into(),
459 DeviceMetadata::new("zero".into(), DeviceId::ZERO, DeviceMode::Char),
460 mem_class.clone(),
461 simple_device_ops::<DevZero>,
462 )?;
463 registry.register_device(
464 locked,
465 kernel_or_task,
466 "full".into(),
467 DeviceMetadata::new("full".into(), DeviceId::FULL, DeviceMode::Char),
468 mem_class.clone(),
469 simple_device_ops::<DevFull>,
470 )?;
471 registry.register_device(
472 locked,
473 kernel_or_task,
474 "random".into(),
475 DeviceMetadata::new("random".into(), DeviceId::RANDOM, DeviceMode::Char),
476 mem_class.clone(),
477 simple_device_ops::<DevRandom>,
478 )?;
479 registry.register_device(
480 locked,
481 kernel_or_task,
482 "urandom".into(),
483 DeviceMetadata::new("urandom".into(), DeviceId::URANDOM, DeviceMode::Char),
484 mem_class.clone(),
485 simple_device_ops::<DevRandom>,
486 )?;
487 registry.register_device(
488 locked,
489 kernel_or_task,
490 "kmsg".into(),
491 DeviceMetadata::new("kmsg".into(), DeviceId::KMSG, DeviceMode::Char),
492 mem_class,
493 open_kmsg,
494 )?;
495 Ok(())
496}