Skip to main content

starnix_core/fs/sysfs/
fs.rs

1// Copyright 2021 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::fs::sysfs::{build_kernel_directory, build_power_directory};
6use crate::task::{CurrentTask, Kernel};
7use crate::vfs::pseudo::simple_directory::SimpleDirectoryMutator;
8use crate::vfs::pseudo::simple_file::BytesFile;
9use crate::vfs::pseudo::stub_empty_file::StubEmptyFile;
10use crate::vfs::{
11    CacheMode, DirEntry, FileSystem, FileSystemHandle, FileSystemOps, FileSystemOptions, FsStr,
12};
13use starnix_logging::{Level, bug_ref, track_stub_log};
14use starnix_types::vfs::default_statfs;
15use starnix_uapi::errors::Errno;
16use starnix_uapi::file_mode::mode;
17use starnix_uapi::{SYSFS_MAGIC, errno, statfs};
18
19struct SysFs;
20impl FileSystemOps for SysFs {
21    fn statfs(&self, _fs: &FileSystem, _current_task: &CurrentTask) -> Result<statfs, Errno> {
22        Ok(default_statfs(SYSFS_MAGIC))
23    }
24    fn name(&self) -> &'static FsStr {
25        "sysfs".into()
26    }
27}
28
29/// Reports lookups of missing `/sys` entries via `track_stub_log!`, to surface sysfs nodes that
30/// userspace expects but which Starnix does not (yet) provide.
31///
32/// Each distinct missing path is logged once, and counted in the kernel's Inspect `stubs` node.
33fn sysfs_not_found_handler(entry: &DirEntry, name: &FsStr) -> Errno {
34    let message = format!("Looking for {name} in {entry:?}");
35    track_stub_log!(Level::Warn, TODO("https://fxbug.dev/493488790"), &message);
36    errno!(ENOENT, message)
37}
38
39impl SysFs {
40    fn new_fs(kernel: &Kernel, options: FileSystemOptions) -> FileSystemHandle {
41        let fs =
42            FileSystem::new(kernel, CacheMode::Cached(kernel.fs_cache_config()), SysFs, options)
43                .expect("sysfs constructed with valid options");
44
45        fn empty_dir(_: &SimpleDirectoryMutator) {}
46
47        let registry = &kernel.device_registry;
48        let root = &registry.objects.root;
49        fs.create_root(fs.allocate_ino(), root.clone());
50        if kernel.features.log_sysfs_lookup_misses {
51            root.set_not_found_handler(sysfs_not_found_handler);
52        }
53        let dir = SimpleDirectoryMutator::new(fs.clone(), root.clone());
54
55        let dir_mode = 0o755;
56        dir.subdir("fs", dir_mode, |dir| {
57            dir.subdir("selinux", dir_mode, empty_dir);
58            dir.subdir("bpf", dir_mode, empty_dir);
59            dir.subdir("cgroup", dir_mode, empty_dir);
60            dir.subdir("fuse", dir_mode, |dir| {
61                dir.subdir("connections", dir_mode, empty_dir);
62            });
63            dir.subdir("pstore", dir_mode, empty_dir);
64        });
65
66        dir.subdir("block", dir_mode, empty_dir);
67
68        dir.subdir("bus", dir_mode, |dir| {
69            dir.subdir("platform", dir_mode, |dir| {
70                dir.subdir("drivers", dir_mode, |dir| {
71                    dir.entry(
72                        "trusty",
73                        StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
74                        mode!(IFREG, 0o444),
75                    );
76                });
77            });
78        });
79
80        dir.subdir("class", dir_mode, |dir| {
81            dir.subdir("net", dir_mode, empty_dir);
82        });
83        dir.subdir("dev", dir_mode, |dir| {
84            dir.subdir("char", dir_mode, empty_dir);
85            dir.subdir("block", dir_mode, empty_dir);
86        });
87        dir.subdir("firmware", dir_mode, |dir| {
88            dir.subdir("devicetree", dir_mode, |dir| {
89                dir.subdir("base", dir_mode, |dir| {
90                    dir.subdir("chosen", dir_mode, |dir| {
91                        dir.subdir("plat", dir_mode, |dir| {
92                            if let Some(device_tree) = &kernel.device_tree {
93                                if let Some(product_bytes) =
94                                    device_tree.root_node.find("plat").and_then(|n| {
95                                        n.get_property("product").map(|p| p.value.clone())
96                                    })
97                                {
98                                    let product_bytes = if product_bytes.len() >= 4 {
99                                        product_bytes.to_vec()
100                                    } else {
101                                        let mut padded_bytes = vec![0; 4];
102                                        let start = 4 - product_bytes.len();
103                                        padded_bytes[start..].copy_from_slice(&product_bytes);
104                                        padded_bytes
105                                    };
106                                    dir.entry(
107                                        "product",
108                                        BytesFile::new_node(product_bytes),
109                                        mode!(IFREG, 0o444),
110                                    );
111                                }
112                            }
113                        });
114                        dir.subdir("config", dir_mode, |dir| {
115                            dir.entry(
116                                "pcbcfg",
117                                StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
118                                mode!(IFREG, 0o444),
119                            );
120                        });
121                    });
122                    dir.subdir("firmware", dir_mode, |dir| {
123                        dir.subdir("android", 0o755, |dir| {
124                            dir.entry(
125                                "compatible",
126                                StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
127                                mode!(IFREG, 0o444),
128                            );
129                            dir.subdir("vbmeta", 0o755, |dir| {
130                                dir.entry(
131                                    "parts",
132                                    StubEmptyFile::new_node(bug_ref!(
133                                        "https://fxbug.dev/452096300"
134                                    )),
135                                    mode!(IFREG, 0o444),
136                                );
137                            });
138                        });
139                    });
140                    dir.subdir("mcu", dir_mode, |dir| {
141                        dir.entry(
142                            "board_type",
143                            BytesFile::new_node(b"starnix".to_vec()),
144                            mode!(IFREG, 0o444),
145                        );
146                    });
147                });
148            });
149        });
150
151        dir.subdir("kernel", dir_mode, |dir| {
152            build_kernel_directory(kernel, dir);
153        });
154
155        dir.subdir("power", 0o755, |dir| {
156            build_power_directory(kernel, dir);
157        });
158
159        dir.subdir("leds", dir_mode, |dir| {
160            dir.entry(
161                "leds",
162                StubEmptyFile::new_node(bug_ref!("https://fxbug.dev/452096300")),
163                mode!(IFREG, 0o444),
164            );
165        });
166
167        dir.subdir("module", dir_mode, empty_dir);
168
169        dir.subdir("devices", dir_mode, |dir| {
170            dir.subdir("leds", dir_mode, |_dir| {});
171            dir.subdir("virtual", dir_mode, |dir| {
172                dir.subdir("leds", dir_mode, |_dir| {});
173            });
174        });
175
176        fs
177    }
178}
179
180struct SysFsHandle(FileSystemHandle);
181
182pub fn sys_fs(
183    current_task: &CurrentTask,
184    _options: FileSystemOptions,
185) -> Result<FileSystemHandle, Errno> {
186    Ok(get_sysfs(current_task.kernel()))
187}
188
189pub fn get_sysfs(kernel: &Kernel) -> FileSystemHandle {
190    kernel
191        .expando
192        .get_or_init(|| SysFsHandle(SysFs::new_fs(kernel, FileSystemOptions::default())))
193        .0
194        .clone()
195}
196
197#[cfg(test)]
198mod tests {
199    use super::*;
200    use crate::task::KernelFeatures;
201    use crate::testing::{spawn_kernel_and_run, spawn_kernel_with_features_and_run};
202    use crate::vfs::MountInfo;
203    use std::sync::Arc;
204
205    #[::fuchsia::test]
206    async fn sysfs_registers_platform_and_soc_devices() {
207        spawn_kernel_and_run(async |current_task| {
208            let objects = &current_task.kernel().device_registry.objects;
209            let root = &objects.root;
210
211            let soc_uevent = root
212                .lookup("devices/platform/soc/uevent".into())
213                .expect("soc uevent node should exist");
214            // Calling soc_device() again should return the cached Device without replacing its
215            // sysfs entries.
216            let _soc = objects.soc_device();
217            let soc_uevent_after = root
218                .lookup("devices/platform/soc/uevent".into())
219                .expect("soc uevent node should still exist");
220            assert!(Arc::ptr_eq(&soc_uevent, &soc_uevent_after));
221
222            assert!(root.lookup("devices/platform/uevent".into()).is_some());
223            assert!(root.lookup("bus/platform/devices/soc".into()).is_some());
224        })
225        .await;
226    }
227
228    #[::fuchsia::test]
229    async fn test_log_sysfs_lookup_misses() {
230        for (log_sysfs_lookup_misses, name) in
231            [(false, "missing_when_disabled"), (true, "missing_when_enabled")]
232        {
233            spawn_kernel_with_features_and_run(
234                async move |current_task| {
235                    let sysfs = get_sysfs(current_task.kernel());
236                    let res = sysfs.root().component_lookup(
237                        current_task,
238                        &MountInfo::detached(),
239                        name.into(),
240                    );
241                    assert_eq!(res.unwrap_err(), errno!(ENOENT));
242                },
243                KernelFeatures { log_sysfs_lookup_misses, ..Default::default() },
244            )
245            .await;
246
247            let inspector =
248                starnix_logging::track_stub_lazy_node_callback().await.expect("lazy node callback");
249            let hierarchy = fuchsia_inspect::reader::read(&inspector).await.expect("read inspect");
250            let expected =
251                format!(r#"Looking for {name} in DirEntry {{ fs: "sysfs", path: "/" }}"#);
252            assert_eq!(hierarchy.get_child(&expected).is_some(), log_sysfs_lookup_misses);
253        }
254    }
255
256    #[::fuchsia::test]
257    async fn sysfs_registers_stub_devices() {
258        spawn_kernel_and_run(async |current_task| {
259            let root = &current_task.kernel().device_registry.objects.root;
260
261            // SoC identification device.
262            assert!(root.lookup("bus/soc/devices/soc0".into()).is_some());
263            assert!(root.lookup("devices/soc0/revision".into()).is_some());
264            assert!(root.lookup("devices/soc0/uevent".into()).is_some());
265
266            // Backing device info and power supply class.
267            assert!(root.lookup("class/bdi/0:80".into()).is_some());
268            assert!(root.lookup("devices/virtual/bdi/0:80/read_ahead_kb".into()).is_some());
269            assert!(root.lookup("class/power_supply".into()).is_some());
270            assert!(root.lookup("class/power_supply/battery".into()).is_none());
271        })
272        .await;
273    }
274
275    #[::fuchsia::test]
276    async fn sysfs_registers_cpu_devices() {
277        spawn_kernel_and_run(async |current_task| {
278            let root = &current_task.kernel().device_registry.objects.root;
279
280            assert!(root.lookup("devices/system/cpu/uevent".into()).is_some());
281            assert!(root.lookup("devices/system/cpu/online".into()).is_some());
282            assert!(root.lookup("devices/system/cpu/possible".into()).is_some());
283            assert!(root.lookup("devices/system/cpu/cpufreq".into()).is_some());
284            assert!(root.lookup("devices/system/cpu/cpu0/uevent".into()).is_some());
285            assert!(root.lookup("devices/system/cpu/cpu0/topology".into()).is_some());
286            assert!(root.lookup("bus/cpu/devices/cpu0".into()).is_some());
287        })
288        .await;
289    }
290}