Skip to content

Commit 4f87eb2

Browse files
BennoLossinhoshinolina
authored andcommitted
rust: init: add ..Zeroable::zeroed() syntax for zeroing all missing fields
Add the struct update syntax to the init macros, but only for `..Zeroable::zeroed()`. Adding this at the end of the struct initializer allows one to omit fields from the initializer, these fields will be initialized with 0x00 set to every byte. Only types that implement the `Zeroable` trait can utilize this. Suggested-by: Asahi Lina <[email protected]> Signed-off-by: Benno Lossin <[email protected]>
1 parent 4bee8a6 commit 4f87eb2

1 file changed

Lines changed: 185 additions & 9 deletions

File tree

rust/kernel/init.rs

Lines changed: 185 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -505,14 +505,18 @@ macro_rules! stack_try_pin_init {
505505
/// - Fields that you want to initialize in-place have to use `<-` instead of `:`.
506506
/// - In front of the initializer you can write `&this in` to have access to a [`NonNull<Self>`]
507507
/// pointer named `this` inside of the initializer.
508+
/// - Using struct update syntax one can place `..Zeroable::zeroed()` at the very end of the
509+
/// struct, this initializes every field with 0 and then runs all initializers specified in the
510+
/// body. This can only be done if [`Zeroable`] is implemented for the struct.
508511
///
509512
/// For instance:
510513
///
511514
/// ```rust
512515
/// # use kernel::pin_init;
513-
/// # use macros::pin_data;
516+
/// # use macros::{pin_data, Zeroable};
514517
/// # use core::{ptr::addr_of_mut, marker::PhantomPinned};
515518
/// #[pin_data]
519+
/// #[derive(Zeroable)]
516520
/// struct Buf {
517521
/// // `ptr` points into `buf`.
518522
/// ptr: *mut u8,
@@ -525,6 +529,10 @@ macro_rules! stack_try_pin_init {
525529
/// ptr: unsafe { addr_of_mut!((*this.as_ptr()).buf).cast() },
526530
/// pin: PhantomPinned,
527531
/// });
532+
/// pin_init!(Buf {
533+
/// buf: [1; 64],
534+
/// ..Zeroable::zeroed(),
535+
/// });
528536
/// ```
529537
///
530538
/// [`try_pin_init!`]: kernel::try_pin_init
@@ -536,11 +544,12 @@ macro_rules! pin_init {
536544
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
537545
$($fields:tt)*
538546
}) => {
539-
$crate::try_pin_init!(
547+
$crate::try_pin_init!(parse_zeroable_end:
540548
@this($($this)?),
541549
@typ($t $(::<$($generics),*>)?),
542550
@fields($($fields)*),
543551
@error(::core::convert::Infallible),
552+
@munch_fields($($fields)*),
544553
)
545554
};
546555
}
@@ -589,28 +598,76 @@ macro_rules! try_pin_init {
589598
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
590599
$($fields:tt)*
591600
}) => {
592-
$crate::try_pin_init!(
601+
$crate::try_pin_init!(parse_zeroable_end:
593602
@this($($this)?),
594603
@typ($t $(::<$($generics),*>)? ),
595604
@fields($($fields)*),
596605
@error($crate::error::Error),
606+
@munch_fields($($fields)*),
597607
)
598608
};
599609
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
600610
$($fields:tt)*
601611
}? $err:ty) => {
602-
$crate::try_pin_init!(
612+
$crate::try_pin_init!(parse_zeroable_end:
603613
@this($($this)?),
604614
@typ($t $(::<$($generics),*>)? ),
605615
@fields($($fields)*),
606616
@error($err),
617+
@munch_fields($($fields)*),
618+
)
619+
};
620+
(parse_zeroable_end:
621+
@this($($this:ident)?),
622+
@typ($t:ident $(::<$($generics:ty),*>)?),
623+
@fields($($fields:tt)*),
624+
@error($err:ty),
625+
@munch_fields(),
626+
) => {
627+
$crate::try_pin_init!(
628+
@this($($this)?),
629+
@typ($t $(::<$($generics),*>)?),
630+
@fields($($fields)*),
631+
@error($err),
632+
@zeroed(), // nothing means we do not zero unmentioned fields
607633
)
608634
};
635+
(parse_zeroable_end:
636+
@this($($this:ident)?),
637+
@typ($t:ident $(::<$($generics:ty),*>)?),
638+
@fields($($fields:tt)*),
639+
@error($err:ty),
640+
@munch_fields(..Zeroable::zeroed()),
641+
) => {
642+
$crate::try_pin_init!(
643+
@this($($this)?),
644+
@typ($t $(::<$($generics),*>)?),
645+
@fields($($fields)*),
646+
@error($err),
647+
@zeroed(()), // () means we zero unmentioned fields
648+
)
649+
};
650+
(parse_zeroable_end:
651+
@this($($this:ident)?),
652+
@typ($t:ident $(::<$($generics:ty),*>)?),
653+
@fields($($fields:tt)*),
654+
@error($err:ty),
655+
@munch_fields($ignore:tt $($rest:tt)*),
656+
) => {
657+
$crate::try_pin_init!(parse_zeroable_end:
658+
@this($($this)?),
659+
@typ($t $(::<$($generics),*>)?),
660+
@fields($($fields)*),
661+
@error($err),
662+
@munch_fields($($rest)*),
663+
)
664+
};
609665
(
610666
@this($($this:ident)?),
611667
@typ($t:ident $(::<$($generics:ty),*>)?),
612668
@fields($($fields:tt)*),
613669
@error($err:ty),
670+
@zeroed($($init_zeroed:expr)?),
614671
) => {{
615672
// We do not want to allow arbitrary returns, so we declare this type as the `Ok` return
616673
// type and shadow it later when we insert the arbitrary user code. That way there will be
@@ -628,6 +685,19 @@ macro_rules! try_pin_init {
628685
{
629686
// Shadow the structure so it cannot be used to return early.
630687
struct __InitOk;
688+
// If `$init_zeroed` is present, we should not error on unmentioned fields and
689+
// set the whole struct to zero first.
690+
//
691+
// For type inference reasons we do not use `init::zeroed`, but instead
692+
// write_bytes.
693+
$({
694+
// We need to ensure the type actually implements `Zeroable`.
695+
fn is_zeroable<T: Zeroable>(ptr: *mut T) {}
696+
is_zeroable(slot);
697+
// SAFETY: the type implements `Zeroable`.
698+
unsafe { ::core::ptr::write_bytes(slot, 0, 1) };
699+
$init_zeroed
700+
})?
631701
// Create the `this` so it can be referenced by the user inside of the
632702
// expressions creating the individual fields.
633703
$(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)?
@@ -666,7 +736,7 @@ macro_rules! try_pin_init {
666736
@data($data:ident),
667737
@slot($slot:ident),
668738
@guards($($guards:ident,)*),
669-
@munch_fields($(,)?),
739+
@munch_fields($(..Zeroable::zeroed())? $(,)?),
670740
) => {
671741
// Endpoint of munching, no fields are left. If execution reaches this point, all fields
672742
// have been initialized. Therefore we can now dismiss the guards by forgetting them.
@@ -730,6 +800,28 @@ macro_rules! try_pin_init {
730800
@munch_fields($($rest)*),
731801
);
732802
};
803+
(make_initializer:
804+
@slot($slot:ident),
805+
@type_name($t:ident),
806+
@munch_fields(..Zeroable::zeroed() $(,)?),
807+
@acc($($acc:tt)*),
808+
) => {
809+
// Endpoint, nothing more to munch, create the initializer. Without erroring on extra
810+
// fields. We want to have the same semantics as a struct initializer with struct update
811+
// syntax, so we create one first.
812+
// Since we are in the `if false` branch, this will never get executed. We abuse `slot` to
813+
// get the correct type inference here:
814+
unsafe {
815+
// We have to force zeroed to have the correct type.
816+
let mut zeroed = ::core::mem::zeroed();
817+
::core::ptr::write($slot, zeroed);
818+
zeroed = ::core::mem::zeroed();
819+
::core::ptr::write($slot, $t {
820+
$($acc)*
821+
..zeroed
822+
});
823+
}
824+
};
733825
(make_initializer:
734826
@slot($slot:ident),
735827
@type_name($t:ident),
@@ -793,11 +885,12 @@ macro_rules! init {
793885
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
794886
$($fields:tt)*
795887
}) => {
796-
$crate::try_init!(
888+
$crate::try_init!(parse_zeroable_end:
797889
@this($($this)?),
798890
@typ($t $(::<$($generics),*>)?),
799891
@fields($($fields)*),
800892
@error(::core::convert::Infallible),
893+
@munch_fields($($fields)*),
801894
)
802895
}
803896
}
@@ -840,28 +933,76 @@ macro_rules! try_init {
840933
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
841934
$($fields:tt)*
842935
}) => {
843-
$crate::try_init!(
936+
$crate::try_init!(parse_zeroable_end:
844937
@this($($this)?),
845938
@typ($t $(::<$($generics),*>)?),
846939
@fields($($fields)*),
847940
@error($crate::error::Error),
941+
@munch_fields($($fields)*),
848942
)
849943
};
850944
($(&$this:ident in)? $t:ident $(::<$($generics:ty),* $(,)?>)? {
851945
$($fields:tt)*
852946
}? $err:ty) => {
947+
$crate::try_init!(parse_zeroable_end:
948+
@this($($this)?),
949+
@typ($t $(::<$($generics),*>)?),
950+
@fields($($fields)*),
951+
@error($err),
952+
@munch_fields($($fields)*),
953+
)
954+
};
955+
(parse_zeroable_end:
956+
@this($($this:ident)?),
957+
@typ($t:ident $(::<$($generics:ty),*>)?),
958+
@fields($($fields:tt)*),
959+
@error($err:ty),
960+
@munch_fields(),
961+
) => {
853962
$crate::try_init!(
854963
@this($($this)?),
855964
@typ($t $(::<$($generics),*>)?),
856965
@fields($($fields)*),
857966
@error($err),
967+
@zeroed(), // Nothing means we do not zero unmentioned fields.
858968
)
859969
};
970+
(parse_zeroable_end:
971+
@this($($this:ident)?),
972+
@typ($t:ident $(::<$($generics:ty),*>)?),
973+
@fields($($fields:tt)*),
974+
@error($err:ty),
975+
@munch_fields(..Zeroable::zeroed()),
976+
) => {
977+
$crate::try_init!(
978+
@this($($this)?),
979+
@typ($t $(::<$($generics),*>)?),
980+
@fields($($fields)*),
981+
@error($err),
982+
@zeroed(()), // () means we zero unmentioned fields.
983+
)
984+
};
985+
(parse_zeroable_end:
986+
@this($($this:ident)?),
987+
@typ($t:ident $(::<$($generics:ty),*>)?),
988+
@fields($($fields:tt)*),
989+
@error($err:ty),
990+
@munch_fields($ignore:tt $($rest:tt)*),
991+
) => {
992+
$crate::try_init!(parse_zeroable_end:
993+
@this($($this)?),
994+
@typ($t $(::<$($generics),*>)?),
995+
@fields($($fields)*),
996+
@error($err),
997+
@munch_fields($($rest)*),
998+
)
999+
};
8601000
(
8611001
@this($($this:ident)?),
8621002
@typ($t:ident $(::<$($generics:ty),*>)?),
8631003
@fields($($fields:tt)*),
8641004
@error($err:ty),
1005+
@zeroed($($init_zeroed:expr)?),
8651006
) => {{
8661007
// We do not want to allow arbitrary returns, so we declare this type as the `Ok` return
8671008
// type and shadow it later when we insert the arbitrary user code. That way there will be
@@ -879,6 +1020,19 @@ macro_rules! try_init {
8791020
{
8801021
// Shadow the structure so it cannot be used to return early.
8811022
struct __InitOk;
1023+
// If `$init_zeroed` is present, we should not error on unmentioned fields and
1024+
// set the whole struct to zero first.
1025+
//
1026+
// For type inference reasons we do not use `init::zeroed`, but instead
1027+
// write_bytes.
1028+
$({
1029+
// We need to ensure the type actually implements `Zeroable`.
1030+
fn is_zeroable<T: Zeroable>(ptr: *mut T) {}
1031+
is_zeroable(slot);
1032+
// SAFETY: the type implements `Zeroable`.
1033+
unsafe { ::core::ptr::write_bytes(slot, 0, 1) };
1034+
$init_zeroed
1035+
})?
8821036
// Create the `this` so it can be referenced by the user inside of the
8831037
// expressions creating the individual fields.
8841038
$(let $this = unsafe { ::core::ptr::NonNull::new_unchecked(slot) };)?
@@ -915,7 +1069,7 @@ macro_rules! try_init {
9151069
(init_slot:
9161070
@slot($slot:ident),
9171071
@guards($($guards:ident,)*),
918-
@munch_fields( $(,)?),
1072+
@munch_fields($(..Zeroable::zeroed())? $(,)?),
9191073
) => {
9201074
// Endpoint of munching, no fields are left. If execution reaches this point, all fields
9211075
// have been initialized. Therefore we can now dismiss the guards by forgetting them.
@@ -982,7 +1136,29 @@ macro_rules! try_init {
9821136
(make_initializer:
9831137
@slot($slot:ident),
9841138
@type_name($t:ident),
985-
@munch_fields( $(,)?),
1139+
@munch_fields(..Zeroable::zeroed() $(,)?),
1140+
@acc($($acc:tt)*),
1141+
) => {
1142+
// Endpoint, nothing more to munch, create the initializer. Without erroring on extra
1143+
// fields. We want to have the same semantics as a struct initializer with struct update
1144+
// syntax, so we create one first.
1145+
// Since we are in the `if false` branch, this will never get executed. We abuse `slot` to
1146+
// get the correct type inference here:
1147+
unsafe {
1148+
// We have to force zeroed to have the correct type.
1149+
let mut zeroed = ::core::mem::zeroed();
1150+
::core::ptr::write($slot, zeroed);
1151+
zeroed = ::core::mem::zeroed();
1152+
::core::ptr::write($slot, $t {
1153+
$($acc)*
1154+
..zeroed
1155+
});
1156+
}
1157+
};
1158+
(make_initializer:
1159+
@slot($slot:ident),
1160+
@type_name($t:ident),
1161+
@munch_fields($(,)?),
9861162
@acc($($acc:tt)*),
9871163
) => {
9881164
// Endpoint, nothing more to munch, create the initializer.

0 commit comments

Comments
 (0)