1use crate::device::DeviceMode;
6use crate::device::kobject::{Bus, Class, Device, DeviceMetadata, Subsystem};
7use crate::fs::sysfs::{build_device_directory, get_sysfs, register_cpu_devices};
8use crate::task::Kernel;
9use crate::vfs::pseudo::simple_directory::{SimpleDirectory, SimpleDirectoryMutator};
10use crate::vfs::pseudo::stub_empty_file::StubEmptyFile;
11use crate::vfs::{FileSystemHandle, FsStr, FsString};
12use starnix_logging::bug_ref;
13use starnix_uapi::file_mode::mode;
14use std::sync::{Arc, OnceLock};
15
16pub struct KObjectStore {
21 pub root: Arc<SimpleDirectory>,
23
24 fs: OnceLock<FileSystemHandle>,
26
27 virtual_device: OnceLock<Device>,
29
30 platform_device: OnceLock<Device>,
32
33 system_device: OnceLock<Device>,
35
36 soc_device: OnceLock<Device>,
38}
39
40impl KObjectStore {
41 pub fn init(&self, kernel: &Kernel) {
42 self.fs.set(get_sysfs(kernel)).unwrap();
43 self.register_initial_devices(kernel);
44 }
45
46 fn register_initial_devices(&self, kernel: &Kernel) {
47 let registry = &kernel.device_registry;
48
49 register_cpu_devices(kernel);
51 let _ = self.power_supply_class();
52
53 registry.add_numberless_device(
56 "0:80".into(),
57 None,
58 self.get_or_create_class("bdi".into()),
59 |device, dir| {
60 build_device_directory(device, dir);
61 add_stub_attributes(dir, &["max_ratio", "read_ahead_kb"]);
62 },
63 );
64
65 let _ = self.soc_device();
69
70 registry.add_bus_device(
73 "soc0".into(),
74 None,
75 self.get_or_create_bus("soc".into()),
76 |device, dir| {
77 build_device_directory(device, dir);
78 add_stub_attributes(dir, &["revision", "serial_number"]);
79 },
80 );
81 }
82
83 pub(super) fn fs(&self) -> &FileSystemHandle {
84 self.fs.get().expect("sysfs should be initialized")
85 }
86
87 pub fn platform_bus(&self) -> Bus {
89 self.get_or_create_bus("platform".into())
90 }
91
92 pub fn virtual_device(&self) -> Device {
94 self.virtual_device
95 .get_or_init(|| {
96 self.create_device(
97 "virtual".into(),
98 None,
99 None,
100 None,
101 |_, _| {},
102 )
103 })
104 .clone()
105 }
106
107 pub fn platform_device(&self) -> Device {
109 self.platform_device
110 .get_or_init(|| {
111 self.create_device(
112 "platform".into(),
113 None,
114 None,
115 None,
116 build_device_directory,
117 )
118 })
119 .clone()
120 }
121
122 pub fn system_device(&self) -> Device {
126 self.system_device
127 .get_or_init(|| {
128 self.create_device(
129 "system".into(),
130 None,
131 None,
132 None,
133 |_, _| {},
134 )
135 })
136 .clone()
137 }
138
139 pub fn soc_device(&self) -> Device {
141 self.soc_device
144 .get_or_init(|| {
145 self.create_device(
146 "soc".into(),
147 Some(self.platform_device()),
148 Some(self.platform_bus().into()),
149 None,
150 build_device_directory,
151 )
152 })
153 .clone()
154 }
155
156 pub fn block_class(&self) -> Class {
158 self.get_or_create_class("block".into())
159 }
160
161 pub fn thermal_class(&self) -> Class {
163 self.get_or_create_class("thermal".into())
164 }
165
166 pub fn powercap_class(&self) -> Class {
168 self.get_or_create_class("powercap".into())
169 }
170
171 pub fn power_supply_class(&self) -> Class {
173 self.get_or_create_class("power_supply".into())
174 }
175
176 pub fn graphics_class(&self) -> Class {
178 self.get_or_create_class("graphics".into())
179 }
180
181 pub fn input_class(&self) -> Class {
183 self.get_or_create_class("input".into())
184 }
185
186 pub fn mem_class(&self) -> Class {
188 self.get_or_create_class("mem".into())
189 }
190
191 pub fn net_class(&self) -> Class {
193 self.get_or_create_class("net".into())
194 }
195
196 pub fn misc_class(&self) -> Class {
198 self.get_or_create_class("misc".into())
199 }
200
201 pub fn tty_class(&self) -> Class {
203 self.get_or_create_class("tty".into())
204 }
205
206 pub fn dma_heap_class(&self) -> Class {
208 self.get_or_create_class("dma_heap".into())
209 }
210
211 pub fn starnix_class(&self) -> Class {
219 self.get_or_create_class("starnix".into())
220 }
221
222 pub fn rtc_class(&self) -> Class {
226 self.get_or_create_class("rtc".into())
227 }
228
229 fn ensure_dir(&self, path: &[&FsStr]) -> Arc<SimpleDirectory> {
230 let fs = self.fs();
231 let mut dir = self.root.clone();
232 for component in path {
233 dir = dir.subdir(fs, component, 0o755);
234 }
235 dir
236 }
237
238 fn lookup_dir(&self, path: &[&FsStr]) -> Option<Arc<SimpleDirectory>> {
239 let mut dir = self.root.clone();
240 for component in path {
241 dir = dir.get_dir(component)?;
242 }
243 Some(dir)
244 }
245
246 pub fn get_or_create_bus(&self, name: &FsStr) -> Bus {
250 let name = name.to_owned();
251 let devices = self.ensure_dir(&[b"bus".into(), name.as_ref(), b"devices".into()]);
252 Bus::new(name, devices)
253 }
254
255 pub fn get_or_create_class(&self, name: &FsStr) -> Class {
259 let name = name.to_owned();
260 let devices = self.ensure_dir(&[b"class".into(), name.as_ref()]);
261 Class::new(name, devices)
262 }
263
264 pub fn class_with_dir(
265 &self,
266 name: &FsStr,
267 build_directory: impl FnOnce(&SimpleDirectoryMutator),
268 ) -> Class {
269 let class = self.get_or_create_class(name);
270 class.devices().edit(self.fs(), build_directory);
271 class
272 }
273
274 pub(super) fn create_device(
285 &self,
286 name: &FsStr,
287 parent: Option<Device>,
288 subsystem: Option<Subsystem>,
289 metadata: Option<DeviceMetadata>,
290 build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
291 ) -> Device {
292 let parent = match (parent, &subsystem) {
293 (None, Some(Subsystem::Class(_))) => Some(self.virtual_device()),
294 (parent, _) => parent,
295 };
296 let dir = if let Some(glue_dir) =
297 Device::glue_dir_name_for(parent.as_ref(), subsystem.as_ref())
298 {
299 let parent_dir = parent
300 .as_ref()
301 .and_then(Device::dir)
302 .expect("parent device directory exists in sysfs");
303 parent_dir.nested_subdir(self.fs(), glue_dir, 0o755, name, 0o755)
304 } else if let Some(parent) = parent.as_ref() {
305 let parent_dir = parent.dir().expect("parent device directory exists in sysfs");
306 parent_dir.subdir(self.fs(), name, 0o755)
307 } else {
308 self.ensure_dir(&[b"devices".into()]).subdir(self.fs(), name, 0o755)
309 };
310 let device = Device::new(name.to_owned(), parent, subsystem, metadata, dir.clone());
311 dir.edit(self.fs(), |mutator| {
312 build_directory(&device, mutator);
313 });
314 self.add(&device);
315 device
316 }
317
318 fn add(&self, device: &Device) {
319 let name = device.name();
320
321 match device.subsystem() {
323 Some(Subsystem::Bus(bus)) => {
324 bus.devices().edit(self.fs(), |dir| {
325 dir.symlink(name, device.path_from_depth(3).as_ref());
326 });
327 }
328 Some(Subsystem::Class(class)) => {
329 class.devices().edit(self.fs(), |dir| {
330 dir.symlink(name, device.path_from_depth(2).as_ref());
331 });
332 }
333 None => {}
334 }
335
336 if let Some(metadata) = device.metadata() {
337 let device_number = FsString::from(metadata.devt.to_string());
338 let dev_subdir: &FsStr = match metadata.mode {
339 DeviceMode::Block => {
340 self.ensure_dir(&[b"block".into()]).edit(self.fs(), |dir| {
341 dir.symlink(name, device.path_from_depth(1).as_ref());
342 });
343 b"block".into()
344 }
345 DeviceMode::Char => b"char".into(),
346 };
347 self.ensure_dir(&[b"dev".into(), dev_subdir]).edit(self.fs(), |dir| {
348 dir.symlink(device_number.as_ref(), device.path_from_depth(2).as_ref());
349 });
350 }
351 }
352
353 pub(super) fn remove(&self, device: &Device) {
360 let name = device.name();
361 if let Some(metadata) = device.metadata() {
363 let device_number: FsString = metadata.devt.to_string().into();
364 let dev_subdir: &FsStr = match metadata.mode {
365 DeviceMode::Block => {
366 if let Some(block_dir) = self.lookup_dir(&[b"block".into()]) {
367 block_dir.remove(name);
368 }
369 b"block".into()
370 }
371 DeviceMode::Char => b"char".into(),
372 };
373 if let Some(dev_dir) = self.lookup_dir(&[b"dev".into(), dev_subdir]) {
374 dev_dir.remove(device_number.as_ref());
375 }
376 }
377 match device.subsystem() {
378 Some(Subsystem::Bus(bus)) => {
379 bus.devices().remove(name);
380 }
381 Some(Subsystem::Class(class)) => {
382 class.devices().remove(name);
383 }
384 None => {}
385 }
386 if let Some(glue_dir) = device.glue_dir_name() {
388 if let Some(parent_dir) = device.parent().and_then(Device::dir) {
389 parent_dir.remove_from_subdir_if_empty(glue_dir, name);
390 }
391 } else if let Some(parent) = device.parent() {
392 if let Some(parent_dir) = parent.dir() {
393 parent_dir.remove(name);
394 }
395 } else if let Some(devices_dir) = self.lookup_dir(&[b"devices".into()]) {
396 devices_dir.remove(name);
397 }
398 }
399}
400
401impl Default for KObjectStore {
402 fn default() -> Self {
403 Self {
404 root: SimpleDirectory::new(),
405 fs: OnceLock::new(),
406 virtual_device: OnceLock::new(),
407 platform_device: OnceLock::new(),
408 system_device: OnceLock::new(),
409 soc_device: OnceLock::new(),
410 }
411 }
412}
413
414fn add_stub_attributes(dir: &SimpleDirectoryMutator, names: &[&str]) {
419 for name in names {
420 dir.entry(
421 name,
422 StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
423 mode!(IFREG, 0o444),
424 );
425 }
426}