1use core::ffi::CStr;
4
5use alloc::{borrow::ToOwned, string::String, vec, vec::Vec};
6use axerrno::{AxError, AxResult};
7use axhal::{
8 mem::virt_to_phys,
9 paging::{MappingFlags, PageSize},
10};
11use axmm::{AddrSpace, kernel_aspace};
12use kernel_elf_parser::{AuxvEntry, ELFParser, app_stack_region};
13use memory_addr::{MemoryAddr, PAGE_SIZE_4K, VirtAddr};
14use xmas_elf::{ElfFile, program::SegmentData};
15
16pub fn new_user_aspace_empty() -> AxResult<AddrSpace> {
18 AddrSpace::new_empty(
19 VirtAddr::from_usize(axconfig::plat::USER_SPACE_BASE),
20 axconfig::plat::USER_SPACE_SIZE,
21 )
22}
23
24pub fn copy_from_kernel(aspace: &mut AddrSpace) -> AxResult {
27 if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "loongarch64") {
28 aspace.copy_mappings_from(&kernel_aspace().lock())?;
32 }
33 Ok(())
34}
35
36pub fn map_trampoline(aspace: &mut AddrSpace) -> AxResult {
38 let signal_trampoline_paddr = virt_to_phys(axsignal::arch::signal_trampoline_address().into());
39 aspace.map_linear(
40 axconfig::plat::SIGNAL_TRAMPOLINE.into(),
41 signal_trampoline_paddr,
42 PAGE_SIZE_4K,
43 MappingFlags::READ | MappingFlags::EXECUTE | MappingFlags::USER,
44 PageSize::Size4K,
45 )?;
46 Ok(())
47}
48
49fn map_elf(uspace: &mut AddrSpace, elf: &ElfFile) -> AxResult<(VirtAddr, [AuxvEntry; 16])> {
58 let uspace_base = uspace.base().as_usize();
59 let elf_parser = ELFParser::new(
60 elf,
61 axconfig::plat::USER_INTERP_BASE,
62 Some(uspace_base as isize),
63 uspace_base,
64 )
65 .map_err(|_| AxError::InvalidData)?;
66
67 for segement in elf_parser.ph_load() {
68 debug!(
69 "Mapping ELF segment: [{:#x?}, {:#x?}) flags: {:#x?}",
70 segement.vaddr,
71 segement.vaddr + segement.memsz as usize,
72 segement.flags
73 );
74 let seg_pad = segement.vaddr.align_offset_4k();
75 assert_eq!(seg_pad, segement.offset % PAGE_SIZE_4K);
76
77 let seg_align_size =
78 (segement.memsz as usize + seg_pad + PAGE_SIZE_4K - 1) & !(PAGE_SIZE_4K - 1);
79 uspace.map_alloc(
80 segement.vaddr.align_down_4k(),
81 seg_align_size,
82 segement.flags,
83 true,
84 PageSize::Size4K,
85 )?;
86 let seg_data = elf
87 .input
88 .get(segement.offset..segement.offset + segement.filesz as usize)
89 .ok_or(AxError::InvalidData)?;
90 uspace.write(segement.vaddr, PageSize::Size4K, seg_data)?;
91 }
93
94 Ok((
95 elf_parser.entry().into(),
96 elf_parser.auxv_vector(PAGE_SIZE_4K),
97 ))
98}
99
100pub fn load_user_app(
111 uspace: &mut AddrSpace,
112 args: &[String],
113 envs: &[String],
114) -> AxResult<(VirtAddr, VirtAddr)> {
115 if args.is_empty() {
116 return Err(AxError::InvalidInput);
117 }
118 let file_data = axfs::api::read(args[0].as_str())?;
119 if file_data.starts_with(b"#!") {
120 let head = &file_data[2..file_data.len().min(256)];
121 let pos = head.iter().position(|c| *c == b'\n').unwrap_or(head.len());
122 let line = core::str::from_utf8(&head[..pos]).map_err(|_| AxError::InvalidData)?;
123
124 let new_args: Vec<String> = line
125 .splitn(2, |c: char| c.is_ascii_whitespace())
126 .map(|s| s.trim_ascii().to_owned())
127 .chain(args.iter().cloned())
128 .collect();
129 return load_user_app(uspace, &new_args, envs);
130 }
131 let elf = ElfFile::new(&file_data).map_err(|_| AxError::InvalidData)?;
132
133 if let Some(interp) = elf
134 .program_iter()
135 .find(|ph| ph.get_type() == Ok(xmas_elf::program::Type::Interp))
136 {
137 let interp = match interp.get_data(&elf) {
138 Ok(SegmentData::Undefined(data)) => data,
139 _ => panic!("Invalid data in Interp Elf Program Header"),
140 };
141
142 let mut interp_path = axfs::api::canonicalize(
143 CStr::from_bytes_with_nul(interp)
144 .map_err(|_| AxError::InvalidData)?
145 .to_str()
146 .map_err(|_| AxError::InvalidData)?,
147 )?;
148
149 if interp_path == "/lib/ld-linux-riscv64-lp64.so.1"
150 || interp_path == "/lib64/ld-linux-loongarch-lp64d.so.1"
151 || interp_path == "/lib64/ld-linux-x86-64.so.2"
152 || interp_path == "/lib/ld-linux-aarch64.so.1"
153 {
154 interp_path = String::from("/musl/lib/libc.so");
156 }
157
158 let mut new_args = vec![interp_path];
160 new_args.extend_from_slice(args);
161 return load_user_app(uspace, &new_args, envs);
162 }
163
164 let (entry, mut auxv) = map_elf(uspace, &elf)?;
165 let ustack_end = VirtAddr::from_usize(axconfig::plat::USER_STACK_TOP);
170 let ustack_size = axconfig::plat::USER_STACK_SIZE;
171 let ustack_start = ustack_end - ustack_size;
172 debug!(
173 "Mapping user stack: {:#x?} -> {:#x?}",
174 ustack_start, ustack_end
175 );
176
177 let stack_data = app_stack_region(args, envs, &mut auxv, ustack_start, ustack_size);
178 uspace.map_alloc(
179 ustack_start,
180 ustack_size,
181 MappingFlags::READ | MappingFlags::WRITE | MappingFlags::USER,
182 true,
183 PageSize::Size4K,
184 )?;
185
186 let heap_start = VirtAddr::from_usize(axconfig::plat::USER_HEAP_BASE);
187 let heap_size = axconfig::plat::USER_HEAP_SIZE;
188 uspace.map_alloc(
189 heap_start,
190 heap_size,
191 MappingFlags::READ | MappingFlags::WRITE | MappingFlags::USER,
192 true,
193 PageSize::Size4K,
194 )?;
195
196 let user_sp = ustack_end - stack_data.len();
197
198 uspace.write(user_sp, PageSize::Size4K, stack_data.as_slice())?;
199
200 Ok((entry, user_sp))
201}
202
203#[percpu::def_percpu]
204static mut ACCESSING_USER_MEM: bool = false;
205
206pub fn access_user_memory<R>(f: impl FnOnce() -> R) -> R {
209 ACCESSING_USER_MEM.with_current(|v| {
210 *v = true;
211 let result = f();
212 *v = false;
213 result
214 })
215}
216
217pub fn is_accessing_user_memory() -> bool {
219 ACCESSING_USER_MEM.read_current()
220}