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