Skip to content

Commit

Permalink
rust: generate bindings for helpers
Browse files Browse the repository at this point in the history
Automatically generate rust bindings for helper functions via bindgen.
The corresponding Rust bindings will have their `rust_helper_`
prefix removed, so Rust code can call `bindings::foo` regardless
whether `foo` is directly exposed or exposed via a helper.

When both a directly exposed symbol and a helper exists, the directly
exposed symbol will take precedence.

Signed-off-by: Gary Guo <gary@garyguo.net>
  • Loading branch information
nbdd0121 committed Jul 2, 2021
1 parent 92c2f34 commit 0588a2f
Show file tree
Hide file tree
Showing 17 changed files with 90 additions and 168 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -1839,6 +1839,7 @@ rustfmt:
-o -path $(abs_objtree)/rust/test -prune \
| grep -Fv $(abs_srctree)/rust/alloc \
| grep -Fv $(abs_objtree)/rust/test \
| grep -Fv generated \
| xargs $(RUSTFMT) $(rustfmt_flags)

rustfmtcheck: rustfmt_flags = --check
Expand Down
1 change: 1 addition & 0 deletions rust/.gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: GPL-2.0

bindings_generated.rs
bindings_helpers_generated.rs
exports_*_generated.h
doc/
test/
31 changes: 24 additions & 7 deletions rust/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ extra-$(CONFIG_RUST) += exports_core_generated.h

extra-$(CONFIG_RUST) += libmacros.so

extra-$(CONFIG_RUST) += bindings_generated.rs
extra-$(CONFIG_RUST) += bindings_generated.rs bindings_helpers_generated.rs
obj-$(CONFIG_RUST) += alloc.o kernel.o
extra-$(CONFIG_RUST) += exports_alloc_generated.h exports_kernel_generated.h

Expand All @@ -30,7 +30,7 @@ endif

quiet_cmd_rustdoc = RUSTDOC $(if $(rustdoc_host),H, ) $<
cmd_rustdoc = \
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
OBJTREE=$(abspath $(objtree)) \
$(RUSTDOC) $(if $(rustdoc_host),,$(rustc_cross_flags)) \
$(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rustc_flags))) \
$(rustc_target_flags) -L $(objtree)/rust \
Expand Down Expand Up @@ -73,12 +73,13 @@ rustdoc-kernel: private rustc_target_flags = --extern alloc \
--extern macros=$(objtree)/rust/libmacros.so
rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-core \
rustdoc-macros rustdoc-compiler_builtins rustdoc-alloc \
$(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE
$(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs \
$(objtree)/rust/bindings_helpers_generated.rs FORCE
$(call if_changed,rustdoc)

quiet_cmd_rustc_test_library = RUSTC TL $<
cmd_rustc_test_library = \
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
OBJTREE=$(abspath $(objtree)) \
$(RUSTC) $(filter-out --sysroot=%, $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rustc_flags)))) \
$(rustc_target_flags) --crate-type $(if $(rustc_test_library_proc),proc-macro,rlib) \
--out-dir $(objtree)/rust/test/ --cfg testlib \
Expand Down Expand Up @@ -107,7 +108,7 @@ quiet_cmd_rustdoc_test = RUSTDOC T $<
# so for the moment we skip `-Cpanic=abort`.
quiet_cmd_rustc_test = RUSTC T $<
cmd_rustc_test = \
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
OBJTREE=$(abspath $(objtree)) \
$(RUSTC) --test $(filter-out --sysroot=%, $(filter-out -Cpanic=abort, $(filter-out --emit=%, $(rustc_flags)))) \
$(rustc_target_flags) --out-dir $(objtree)/rust/test \
--sysroot $(objtree)/rust/test/sysroot \
Expand Down Expand Up @@ -228,6 +229,21 @@ $(objtree)/rust/bindings_generated.rs: $(srctree)/rust/kernel/bindings_helper.h
$(srctree)/rust/bindgen_parameters FORCE
$(call if_changed_dep,bindgen)

quiet_cmd_bindgen_helper = BINDGEN $@
cmd_bindgen_helper = \
$(BINDGEN) $< --blacklist-type '.*' --whitelist-var '' \
--whitelist-function 'rust_helper_.*' \
--use-core --with-derive-default --ctypes-prefix c_types \
--no-debug '.*' \
--size_t-is-usize -o $@ -- $(bindgen_c_flags_final) \
-I$(objtree)/rust/ -DMODULE; \
sed -Ei 's/pub fn rust_helper_([a-zA-Z0-9_]*)/\#[link_name="rust_helper_\1"]\n pub fn \1/g' $@

CFLAGS_helpers.o = -I$(objtree)/rust/

$(objtree)/rust/bindings_helpers_generated.rs: $(srctree)/rust/helpers.c FORCE
$(call if_changed_dep,bindgen_helper)

quiet_cmd_exports = EXPORTS $@
cmd_exports = \
$(NM) -p --defined-only $< \
Expand Down Expand Up @@ -265,7 +281,7 @@ $(objtree)/rust/libmacros.so: $(srctree)/rust/macros/lib.rs \

quiet_cmd_rustc_library = $(if $(skip_clippy),RUSTC,$(RUSTC_OR_CLIPPY_QUIET)) L $@
cmd_rustc_library = \
RUST_BINDINGS_FILE=$(abspath $(objtree)/rust/bindings_generated.rs) \
OBJTREE=$(abspath $(objtree)) \
$(if $(skip_clippy),$(RUSTC),$(RUSTC_OR_CLIPPY)) \
$(rustc_flags) $(rustc_cross_flags) $(rustc_target_flags) \
--crate-type rlib --out-dir $(objtree)/rust/ -L $(objtree)/rust/ \
Expand Down Expand Up @@ -303,7 +319,8 @@ $(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \
--extern macros=$(objtree)/rust/libmacros.so
$(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \
$(objtree)/rust/build_error.o \
$(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE
$(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs \
$(objtree)/rust/bindings_helpers_generated.rs FORCE
$(call if_changed_dep,rustc_library)

# Targets that need to expand twice
Expand Down
13 changes: 7 additions & 6 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ unsigned long rust_helper_clear_user(void __user *to, unsigned long n)
return clear_user(to, n);
}

void rust_helper_spin_lock_init(spinlock_t *lock, const char *name,
void rust_helper___spin_lock_init(spinlock_t *lock, const char *name,
struct lock_class_key *key)
{
#ifdef CONFIG_DEBUG_SPINLOCK
Expand All @@ -41,7 +41,7 @@ void rust_helper_spin_lock_init(spinlock_t *lock, const char *name,
spin_lock_init(lock);
#endif
}
EXPORT_SYMBOL_GPL(rust_helper_spin_lock_init);
EXPORT_SYMBOL_GPL(rust_helper___spin_lock_init);

void rust_helper_spin_lock(spinlock_t *lock)
{
Expand Down Expand Up @@ -103,22 +103,23 @@ size_t rust_helper_copy_to_iter(const void *addr, size_t bytes, struct iov_iter
}
EXPORT_SYMBOL_GPL(rust_helper_copy_to_iter);

bool rust_helper_is_err(__force const void *ptr)
bool rust_helper_IS_ERR(__force const void *ptr)
{
return IS_ERR(ptr);
}
EXPORT_SYMBOL_GPL(rust_helper_is_err);
EXPORT_SYMBOL_GPL(rust_helper_IS_ERR);

long rust_helper_ptr_err(__force const void *ptr)
long rust_helper_PTR_ERR(__force const void *ptr)
{
return PTR_ERR(ptr);
}
EXPORT_SYMBOL_GPL(rust_helper_ptr_err);
EXPORT_SYMBOL_GPL(rust_helper_PTR_ERR);

const char *rust_helper_errname(int err)
{
return errname(err);
}
EXPORT_SYMBOL_GPL(rust_helper_errname);

void rust_helper_mutex_lock(struct mutex *lock)
{
Expand Down
24 changes: 21 additions & 3 deletions rust/kernel/bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,37 @@
#![cfg_attr(test, allow(deref_nullptr))]
#![cfg_attr(test, allow(unaligned_references))]
#![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))]

#[allow(
#![allow(
clippy::all,
non_camel_case_types,
non_upper_case_globals,
non_snake_case,
improper_ctypes,
unsafe_op_in_unsafe_fn
)]

mod bindings_raw {
// Use glob import here to expose all helpers.
// Symbols defined within the module will take precedence to the glob import.
pub use super::bindings_helper::*;
use crate::c_types;
include!(concat!(env!("OBJTREE"), "/rust/bindings_generated.rs"));
}

// When both a directly exposed symbol and a helper exists for the same function,
// the directly exposed symbol is preferred and the helper becomes dead code, so
// ignore the warning here.
#[allow(dead_code)]
mod bindings_helper {
// Import the generated bindings for types.
use super::bindings_raw::*;
use crate::c_types;
include!(env!("RUST_BINDINGS_FILE"));
include!(concat!(
env!("OBJTREE"),
"/rust/bindings_helpers_generated.rs"
));
}

pub use bindings_raw::*;

pub const GFP_KERNEL: gfp_t = BINDINGS_GFP_KERNEL;
Expand Down
25 changes: 7 additions & 18 deletions rust/kernel/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,8 @@ impl Error {

impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
extern "C" {
fn rust_helper_errname(err: c_types::c_int) -> *const c_types::c_char;
}
// SAFETY: FFI call.
let name = unsafe { rust_helper_errname(-self.0) };
let name = unsafe { bindings::errname(-self.0) };

if name.is_null() {
// Print out number if no name can be found.
Expand Down Expand Up @@ -199,7 +196,7 @@ where
/// ) -> c_types::c_int {
/// from_kernel_result! {
/// let ptr = devm_alloc(pdev)?;
/// rust_helper_platform_set_drvdata(pdev, ptr);
/// bindings::platform_set_drvdata(pdev, ptr);
/// Ok(0)
/// }
/// }
Expand Down Expand Up @@ -243,28 +240,20 @@ macro_rules! from_kernel_result {
// TODO: remove `dead_code` marker once an in-kernel client is available.
#[allow(dead_code)]
pub(crate) fn from_kernel_err_ptr<T>(ptr: *mut T) -> Result<*mut T> {
extern "C" {
#[allow(improper_ctypes)]
fn rust_helper_is_err(ptr: *const c_types::c_void) -> bool;

#[allow(improper_ctypes)]
fn rust_helper_ptr_err(ptr: *const c_types::c_void) -> c_types::c_long;
}

// CAST: casting a pointer to `*const c_types::c_void` is always valid.
let const_ptr: *const c_types::c_void = ptr.cast();
// SAFETY: the FFI function does not deref the pointer.
if unsafe { rust_helper_is_err(const_ptr) } {
if unsafe { bindings::IS_ERR(const_ptr) } {
// SAFETY: the FFI function does not deref the pointer.
let err = unsafe { rust_helper_ptr_err(const_ptr) };
// CAST: if `rust_helper_is_err()` returns `true`,
// then `rust_helper_ptr_err()` is guaranteed to return a
let err = unsafe { bindings::PTR_ERR(const_ptr) };
// CAST: if `IS_ERR()` returns `true`,
// then `PTR_ERR()` is guaranteed to return a
// negative value greater-or-equal to `-bindings::MAX_ERRNO`,
// which always fits in an `i16`, as per the invariant above.
// And an `i16` always fits in an `i32`. So casting `err` to
// an `i32` can never overflow, and is always valid.
//
// SAFETY: `rust_helper_is_err()` ensures `err` is a
// SAFETY: `IS_ERR()` ensures `err` is a
// negative value greater-or-equal to `-bindings::MAX_ERRNO`
return Err(unsafe { Error::from_kernel_errno_unchecked(err as i32) });
}
Expand Down
20 changes: 3 additions & 17 deletions rust/kernel/iov_iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,12 @@
//! C header: [`include/linux/uio.h`](../../../../include/linux/uio.h)
use crate::{
bindings, c_types,
bindings,
error::Error,
io_buffer::{IoBufferReader, IoBufferWriter},
Result,
};

extern "C" {
fn rust_helper_copy_to_iter(
addr: *const c_types::c_void,
bytes: usize,
i: *mut bindings::iov_iter,
) -> usize;

fn rust_helper_copy_from_iter(
addr: *mut c_types::c_void,
bytes: usize,
i: *mut bindings::iov_iter,
) -> usize;
}

/// Wraps the kernel's `struct iov_iter`.
///
/// # Invariants
Expand Down Expand Up @@ -70,7 +56,7 @@ impl IoBufferWriter for IovIter {
}

unsafe fn write_raw(&mut self, data: *const u8, len: usize) -> Result {
let res = unsafe { rust_helper_copy_to_iter(data as _, len, self.ptr) };
let res = unsafe { bindings::copy_to_iter(data as _, len, self.ptr) };
if res != len {
Err(Error::EFAULT)
} else {
Expand All @@ -85,7 +71,7 @@ impl IoBufferReader for IovIter {
}

unsafe fn read_raw(&mut self, out: *mut u8, len: usize) -> Result {
let res = unsafe { rust_helper_copy_from_iter(out as _, len, self.ptr) };
let res = unsafe { bindings::copy_from_iter(out as _, len, self.ptr) };
if res != len {
Err(Error::EFAULT)
} else {
Expand Down
20 changes: 3 additions & 17 deletions rust/kernel/pages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,6 @@ use crate::{
};
use core::{marker::PhantomData, ptr};

extern "C" {
#[allow(improper_ctypes)]
fn rust_helper_alloc_pages(
gfp_mask: bindings::gfp_t,
order: c_types::c_uint,
) -> *mut bindings::page;

#[allow(improper_ctypes)]
fn rust_helper_kmap(page: *mut bindings::page) -> *mut c_types::c_void;

#[allow(improper_ctypes)]
fn rust_helper_kunmap(page: *mut bindings::page);
}

/// A set of physical pages.
///
/// `Pages` holds a reference to a set of pages of order `ORDER`. Having the order as a generic
Expand All @@ -42,7 +28,7 @@ impl<const ORDER: u32> Pages<ORDER> {
// TODO: Consider whether we want to allow callers to specify flags.
// SAFETY: This only allocates pages. We check that it succeeds in the next statement.
let pages = unsafe {
rust_helper_alloc_pages(
bindings::alloc_pages(
bindings::GFP_KERNEL | bindings::__GFP_ZERO | bindings::__GFP_HIGHMEM,
ORDER,
)
Expand Down Expand Up @@ -141,7 +127,7 @@ impl<const ORDER: u32> Pages<ORDER> {
let page = unsafe { self.pages.add(index) };

// SAFETY: `page` is valid based on the checks above.
let ptr = unsafe { rust_helper_kmap(page) };
let ptr = unsafe { bindings::kmap(page) };
if ptr.is_null() {
return None;
}
Expand Down Expand Up @@ -171,6 +157,6 @@ impl Drop for PageMapping<'_> {
fn drop(&mut self) {
// SAFETY: An instance of `PageMapping` is created only when `kmap` succeeded for the given
// page, so it is safe to unmap it here.
unsafe { rust_helper_kunmap(self.page) };
unsafe { bindings::kunmap(self.page) };
}
}
17 changes: 2 additions & 15 deletions rust/kernel/platdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,6 @@ pub struct Registration {
// (it is fine for multiple threads to have a shared reference to it).
unsafe impl Sync for Registration {}

extern "C" {
#[allow(improper_ctypes)]
fn rust_helper_platform_get_drvdata(
pdev: *const bindings::platform_device,
) -> *mut c_types::c_void;

#[allow(improper_ctypes)]
fn rust_helper_platform_set_drvdata(
pdev: *mut bindings::platform_device,
data: *mut c_types::c_void,
);
}

extern "C" fn probe_callback<P: PlatformDriver>(
pdev: *mut bindings::platform_device,
) -> c_types::c_int {
Expand All @@ -52,7 +39,7 @@ extern "C" fn probe_callback<P: PlatformDriver>(
let drv_data = drv_data.into_pointer() as *mut c_types::c_void;
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
unsafe {
rust_helper_platform_set_drvdata(pdev, drv_data);
bindings::platform_set_drvdata(pdev, drv_data);
}
Ok(0)
}
Expand All @@ -65,7 +52,7 @@ extern "C" fn remove_callback<P: PlatformDriver>(
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
let device_id = unsafe { (*pdev).id };
// SAFETY: `pdev` is guaranteed to be a valid, non-null pointer.
let ptr = unsafe { rust_helper_platform_get_drvdata(pdev) };
let ptr = unsafe { bindings::platform_get_drvdata(pdev) };
// SAFETY:
// - we allocated this pointer using `P::DrvData::into_pointer`,
// so it is safe to turn back into a `P::DrvData`.
Expand Down
10 changes: 1 addition & 9 deletions rust/kernel/rbtree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,6 @@ use core::{
ptr::{addr_of_mut, NonNull},
};

extern "C" {
fn rust_helper_rb_link_node(
node: *mut bindings::rb_node,
parent: *const bindings::rb_node,
rb_link: *mut *mut bindings::rb_node,
);
}

struct Node<K, V> {
links: bindings::rb_node,
key: K,
Expand Down Expand Up @@ -289,7 +281,7 @@ impl<K, V> RBTree<K, V> {
// "forgot" it with `Box::into_raw`.
// SAFETY: All pointers are non-null and valid (`*new_link` is null, but `new_link` is a
// mutable reference).
unsafe { rust_helper_rb_link_node(node_links, parent, new_link) };
unsafe { bindings::rb_link_node(node_links, parent, new_link) };

// SAFETY: All pointers are valid. `node` has just been inserted into the tree.
unsafe { bindings::rb_insert_color(node_links, &mut self.root) };
Expand Down
Loading

0 comments on commit 0588a2f

Please sign in to comment.