add safe wrapper to read union fields

This commit is contained in:
Jianfeng Jiang
2022-11-23 18:13:40 +08:00
parent 41b79cf823
commit 8ebaaa283a
13 changed files with 274 additions and 36 deletions

View File

@ -176,9 +176,7 @@ pub(crate) fn init() {
// set gate type to 1110: 64 bit Interrupt Gate, Present bit to 1, DPL to Ring 0
let p_low = (((p >> 16) & 0xFFFF) << 48) | (p & 0xFFFF);
let trap_entry_option: usize = 0b1000_1110_0000_0000;
let low = (trap_entry_option << 32)
| ((kcs.0 as usize) << 16)
| p_low;
let low = (trap_entry_option << 32) | ((kcs.0 as usize) << 16) | p_low;
let high = p >> 32;
unsafe {
IDT.entries[i] = [low, high];

View File

@ -33,3 +33,34 @@ macro_rules! offset_of {
field_offset
});
}
/// Get the offset of a field within an object as a pointer.
///
/// ```rust
/// #[repr(C)]
/// pub struct Foo {
/// first: u8,
/// second: u32,
/// }
/// let foo = &Foo {first: 0, second: 0};
/// assert!(value_offset!(foo) == (0 as *const Foo));
/// assert!(value_offset!(foo.first) == (0 as *const u8));
/// assert!(value_offset!(foo.second) == (4 as *const u32));
/// ```
#[macro_export]
macro_rules! value_offset {
($container:ident) => ({
let container_addr = &*$container as *const _;
let offset = 0 as *const _;
let _: bool = offset == container_addr;
offset
});
($container:ident.$($field:ident).*) => ({
let container_addr = &*$container as *const _;
// SAFETY: This is safe since we never access the field
let field_addr = unsafe {&($container.$($field).*)} as *const _;
let field_offset = (field_addr as usize- container_addr as usize) as *const _;
let _: bool = field_offset == field_addr;
field_offset
});
}

View File

@ -81,10 +81,7 @@ impl VmSpace {
let page_size = (range.end - range.start) / PAGE_SIZE;
let mut inner = self.memory_set.lock();
for i in 0..page_size {
let res = inner.unmap(start_va);
if res.is_err() {
return res;
}
inner.unmap(start_va)?;
start_va += PAGE_SIZE;
}
Ok(())
@ -224,6 +221,6 @@ impl TryFrom<u64> for VmPerm {
type Error = Error;
fn try_from(value: u64) -> Result<Self> {
VmPerm::from_bits(value as u8).ok_or_else(|| Error::InvalidVmpermBits)
VmPerm::from_bits(value as u8).ok_or(Error::InvalidVmpermBits)
}
}

View File

@ -7,7 +7,10 @@
use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{parse_macro_input, Attribute, Data, DataStruct, DeriveInput, Fields, Generics};
use syn::{
parse_macro_input, Attribute, Data, DataEnum, DataStruct, DataUnion, DeriveInput, Fields,
Generics,
};
#[proc_macro_derive(Pod)]
pub fn derive_pod(input_token: proc_macro::TokenStream) -> proc_macro::TokenStream {
@ -15,8 +18,8 @@ pub fn derive_pod(input_token: proc_macro::TokenStream) -> proc_macro::TokenStre
expand_derive_pod(input).into()
}
const ALLOWED_REPRS: [&'static str; 13] = [
"C", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "usize", "isize", "u128", "i128",
const ALLOWED_REPRS: [&'static str; 11] = [
"C", "u8", "i8", "u16", "i16", "u32", "i32", "u64", "i64", "usize", "isize",
];
fn expand_derive_pod(input: DeriveInput) -> TokenStream {
@ -25,7 +28,8 @@ fn expand_derive_pod(input: DeriveInput) -> TokenStream {
let generics = input.generics;
match input.data {
Data::Struct(data_struct) => impl_pod_for_struct(data_struct, generics, ident, attrs),
Data::Enum(..) | Data::Union(..) => impl_pod_for_enum_or_union(attrs, generics, ident),
Data::Union(data_union) => impl_pod_for_union(data_union, generics, ident, attrs),
Data::Enum(data_enum) => impl_pod_for_enum(data_enum, attrs, generics, ident),
}
}
@ -72,7 +76,45 @@ fn impl_pod_for_struct(
}
}
fn impl_pod_for_enum_or_union(
fn impl_pod_for_union(
data_union: DataUnion,
generics: Generics,
ident: Ident,
attrs: Vec<Attribute>,
) -> TokenStream {
if !has_valid_repr(attrs) {
panic!("{} has invalid repr to implement Pod", ident.to_string());
}
let fields = data_union.fields.named;
// deal with generics
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
let pod_where_predicates = fields
.into_iter()
.map(|field| {
let field_ty = field.ty;
quote! {
#field_ty: ::pod::Pod
}
})
.collect::<Vec<_>>();
// if where_clause is none, we should add a `where` word manually.
if where_clause.is_none() {
quote! {
#[automatically_derived]
unsafe impl #impl_generics ::pod::Pod #type_generics for #ident where #(#pod_where_predicates),* {}
}
} else {
quote! {
#[automatically_derived]
unsafe impl #impl_generics ::pod::Pod #type_generics for #ident #where_clause, #(#pod_where_predicates),* {}
}
}
}
fn impl_pod_for_enum(
data_enum: DataEnum,
attrs: Vec<Attribute>,
generics: Generics,
ident: Ident,
@ -84,6 +126,13 @@ fn impl_pod_for_enum_or_union(
);
}
// check variant
for variant in data_enum.variants {
if None == variant.discriminant {
panic!("Enum can only have fields like Variant=1");
}
}
// deal with generics
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
quote! {

View File

@ -1,6 +1,6 @@
#![no_std]
use core::{fmt::Debug, mem::MaybeUninit};
use core::mem::MaybeUninit;
/// A marker trait for plain old data (POD).
///
@ -16,7 +16,7 @@ use core::{fmt::Debug, mem::MaybeUninit};
/// # Safety
///
/// Marking a non-POD type as POD may cause undefined behaviors.
pub unsafe trait Pod: Copy + Sized + Debug {
pub unsafe trait Pod: Copy + Sized {
/// Creates a new instance of Pod type that is filled with zeroes.
fn new_zeroed() -> Self {
// SAFETY. An all-zero value of `T: Pod` is always valid.
@ -35,7 +35,8 @@ pub unsafe trait Pod: Copy + Sized + Debug {
/// Creates a new instance from the given bytes.
fn from_bytes(bytes: &[u8]) -> Self {
let mut new_self = Self::new_uninit();
new_self.as_bytes_mut().copy_from_slice(bytes);
let copy_len = new_self.as_bytes().len();
new_self.as_bytes_mut().copy_from_slice(&bytes[..copy_len]);
new_self
}