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
80#[derive(Default)]
81struct DevZero;
82impl FileOps for DevZero {
83 fileops_impl_seekless!();
84 fileops_impl_noop_sync!();
85
86 fn mmap(
87 &self,
88 file: &FileObject,
89 current_task: &CurrentTask,
90 addr: DesiredAddress,
91 memory_offset: u64,
92 length: usize,
93 prot_flags: ProtectionFlags,
94 mut options: MappingOptions,
95 ) -> Result<UserAddress, Errno> {
96 let memory = create_anonymous_mapping_memory(length as u64)?;
106
107 options |= MappingOptions::ANONYMOUS;
108
109 current_task.mm()?.map_memory(
110 addr,
111 memory,
112 memory_offset,
113 length,
114 prot_flags,
115 options,
116 MappingName::File(file.to_mapping(None)?),
121 )
122 }
123
124 fn write(
125 &self,
126 _file: &FileObject,
127 _current_task: &CurrentTask,
128 _offset: usize,
129 data: &mut dyn InputBuffer,
130 ) -> Result<usize, Errno> {
131 Ok(data.drain())
132 }
133
134 fn read(
135 &self,
136 _file: &FileObject,
137 _current_task: &CurrentTask,
138 _offset: usize,
139 data: &mut dyn OutputBuffer,
140 ) -> Result<usize, Errno> {
141 data.zero()
142 }
143}
144
145#[derive(Default)]
146struct DevFull;
147impl FileOps for DevFull {
148 fileops_impl_seekless!();
149 fileops_impl_noop_sync!();
150
151 fn write(
152 &self,
153 _file: &FileObject,
154 _current_task: &CurrentTask,
155 _offset: usize,
156 _data: &mut dyn InputBuffer,
157 ) -> Result<usize, Errno> {
158 error!(ENOSPC)
159 }
160
161 fn read(
162 &self,
163 _file: &FileObject,
164 _current_task: &CurrentTask,
165 _offset: usize,
166 data: &mut dyn OutputBuffer,
167 ) -> Result<usize, Errno> {
168 data.write_each(&mut |bytes| {
169 bytes.fill(MaybeUninit::new(0));
170 Ok(bytes.len())
171 })
172 }
173}
174
175struct ZeroOnDropBuffer {
176 buf: Vec<u8>,
177}
178
179impl ZeroOnDropBuffer {
180 fn new(size: usize) -> Self {
181 Self { buf: vec![0u8; size] }
182 }
183}
184
185impl Drop for ZeroOnDropBuffer {
186 fn drop(&mut self) {
187 unsafe {
189 for i in 0..self.buf.len() {
190 std::ptr::write_volatile(self.buf.as_mut_ptr().add(i), 0);
191 }
192 }
193 }
194}
195
196#[derive(Default)]
197pub struct DevRandom;
198impl FileOps for DevRandom {
199 fileops_impl_seekless!();
200 fileops_impl_noop_sync!();
201
202 fn write(
203 &self,
204 _file: &FileObject,
205 _current_task: &CurrentTask,
206 _offset: usize,
207 data: &mut dyn InputBuffer,
208 ) -> Result<usize, Errno> {
209 Ok(data.drain())
210 }
211
212 fn read(
213 &self,
214 _file: &FileObject,
215 _current_task: &CurrentTask,
216 _offset: usize,
217 data: &mut dyn OutputBuffer,
218 ) -> Result<usize, Errno> {
219 let mut rdm = ZeroOnDropBuffer::new(data.available());
222 starnix_crypto::cprng_draw(&mut rdm.buf);
223 data.write(&rdm.buf)
224 }
225
226 fn ioctl(
227 &self,
228 _file: &FileObject,
229 current_task: &CurrentTask,
230 request: u32,
231 arg: starnix_syscalls::SyscallArg,
232 ) -> Result<starnix_syscalls::SyscallResult, Errno> {
233 match request {
234 starnix_uapi::RNDGETENTCNT => {
235 let addr = starnix_uapi::user_address::UserRef::<i32>::new(UserAddress::from(arg));
236 let result = 256;
238 current_task.write_object(addr, &result).map(|_| starnix_syscalls::SUCCESS)
239 }
240 _ => error!(ENOTTY),
241 }
242 }
243}
244
245pub fn open_kmsg(
246 current_task: &CurrentTask,
247 _id: DeviceId,
248 _node: &NamespaceNode,
249 flags: OpenFlags,
250) -> Result<Box<dyn FileOps>, Errno> {
251 if flags.can_read() {
252 Syslog::validate_access(current_task, SyslogAccess::DevKmsgRead)?;
253 }
254 let subscription = if flags.can_read() {
255 Some(Syslog::snapshot_then_subscribe(current_task.kernel())?.into())
256 } else {
257 None
258 };
259 Ok(Box::new(DevKmsg(subscription)))
260}
261
262struct DevKmsg(Option<LockDepMutex<LogSubscription, DevKmsgLock>>);
263
264impl FileOps for DevKmsg {
265 fileops_impl_noop_sync!();
266
267 fn has_persistent_offsets(&self) -> bool {
268 false
269 }
270
271 fn is_seekable(&self) -> bool {
272 true
273 }
274
275 fn seek(
276 &self,
277 _file: &crate::vfs::FileObject,
278 current_task: &crate::task::CurrentTask,
279 _current_offset: starnix_uapi::off_t,
280 target: crate::vfs::SeekTarget,
281 ) -> Result<starnix_uapi::off_t, starnix_uapi::errors::Errno> {
282 match target {
283 SeekTarget::Set(0) => {
284 let Some(ref subscription) = self.0 else {
285 return Ok(0);
286 };
287 let mut guard = subscription.lock();
288 *guard = Syslog::snapshot_then_subscribe(current_task.kernel())?;
289 Ok(0)
290 }
291 SeekTarget::End(0) => {
292 let Some(ref subscription) = self.0 else {
293 return Ok(0);
294 };
295 let mut guard = subscription.lock();
296 *guard = Syslog::subscribe(current_task.kernel())?;
297 Ok(0)
298 }
299 SeekTarget::Data(0) => {
300 track_stub!(TODO("https://fxbug.dev/322874315"), "/dev/kmsg: SEEK_DATA");
301 Ok(0)
302 }
303 SeekTarget::End(_) | SeekTarget::Set(_) | SeekTarget::Data(_) => {
308 error!(ESPIPE, "Unsupported offset")
309 }
310 SeekTarget::Cur(_) => error!(ESPIPE),
313 SeekTarget::Hole(_) => error!(EINVAL, "Unsupported seek target"),
314 }
315 }
316
317 fn wait_async(
318 &self,
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 _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 file: &FileObject,
345 current_task: &CurrentTask,
346 _offset: usize,
347 data: &mut dyn OutputBuffer,
348 ) -> Result<usize, Errno> {
349 file.blocking_op(current_task, FdEvents::POLLIN | FdEvents::POLLHUP, None, || {
350 match self.0.as_ref().unwrap().lock().next() {
351 Some(Ok(log)) => data.write(&log),
352 Some(Err(err)) => Err(err),
353 None => Ok(0),
354 }
355 })
356 }
357
358 fn write(
359 &self,
360 _file: &FileObject,
361 _current_task: &CurrentTask,
362 _offset: usize,
363 data: &mut dyn InputBuffer,
364 ) -> Result<usize, Errno> {
365 let bytes = data.read_all()?;
366 let extract_result = syslog::extract_level(&bytes);
367 let (level, msg_bytes) = match extract_result {
368 None => (Level::Info, bytes.as_slice()),
369 Some((level, bytes_after_level)) => match level {
370 KmsgLevel::Emergency | KmsgLevel::Alert | KmsgLevel::Critical => {
372 (Level::Error, bytes.as_slice())
373 }
374 KmsgLevel::Error => (Level::Error, bytes_after_level),
375 KmsgLevel::Warning => (Level::Warn, bytes_after_level),
376 KmsgLevel::Notice => (Level::Info, bytes.as_slice()),
378 KmsgLevel::Info => (Level::Info, bytes_after_level),
379 KmsgLevel::Debug => (Level::Debug, bytes_after_level),
380 },
381 };
382
383 starnix_logging::with_current_task_info(|info| {
388 starnix_logging::logger().log(
389 &starnix_logging::Record::builder()
394 .level(level)
395 .key_values(&[
396 ("tag", LogOutputTag::Str("kmsg")),
397 ("tag", LogOutputTag::Display(info)),
398 ])
399 .args(format_args!(
400 "{}",
401 String::from_utf8_lossy(msg_bytes).trim_end_matches('\n')
402 ))
403 .build(),
404 );
405 });
406 Ok(bytes.len())
407 }
408}
409
410enum LogOutputTag<'a> {
411 Str(&'a str),
412 Display(&'a dyn std::fmt::Display),
413}
414
415impl<'a> starnix_logging::ToValue for LogOutputTag<'a> {
416 fn to_value(&self) -> starnix_logging::Value<'_> {
417 match self {
418 Self::Str(s) => starnix_logging::Value::from_display(s),
419 Self::Display(d) => starnix_logging::Value::from_dyn_display(d),
420 }
421 }
422}
423
424pub fn mem_device_init<'a>(kernel: &Kernel) -> Result<(), Errno> {
425 let registry = &kernel.device_registry;
426
427 let mem_class = registry.objects.mem_class();
428 registry.register_device(
429 kernel,
430 "null".into(),
431 DeviceMetadata::new("null".into(), DeviceId::NULL, DeviceMode::Char),
432 mem_class.clone(),
433 simple_device_ops::<DevNull>,
434 )?;
435 registry.register_device(
436 kernel,
437 "zero".into(),
438 DeviceMetadata::new("zero".into(), DeviceId::ZERO, DeviceMode::Char),
439 mem_class.clone(),
440 simple_device_ops::<DevZero>,
441 )?;
442 registry.register_device(
443 kernel,
444 "full".into(),
445 DeviceMetadata::new("full".into(), DeviceId::FULL, DeviceMode::Char),
446 mem_class.clone(),
447 simple_device_ops::<DevFull>,
448 )?;
449 registry.register_device(
450 kernel,
451 "random".into(),
452 DeviceMetadata::new("random".into(), DeviceId::RANDOM, DeviceMode::Char),
453 mem_class.clone(),
454 simple_device_ops::<DevRandom>,
455 )?;
456 registry.register_device(
457 kernel,
458 "urandom".into(),
459 DeviceMetadata::new("urandom".into(), DeviceId::URANDOM, DeviceMode::Char),
460 mem_class.clone(),
461 simple_device_ops::<DevRandom>,
462 )?;
463 registry.register_device(
464 kernel,
465 "kmsg".into(),
466 DeviceMetadata::new("kmsg".into(), DeviceId::KMSG, DeviceMode::Char),
467 mem_class,
468 open_kmsg,
469 )?;
470 Ok(())
471}