Skip to content

Commit 34394d2

Browse files
committed
docs: full documentation of universal_overlay_handler.rs
Signed-off-by: Artie Poole <stuart.poole@canonical.com>
1 parent af2b305 commit 34394d2

1 file changed

Lines changed: 192 additions & 21 deletions

File tree

daemon/src/platforms/universal_components/universal_overlay_handler.rs

Lines changed: 192 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,49 +10,154 @@
1010
//
1111
// You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
1212

13+
//! Universal device tree overlay handler implementation.
14+
//!
15+
//! This module provides the [`UniversalOverlayHandler`] struct, which implements the
16+
//! [`OverlayHandler`] trait for generic device tree overlay management using the Linux
17+
//! configfs mechanism. It handles overlay application, removal, and status checking
18+
//! without vendor-specific logic.
19+
//!
20+
//! # Configfs Interface
21+
//!
22+
//! ## Overlay Directory Structure
23+
//!
24+
//! Overlays are managed in `/sys/kernel/config/device-tree/overlays/<handle>/`:
25+
//! ```text
26+
//! /sys/kernel/config/device-tree/overlays/my_overlay/
27+
//! ├── dtbo # Device tree blob (appears unused in current implementation)
28+
//! ├── path # Write overlay source path here to apply; read to verify application
29+
//! └── status # Read to check if overlay was applied successfully
30+
//!```
31+
//!
32+
//! # Overlay Application Flow
33+
//!
34+
//! 1. Create overlay directory in configfs
35+
//! 2. Write overlay source path to the `path` file
36+
//! 3. Verify overlay applied by checking both `path` and `status` files
37+
//!
38+
//! # Examples
39+
//!
40+
//! ```rust,no_run
41+
//! use daemon::platforms::universal_components::universal_overlay_handler::UniversalOverlayHandler;
42+
//! use daemon::platforms::platform::OverlayHandler;
43+
//! use std::path::Path;
44+
//!
45+
//! # fn example() -> Result<(), daemon::error::FpgadError> {
46+
//! let handler = platform_for_known_platform("universal").overlay_handler("my_overlay")?;
47+
//!
48+
//! // Apply overlay
49+
//! handler.apply_overlay(Path::new("/lib/firmware/design.dtbo"))?;
50+
//!
51+
//! // Check status
52+
//! let status = handler.status()?;
53+
//! println!("Overlay status: {}", status);
54+
//!
55+
//! // Remove overlay
56+
//! handler.remove_overlay()?;
57+
//! # Ok(())
58+
//! # }
59+
//! ```
60+
1361
use crate::config;
1462
use crate::error::FpgadError;
1563
use crate::platforms::platform::OverlayHandler;
1664
use crate::system_io::{fs_create_dir, fs_read, fs_remove_dir, fs_write};
1765
use log::{info, trace};
1866
use std::path::{Path, PathBuf};
1967

20-
/// Takes a handle and creates and stores an appropriate overlay_fs_path in this object.
21-
/// The overlay_fs_path is static apart from the handle associated with each
22-
/// device, overlay or bitstream, and so the handle is specified by the user here and the rest
23-
/// is fixed.
68+
/// Construct the configfs path for an overlay directory.
69+
///
70+
/// Creates the full path to an overlay's configfs directory by combining the
71+
/// base overlay control directory with the provided handle.
72+
/// See [config::OVERLAY_CONTROL_DIR] for information about the base path.
73+
///
74+
/// # Arguments
75+
///
76+
/// * `overlay_handle` - The overlay handle (directory name)
77+
///
78+
/// # Returns: `PathBuf`
79+
/// * Full path to overlay directory in configfs
80+
///
81+
/// # Examples
82+
///
83+
/// ```rust,no_run
84+
/// # use std::path::PathBuf;
85+
/// # fn construct_overlay_fs_path(overlay_handle: &str) -> PathBuf {
86+
/// # PathBuf::from("/sys/kernel/config/device-tree/overlays").join(overlay_handle)
87+
/// # }
88+
/// let path = construct_overlay_fs_path("my_overlay");
89+
/// assert!(path.to_string_lossy().contains("my_overlay"));
90+
/// ```
2491
fn construct_overlay_fs_path(overlay_handle: &str) -> PathBuf {
2592
let overlay_fs_path = PathBuf::from(config::OVERLAY_CONTROL_DIR).join(overlay_handle);
2693
trace!("overlay_fs_path will be {overlay_fs_path:?}");
2794
overlay_fs_path
2895
}
2996

30-
/// Stores the three relevant paths: source files for dtbo/bitstream and the overlayfs dir to which
31-
/// the dtbo path was written.
97+
/// Universal device tree overlay handler using Linux configfs.
98+
///
99+
/// This struct manages a single device tree overlay through the Linux configfs
100+
/// mechanism. It stores the overlay's configfs directory path and provides methods
101+
/// to apply, remove, and check the status of the overlay.
102+
///
103+
/// # Fields
104+
///
105+
/// * `overlay_fs_path` - Path to the overlay's directory in configfs
106+
/// (e.g., `/sys/kernel/config/device-tree/overlays/my_overlay`)
107+
///
108+
/// see [config::OVERLAY_CONTROL_DIR] for the base overlay control directory.
32109
#[derive(Debug)]
33110
pub struct UniversalOverlayHandler {
34111
/// The path which points to the overlay virtual filesystem's dir which contains
35112
/// `path`, `status` and `dtbo` virtual files for overlay control. `dtbo` appears unused?
36113
overlay_fs_path: PathBuf,
37114
}
38115

116+
/// Implementation of helper methods for UniversalOverlayHandler.
39117
impl UniversalOverlayHandler {
118+
/// Read the overlay status from the configfs `status` file.
119+
///
120+
/// Reads from the `<overlay_fs_path>/status` file and returns
121+
/// the status string with trailing newlines removed.
122+
///
123+
/// # Returns: `Result<String, FpgadError>`
124+
/// * `Ok(String)` - Status string (typically "applied" or empty)
125+
/// * `Err(FpgadError::IORead)` - Failed to read status file
40126
fn get_vfs_status(&self) -> Result<String, FpgadError> {
41127
let status_path = self.overlay_fs_path()?.join("status");
42128
trace!("Reading from {status_path:?}");
43129
fs_read(&status_path).map(|s| s.trim_end_matches('\n').to_string())
44130
}
45-
/// Read path from <overlay_fs_path>/path file and verify that what was meant to be applied
46-
/// was applied.
131+
132+
/// Read the overlay path from the configfs `path` file.
133+
///
134+
/// Reads from the `<overlay_fs_path>/path` file which contains
135+
/// the device tree path where the overlay was applied.
136+
///
137+
/// # Returns: `Result<PathBuf, FpgadError>`
138+
/// * `Ok(PathBuf)` - Device tree path from the path file
139+
/// * `Err(FpgadError::IORead)` - Failed to read path file
47140
fn get_vfs_path(&self) -> Result<PathBuf, FpgadError> {
48141
let path_path = self.overlay_fs_path()?.join("path");
49142
trace!("Reading from {path_path:?}");
50143
let path_string = fs_read(&path_path).map(|s| s.trim_end_matches('\n').to_string())?;
51144
Ok(PathBuf::from(path_string))
52145
}
53146

54-
/// When an overlay fails to be applied, it may show as "applied" status but the path will
55-
/// be empty. Therefore, this checks both match what is expected.
147+
/// Verify that an overlay was successfully applied.
148+
///
149+
/// Checks both the `path` and `status` files within `overlay_fs_path` to ensure that
150+
/// the overlay was correctly applied. Sometimes an overlay may show "applied" status
151+
/// but have an empty path, indicating a failure.
152+
///
153+
/// # Arguments
154+
///
155+
/// * `source_path_rel` - The source path that was written to apply the overlay
156+
///
157+
/// # Returns: `Result<(), FpgadError>`
158+
/// * `Ok(())` - Overlay successfully applied (path matches and status is "applied")
159+
/// * `Err(FpgadError::OverlayStatus)` - Path doesn't match or status not "applied"
160+
/// * `Err(FpgadError::IORead)` - Failed to read path or status file
56161
fn vfs_check_applied(&self, source_path_rel: &Path) -> Result<(), FpgadError> {
57162
let path_file_contents = &self.get_vfs_path()?;
58163
if path_file_contents.ends_with(source_path_rel) {
@@ -79,11 +184,32 @@ impl UniversalOverlayHandler {
79184
}
80185
}
81186

187+
/// Implementation of the OverlayHandler trait for UniversalOverlayHandler.
82188
impl OverlayHandler for UniversalOverlayHandler {
83-
/// Attempts to apply a device tree overlay which should trigger a firmware load.
84-
/// There are multiple ways to trigger a firmware load so this is not valid if the
85-
/// dtbo doesn't contain a firmware to load.
86-
/// Calls prepare_for_load to ensure paths are valid etc. beforehand.
189+
/// Apply a device tree overlay through configfs.
190+
///
191+
/// Creates the overlay directory in configfs, writes the overlay source path to the
192+
/// `path` file, and verifies successful application. This may trigger automatic
193+
/// firmware loading if the overlay specifies a bitstream.
194+
///
195+
/// # Arguments
196+
///
197+
/// * `source_path_rel` - Path to the overlay file (can be absolute or relative to firmware path)
198+
///
199+
/// # Returns: `Result<(), FpgadError>`
200+
/// * `Ok(())` - Overlay applied and verified successfully
201+
/// * `Err(FpgadError::Argument)` - Overlay with this handle already exists
202+
/// * `Err(FpgadError::IOCreate)` - Failed to create overlay directory
203+
/// * `Err(FpgadError::Internal)` - configfs didn't create `path` file (not mounted?)
204+
/// * `Err(FpgadError::IOWrite)` - Failed to write overlay path
205+
/// * `Err(FpgadError::OverlayStatus)` - Overlay didn't apply correctly
206+
///
207+
/// # Notes
208+
///
209+
/// - There are multiple ways device tree overlays can trigger firmware loading
210+
/// - This method is not valid if the dtbo doesn't contain firmware to load
211+
/// - The overlay directory must not already exist. [`OverlayHandler::remove_overlay`] can be called
212+
/// to remove it first.
87213
fn apply_overlay(&self, source_path_rel: &Path) -> Result<(), FpgadError> {
88214
let overlay_fs_path = self.overlay_fs_path()?;
89215
if overlay_fs_path.exists() {
@@ -115,20 +241,42 @@ impl OverlayHandler for UniversalOverlayHandler {
115241
self.vfs_check_applied(source_path_rel)
116242
}
117243

118-
/// Attempts to delete overlay_fs_path
244+
/// Remove a device tree overlay from configfs.
245+
///
246+
/// Removes the overlay directory from configfs, which deactivates the overlay and
247+
/// restores the original device tree state.
248+
///
249+
/// # Returns: `Result<(), FpgadError>`
250+
/// * `Ok(())` - Overlay directory removed successfully
251+
/// * `Err(FpgadError::IODelete)` - Failed to remove directory (not empty, doesn't exist, etc.)
119252
fn remove_overlay(&self) -> Result<(), FpgadError> {
120253
let overlay_fs_path = self.overlay_fs_path()?;
121254
fs_remove_dir(overlay_fs_path)
122255
}
123256

124-
/// WARNING NOT IMPLEMENTED:
125-
/// This is where the required fpga flags will be determined from the dtbo,
126-
/// such as compressed or encrypted.
257+
/// Get the required FPGA programming flags from the overlay.
258+
///
259+
/// # Warning: Not Implemented
260+
///
261+
/// This method is intentionally unimplemented for the universal overlay handler.
262+
/// In a platform specific implementation (i.e. a softener) this would implement
263+
/// logic to parse the overlay/relevant package and determine any required FPGA
264+
/// programming flags.
265+
///
266+
/// # Returns: `Result<isize, FpgadError>`
267+
/// * `Ok(0)` - Always returns 0 (no flags)
127268
fn required_flags(&self) -> Result<isize, FpgadError> {
128269
Ok(0)
129270
}
130271

131-
/// Read status from <overlay_fs_path>/status file and verify that it is "applied"
272+
/// Get the current status of the overlay.
273+
///
274+
/// Reads both the `path` and `status` files from configfs and returns a combined
275+
/// status string. If the overlay directory doesn't exist, returns "not present".
276+
///
277+
/// # Returns: `Result<String, FpgadError>`
278+
/// * `Ok(String)` - Status string (e.g., "/path/in/tree applied" or "not present")
279+
/// * `Err(FpgadError::IORead)` - Failed to read status or path file
132280
fn status(&self) -> Result<String, FpgadError> {
133281
if !self.overlay_fs_path()?.exists() {
134282
return Ok("not present".into());
@@ -138,14 +286,37 @@ impl OverlayHandler for UniversalOverlayHandler {
138286
Ok(format!("{path:?} {status}"))
139287
}
140288

141-
/// Checks that the overlay_fs_path is stored at time of call and returns it if so (unwraps Option into Result)
289+
/// Get the filesystem path to the overlay directory.
290+
///
291+
/// Returns the stored overlay configfs path.
292+
///
293+
/// # Returns: `Result<&Path, FpgadError>`
294+
/// * `Ok(&Path)` - Path to overlay directory in configfs
142295
fn overlay_fs_path(&self) -> Result<&Path, FpgadError> {
143296
Ok(self.overlay_fs_path.as_path())
144297
}
145298
}
146299

147300
impl UniversalOverlayHandler {
148-
/// Scans the package dir for required files
301+
/// Create a new UniversalOverlayHandler for the specified overlay.
302+
///
303+
/// Constructs the configfs path for the overlay but doesn't create the directory
304+
/// or validate its existence. Directory creation happens in [`apply_overlay`](OverlayHandler::apply_overlay).
305+
///
306+
/// # Arguments
307+
///
308+
/// * `overlay_handle` - The overlay handle (directory name in configfs)
309+
///
310+
/// # Returns: `Self`
311+
/// * New UniversalOverlayHandler instance
312+
///
313+
/// # Examples
314+
///
315+
/// ```rust,no_run
316+
/// # use daemon::platforms::platform::platform_for_known_platform;
317+
///
318+
/// let handler = platform_for_known_platform("universal").overlay_handler("my_overlay")?;
319+
/// ```
149320
pub(crate) fn new(overlay_handle: &str) -> Self {
150321
UniversalOverlayHandler {
151322
overlay_fs_path: construct_overlay_fs_path(overlay_handle),

0 commit comments

Comments
 (0)