starnix_core/device/
kobject_store.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::device::DeviceMode;
6use crate::device::kobject::{Bus, Class, Device, DeviceMetadata};
7use crate::fs::sysfs::get_sysfs;
8use crate::task::Kernel;
9use crate::vfs::pseudo::simple_directory::{SimpleDirectory, SimpleDirectoryMutator};
10use crate::vfs::{FileSystemHandle, FsStr, FsString};
11use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked};
12use std::sync::{Arc, OnceLock};
13
14/// The owner of all the KObjects in sysfs.
15///
16/// This structure holds strong references to the KObjects that are visible in sysfs. These
17/// objects are organized into hierarchies that make it easier to implement sysfs.
18pub struct KObjectStore {
19    /// The root of the sysfs hierarchy.
20    pub root: Arc<SimpleDirectory>,
21
22    /// The sysfs filesystem in which the KObjects are stored.
23    fs: OnceLock<FileSystemHandle>,
24}
25
26impl KObjectStore {
27    pub fn init<L>(&self, locked: &mut Locked<L>, kernel: &Kernel)
28    where
29        L: LockEqualOrBefore<FileOpsCore>,
30    {
31        self.fs.set(get_sysfs(locked, kernel)).unwrap();
32    }
33
34    fn fs(&self) -> &FileSystemHandle {
35        self.fs.get().expect("sysfs should be initialized")
36    }
37
38    /// The virtual bus kobject where all virtual and pseudo devices are stored.
39    pub fn virtual_bus(&self) -> Bus {
40        let name: FsString = b"virtual".into();
41        let dir = self.ensure_dir(&[b"devices".into(), name.as_ref()]);
42        Bus::new(name, dir, None)
43    }
44
45    /// The device class used for virtual block devices.
46    pub fn virtual_block_class(&self) -> Class {
47        self.get_or_create_class("block".into(), self.virtual_bus())
48    }
49
50    /// The device class used for virtual thermal devices.
51    pub fn virtual_thermal_class(&self) -> Class {
52        self.get_or_create_class("thermal".into(), self.virtual_bus())
53    }
54
55    /// The device class used for virtual graphics devices.
56    pub fn graphics_class(&self) -> Class {
57        self.get_or_create_class("graphics".into(), self.virtual_bus())
58    }
59
60    /// The device class used for virtual input devices.
61    pub fn input_class(&self) -> Class {
62        self.get_or_create_class("input".into(), self.virtual_bus())
63    }
64
65    /// The device class used for virtual mem devices.
66    pub fn mem_class(&self) -> Class {
67        self.get_or_create_class("mem".into(), self.virtual_bus())
68    }
69
70    /// The device class used for virtual net devices.
71    pub fn net_class(&self) -> Class {
72        self.get_or_create_class("net".into(), self.virtual_bus())
73    }
74
75    /// The device class used for virtual misc devices.
76    pub fn misc_class(&self) -> Class {
77        self.get_or_create_class("misc".into(), self.virtual_bus())
78    }
79
80    /// The device class used for virtual tty devices.
81    pub fn tty_class(&self) -> Class {
82        self.get_or_create_class("tty".into(), self.virtual_bus())
83    }
84
85    /// The device class used for virtual dma_heap devices.
86    pub fn dma_heap_class(&self) -> Class {
87        self.get_or_create_class("dma_heap".into(), self.virtual_bus())
88    }
89
90    /// An incorrect device class.
91    ///
92    /// This class exposes the name "starnix" to userspace, which is incorrect. Instead, devices
93    /// should use a class that represents their usage rather than their implementation.
94    ///
95    /// This class exists because a number of devices incorrectly use this class. We should fix
96    /// those devices to report their proper class.
97    pub fn starnix_class(&self) -> Class {
98        self.get_or_create_class("starnix".into(), self.virtual_bus())
99    }
100
101    /// Real-time clock class.
102    ///
103    /// Becomes `/sys/class/rtc/...`.
104    pub fn rtc_class(&self) -> Class {
105        self.get_or_create_class("rtc".into(), self.virtual_bus())
106    }
107
108    fn ensure_dir(&self, path: &[&FsStr]) -> Arc<SimpleDirectory> {
109        let fs = self.fs();
110        let mut dir = self.root.clone();
111        for component in path {
112            dir = dir.subdir(fs, component, 0o755);
113        }
114        dir
115    }
116
117    fn edit_dir(&self, path: &[&FsStr], callback: impl FnOnce(&SimpleDirectoryMutator)) {
118        let dir = self.ensure_dir(path);
119        let mutator = SimpleDirectoryMutator::new(self.fs().clone(), dir);
120        callback(&mutator);
121    }
122
123    /// Get a bus by name.
124    ///
125    /// If the bus does not exist, this function will create it.
126    pub fn get_or_create_bus(&self, name: &FsStr) -> Bus {
127        let name = name.to_owned();
128        let dir = self.ensure_dir(&[b"devices".into(), name.as_ref()]);
129        let collection = self.ensure_dir(&[b"bus".into(), name.as_ref(), b"devices".into()]);
130        Bus::new(name, dir, Some(collection))
131    }
132
133    /// Get a class by name.
134    ///
135    /// If the bus does not exist, this function will create it.
136    pub fn get_or_create_class(&self, name: &FsStr, bus: Bus) -> Class {
137        let name = name.to_owned();
138        let dir = bus.dir.subdir(self.fs(), name.as_ref(), 0o755);
139        let collection = self.ensure_dir(&[b"class".into(), name.as_ref()]);
140        Class::new(name, dir, bus, collection)
141    }
142
143    pub fn class_with_dir(
144        &self,
145        name: &FsStr,
146        bus: Bus,
147        build_collection: impl FnOnce(&SimpleDirectoryMutator),
148    ) -> Class {
149        let class = self.get_or_create_class(name, bus);
150        let mutator = SimpleDirectoryMutator::new(self.fs().clone(), class.collection.clone());
151        build_collection(&mutator);
152        class
153    }
154
155    fn block(&self, callback: impl FnOnce(&SimpleDirectoryMutator)) {
156        self.edit_dir(&[b"block".into()], callback);
157    }
158
159    fn dev_block(&self, callback: impl FnOnce(&SimpleDirectoryMutator)) {
160        self.edit_dir(&[b"dev".into(), b"block".into()], callback);
161    }
162
163    fn dev_char(&self, callback: impl FnOnce(&SimpleDirectoryMutator)) {
164        self.edit_dir(&[b"dev".into(), b"char".into()], callback);
165    }
166
167    /// Create a device and add that device to the store.
168    ///
169    /// Rather than use this function directly, you should register your device with the
170    /// `DeviceRegistry`. The `DeviceRegistry` will create the KObject for the device as
171    /// part of the registration process.
172    ///
173    /// If you create the device yourself, userspace will not be able to instantiate the
174    /// device because the `DeviceType` will not be registered with the `DeviceRegistry`.
175    pub(super) fn create_device(
176        &self,
177        name: &FsStr,
178        metadata: Option<DeviceMetadata>,
179        class: Class,
180        build_directory: impl FnOnce(&Device, &SimpleDirectoryMutator),
181    ) -> Device {
182        let device = Device::new(name.to_owned(), class, metadata);
183        device.class.dir.edit(self.fs(), |dir| {
184            dir.subdir2(name, 0o755, |dir| {
185                build_directory(&device, dir);
186            });
187        });
188        self.add(&device);
189        device
190    }
191
192    fn add(&self, device: &Device) {
193        let class = &device.class;
194        let name = device.name.as_ref();
195
196        let up_device = device.path_from_depth(1);
197        let up_up_device = device.path_from_depth(2);
198
199        // Insert the newly created device into various views.
200        class.collection.edit(self.fs(), |dir| {
201            dir.symlink(name, up_up_device.as_ref());
202        });
203
204        if let Some(metadata) = &device.metadata {
205            let device_number = FsString::from(metadata.device_type.to_string());
206            match metadata.mode {
207                DeviceMode::Block => {
208                    self.block(|dir| dir.symlink(name, up_device.as_ref()));
209                    self.dev_block(|dir| {
210                        dir.symlink(device_number.as_ref(), up_up_device.as_ref());
211                    });
212                }
213                DeviceMode::Char => {
214                    self.dev_char(|dir| {
215                        dir.symlink(device_number.as_ref(), up_up_device.as_ref());
216                    });
217                }
218            }
219        }
220
221        if let Some(bus_collection) = &class.bus.collection {
222            bus_collection.edit(self.fs(), |dir| {
223                dir.symlink(name, device.path_from_depth(3).as_ref());
224            });
225        }
226    }
227
228    /// Destroy a device.
229    ///
230    /// This function removes the KObject for the device from the store.
231    ///
232    /// Most clients hold weak references to KObjects, which means those references will become
233    /// invalid shortly after this function is called.
234    pub(super) fn remove(&self, device: &Device) {
235        let name = device.name.as_ref();
236        // Remove the device from its views in the reverse order in which it was added.
237        if let Some(bus_collection) = &device.class.bus.collection {
238            bus_collection.remove(name);
239        }
240        if let Some(metadata) = &device.metadata {
241            let device_number: FsString = metadata.device_type.to_string().into();
242            match metadata.mode {
243                DeviceMode::Block => {
244                    self.dev_block(|dir| dir.remove(device_number.as_ref()));
245                    self.block(|dir| dir.remove(name));
246                }
247                DeviceMode::Char => {
248                    self.dev_char(|dir| dir.remove(device_number.as_ref()));
249                }
250            }
251        }
252        device.class.collection.remove(name);
253        // Finally, remove the device from the object store.
254        device.class.dir.remove(name);
255    }
256}
257
258impl Default for KObjectStore {
259    fn default() -> Self {
260        Self { root: SimpleDirectory::new(), fs: OnceLock::new() }
261    }
262}