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