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