[RFC PATCH 1/3] rust: implement wrapper for acpi_object

Gladyshev Ilya posted 3 patches 1 month, 3 weeks ago
There is a newer version of this series
[RFC PATCH 1/3] rust: implement wrapper for acpi_object
Posted by Gladyshev Ilya 1 month, 3 weeks ago
ACPI Object is represented via union on C-side. On Rust side, it's
zero-cost type wrapper for each ACPI Type, with individual methods for
getters and other interactions.

Signed-off-by: Gladyshev Ilya <foxido@foxido.dev>
---
 rust/kernel/acpi.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)

diff --git a/rust/kernel/acpi.rs b/rust/kernel/acpi.rs
index 9b8efa623130..0a164ca8cceb 100644
--- a/rust/kernel/acpi.rs
+++ b/rust/kernel/acpi.rs
@@ -63,3 +63,106 @@ macro_rules! acpi_device_table {
         $crate::module_device_table!("acpi", $module_table_name, $table_name);
     };
 }
+
+/// An ACPI object.
+///
+/// This structure represents the Rust abstraction for a C [`struct acpi_object`].
+/// You probably want to convert it into actual object type.
+///
+/// # Example
+/// ```
+/// # use kernel::prelude::*
+/// use kernel::acpi::{AcpiObject};
+///
+/// fn read_first_acpi_byte(obj: &AcpiObject) -> Result<u8> {
+///     if obj.type_id() != AcpiBuffer::ACPI_TYPE {
+///         return Err(EINVAL);
+///     }
+///
+///     let obj: &AcpiBuffer = obj.try_into()?;
+///
+///     Ok(obj.payload()[0])
+/// }
+/// ```
+#[repr(transparent)]
+pub struct AcpiObject(bindings::acpi_object);
+
+impl AcpiObject {
+    /// Returns object type (see `acpitypes.h`)
+    pub fn type_id(&self) -> u32 {
+        // SAFETY: `type` field is valid in all union variants
+        unsafe { self.0.type_ }
+    }
+}
+
+/// Generate AcpiObject subtype
+///
+/// For given subtype implements
+/// - `TryFrom<&AcpiObject> for &SubType` trait
+/// - unsafe try_from_unchecked() with same semantics, but without type check
+macro_rules! acpi_object_subtype {
+    ($subtype_name:ident <- ($acpi_type:ident, $field_name:ident, $union_type:ty)) => {
+        /// Wraps `acpi_object` subtype
+        #[repr(transparent)]
+        pub struct $subtype_name($union_type);
+
+        impl TryFrom<&AcpiObject> for &$subtype_name {
+            type Error = Error;
+
+            fn try_from(value: &AcpiObject) -> core::result::Result<Self, Self::Error> {
+                // SAFETY: type_ field present in all union types and is always valid
+                let real_type = unsafe { value.0.type_ };
+
+                if (real_type != $subtype_name::ACPI_TYPE) {
+                    return Err(EINVAL);
+                }
+
+                // SAFETY: We validated union subtype
+                Ok(unsafe {
+                    ::core::mem::transmute::<&$union_type, &$subtype_name>(&value.0.$field_name)
+                })
+            }
+        }
+
+        impl $subtype_name {
+            /// This ACPI type int value (see `acpitypes.h`)
+            pub const ACPI_TYPE: u32 = bindings::$acpi_type;
+
+            /// Converts AcpiObject reference into exact ACPI type wrapper
+            ///
+            /// # Safety
+            ///
+            /// Assumes that value is correct (`Self`) subtype
+            pub unsafe fn try_from_unchecked(value: &AcpiObject) -> &Self {
+                // SAFETY: Only unsafety comes from unchecked transformation and
+                // we transfered
+                unsafe {
+                    ::core::mem::transmute::<&$union_type, &$subtype_name>(&value.0.$field_name)
+                }
+            }
+        }
+    };
+}
+
+acpi_object_subtype!(AcpiInteger
+    <- (ACPI_TYPE_INTEGER, integer, bindings::acpi_object__bindgen_ty_1));
+acpi_object_subtype!(AcpiString
+    <- (ACPI_TYPE_STRING, string, bindings::acpi_object__bindgen_ty_2));
+acpi_object_subtype!(AcpiBuffer
+    <- (ACPI_TYPE_BUFFER, buffer, bindings::acpi_object__bindgen_ty_3));
+acpi_object_subtype!(AcpiPackage
+    <- (ACPI_TYPE_PACKAGE, package, bindings::acpi_object__bindgen_ty_4));
+acpi_object_subtype!(AcpiReference
+    <- (ACPI_TYPE_LOCAL_REFERENCE, reference, bindings::acpi_object__bindgen_ty_5));
+acpi_object_subtype!(AcpiProcessor
+    <- (ACPI_TYPE_PROCESSOR, processor, bindings::acpi_object__bindgen_ty_6));
+acpi_object_subtype!(AcpiPowerResource
+    <- (ACPI_TYPE_POWER, power_resource, bindings::acpi_object__bindgen_ty_7));
+
+impl AcpiBuffer {
+    /// Get Buffer's content
+    pub fn payload(&self) -> &[u8] {
+        // SAFETY: (pointer, length) indeed represents byte slice
+        unsafe { ::core::slice::from_raw_parts(self.0.pointer, self.0.length as usize) }
+    }
+}
-- 
2.51.1.dirty
Re: [RFC PATCH 1/3] rust: implement wrapper for acpi_object
Posted by Rafael J. Wysocki 1 month, 2 weeks ago
On Sun, Dec 21, 2025 at 7:24 PM Gladyshev Ilya <foxido@foxido.dev> wrote:
>
> ACPI Object is represented via union on C-side. On Rust side, it's
> zero-cost type wrapper for each ACPI Type, with individual methods for
> getters and other interactions.

This is ACPICA stuff though and switching over ACPICA to Rust any time
soon is rather unlikely.

Is this really needed on the Rust side?

> Signed-off-by: Gladyshev Ilya <foxido@foxido.dev>
> ---
>  rust/kernel/acpi.rs | 103 ++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 103 insertions(+)
>
> diff --git a/rust/kernel/acpi.rs b/rust/kernel/acpi.rs
> index 9b8efa623130..0a164ca8cceb 100644
> --- a/rust/kernel/acpi.rs
> +++ b/rust/kernel/acpi.rs
> @@ -63,3 +63,106 @@ macro_rules! acpi_device_table {
>          $crate::module_device_table!("acpi", $module_table_name, $table_name);
>      };
>  }
> +
> +/// An ACPI object.
> +///
> +/// This structure represents the Rust abstraction for a C [`struct acpi_object`].
> +/// You probably want to convert it into actual object type.
> +///
> +/// # Example
> +/// ```
> +/// # use kernel::prelude::*
> +/// use kernel::acpi::{AcpiObject};
> +///
> +/// fn read_first_acpi_byte(obj: &AcpiObject) -> Result<u8> {
> +///     if obj.type_id() != AcpiBuffer::ACPI_TYPE {
> +///         return Err(EINVAL);
> +///     }
> +///
> +///     let obj: &AcpiBuffer = obj.try_into()?;
> +///
> +///     Ok(obj.payload()[0])
> +/// }
> +/// ```
> +#[repr(transparent)]
> +pub struct AcpiObject(bindings::acpi_object);
> +
> +impl AcpiObject {
> +    /// Returns object type (see `acpitypes.h`)
> +    pub fn type_id(&self) -> u32 {
> +        // SAFETY: `type` field is valid in all union variants
> +        unsafe { self.0.type_ }
> +    }
> +}
> +
> +/// Generate AcpiObject subtype
> +///
> +/// For given subtype implements
> +/// - `TryFrom<&AcpiObject> for &SubType` trait
> +/// - unsafe try_from_unchecked() with same semantics, but without type check
> +macro_rules! acpi_object_subtype {
> +    ($subtype_name:ident <- ($acpi_type:ident, $field_name:ident, $union_type:ty)) => {
> +        /// Wraps `acpi_object` subtype
> +        #[repr(transparent)]
> +        pub struct $subtype_name($union_type);
> +
> +        impl TryFrom<&AcpiObject> for &$subtype_name {
> +            type Error = Error;
> +
> +            fn try_from(value: &AcpiObject) -> core::result::Result<Self, Self::Error> {
> +                // SAFETY: type_ field present in all union types and is always valid
> +                let real_type = unsafe { value.0.type_ };
> +
> +                if (real_type != $subtype_name::ACPI_TYPE) {
> +                    return Err(EINVAL);
> +                }
> +
> +                // SAFETY: We validated union subtype
> +                Ok(unsafe {
> +                    ::core::mem::transmute::<&$union_type, &$subtype_name>(&value.0.$field_name)
> +                })
> +            }
> +        }
> +
> +        impl $subtype_name {
> +            /// This ACPI type int value (see `acpitypes.h`)
> +            pub const ACPI_TYPE: u32 = bindings::$acpi_type;
> +
> +            /// Converts AcpiObject reference into exact ACPI type wrapper
> +            ///
> +            /// # Safety
> +            ///
> +            /// Assumes that value is correct (`Self`) subtype
> +            pub unsafe fn try_from_unchecked(value: &AcpiObject) -> &Self {
> +                // SAFETY: Only unsafety comes from unchecked transformation and
> +                // we transfered
> +                unsafe {
> +                    ::core::mem::transmute::<&$union_type, &$subtype_name>(&value.0.$field_name)
> +                }
> +            }
> +        }
> +    };
> +}
> +
> +acpi_object_subtype!(AcpiInteger
> +    <- (ACPI_TYPE_INTEGER, integer, bindings::acpi_object__bindgen_ty_1));
> +acpi_object_subtype!(AcpiString
> +    <- (ACPI_TYPE_STRING, string, bindings::acpi_object__bindgen_ty_2));
> +acpi_object_subtype!(AcpiBuffer
> +    <- (ACPI_TYPE_BUFFER, buffer, bindings::acpi_object__bindgen_ty_3));
> +acpi_object_subtype!(AcpiPackage
> +    <- (ACPI_TYPE_PACKAGE, package, bindings::acpi_object__bindgen_ty_4));
> +acpi_object_subtype!(AcpiReference
> +    <- (ACPI_TYPE_LOCAL_REFERENCE, reference, bindings::acpi_object__bindgen_ty_5));
> +acpi_object_subtype!(AcpiProcessor
> +    <- (ACPI_TYPE_PROCESSOR, processor, bindings::acpi_object__bindgen_ty_6));
> +acpi_object_subtype!(AcpiPowerResource
> +    <- (ACPI_TYPE_POWER, power_resource, bindings::acpi_object__bindgen_ty_7));
> +
> +impl AcpiBuffer {
> +    /// Get Buffer's content
> +    pub fn payload(&self) -> &[u8] {
> +        // SAFETY: (pointer, length) indeed represents byte slice
> +        unsafe { ::core::slice::from_raw_parts(self.0.pointer, self.0.length as usize) }
> +    }
> +}
> --
Re: [RFC PATCH 1/3] rust: implement wrapper for acpi_object
Posted by Gladyshev Ilya 1 month, 2 weeks ago
On 12/22/25 22:32, Rafael J. Wysocki wrote:
> On Sun, Dec 21, 2025 at 7:24 PM Gladyshev Ilya <foxido@foxido.dev> wrote:
>>
>> ACPI Object is represented via union on C-side. On Rust side, it's
>> zero-cost type wrapper for each ACPI Type, with individual methods for
>> getters and other interactions.
> 
> This is ACPICA stuff though and switching over ACPICA to Rust any time
> soon is rather unlikely.
> 
> Is this really needed on the Rust side?

At least acpi_buffer is needed, and for now, only that will be wrapped 
(I will remove other types).

Note that there are no actual types on the Rust side, only transparent 
wrappers around C acpi structs (for type safety).
Re: [RFC PATCH 1/3] rust: implement wrapper for acpi_object
Posted by Danilo Krummrich 1 month, 2 weeks ago
On Sun Dec 21, 2025 at 7:22 PM CET, Gladyshev Ilya wrote:
> +/// An ACPI object.
> +///
> +/// This structure represents the Rust abstraction for a C [`struct acpi_object`].
> +/// You probably want to convert it into actual object type.

I think this is a good place to link the corresponding types.

> +///
> +/// # Example
> +/// ```
> +/// # use kernel::prelude::*
> +/// use kernel::acpi::{AcpiObject};

Braces not needed.

> +///
> +/// fn read_first_acpi_byte(obj: &AcpiObject) -> Result<u8> {
> +///     if obj.type_id() != AcpiBuffer::ACPI_TYPE {
> +///         return Err(EINVAL);
> +///     }

Given the try_into() conversion below this check is unnecessary.

> +///     let obj: &AcpiBuffer = obj.try_into()?;
> +///
> +///     Ok(obj.payload()[0])
> +/// }
> +/// ```
> +#[repr(transparent)]
> +pub struct AcpiObject(bindings::acpi_object);
> +
> +impl AcpiObject {
> +    /// Returns object type (see `acpitypes.h`)
> +    pub fn type_id(&self) -> u32 {
> +        // SAFETY: `type` field is valid in all union variants

Here and in a lot of other places, please end with a period.

> +        unsafe { self.0.type_ }
> +    }
> +}
> +
> +/// Generate AcpiObject subtype
> +///
> +/// For given subtype implements
> +/// - `TryFrom<&AcpiObject> for &SubType` trait
> +/// - unsafe try_from_unchecked() with same semantics, but without type check
> +macro_rules! acpi_object_subtype {
> +    ($subtype_name:ident <- ($acpi_type:ident, $field_name:ident, $union_type:ty)) => {
> +        /// Wraps `acpi_object` subtype
> +        #[repr(transparent)]
> +        pub struct $subtype_name($union_type);
> +
> +        impl TryFrom<&AcpiObject> for &$subtype_name {
> +            type Error = Error;
> +
> +            fn try_from(value: &AcpiObject) -> core::result::Result<Self, Self::Error> {
> +                // SAFETY: type_ field present in all union types and is always valid
> +                let real_type = unsafe { value.0.type_ };
> +
> +                if (real_type != $subtype_name::ACPI_TYPE) {
> +                    return Err(EINVAL);
> +                }

This should just be

	if (value.type_id() != $subtype_name::ACPI_TYPE) {
	    return Err(EINVAL);
	}

> +
> +                // SAFETY: We validated union subtype

When writing safety comments, please read the safety documentation of the
corresponding function and try to cover all requirements listed as bullet
points.

> +                Ok(unsafe {
> +                    ::core::mem::transmute::<&$union_type, &$subtype_name>(&value.0.$field_name)
> +                })
> +            }
> +        }
> +
> +        impl $subtype_name {
> +            /// This ACPI type int value (see `acpitypes.h`)
> +            pub const ACPI_TYPE: u32 = bindings::$acpi_type;
> +
> +            /// Converts AcpiObject reference into exact ACPI type wrapper
> +            ///
> +            /// # Safety
> +            ///
> +            /// Assumes that value is correct (`Self`) subtype
> +            pub unsafe fn try_from_unchecked(value: &AcpiObject) -> &Self {

The name try_from_unchecked() implies that the function is fallible, but it
isn't. I suggest calling it something along the lines of cast_unchecked().

> +                // SAFETY: Only unsafety comes from unchecked transformation and
> +                // we transfered
> +                unsafe {
> +                    ::core::mem::transmute::<&$union_type, &$subtype_name>(&value.0.$field_name)
> +                }
> +            }
> +        }
> +    };
> +}
> +
> +acpi_object_subtype!(AcpiInteger
> +    <- (ACPI_TYPE_INTEGER, integer, bindings::acpi_object__bindgen_ty_1));
> +acpi_object_subtype!(AcpiString
> +    <- (ACPI_TYPE_STRING, string, bindings::acpi_object__bindgen_ty_2));
> +acpi_object_subtype!(AcpiBuffer
> +    <- (ACPI_TYPE_BUFFER, buffer, bindings::acpi_object__bindgen_ty_3));
> +acpi_object_subtype!(AcpiPackage
> +    <- (ACPI_TYPE_PACKAGE, package, bindings::acpi_object__bindgen_ty_4));
> +acpi_object_subtype!(AcpiReference
> +    <- (ACPI_TYPE_LOCAL_REFERENCE, reference, bindings::acpi_object__bindgen_ty_5));
> +acpi_object_subtype!(AcpiProcessor
> +    <- (ACPI_TYPE_PROCESSOR, processor, bindings::acpi_object__bindgen_ty_6));
> +acpi_object_subtype!(AcpiPowerResource
> +    <- (ACPI_TYPE_POWER, power_resource, bindings::acpi_object__bindgen_ty_7));
> +
> +impl AcpiBuffer {
> +    /// Get Buffer's content
> +    pub fn payload(&self) -> &[u8] {
> +        // SAFETY: (pointer, length) indeed represents byte slice
> +        unsafe { ::core::slice::from_raw_parts(self.0.pointer, self.0.length as usize) }
> +    }
> +}

What about the values of the other types? How are they accessed?

Also, I think it would be better to use a Deref impl rather than a method.
Re: [RFC PATCH 1/3] rust: implement wrapper for acpi_object
Posted by Gladyshev Ilya 1 month, 2 weeks ago
On 12/22/25 14:35, Danilo Krummrich wrote:
>> +acpi_object_subtype!(AcpiInteger
>> +    <- (ACPI_TYPE_INTEGER, integer, bindings::acpi_object__bindgen_ty_1));
>> +acpi_object_subtype!(AcpiString
>> +    <- (ACPI_TYPE_STRING, string, bindings::acpi_object__bindgen_ty_2));
>> +acpi_object_subtype!(AcpiBuffer
>> +    <- (ACPI_TYPE_BUFFER, buffer, bindings::acpi_object__bindgen_ty_3));
>> +acpi_object_subtype!(AcpiPackage
>> +    <- (ACPI_TYPE_PACKAGE, package, bindings::acpi_object__bindgen_ty_4));
>> +acpi_object_subtype!(AcpiReference
>> +    <- (ACPI_TYPE_LOCAL_REFERENCE, reference, bindings::acpi_object__bindgen_ty_5));
>> +acpi_object_subtype!(AcpiProcessor
>> +    <- (ACPI_TYPE_PROCESSOR, processor, bindings::acpi_object__bindgen_ty_6));
>> +acpi_object_subtype!(AcpiPowerResource
>> +    <- (ACPI_TYPE_POWER, power_resource, bindings::acpi_object__bindgen_ty_7));
>> +
>> +impl AcpiBuffer {
>> +    /// Get Buffer's content
>> +    pub fn payload(&self) -> &[u8] {
>> +        // SAFETY: (pointer, length) indeed represents byte slice
>> +        unsafe { ::core::slice::from_raw_parts(self.0.pointer, self.0.length as usize) }
>> +    }
>> +}
> 
> What about the values of the other types? How are they accessed?

I couldn't really decide between implementing all types or only the one 
needed... Probably, I should provide simple implementations for all the 
others, I will fix that.

> Also, I think it would be better to use a Deref impl rather than a method.

Wouldn't it be confusing to overload Deref on a non "pointer-like" type 
just for an implicit cast?

Overall, thank you for your patience and review. I will fix the other 
comments from both of your emails.
Re: [RFC PATCH 1/3] rust: implement wrapper for acpi_object
Posted by Danilo Krummrich 1 month, 2 weeks ago
On Mon Dec 22, 2025 at 10:47 PM CET, Gladyshev Ilya wrote:
> I couldn't really decide between implementing all types or only the one 
> needed... Probably, I should provide simple implementations for all the 
> others, I will fix that.

If they are not needed by any of the drivers you're aiming at, you should
probably just drop them.

> Wouldn't it be confusing to overload Deref on a non "pointer-like" type 
> just for an implicit cast?

What do you mean with overload Deref? What I mean is

	impl Deref for AcpiBuffer {
		type Target = [u8];

		[...]
	}

	impl Deref for AcpiInteger {
		type Target = u64;

		[...]
	}

	impl Deref for AcpiString {
		type Target = CStr;

		[...]
	}

etc.
Re: [RFC PATCH 1/3] rust: implement wrapper for acpi_object
Posted by Gladyshev Ilya 1 month, 2 weeks ago
On 12/23/25 01:44, Danilo Krummrich wrote:
> On Mon Dec 22, 2025 at 10:47 PM CET, Gladyshev Ilya wrote:
>> I couldn't really decide between implementing all types or only the one
>> needed... Probably, I should provide simple implementations for all the
>> others, I will fix that.
> 
> If they are not needed by any of the drivers you're aiming at, you should
> probably just drop them.

Ack.

>> Wouldn't it be confusing to overload Deref on a non "pointer-like" type
>> just for an implicit cast?
> 
> What do you mean with overload Deref? What I mean is
> 
> 	impl Deref for AcpiBuffer {
> 		type Target = [u8];
> 
> 		[...]
> 	}

I meant the same, just used strange terminology, sorry. If I understand 
it correctly, Deref trait gives you "overloaded" dereference operator as 
well as implicit coercion in many cases, and I don't know if I want them.

Personally, I prefer the explicit style like:
```
let a: &AcpiInteger = /* ... */;
call_func(/* u64: */ a.val())
```

rather than:

```
let a: &AcpiInteger = /* ... */;
call_func(/* u64: */ *a)
```

The former feels clearer to me; the latter gives me "smart pointer" 
vibes and feels a bit confusing, because there are no smart pointers.

That said, I'm not a Rust expert at all -- so if you believe this change 
is better, I'll implement it your way.