Skip to content

Commit

Permalink
Exposes kernel page size via ELF note (#934)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Aug 18, 2024
1 parent 30b712b commit 0bd471b
Show file tree
Hide file tree
Showing 12 changed files with 238 additions and 71 deletions.
39 changes: 0 additions & 39 deletions src/macros/src/cpu_abi.rs

This file was deleted.

131 changes: 131 additions & 0 deletions src/macros/src/elf.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
}
38 changes: 16 additions & 22 deletions src/macros/src/errno.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
16 changes: 10 additions & 6 deletions src/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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()
}
Expand Down
1 change: 1 addition & 0 deletions src/obkrnl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ version = "0.1.0"
edition = "2021"

[dependencies]
macros = { path = "../macros" }
obconf = { path = "../obconf" }
obvirt = { path = "../obvirt" }
6 changes: 2 additions & 4 deletions src/obkrnl/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
_ => {}
}
Expand Down
1 change: 1 addition & 0 deletions src/obkrnl/src/config/aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const PAGE_SIZE: usize = 0x1000;
14 changes: 14 additions & 0 deletions src/obkrnl/src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -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 }
Expand All @@ -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();
1 change: 1 addition & 0 deletions src/obkrnl/src/config/x86_64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const PAGE_SIZE: usize = 0x1000;
60 changes: 60 additions & 0 deletions src/obkrnl/src/imgfmt/elf/mod.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
1 change: 1 addition & 0 deletions src/obkrnl/src/imgfmt/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod elf;
1 change: 1 addition & 0 deletions src/obkrnl/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use obconf::BootEnv;

mod config;
mod console;
mod imgfmt;

/// Entry point of the kernel.
///
Expand Down

0 comments on commit 0bd471b

Please sign in to comment.