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