11//! vDSO data management.
22extern crate alloc;
33extern crate log;
4- use alloc:: alloc:: alloc_zeroed;
4+ use alloc:: { alloc:: alloc_zeroed, vec :: Vec } ;
55use core:: alloc:: Layout ;
66
77use axerrno:: { AxError , AxResult } ;
88use axplat:: { mem:: virt_to_phys, time:: monotonic_time_nanos} ;
9-
10- const PAGE_SIZE_4K : usize = 4096 ;
9+ use kernel_elf_parser:: { AuxEntry , AuxType } ;
10+ use log:: { info, warn} ;
11+ use memory_addr:: { MemoryAddr , PAGE_SIZE_4K } ;
1112
1213/// Global vDSO data instance
1314#[ unsafe( link_section = ".data" ) ]
@@ -18,11 +19,15 @@ pub fn init_vdso_data() {
1819 unsafe {
1920 let data_ptr = core:: ptr:: addr_of_mut!( VDSO_DATA ) ;
2021 ( * data_ptr) . time_update ( ) ;
21- log :: info!( "vDSO data initialized at {:#x}" , data_ptr as usize ) ;
22+ info ! ( "vDSO data initialized at {:#x}" , data_ptr as usize ) ;
2223 #[ cfg( target_arch = "aarch64" ) ]
2324 {
2425 crate :: vdso_data:: enable_cntvct_access ( ) ;
25- log:: info!( "vDSO CNTVCT access enabled" ) ;
26+ info ! ( "vDSO CNTVCT access enabled" ) ;
27+ }
28+ #[ cfg( target_arch = "x86_64" ) ]
29+ {
30+ ( * data_ptr) . enable_pvclock ( ) ;
2631 }
2732 #[ cfg( target_arch = "x86_64" ) ]
2833 {
@@ -122,3 +127,133 @@ pub fn calculate_vdso_aslr_addr(
122127
123128 ( base_addr, vdso_addr)
124129}
130+
131+ /// Load vDSO into the given user address space and update auxv accordingly.
132+ pub fn load_vdso_data < F1 , F2 , F3 > ( auxv : & mut Vec < AuxEntry > , f1 : F1 , f2 : F2 , f3 : F3 ) -> AxResult < ( ) >
133+ where
134+ F1 : FnOnce ( usize , axplat:: mem:: PhysAddr , usize ) -> AxResult < ( ) > ,
135+ F2 : FnOnce ( usize , usize ) -> AxResult < ( ) > ,
136+ F3 : FnMut (
137+ usize ,
138+ axplat:: mem:: PhysAddr ,
139+ usize ,
140+ & xmas_elf:: program:: ProgramHeader64 ,
141+ ) -> AxResult < ( ) > ,
142+ {
143+ unsafe extern "C" {
144+ static vdso_start: u8 ;
145+ static vdso_end: u8 ;
146+ }
147+ let ( vdso_kstart, vdso_kend) = unsafe {
148+ (
149+ & vdso_start as * const u8 as usize ,
150+ & vdso_end as * const u8 as usize ,
151+ )
152+ } ;
153+ info ! ( "vdso_kstart: {vdso_kstart:#x}, vdso_kend: {vdso_kend:#x}" ) ;
154+
155+ if vdso_kend <= vdso_kstart {
156+ warn ! (
157+ "vDSO binary is missing or invalid: vdso_kstart={vdso_kstart:#x}, \
158+ vdso_kend={vdso_kend:#x}. vDSO will not be loaded and AT_SYSINFO_EHDR will not be \
159+ set."
160+ ) ;
161+ return Err ( AxError :: InvalidExecutable ) ;
162+ }
163+
164+ let ( vdso_paddr_page, vdso_bytes, vdso_size, vdso_page_offset, alloc_info) =
165+ prepare_vdso_pages ( vdso_kstart, vdso_kend) . map_err ( |_| AxError :: InvalidExecutable ) ?;
166+
167+ let mut alloc_guard = crate :: guard:: VdsoAllocGuard :: new ( alloc_info) ;
168+
169+ let ( _base_addr, vdso_user_addr) =
170+ calculate_vdso_aslr_addr ( vdso_kstart, vdso_kend, vdso_page_offset) ;
171+
172+ match kernel_elf_parser:: ELFHeadersBuilder :: new ( vdso_bytes) . and_then ( |b| {
173+ let range = b. ph_range ( ) ;
174+ b. build ( & vdso_bytes[ range. start as usize ..range. end as usize ] )
175+ } ) {
176+ Ok ( headers) => {
177+ map_vdso_segments (
178+ headers,
179+ vdso_user_addr,
180+ vdso_paddr_page,
181+ vdso_page_offset,
182+ f3,
183+ ) ?;
184+ alloc_guard. disarm ( ) ;
185+ }
186+ Err ( _) => {
187+ info ! ( "vDSO ELF parsing failed, using fallback mapping" ) ;
188+ let map_user_start = if vdso_page_offset == 0 {
189+ vdso_user_addr
190+ } else {
191+ vdso_user_addr - vdso_page_offset
192+ } ;
193+ f1 ( map_user_start, vdso_paddr_page, vdso_size) ?;
194+ alloc_guard. disarm ( ) ;
195+ }
196+ }
197+
198+ map_vvar_and_push_aux ( auxv, vdso_user_addr, f2) ?;
199+
200+ Ok ( ( ) )
201+ }
202+
203+ fn map_vvar_and_push_aux < F > ( auxv : & mut Vec < AuxEntry > , vdso_user_addr : usize , f : F ) -> AxResult < ( ) >
204+ where
205+ F : FnOnce ( usize , usize ) -> AxResult < ( ) > ,
206+ {
207+ use crate :: config:: VVAR_PAGES ;
208+ let vvar_user_addr = vdso_user_addr - VVAR_PAGES * PAGE_SIZE_4K ;
209+ let vvar_paddr = vdso_data_paddr ( ) ;
210+
211+ f ( vvar_user_addr, vvar_paddr) ?;
212+
213+ info ! (
214+ "Mapped vvar pages at user {:#x}..{:#x} -> paddr {:#x}" ,
215+ vvar_user_addr,
216+ vvar_user_addr + VVAR_PAGES * PAGE_SIZE_4K ,
217+ vvar_paddr,
218+ ) ;
219+
220+ let aux_entry = AuxEntry :: new ( AuxType :: SYSINFO_EHDR , vdso_user_addr) ;
221+ auxv. push ( aux_entry) ;
222+
223+ Ok ( ( ) )
224+ }
225+
226+ fn map_vdso_segments < F > (
227+ headers : kernel_elf_parser:: ELFHeaders ,
228+ vdso_user_addr : usize ,
229+ vdso_paddr_page : axplat:: mem:: PhysAddr ,
230+ vdso_page_offset : usize ,
231+ mut f : F ,
232+ ) -> AxResult < ( ) >
233+ where
234+ F : FnMut (
235+ usize ,
236+ axplat:: mem:: PhysAddr ,
237+ usize ,
238+ & xmas_elf:: program:: ProgramHeader64 ,
239+ ) -> AxResult < ( ) > ,
240+ {
241+ info ! ( "vDSO ELF parsed successfully, mapping segments" ) ;
242+ for ph in headers
243+ . ph
244+ . iter ( )
245+ . filter ( |ph| ph. get_type ( ) == Ok ( xmas_elf:: program:: Type :: Load ) )
246+ {
247+ let vaddr = ph. virtual_addr as usize ;
248+ let seg_pad = vaddr. align_offset_4k ( ) + vdso_page_offset;
249+ let seg_align_size =
250+ ( ph. mem_size as usize + seg_pad + PAGE_SIZE_4K - 1 ) & !( PAGE_SIZE_4K - 1 ) ;
251+
252+ let map_base_user = vdso_user_addr & !( PAGE_SIZE_4K - 1 ) ;
253+ let seg_user_start = map_base_user + vaddr. align_down_4k ( ) ;
254+ let seg_paddr = vdso_paddr_page + vaddr. align_down_4k ( ) ;
255+
256+ f ( seg_user_start, seg_paddr, seg_align_size, ph) ?;
257+ }
258+ Ok ( ( ) )
259+ }
0 commit comments