Skip to content

Commit

Permalink
rust: map long to isize and char to u8
Browse files Browse the repository at this point in the history
The following FFI types are replaced compared to `core::ffi`:

1. `char` type is now always mapped to `u8`, since kernel uses
   `-funsigned-char` on the C code. `core::ffi` maps it to platform
   default ABI, which can be either signed or unsigned.

2. `long` is now always mapped to `isize`. It's very common in the
   kernel to use `long` to represent a pointer-sized integer, and in
   fact `intptr_t` is a typedef of `long` in the kernel. Enforce this
   mapping rather than mapping to `i32/i64` depending on platform can
   save us a lot of unnecessary casts.

Signed-off-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Link: https://lore.kernel.org/r/20240913213041.395655-5-gary@garyguo.net
[ Moved `uaccess` changes from the next commit, since they were
  irrefutable patterns that Rust >= 1.82.0 warns about. Reworded
  slightly and reformatted a few documentation comments. Rebased on
  top of `rust-next`. - Miguel ]
Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
  • Loading branch information
nbdd0121 authored and ojeda committed Nov 10, 2024
1 parent d072acd commit dfcbb0c
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 26 deletions.
37 changes: 36 additions & 1 deletion rust/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,39 @@

#![no_std]

pub use core::ffi::*;
macro_rules! alias {
($($name:ident = $ty:ty;)*) => {$(
#[allow(non_camel_case_types, missing_docs)]
pub type $name = $ty;

// Check size compatibility with `core`.
const _: () = assert!(
core::mem::size_of::<$name>() == core::mem::size_of::<core::ffi::$name>()
);
)*}
}

alias! {
// `core::ffi::c_char` is either `i8` or `u8` depending on architecture. In the kernel, we use
// `-funsigned-char` so it's always mapped to `u8`.
c_char = u8;

c_schar = i8;
c_uchar = u8;

c_short = i16;
c_ushort = u16;

c_int = i32;
c_uint = u32;

// In the kernel, `intptr_t` is defined to be `long` in all platforms, so we can map the type to
// `isize`.
c_long = isize;
c_ulong = usize;

c_longlong = i64;
c_ulonglong = u64;
}

pub use core::ffi::c_void;
5 changes: 1 addition & 4 deletions rust/kernel/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,8 @@ impl Error {

/// Returns the error encoded as a pointer.
pub fn to_ptr<T>(self) -> *mut T {
#[cfg_attr(target_pointer_width = "32", allow(clippy::useless_conversion))]
// SAFETY: `self.0` is a valid error due to its invariant.
unsafe {
bindings::ERR_PTR(self.0.get().into()) as *mut _
}
unsafe { bindings::ERR_PTR(self.0.get() as _) as *mut _ }
}

/// Returns a string representing the error, if one exists.
Expand Down
2 changes: 1 addition & 1 deletion rust/kernel/firmware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use core::ptr::NonNull;
/// One of the following: `bindings::request_firmware`, `bindings::firmware_request_nowarn`,
/// `bindings::firmware_request_platform`, `bindings::request_firmware_direct`.
struct FwFunc(
unsafe extern "C" fn(*mut *const bindings::firmware, *const i8, *mut bindings::device) -> i32,
unsafe extern "C" fn(*mut *const bindings::firmware, *const u8, *mut bindings::device) -> i32,
);

impl FwFunc {
Expand Down
27 changes: 7 additions & 20 deletions rust/kernel/uaccess.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{
alloc::Flags,
bindings,
error::Result,
ffi::{c_ulong, c_void},
ffi::c_void,
prelude::*,
transmute::{AsBytes, FromBytes},
};
Expand Down Expand Up @@ -224,13 +224,9 @@ impl UserSliceReader {
if len > self.length {
return Err(EFAULT);
}
let Ok(len_ulong) = c_ulong::try_from(len) else {
return Err(EFAULT);
};
// SAFETY: `out_ptr` points into a mutable slice of length `len_ulong`, so we may write
// SAFETY: `out_ptr` points into a mutable slice of length `len`, so we may write
// that many bytes to it.
let res =
unsafe { bindings::copy_from_user(out_ptr, self.ptr as *const c_void, len_ulong) };
let res = unsafe { bindings::copy_from_user(out_ptr, self.ptr as *const c_void, len) };
if res != 0 {
return Err(EFAULT);
}
Expand Down Expand Up @@ -259,9 +255,6 @@ impl UserSliceReader {
if len > self.length {
return Err(EFAULT);
}
let Ok(len_ulong) = c_ulong::try_from(len) else {
return Err(EFAULT);
};
let mut out: MaybeUninit<T> = MaybeUninit::uninit();
// SAFETY: The local variable `out` is valid for writing `size_of::<T>()` bytes.
//
Expand All @@ -272,7 +265,7 @@ impl UserSliceReader {
bindings::_copy_from_user(
out.as_mut_ptr().cast::<c_void>(),
self.ptr as *const c_void,
len_ulong,
len,
)
};
if res != 0 {
Expand Down Expand Up @@ -335,12 +328,9 @@ impl UserSliceWriter {
if len > self.length {
return Err(EFAULT);
}
let Ok(len_ulong) = c_ulong::try_from(len) else {
return Err(EFAULT);
};
// SAFETY: `data_ptr` points into an immutable slice of length `len_ulong`, so we may read
// SAFETY: `data_ptr` points into an immutable slice of length `len`, so we may read
// that many bytes from it.
let res = unsafe { bindings::copy_to_user(self.ptr as *mut c_void, data_ptr, len_ulong) };
let res = unsafe { bindings::copy_to_user(self.ptr as *mut c_void, data_ptr, len) };
if res != 0 {
return Err(EFAULT);
}
Expand All @@ -359,9 +349,6 @@ impl UserSliceWriter {
if len > self.length {
return Err(EFAULT);
}
let Ok(len_ulong) = c_ulong::try_from(len) else {
return Err(EFAULT);
};
// SAFETY: The reference points to a value of type `T`, so it is valid for reading
// `size_of::<T>()` bytes.
//
Expand All @@ -372,7 +359,7 @@ impl UserSliceWriter {
bindings::_copy_to_user(
self.ptr as *mut c_void,
(value as *const T).cast::<c_void>(),
len_ulong,
len,
)
};
if res != 0 {
Expand Down

0 comments on commit dfcbb0c

Please sign in to comment.