1use 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
29fn 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 = ®istry.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 = ¤t_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 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 = ¤t_task.kernel().device_registry.objects.root;
260
261 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 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 = ¤t_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}