starry_core/
mm.rs

1//! User address space management.
2
3use 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
16/// Creates a new empty user address space.
17pub 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
24/// If the target architecture requires it, the kernel portion of the address
25/// space will be copied to the user address space.
26pub fn copy_from_kernel(aspace: &mut AddrSpace) -> AxResult {
27    if !cfg!(target_arch = "aarch64") && !cfg!(target_arch = "loongarch64") {
28        // ARMv8 (aarch64) and LoongArch64 use separate page tables for user space
29        // (aarch64: TTBR0_EL1, LoongArch64: PGDL), so there is no need to copy the
30        // kernel portion to the user page table.
31        aspace.copy_mappings_from(&kernel_aspace().lock())?;
32    }
33    Ok(())
34}
35
36/// Map the signal trampoline to the user address space.
37pub 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
49/// Map the elf file to the user address space.
50///
51/// # Arguments
52/// - `uspace`: The address space of the user app.
53/// - `elf`: The elf file.
54///
55/// # Returns
56/// - The entry point of the user app.
57fn 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        // TDOO: flush the I-cache
92    }
93
94    Ok((
95        elf_parser.entry().into(),
96        elf_parser.auxv_vector(PAGE_SIZE_4K),
97    ))
98}
99
100/// Load the user app to the user address space.
101///
102/// # Arguments
103/// - `uspace`: The address space of the user app.
104/// - `args`: The arguments of the user app. The first argument is the path of the user app.
105/// - `envs`: The environment variables of the user app.
106///
107/// # Returns
108/// - The entry point of the user app.
109/// - The stack pointer of the user app.
110pub 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            // TODO: Use soft link
155            interp_path = String::from("/musl/lib/libc.so");
156        }
157
158        // Set the first argument to the path of the user app.
159        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    // The user stack is divided into two parts:
166    // `ustack_start` -> `ustack_pointer`: It is the stack space that users actually read and write.
167    // `ustack_pointer` -> `ustack_end`: It is the space that contains the arguments, environment variables and auxv passed to the app.
168    //  When the app starts running, the stack pointer points to `ustack_pointer`.
169    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
206/// Enables scoped access into user memory, allowing page faults to occur inside
207/// kernel.
208pub 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
217/// Check if the current thread is accessing user memory.
218pub fn is_accessing_user_memory() -> bool {
219    ACCESSING_USER_MEM.read_current()
220}