starry_api/imp/mm/
mmap.rs

1use alloc::vec;
2use axerrno::{LinuxError, LinuxResult};
3use axhal::paging::{MappingFlags, PageSize};
4use axtask::{TaskExtRef, current};
5use linux_raw_sys::general::*;
6use memory_addr::{MemoryAddr, VirtAddr, VirtAddrRange, align_up_4k};
7
8use crate::file::{File, FileLike};
9
10bitflags::bitflags! {
11    /// `PROT_*` flags for use with [`sys_mmap`].
12    ///
13    /// For `PROT_NONE`, use `ProtFlags::empty()`.
14    #[derive(Debug)]
15    struct MmapProt: u32 {
16        /// Page can be read.
17        const READ = PROT_READ;
18        /// Page can be written.
19        const WRITE = PROT_WRITE;
20        /// Page can be executed.
21        const EXEC = PROT_EXEC;
22        /// Extend change to start of growsdown vma (mprotect only).
23        const GROWDOWN = PROT_GROWSDOWN;
24        /// Extend change to start of growsup vma (mprotect only).
25        const GROWSUP = PROT_GROWSUP;
26    }
27}
28
29impl From<MmapProt> for MappingFlags {
30    fn from(value: MmapProt) -> Self {
31        let mut flags = MappingFlags::USER;
32        if value.contains(MmapProt::READ) {
33            flags |= MappingFlags::READ;
34        }
35        if value.contains(MmapProt::WRITE) {
36            flags |= MappingFlags::WRITE;
37        }
38        if value.contains(MmapProt::EXEC) {
39            flags |= MappingFlags::EXECUTE;
40        }
41        flags
42    }
43}
44
45bitflags::bitflags! {
46    /// flags for sys_mmap
47    ///
48    /// See <https://github.com/bminor/glibc/blob/master/bits/mman.h>
49    #[derive(Debug)]
50    struct MmapFlags: u32 {
51        /// Share changes
52        const SHARED = MAP_SHARED;
53        /// Changes private; copy pages on write.
54        const PRIVATE = MAP_PRIVATE;
55        /// Map address must be exactly as requested, no matter whether it is available.
56        const FIXED = MAP_FIXED;
57        /// Don't use a file.
58        const ANONYMOUS = MAP_ANONYMOUS;
59        /// Don't check for reservations.
60        const NORESERVE = MAP_NORESERVE;
61        /// Allocation is for a stack.
62        const STACK = MAP_STACK;
63        /// Huge page
64        const HUGE = MAP_HUGETLB;
65        /// Huge page 1g size
66        const HUGE_1GB = MAP_HUGETLB | MAP_HUGE_1GB;
67    }
68}
69
70pub fn sys_mmap(
71    addr: usize,
72    length: usize,
73    prot: u32,
74    flags: u32,
75    fd: i32,
76    offset: isize,
77) -> LinuxResult<isize> {
78    let curr = current();
79    let process_data = curr.task_ext().process_data();
80    let mut aspace = process_data.aspace.lock();
81    let permission_flags = MmapProt::from_bits_truncate(prot);
82    // TODO: check illegal flags for mmap
83    // An example is the flags contained none of MAP_PRIVATE, MAP_SHARED, or MAP_SHARED_VALIDATE.
84    let map_flags = MmapFlags::from_bits_truncate(flags);
85    if map_flags.contains(MmapFlags::PRIVATE | MmapFlags::SHARED) {
86        return Err(LinuxError::EINVAL);
87    }
88
89    info!(
90        "sys_mmap: addr: {:x?}, length: {:x?}, prot: {:?}, flags: {:?}, fd: {:?}, offset: {:?}",
91        addr, length, permission_flags, map_flags, fd, offset
92    );
93
94    let page_size = if map_flags.contains(MmapFlags::HUGE_1GB) {
95        PageSize::Size1G
96    } else if map_flags.contains(MmapFlags::HUGE) {
97        PageSize::Size2M
98    } else {
99        PageSize::Size4K
100    };
101
102    let start = addr.align_down(page_size);
103    let end = (addr + length).align_up(page_size);
104    let aligned_length = end - start;
105    debug!(
106        "start: {:x?}, end: {:x?}, aligned_length: {:x?}",
107        start, end, aligned_length
108    );
109
110    let start_addr = if map_flags.contains(MmapFlags::FIXED) {
111        if start == 0 {
112            return Err(LinuxError::EINVAL);
113        }
114        let dst_addr = VirtAddr::from(start);
115        aspace.unmap(dst_addr, aligned_length)?;
116        dst_addr
117    } else {
118        aspace
119            .find_free_area(
120                VirtAddr::from(start),
121                aligned_length,
122                VirtAddrRange::new(aspace.base(), aspace.end()),
123                page_size,
124            )
125            .or(aspace.find_free_area(
126                aspace.base(),
127                aligned_length,
128                VirtAddrRange::new(aspace.base(), aspace.end()),
129                page_size,
130            ))
131            .ok_or(LinuxError::ENOMEM)?
132    };
133
134    let populate = if fd == -1 {
135        false
136    } else {
137        !map_flags.contains(MmapFlags::ANONYMOUS)
138    };
139
140    aspace.map_alloc(
141        start_addr,
142        aligned_length,
143        permission_flags.into(),
144        populate,
145        page_size,
146    )?;
147
148    if populate {
149        let file = File::from_fd(fd)?;
150        let file = file.inner();
151        let file_size = file.get_attr()?.size() as usize;
152        if offset < 0 || offset as usize >= file_size {
153            return Err(LinuxError::EINVAL);
154        }
155        let offset = offset as usize;
156        let length = core::cmp::min(length, file_size - offset);
157        let mut buf = vec![0u8; length];
158        file.read_at(offset as u64, &mut buf)?;
159        aspace.write(start_addr, page_size, &buf)?;
160    }
161    Ok(start_addr.as_usize() as _)
162}
163
164pub fn sys_munmap(addr: usize, length: usize) -> LinuxResult<isize> {
165    let curr = current();
166    let process_data = curr.task_ext().process_data();
167    let mut aspace = process_data.aspace.lock();
168    let length = align_up_4k(length);
169    let start_addr = VirtAddr::from(addr);
170    aspace.unmap(start_addr, length)?;
171    axhal::arch::flush_tlb(None);
172    Ok(0)
173}
174
175pub fn sys_mprotect(addr: usize, length: usize, prot: u32) -> LinuxResult<isize> {
176    // TODO: implement PROT_GROWSUP & PROT_GROWSDOWN
177    let Some(permission_flags) = MmapProt::from_bits(prot) else {
178        return Err(LinuxError::EINVAL);
179    };
180    if permission_flags.contains(MmapProt::GROWDOWN | MmapProt::GROWSUP) {
181        return Err(LinuxError::EINVAL);
182    }
183
184    let curr = current();
185    let process_data = curr.task_ext().process_data();
186    let mut aspace = process_data.aspace.lock();
187    let length = align_up_4k(length);
188    let start_addr = VirtAddr::from(addr);
189    aspace.protect(start_addr, length, permission_flags.into())?;
190
191    Ok(0)
192}