Exposes kernel page size via ELF note (#934)

This commit is contained in:
Putta Khunchalee 2024-08-18 22:37:02 +07:00 committed by GitHub
parent 30b712b152
commit 0bd471b4c1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 238 additions and 71 deletions

View File

@ -1,39 +0,0 @@
use proc_macro2::TokenStream;
use quote::quote_spanned;
use syn::spanned::Spanned;
use syn::{Error, ItemFn};
pub fn transform(item: ItemFn) -> syn::Result<TokenStream> {
let span = item.span();
if item.sig.abi.is_some() {
return Err(Error::new(span, "expected a function without `extern`"));
} else if item.sig.asyncness.is_some() {
return Err(Error::new(span, "expected non-async function"));
} else if item.sig.constness.is_some() {
return Err(Error::new(span, "expected non-const function"));
} else if item.sig.generics.lt_token.is_some() {
return Err(Error::new(span, "expected non-generic function"));
} else if item.sig.variadic.is_some() {
return Err(Error::new(span, "expected non-variadic function"));
}
#[cfg(target_arch = "x86_64")]
let abi = "sysv64";
#[cfg(target_arch = "aarch64")]
let abi = "C";
let attrs = item.attrs;
let vis = item.vis;
let safety = item.sig.unsafety;
let name = item.sig.ident;
let args = item.sig.inputs;
let ret = item.sig.output;
let block = item.block;
Ok(quote_spanned! {span=>
#(#attrs)*
#vis #safety extern #abi fn #name(#args) #ret {
#block
}
})
}

131
src/macros/src/elf.rs Normal file
View File

@ -0,0 +1,131 @@
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::meta::ParseNestedMeta;
use syn::{parse_quote, Error, ItemStatic, LitInt, LitStr, Meta, StaticMutability, Type};
const OPT_SECTION: &str = "section";
const OPT_NAME: &str = "name";
const OPT_TY: &str = "ty";
pub fn transform_note(opts: Options, mut item: ItemStatic) -> syn::Result<TokenStream> {
// Forbid "used" and "link_section" attribute.
fn unsupported_attr(attr: impl ToTokens) -> syn::Result<TokenStream> {
Err(Error::new_spanned(attr, "unsupported attribute"))
}
for attr in &item.attrs {
match &attr.meta {
Meta::Path(p) => {
if p.is_ident("used") {
return unsupported_attr(p);
}
}
Meta::List(_) => {}
Meta::NameValue(a) => {
if a.path.is_ident("link_section") {
return unsupported_attr(&a.path);
}
}
}
}
// Disallow mutable.
if let StaticMutability::Mut(t) = &item.mutability {
return Err(Error::new_spanned(t, "mutable note is not supported"));
}
// Get section name.
let section = match opts.section {
Some(v) => v,
None => {
return Err(Error::new(
Span::call_site(),
format_args!("missing `{OPT_SECTION}` option"),
));
}
};
// Get namespace.
let mut name = match opts.name {
Some(raw) => {
let val = raw.value();
if val.contains('\0') {
return Err(Error::new_spanned(
raw,
"note name cannot contains NUL character",
));
}
val
}
None => {
return Err(Error::new(
Span::call_site(),
format_args!("missing `{OPT_NAME}` option"),
));
}
};
name.push('\0');
// Get type
let ty: u32 = match opts.ty {
Some(v) => v.base10_parse()?,
None => {
return Err(Error::new(
Span::call_site(),
format_args!("missing `{OPT_TY}` option"),
));
}
};
// Replace type.
let nlen = name.len();
let dlen = match item.ty.as_ref() {
Type::Array(arr) => match arr.elem.as_ref() {
Type::Path(elem) if elem.qself.is_none() && elem.path.is_ident("u8") => &arr.len,
t => return Err(Error::new_spanned(t, "expect `u8`")),
},
t => return Err(Error::new_spanned(t, "expect array of `u8`")),
};
item.ty = parse_quote!(crate::imgfmt::elf::Note<#nlen, { #dlen }>);
// Replace value.
let name = name.as_bytes();
let desc = item.expr;
item.expr = parse_quote!(unsafe { crate::imgfmt::elf::Note::new([#(#name),*], #ty, #desc) });
// Compose.
Ok(quote! {
#[cfg(not(test))]
#[used]
#[link_section = #section]
#item
})
}
#[derive(Default)]
pub struct Options {
section: Option<LitStr>,
name: Option<LitStr>,
ty: Option<LitInt>,
}
impl Options {
pub fn parse(&mut self, m: ParseNestedMeta) -> syn::Result<()> {
if m.path.is_ident(OPT_SECTION) {
self.section = Some(m.value()?.parse()?);
} else if m.path.is_ident(OPT_NAME) {
self.name = Some(m.value()?.parse()?);
} else if m.path.is_ident(OPT_TY) {
self.ty = Some(m.value()?.parse()?);
} else {
return Err(m.error("unknown option"));
}
Ok(())
}
}

View File

@ -76,33 +76,27 @@ fn process_variant(variant: &Variant, enum_name: &Ident) -> syn::Result<TokenStr
match &variant.fields {
Fields::Named(_) => todo!("Named fields are not supported yet"),
Fields::Unnamed(fields) => {
let ref fields = fields.unnamed;
let fields = &fields.unnamed;
let mut pos = None;
fields
.iter()
.enumerate()
.try_for_each(|(i, field)| {
for attr in field.attrs.iter() {
if attr.path().is_ident("source") || attr.path().is_ident("from") {
if let Some(_) = pos.replace(i) {
return Err(syn::Error::new_spanned(
attr,
format!(
"multiple fields marked with either #[source] or #[from] found. \
Only one field is allowed"
),
))
}
}
fields.iter().enumerate().try_for_each(|(i, field)| {
for attr in field.attrs.iter() {
if (attr.path().is_ident("source") || attr.path().is_ident("from"))
&& pos.replace(i).is_some()
{
return Err(syn::Error::new_spanned(
attr,
"multiple fields marked with either #[source] or #[from] found. \
Only one field is allowed",
));
}
}
Ok(())
})?;
Ok(())
})?;
return match pos {
match pos {
Some(pos) => {
let variant_name = &variant.ident;
// The field at index `pos` is the one we are interested in
@ -117,7 +111,7 @@ fn process_variant(variant: &Variant, enum_name: &Ident) -> syn::Result<TokenStr
variant,
"no fields of this variant are marked with either #[source] or #[from]",
)),
};
}
}
Fields::Unit => Err(syn::Error::new_spanned(
variant,

View File

@ -1,17 +1,21 @@
use proc_macro::TokenStream;
use syn::{parse_macro_input, Error, ItemEnum, ItemFn, LitStr};
use syn::{parse_macro_input, Error, ItemEnum, ItemStatic, LitStr};
mod cpu_abi;
mod elf;
mod enum_conversions;
mod errno;
mod vpath;
/// Add `extern "sysv64"` on x86-64 or `extern "aapcs"` on AArch64.
/// Note will not produced for test target.
#[proc_macro_attribute]
pub fn cpu_abi(_: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as ItemFn);
pub fn elf_note(args: TokenStream, item: TokenStream) -> TokenStream {
let item = parse_macro_input!(item as ItemStatic);
let mut opts = self::elf::Options::default();
let parser = syn::meta::parser(|m| opts.parse(m));
cpu_abi::transform(item)
parse_macro_input!(args with parser);
self::elf::transform_note(opts, item)
.unwrap_or_else(Error::into_compile_error)
.into()
}

View File

@ -4,5 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
macros = { path = "../macros" }
obconf = { path = "../obconf" }
obvirt = { path = "../obvirt" }

View File

@ -4,12 +4,10 @@ fn main() {
match target.as_str() {
"aarch64-unknown-none-softfloat" => {
println!("cargo::rustc-link-arg-bins=--pie");
println!("cargo::rustc-link-arg-bins=-zcommon-page-size=0x4000");
println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x4000");
println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x1000");
}
"x86_64-unknown-none" => {
println!("cargo::rustc-link-arg-bins=-zcommon-page-size=0x4000");
println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x4000");
println!("cargo::rustc-link-arg-bins=-zmax-page-size=0x1000");
}
_ => {}
}

View File

@ -0,0 +1 @@
pub const PAGE_SIZE: usize = 0x1000;

View File

@ -1,6 +1,17 @@
use core::ptr::null;
use macros::elf_note;
use obconf::BootEnv;
#[cfg(target_arch = "aarch64")]
pub use self::aarch64::*;
#[cfg(target_arch = "x86_64")]
pub use self::x86_64::*;
#[cfg(target_arch = "aarch64")]
mod aarch64;
#[cfg(target_arch = "x86_64")]
mod x86_64;
pub fn boot_env() -> &'static BootEnv {
// SAFETY: This is safe because the set_boot_env() requirements.
unsafe { &*BOOT_ENV }
@ -14,3 +25,6 @@ pub unsafe fn set_boot_env(env: &'static BootEnv) {
}
static mut BOOT_ENV: *const BootEnv = null();
#[elf_note(section = ".note.obkrnl.page-size", name = "obkrnl", ty = 0)]
static NOTE_PAGE_SIZE: [u8; size_of::<usize>()] = PAGE_SIZE.to_ne_bytes();

View File

@ -0,0 +1 @@
pub const PAGE_SIZE: usize = 0x1000;

View File

@ -0,0 +1,60 @@
use core::ops::Deref;
/// Single ELF note.
#[repr(C)]
pub struct Note<const N: usize, const D: usize> {
hdr: NoteHdr,
name: [u8; N],
desc: NoteDesc<D>,
}
impl<const N: usize, const D: usize> Note<N, D> {
/// # Safety
/// `name` must contains NUL as a last element.
pub const unsafe fn new(name: [u8; N], ty: u32, desc: [u8; D]) -> Self {
Self {
hdr: NoteHdr {
name_len: N as _,
desc_len: D as _,
ty,
},
name,
desc: NoteDesc(desc),
}
}
}
/// Implementation of `Elf64_Nhdr` and `Elf32_Nhdr` structure.
#[repr(C)]
pub struct NoteHdr {
/// n_namesz.
pub name_len: u32,
/// n_descsz.
pub desc_len: u32,
/// n_type.
pub ty: u32,
}
/// Note description.
#[repr(C, align(4))]
pub struct NoteDesc<const L: usize>([u8; L]);
impl<const L: usize> Deref for NoteDesc<L> {
type Target = [u8; L];
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
use core::mem::offset_of;
#[test]
fn note() {
assert_eq!(offset_of!(Note::<3, 1>, name), 12);
assert_eq!(offset_of!(Note::<3, 1>, desc), 16);
}
}

View File

@ -0,0 +1 @@
pub mod elf;

View File

@ -8,6 +8,7 @@ use obconf::BootEnv;
mod config;
mod console;
mod imgfmt;
/// Entry point of the kernel.
///