Skip to main content

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