From 6e56458e527aa304dd2e8af31fdea4b72f653017 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Sun, 9 Oct 2022 17:51:25 +0800 Subject: [PATCH] add derive procedural macro for pod --- src/Cargo.lock | 10 ++++++ src/Cargo.toml | 1 + src/kxos-frame-pod-derive/Cargo.toml | 14 ++++++++ src/kxos-frame-pod-derive/src/lib.rs | 49 ++++++++++++++++++++++++++++ src/kxos-std/Cargo.toml | 1 + src/kxos-std/src/lib.rs | 2 ++ src/kxos-std/src/memory/mod.rs | 8 ++--- src/kxos-std/src/syscall/futex.rs | 4 +-- src/kxos-std/src/syscall/readlink.rs | 4 +-- src/kxos-std/src/syscall/uname.rs | 13 ++++---- src/kxos-std/src/syscall/wait4.rs | 2 +- src/kxos-std/src/syscall/write.rs | 4 +-- src/kxos-std/src/syscall/writev.rs | 11 ++++--- 13 files changed, 101 insertions(+), 22 deletions(-) create mode 100644 src/kxos-frame-pod-derive/Cargo.toml create mode 100644 src/kxos-frame-pod-derive/src/lib.rs diff --git a/src/Cargo.lock b/src/Cargo.lock index 131828546..890bb973a 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -97,6 +97,15 @@ dependencies = [ "x86_64", ] +[[package]] +name = "kxos-frame-pod-derive" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "kxos-pci" version = "0.1.0" @@ -114,6 +123,7 @@ version = "0.1.0" dependencies = [ "bitflags", "kxos-frame", + "kxos-frame-pod-derive", "kxos-pci", "kxos-virtio", "lazy_static", diff --git a/src/Cargo.toml b/src/Cargo.toml index 46213d53d..b1e0c20a6 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -17,6 +17,7 @@ members = [ "kxos-pci", "kxos-virtio", "kxos-util", + "kxos-frame-pod-derive", ] [package.metadata.bootloader] diff --git a/src/kxos-frame-pod-derive/Cargo.toml b/src/kxos-frame-pod-derive/Cargo.toml new file mode 100644 index 000000000..fff519277 --- /dev/null +++ b/src/kxos-frame-pod-derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "kxos-frame-pod-derive" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = "1.0.90" diff --git a/src/kxos-frame-pod-derive/src/lib.rs b/src/kxos-frame-pod-derive/src/lib.rs new file mode 100644 index 000000000..bb0243c6b --- /dev/null +++ b/src/kxos-frame-pod-derive/src/lib.rs @@ -0,0 +1,49 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Data, DataStruct, DeriveInput, Fields}; + +#[proc_macro_derive(Pod)] +pub fn derive_pod(input_token: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input = parse_macro_input!(input_token as DeriveInput); + expand_derive_pod(input).into() +} + +fn expand_derive_pod(input: DeriveInput) -> TokenStream { + let ident = input.ident; + let fields = match input.data { + Data::Struct(DataStruct { fields, .. }) => match fields { + Fields::Named(fields_named) => fields_named.named, + Fields::Unnamed(fields_unnamed) => fields_unnamed.unnamed, + Fields::Unit => panic!("derive pod does not work for struct with unit field"), + }, + // Panic on compilation time if one tries to derive pod for enum or union. + // It may not be a good idea, but works now. + _ => panic!("derive pod only works for struct now."), + }; + + // deal with generics + let (impl_generics, type_generics, where_clause) = input.generics.split_for_impl(); + + let pod_where_predicates = fields + .into_iter() + .map(|field| { + let field_ty = field.ty; + quote! { + #field_ty: ::kxos_frame::Pod + } + }) + .collect::>(); + + // if where_clause is none, we should add a `where` word manually. + if where_clause.is_none() { + quote! { + #[automatically_derived] + unsafe impl #impl_generics ::kxos_frame::Pod #type_generics for #ident where #(#pod_where_predicates),* {} + } + } else { + quote! { + #[automatically_derived] + unsafe impl #impl_generics ::kxos_frame::Pod #type_generics for #ident #where_clause, #(#pod_where_predicates),* {} + } + } +} diff --git a/src/kxos-std/Cargo.toml b/src/kxos-std/Cargo.toml index 8aa13c562..9df4831b9 100644 --- a/src/kxos-std/Cargo.toml +++ b/src/kxos-std/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] kxos-frame = {path = "../kxos-frame"} +kxos-frame-pod-derive = {path = "../kxos-frame-pod-derive"} kxos-pci = {path="../kxos-pci"} kxos-virtio = {path="../kxos-virtio"} diff --git a/src/kxos-std/src/lib.rs b/src/kxos-std/src/lib.rs index ed14a6317..d40b2febd 100644 --- a/src/kxos-std/src/lib.rs +++ b/src/kxos-std/src/lib.rs @@ -20,6 +20,8 @@ mod memory; mod process; pub mod syscall; mod util; +#[macro_use] +extern crate kxos_frame_pod_derive; pub fn init() { driver::init(); diff --git a/src/kxos-std/src/memory/mod.rs b/src/kxos-std/src/memory/mod.rs index 38b1219c4..372c01ab5 100644 --- a/src/kxos-std/src/memory/mod.rs +++ b/src/kxos-std/src/memory/mod.rs @@ -33,7 +33,7 @@ pub fn load_elf_to_vm_space<'a>( } /// copy bytes from user space of current process. The bytes len is the len of dest. -pub fn copy_bytes_from_user(src: Vaddr, dest: &mut [u8]) { +pub fn read_bytes_from_user(src: Vaddr, dest: &mut [u8]) { let current = Process::current(); let vm_space = current .vm_space() @@ -42,7 +42,7 @@ pub fn copy_bytes_from_user(src: Vaddr, dest: &mut [u8]) { } /// copy val (Plain of Data type) from user space of current process. -pub fn copy_val_from_user(src: Vaddr) -> T { +pub fn read_val_from_user(src: Vaddr) -> T { let current = Process::current(); let vm_space = current .vm_space() @@ -60,10 +60,10 @@ pub fn write_bytes_to_user(dest: Vaddr, src: &[u8]) { } /// write val (Plain of Data type) to user space of current process. -pub fn write_val_to_user(dest: Vaddr, val: T) { +pub fn write_val_to_user(dest: Vaddr, val: &T) { let current = Process::current(); let vm_space = current .vm_space() .expect("[Internal error]Current should have vm space to write val to user"); - vm_space.write_val(dest, &val).expect("write val failed"); + vm_space.write_val(dest, val).expect("write val failed"); } diff --git a/src/kxos-std/src/syscall/futex.rs b/src/kxos-std/src/syscall/futex.rs index ff1e1e3d2..f3d57065f 100644 --- a/src/kxos-std/src/syscall/futex.rs +++ b/src/kxos-std/src/syscall/futex.rs @@ -1,4 +1,4 @@ -use crate::{memory::copy_val_from_user, syscall::SYS_FUTEX}; +use crate::{memory::read_val_from_user, syscall::SYS_FUTEX}; use super::SyscallResult; use alloc::{sync::Arc, vec::Vec}; @@ -340,7 +340,7 @@ impl FutexKey { pub fn load_val(&self) -> i32 { // FIXME: how to implement a atomic load? warn!("implement an atomic load"); - copy_val_from_user(self.0) + read_val_from_user(self.0) } pub fn addr(&self) -> Vaddr { diff --git a/src/kxos-std/src/syscall/readlink.rs b/src/kxos-std/src/syscall/readlink.rs index 07245f459..bbb443f27 100644 --- a/src/kxos-std/src/syscall/readlink.rs +++ b/src/kxos-std/src/syscall/readlink.rs @@ -4,7 +4,7 @@ use alloc::ffi::CString; use kxos_frame::{debug, vm::Vaddr}; use crate::{ - memory::{copy_bytes_from_user, write_bytes_to_user}, + memory::{read_bytes_from_user, write_bytes_to_user}, process::Process, syscall::SYS_READLINK, }; @@ -32,7 +32,7 @@ pub fn do_sys_readlink(filename_ptr: Vaddr, user_buf_ptr: Vaddr, user_buf_len: u let mut filename_buffer = [0u8; MAX_FILENAME_LEN]; let current = Process::current(); - copy_bytes_from_user(filename_ptr, &mut filename_buffer); + read_bytes_from_user(filename_ptr, &mut filename_buffer); let filename = CStr::from_bytes_until_nul(&filename_buffer).expect("Invalid filename"); debug!("filename = {:?}", filename); if filename == CString::new("/proc/self/exe").unwrap().as_c_str() { diff --git a/src/kxos-std/src/syscall/uname.rs b/src/kxos-std/src/syscall/uname.rs index a9a7c56e6..a62a10d18 100644 --- a/src/kxos-std/src/syscall/uname.rs +++ b/src/kxos-std/src/syscall/uname.rs @@ -5,10 +5,13 @@ use kxos_frame::{debug, vm::Vaddr}; use lazy_static::lazy_static; use crate::{ - memory::write_bytes_to_user, + memory::write_val_to_user, syscall::{SyscallResult, SYS_UNAME}, }; +// The values are used to fool glibc. Since glibc will check the version and os name +// We don't use lazy static to generate a static UTS_NAME is because lazy static will create a new struct, which does not inherent the trait.å + lazy_static! { /// used to fool glibc static ref SYS_NAME: CString = CString::new("Linux").unwrap(); @@ -31,6 +34,7 @@ lazy_static! { const UTS_FIELD_LEN: usize = 65; +#[derive(Debug, Clone, Copy, Pod)] #[repr(C)] struct UtsName { sysname: [u8; UTS_FIELD_LEN], @@ -71,11 +75,6 @@ pub fn do_sys_uname(old_uname_addr: Vaddr) -> usize { debug!("uts name size: {}", core::mem::size_of::()); debug!("uts name align: {}", core::mem::align_of::()); - write_bytes_to_user(old_uname_addr, &UTS_NAME.sysname); - write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN, &UTS_NAME.nodename); - write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN * 2, &UTS_NAME.release); - write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN * 3, &UTS_NAME.version); - write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN * 4, &UTS_NAME.machine); - write_bytes_to_user(old_uname_addr + UTS_FIELD_LEN * 5, &UTS_NAME.domainname); + write_val_to_user(old_uname_addr, &*UTS_NAME); 0 } diff --git a/src/kxos-std/src/syscall/wait4.rs b/src/kxos-std/src/syscall/wait4.rs index 6e0574fca..28ff3d434 100644 --- a/src/kxos-std/src/syscall/wait4.rs +++ b/src/kxos-std/src/syscall/wait4.rs @@ -17,7 +17,7 @@ pub fn sys_wait4(wait_pid: u64, exit_status_ptr: u64, wait_options: u64) -> Sysc let process_filter = ProcessFilter::from_wait_pid(wait_pid as _); let (return_pid, exit_code) = wait_child_exit(process_filter, wait_options); if return_pid != 0 && exit_status_ptr != 0 { - write_val_to_user(exit_status_ptr as _, exit_code); + write_val_to_user(exit_status_ptr as _, &exit_code); } SyscallResult::Return(return_pid as _) diff --git a/src/kxos-std/src/syscall/write.rs b/src/kxos-std/src/syscall/write.rs index ab85154e4..123c68436 100644 --- a/src/kxos-std/src/syscall/write.rs +++ b/src/kxos-std/src/syscall/write.rs @@ -1,7 +1,7 @@ use alloc::vec; use kxos_frame::{debug, info}; -use crate::{memory::copy_bytes_from_user, syscall::SYS_WRITE}; +use crate::{memory::read_bytes_from_user, syscall::SYS_WRITE}; use super::SyscallResult; @@ -14,7 +14,7 @@ pub fn sys_write(fd: u64, user_buf_ptr: u64, user_buf_len: u64) -> SyscallResult if fd == STDOUT || fd == STDERR { let mut buffer = vec![0u8; user_buf_len as usize]; - copy_bytes_from_user(user_buf_ptr as usize, &mut buffer); + read_bytes_from_user(user_buf_ptr as usize, &mut buffer); let content = alloc::str::from_utf8(buffer.as_slice()).expect("Invalid content"); // TODO: print content if fd == STDOUT { info!("Message from user mode: {:?}", content); diff --git a/src/kxos-std/src/syscall/writev.rs b/src/kxos-std/src/syscall/writev.rs index 77f101a31..fdec71e13 100644 --- a/src/kxos-std/src/syscall/writev.rs +++ b/src/kxos-std/src/syscall/writev.rs @@ -2,7 +2,7 @@ use alloc::vec; use kxos_frame::{debug, info, vm::Vaddr}; use crate::{ - memory::{copy_bytes_from_user, copy_val_from_user}, + memory::{read_bytes_from_user, read_val_from_user}, syscall::SYS_WRITEV, }; @@ -10,6 +10,8 @@ use super::SyscallResult; const IOVEC_MAX: usize = 256; +#[repr(C)] +#[derive(Debug, Clone, Copy, Pod)] pub struct IoVec { base: Vaddr, len: usize, @@ -27,12 +29,13 @@ pub fn do_sys_writev(fd: u64, io_vec_addr: Vaddr, io_vec_count: usize) -> usize debug!("io_vec_counter = 0x{:x}", io_vec_count); let mut write_len = 0; for i in 0..io_vec_count { - let base = copy_val_from_user::(io_vec_addr + i * 8); - let len = copy_val_from_user::(io_vec_addr + i * 8 + 8); + let io_vec = read_val_from_user::(io_vec_addr + i * 8); + let base = io_vec.base; + let len = io_vec.len; debug!("base = 0x{:x}", base); debug!("len = {}", len); let mut buffer = vec![0u8; len]; - copy_bytes_from_user(base, &mut buffer); + read_bytes_from_user(base, &mut buffer); let content = alloc::str::from_utf8(&buffer).unwrap(); write_len += len; if fd == 1 {