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