Implement arch-aware vDSO

This commit is contained in:
Zejun Zhao 2025-08-08 05:15:53 +08:00 committed by Ruihan Li
parent 7fad653992
commit e68631d1b6
3 changed files with 113 additions and 23 deletions

View File

@ -73,6 +73,8 @@ pub mod syscall;
pub mod thread;
pub mod time;
mod util;
// TODO: Add vDSO support for other architectures.
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
pub(crate) mod vdso;
pub mod vm;
@ -106,6 +108,7 @@ pub fn init() {
fs::rootfs::init(boot_info().initramfs.expect("No initramfs found!")).unwrap();
device::init().unwrap();
syscall::init();
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
vdso::init();
process::init();
}

View File

@ -25,7 +25,6 @@ use crate::{
process_vm::{AuxKey, AuxVec, ProcessVm},
TermStatus,
},
vdso::{vdso_vmo, VDSO_VMO_SIZE},
vm::{
perms::VmPerms,
util::duplicate_frame,
@ -49,10 +48,15 @@ pub fn load_elf_to_vm(
let ldso = lookup_and_parse_ldso(&elf_headers, &elf_file, fs_resolver)?;
match init_and_map_vmos(process_vm, ldso, &elf_headers, &elf_file) {
#[cfg_attr(
not(any(target_arch = "x86_64", target_arch = "riscv64")),
expect(unused_mut)
)]
Ok((_range, entry_point, mut aux_vec)) => {
// Map and set vdso entry.
// Since vdso does not require being mapped to any specific address,
// vdso is mapped after the elf file, heap and stack are mapped.
// Map the vDSO and set the entry.
// Since the vDSO does not require being mapped to any specific address,
// the vDSO is mapped after the ELF file, heap, and stack.
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
if let Some(vdso_text_base) = map_vdso_to_vm(process_vm) {
aux_vec
.set(AuxKey::AT_SYSINFO_EHDR, vdso_text_base as u64)
@ -507,26 +511,36 @@ pub fn init_aux_vec(
}
/// Maps the vDSO VMO to the corresponding virtual memory address.
#[cfg(any(target_arch = "x86_64", target_arch = "riscv64"))]
fn map_vdso_to_vm(process_vm: &ProcessVm) -> Option<Vaddr> {
use crate::vdso::{vdso_vmo, VDSO_VMO_LAYOUT};
let process_vmar = process_vm.lock_root_vmar();
let root_vmar = process_vmar.unwrap();
let vdso_vmo = vdso_vmo()?;
let options = root_vmar
.new_map(VDSO_VMO_SIZE, VmPerms::empty())
.new_map(VDSO_VMO_LAYOUT.size, VmPerms::empty())
.unwrap()
.vmo(vdso_vmo.dup().unwrap());
let vdso_data_base = options.build().unwrap();
let vdso_text_base = vdso_data_base + 0x4000;
let vdso_vmo_base = options.build().unwrap();
let vdso_data_base = vdso_vmo_base + VDSO_VMO_LAYOUT.data_segment_offset;
let vdso_text_base = vdso_vmo_base + VDSO_VMO_LAYOUT.text_segment_offset;
let data_perms = VmPerms::READ | VmPerms::WRITE;
let text_perms = VmPerms::READ | VmPerms::EXEC;
root_vmar
.protect(data_perms, vdso_data_base..vdso_data_base + PAGE_SIZE)
.protect(
data_perms,
vdso_data_base..(vdso_data_base + VDSO_VMO_LAYOUT.data_segment_size),
)
.unwrap();
root_vmar
.protect(text_perms, vdso_text_base..vdso_text_base + PAGE_SIZE)
.protect(
text_perms,
vdso_text_base..(vdso_text_base + VDSO_VMO_LAYOUT.text_segment_size),
)
.unwrap();
Some(vdso_text_base)
}

View File

@ -19,6 +19,7 @@ use aster_rights::Rights;
use aster_time::{read_monotonic_time, Instant};
use aster_util::coeff::Coeff;
use ostd::{
const_assert,
mm::{UFrame, VmIo, VmIoOnce, PAGE_SIZE},
sync::SpinLock,
Pod,
@ -186,6 +187,12 @@ impl VdsoData {
}
}
macro_rules! vdso_data_field_offset {
($field:ident) => {
VDSO_VMO_LAYOUT.data_offset + core::mem::offset_of!(VdsoData, $field)
};
}
/// The vDSO singleton.
///
/// See [the module-level documentations](self) for more about the vDSO mechanism.
@ -201,9 +208,6 @@ struct Vdso {
data_frame: UFrame,
}
/// The size of the vDSO VMO.
pub const VDSO_VMO_SIZE: usize = 5 * PAGE_SIZE;
impl Vdso {
/// Constructs a new `Vdso`, including an initialized `VdsoData` and a VMO of the vDSO.
fn new() -> Self {
@ -211,10 +215,12 @@ impl Vdso {
vdso_data.init();
let (vdso_vmo, data_frame) = {
let vmo_options = VmoOptions::<Rights>::new(VDSO_VMO_SIZE);
let vmo_options = VmoOptions::<Rights>::new(VDSO_VMO_LAYOUT.size);
let vdso_vmo = vmo_options.alloc().unwrap();
// Write vDSO data to vDSO VMO.
vdso_vmo.write_bytes(0x80, vdso_data.as_bytes()).unwrap();
vdso_vmo
.write_bytes(VDSO_VMO_LAYOUT.data_offset, vdso_data.as_bytes())
.unwrap();
let vdso_lib_vmo = {
let vdso_path = FsPath::new(AT_FDCWD, "/lib/x86_64-linux-gnu/vdso64.so").unwrap();
@ -222,10 +228,12 @@ impl Vdso {
let vdso_lib = fs_resolver.lookup(&vdso_path).unwrap();
vdso_lib.inode().page_cache().unwrap()
};
let mut vdso_text = Box::new([0u8; PAGE_SIZE]);
let mut vdso_text = Box::new([0u8; VDSO_VMO_LAYOUT.text_segment_size]);
vdso_lib_vmo.read_bytes(0, &mut *vdso_text).unwrap();
// Write vDSO library to vDSO VMO.
vdso_vmo.write_bytes(0x4000, &*vdso_text).unwrap();
vdso_vmo
.write_bytes(VDSO_VMO_LAYOUT.text_segment_offset, &*vdso_text)
.unwrap();
let data_frame = vdso_vmo.try_commit_page(0).unwrap();
(vdso_vmo, data_frame)
@ -244,9 +252,13 @@ impl Vdso {
data.update_high_res_instant(instant, instant_cycles);
// Update begins.
self.data_frame.write_once(0x80, &1).unwrap();
self.data_frame
.write_once(vdso_data_field_offset!(seq), &1)
.unwrap();
self.data_frame.write_val(0x88, &instant_cycles).unwrap();
self.data_frame
.write_val(vdso_data_field_offset!(last_cycles), &instant_cycles)
.unwrap();
for clock_id in HIGH_RES_CLOCK_IDS {
self.update_data_frame_instant(clock_id, &mut data);
}
@ -254,7 +266,9 @@ impl Vdso {
// Update finishes.
// FIXME: To synchronize with the vDSO library, this needs to be an atomic write with the
// Release memory order.
self.data_frame.write_once(0x80, &0).unwrap();
self.data_frame
.write_once(vdso_data_field_offset!(seq), &0)
.unwrap();
}
fn update_coarse_res_instant(&self, instant: Instant) {
@ -263,7 +277,9 @@ impl Vdso {
data.update_coarse_res_instant(instant);
// Update begins.
self.data_frame.write_once(0x80, &1).unwrap();
self.data_frame
.write_once(vdso_data_field_offset!(seq), &1)
.unwrap();
for clock_id in COARSE_RES_CLOCK_IDS {
self.update_data_frame_instant(clock_id, &mut data);
@ -272,15 +288,20 @@ impl Vdso {
// Update finishes.
// FIXME: To synchronize with the vDSO library, this needs to be an atomic write with the
// Release memory order.
self.data_frame.write_once(0x80, &0).unwrap();
self.data_frame
.write_once(vdso_data_field_offset!(seq), &0)
.unwrap();
}
/// Updates the requisite fields of the vDSO data in the frame.
fn update_data_frame_instant(&self, clockid: ClockId, data: &mut VdsoData) {
let clock_index = clockid as usize;
let secs_offset = 0xA0 + clock_index * 0x10;
let nanos_info_offset = 0xA8 + clock_index * 0x10;
let secs_offset =
vdso_data_field_offset!(basetime) + clock_index * size_of::<VdsoInstant>();
let nanos_info_offset = vdso_data_field_offset!(basetime)
+ core::mem::offset_of!(VdsoInstant, nanos_info)
+ clock_index * size_of::<VdsoInstant>();
self.data_frame
.write_val(secs_offset, &data.basetime[clock_index].secs)
.unwrap();
@ -340,3 +361,55 @@ pub(super) fn init() {
pub(crate) fn vdso_vmo() -> Option<Arc<Vmo>> {
VDSO.get().map(|vdso| vdso.vmo.clone())
}
#[cfg(target_arch = "x86_64")]
pub const VDSO_VMO_LAYOUT: VdsoVmoLayout = VdsoVmoLayout {
// https://elixir.bootlin.com/linux/v6.2.10/source/arch/x86/entry/vdso/vdso-layout.lds.S#L20
data_segment_offset: 0,
data_segment_size: PAGE_SIZE,
// https://elixir.bootlin.com/linux/v6.2.10/source/arch/x86/entry/vdso/vdso-layout.lds.S#L19
text_segment_offset: 4 * PAGE_SIZE,
text_segment_size: PAGE_SIZE,
// https://elixir.bootlin.com/linux/v6.2.10/source/arch/x86/include/asm/vvar.h#L51
data_offset: 0x80,
size: 5 * PAGE_SIZE,
};
#[cfg(target_arch = "riscv64")]
pub const VDSO_VMO_LAYOUT: VdsoVmoLayout = VdsoVmoLayout {
// https://elixir.bootlin.com/linux/v6.2.10/source/arch/riscv/kernel/vdso.c#L247
data_segment_offset: 0,
data_segment_size: PAGE_SIZE,
// https://elixir.bootlin.com/linux/v6.2.10/source/arch/riscv/kernel/vdso.c#L256
text_segment_offset: 2 * PAGE_SIZE,
text_segment_size: PAGE_SIZE,
// https://elixir.bootlin.com/linux/v6.2.10/source/arch/riscv/kernel/vdso.c#L47
data_offset: 0,
size: 3 * PAGE_SIZE,
};
pub struct VdsoVmoLayout {
pub data_segment_offset: usize,
pub data_segment_size: usize,
pub text_segment_offset: usize,
pub text_segment_size: usize,
pub data_offset: usize,
pub size: usize,
}
const_assert!(VDSO_VMO_LAYOUT.data_segment_offset % PAGE_SIZE == 0);
const_assert!(VDSO_VMO_LAYOUT.data_segment_size % PAGE_SIZE == 0);
const_assert!(VDSO_VMO_LAYOUT.text_segment_offset % PAGE_SIZE == 0);
const_assert!(VDSO_VMO_LAYOUT.text_segment_size % PAGE_SIZE == 0);
const_assert!(VDSO_VMO_LAYOUT.size % PAGE_SIZE == 0);
// Ensure that the vDSO data at `VDSO_VMO_LAYOUT.data_offset` is in the data segment.
//
// `VDSO_VMO_LAYOUT.data_segment_offset <= VDSO_VMO_LAYOUT.data_offset` should also hold, but we
// skipped that assertion due to the broken `clippy::absurd_extreme_comparisons` lint.
const_assert!(
VDSO_VMO_LAYOUT.data_offset + size_of::<VdsoData>()
<= VDSO_VMO_LAYOUT.data_segment_offset + VDSO_VMO_LAYOUT.data_segment_size
);