Przeglądaj źródła

feat(syscall): add mmap and munmap

greatbridf 1 rok temu
rodzic
commit
50b9527a09
4 zmienionych plików z 215 dodań i 14 usunięć
  1. 24 0
      gblibc/include/sys/mman.h
  2. 28 14
      include/kernel/mm.hpp
  3. 95 0
      src/kernel/mem.cpp
  4. 68 0
      src/kernel/syscall.cpp

+ 24 - 0
gblibc/include/sys/mman.h

@@ -0,0 +1,24 @@
+#ifndef __GBLIBC_SYS_MMAN_H
+#define __GBLIBC_SYS_MMAN_H
+
+#include <sys/types.h>
+
+#define MAP_SHARED 0x01
+#define MAP_PRIVATE 0x02
+#define MAP_FIXED 0x10
+#define MAP_ANONYMOUS 0x20
+
+#define PROT_NONE 0
+#define PROT_READ 1
+#define PROT_WRITE 2
+#define PROT_EXEC 4
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 28 - 14
include/kernel/mm.hpp

@@ -160,7 +160,7 @@ public:
     constexpr void* end() const noexcept
     { return vptradd(start, pgs->size() * PAGE_SIZE); }
     constexpr bool is_kernel_space() const noexcept
-    { return start >= std::bit_cast<void*>(0xc0000000); }
+    { return attr.system; }
     constexpr bool is_avail(void* ostart, void* oend) const noexcept
     {
         void* m_start = start;
@@ -170,17 +170,32 @@ public:
     }
 
     void append_page(pd_t pd, const page& pg, uint32_t attr, bool priv);
+
+    /**
+     * @brief Splits the memory block at the specified address.
+     * 
+     * @param addr The address at which the memory block will be split.
+     * @return The new memory block created after splitting.
+     */
+    mm split(void* addr);
+
+    constexpr bool operator<(const mm& rhs) const noexcept
+    { return end() <= rhs.start; }
+    constexpr bool operator<(void* rhs) const noexcept
+    { return end() <= rhs; }
+    friend constexpr bool operator<(void* lhs, const mm& rhs) noexcept
+    { return lhs < rhs.start; }
 };
 
 class mm_list {
 private:
     struct comparator {
         constexpr bool operator()(const mm& lhs, const mm& rhs) const noexcept
-        { return lhs.start < rhs.start; }
+        { return lhs < rhs; }
         constexpr bool operator()(const mm& lhs, void* rhs) const noexcept
-        { return lhs.end() <= rhs; }
+        { return lhs < rhs; }
         constexpr bool operator()(void* lhs, const mm& rhs) const noexcept
-        { return lhs < rhs.start; }
+        { return lhs < rhs; }
     };
 
 public:
@@ -217,6 +232,10 @@ public:
     int register_brk(void* addr);
     void* set_brk(void* addr);
 
+    void* find_avail(void* hint, size_t len, bool priv) const;
+
+    int unmap(void* start, size_t len, bool priv);
+
     constexpr mm& addarea(void* start, bool w, bool system)
     {
         auto [ iter, inserted ] = m_areas.emplace(mm {
@@ -243,13 +262,13 @@ public:
                 continue;
             }
 
-            this->unmap(iter);
+            this->unmap(*iter);
             iter = m_areas.erase(iter);
         }
         m_brk = nullptr;
     }
 
-    inline void unmap(iterator area)
+    inline void unmap(mm& area)
     {
         int i = 0;
 
@@ -258,7 +277,7 @@ public:
         // should be faster. otherwise, we use movl cr3
         // bool should_invlpg = (area->pgs->size() > 4);
 
-        for (auto& pg : *area->pgs) {
+        for (auto& pg : *area.pgs) {
             kernel::paccess pa(pg.pg_pteidx >> 12);
             auto pt = (pt_t)pa.ptr();
             assert(pt);
@@ -267,16 +286,11 @@ public:
 
             free_page(&pg);
 
-            invalidate_tlb((uint32_t)area->start + (i++) * PAGE_SIZE);
+            invalidate_tlb((uint32_t)area.start + (i++) * PAGE_SIZE);
         }
-        types::pdelete<types::kernel_ident_allocator>(area->pgs);
+        types::pdelete<types::kernel_ident_allocator>(area.pgs);
     }
 
-    constexpr iterator iterfind(void* lp)
-    { return m_areas.find(lp); }
-    constexpr const_iterator iterfind(void* lp) const
-    { return m_areas.find(lp); }
-
     constexpr mm* find(void* lp)
     {
         auto iter = m_areas.find(lp);

+ 95 - 0
src/kernel/mem.cpp

@@ -281,6 +281,76 @@ void* mm_list::set_brk(void* addr)
     return curbrk;
 }
 
+void* mm_list::find_avail(void* hint, size_t len, bool priv) const
+{
+    void* addr = hint;
+    if (!addr) {
+        // default value of mmapp'ed area
+        if (!priv)
+            addr = (void*)0x40000000;
+        else
+            addr = (void*)0xe0000000;
+    }
+
+    while (!is_avail(addr, len)) {
+        auto iter = m_areas.lower_bound(addr);
+        if (iter == m_areas.end())
+            return nullptr;
+
+        addr = iter->end();
+    }
+
+    if (!priv && addr >= (void*)0xc0000000)
+        return nullptr;
+
+    return addr;
+}
+
+// TODO: write dirty pages to file
+int mm_list::unmap(void* start, size_t len, bool system)
+{
+    ptr_t addr = (ptr_t)start;
+    void* end = vptradd(start, align_up<12>(len));
+
+    // standard says that addr and len MUST be
+    // page-aligned or the call is invalid
+    if (addr % PAGE_SIZE != 0)
+        return -EINVAL;
+
+    // if doing user mode unmapping, check area privilege
+    if (!system) {
+        if (addr >= 0xc0000000 || end > (void*)0xc0000000)
+            return -EINVAL;
+    }
+
+    auto iter = m_areas.lower_bound(start);
+
+    for ( ; iter != m_areas.end() && *iter < end; ) {
+        if (!(start < *iter) && start != iter->start) {
+            mm newmm = iter->split(start);
+            unmap(newmm);
+            ++iter;
+            continue;
+        }
+        else if (!(*iter < end)) {
+            mm newmm = iter->split(end);
+            unmap(*iter);
+            m_areas.erase(iter);
+
+            bool inserted;
+            std::tie(std::ignore, inserted) = m_areas.emplace(std::move(newmm));
+            assert(inserted);
+            break;
+        }
+        else {
+            unmap(*iter);
+            iter = m_areas.erase(iter);
+        }
+    }
+
+    return GB_OK;
+}
+
 mm& mm_list::add_empty_area(void *start, std::size_t page_count,
     uint32_t page_attr, bool w, bool system)
 {
@@ -367,6 +437,31 @@ void mm::append_page(pd_t pd, const page& pg, uint32_t attr, bool priv)
     emplaced.attr = attr;
 }
 
+mm mm::split(void *addr)
+{
+    assert(addr > start && addr < end());
+    assert((ptr_t)addr % PAGE_SIZE == 0);
+
+    size_t this_count = vptrdiff(addr, start) / PAGE_SIZE;
+    size_t new_count = pgs->size() - this_count;
+
+    mm newmm {
+        .start = addr,
+        .attr { attr },
+        .pgs = types::_new<types::kernel_ident_allocator, mm::pages_vector>(
+        ),
+        .mapped_file = mapped_file,
+        .file_offset = file_offset,
+    };
+
+    for (size_t i = 0; i < new_count; ++i) {
+        newmm.pgs->emplace_back(pgs->back());
+        pgs->pop_back();
+    }
+
+    return newmm;
+}
+
 int mmap(
     void* hint,
     size_t len,

+ 68 - 0
src/kernel/syscall.cpp

@@ -3,6 +3,7 @@
 #include <assert.h>
 #include <bits/ioctl.h>
 #include <sys/prctl.h>
+#include <sys/mman.h>
 #include <time.h>
 #include <kernel/user/thread_local.hpp>
 #include <kernel/errno.h>
@@ -576,6 +577,71 @@ int _syscall_brk(interrupt_stack* data)
     return (int)current_process->mms.set_brk(addr);
 }
 
+int _syscall_mmap_pgoff(interrupt_stack* data)
+{
+    SYSCALL_ARG1(void*, addr);
+    SYSCALL_ARG2(size_t, len);
+    SYSCALL_ARG3(int, prot);
+    SYSCALL_ARG4(int, flags);
+    SYSCALL_ARG5(int, fd);
+    SYSCALL_ARG6(off_t, pgoffset);
+
+    if ((ptr_t)addr % PAGE_SIZE != 0)
+        return -EINVAL;
+    if (len == 0)
+        return -EINVAL;
+
+    len = align_up<12>(len);
+
+    // TODO: shared mappings
+    if (flags & MAP_SHARED)
+        return -ENOMEM;
+
+    if (flags & MAP_ANONYMOUS) {
+        if (fd != -1)
+            return -EINVAL;
+        if (pgoffset != 0)
+            return -EINVAL;
+
+        if (!(flags & MAP_PRIVATE))
+            return -EINVAL;
+
+        auto& mms = current_process->mms;
+
+        // do unmapping, equal to munmap, MAP_FIXED set
+        if (prot == PROT_NONE) {
+            auto ret = mms.unmap(addr, len, false);
+            if (ret != GB_OK)
+                return ret;
+        }
+        else {
+            // TODO: add NULL check in mm_list
+            if (!addr || !mms.is_avail(addr, len)) {
+                if (flags & MAP_FIXED)
+                    return -ENOMEM;
+                addr = mms.find_avail(addr, len, false);
+            }
+
+            // TODO: append pages to the end of area if possible
+            mms.add_empty_area(addr, len / PAGE_SIZE,
+                PAGE_COW, prot & PROT_WRITE, false);
+        }
+    }
+
+    return (int)addr;
+}
+
+int _syscall_munmap(interrupt_stack* data)
+{
+    SYSCALL_ARG1(void*, addr);
+    SYSCALL_ARG2(size_t, len);
+
+    if ((ptr_t)addr % PAGE_SIZE != 0)
+        return -EINVAL;
+
+    return current_process->mms.unmap(addr, len, false);
+}
+
 extern "C" void syscall_entry(interrupt_stack* data)
 {
     int syscall_no = SYSCALL_NO;
@@ -619,11 +685,13 @@ void init_syscall(void)
     syscall_handlers[0x3f] = _syscall_dup2;
     syscall_handlers[0x40] = _syscall_getppid;
     syscall_handlers[0x42] = _syscall_setsid;
+    syscall_handlers[0x5b] = _syscall_munmap;
     syscall_handlers[0x84] = _syscall_getdents;
     syscall_handlers[0x92] = _syscall_writev;
     syscall_handlers[0x93] = _syscall_getsid;
     syscall_handlers[0xac] = _syscall_prctl;
     syscall_handlers[0xb7] = _syscall_getcwd;
+    syscall_handlers[0xc0] = _syscall_mmap_pgoff;
     syscall_handlers[0xc7] = _syscall_getuid;
     syscall_handlers[0xf3] = _syscall_set_thread_area;
     syscall_handlers[0xfc] = _syscall_exit; // we implement exit_group as exit for now