Browse Source

fix: mmaped pages of child procs may be empty

greatbridf 2 years ago
parent
commit
f41f73a8b1
3 changed files with 64 additions and 54 deletions
  1. 11 12
      include/kernel/mm.hpp
  2. 33 29
      src/kernel/interrupt.cpp
  3. 20 13
      src/kernel/mem.cpp

+ 11 - 12
include/kernel/mm.hpp

@@ -18,18 +18,18 @@
 
 constexpr size_t THREAD_KERNEL_STACK_SIZE = 2 * PAGE_SIZE;
 
+constexpr uint32_t PAGE_COW = (1 << 0);
+constexpr uint32_t PAGE_MMAP = (1 << 1);
+#define PAGE_COW PAGE_COW
+#define PAGE_MMAP PAGE_MMAP
+
 struct page {
     page_t phys_page_id;
     size_t* ref_count;
     // 0 :11 : pte_index
     // 12:31 : pt_page
     uint32_t pg_pteidx;
-    union {
-        uint32_t v;
-        struct {
-            uint32_t cow : 1;
-        } in;
-    } attr;
+    uint32_t attr;
 };
 
 // private memory mapping
@@ -123,7 +123,7 @@ public:
         return (start >= m_end || end <= m_start);
     }
 
-    int append_page(page* pg, bool present, bool write, bool priv, bool cow);
+    int append_page(page& pg, uint32_t attr, bool priv);
 };
 
 namespace kernel {
@@ -250,17 +250,16 @@ public:
     {
         auto area = this->addarea(
             src.start, src.attr.in.write, src.attr.in.system);
+
         if (src.mapped_file) {
             area->mapped_file = src.mapped_file;
             area->file_offset = src.file_offset;
         }
 
         for (auto& pg : *src.pgs) {
-            if (area->append_page(&pg,
-                    true,
-                    src.attr.in.write,
-                    src.attr.in.system,
-                    true)
+            if (area->append_page(pg,
+                    PAGE_COW | (pg.attr & PAGE_MMAP),
+                    src.attr.in.system)
                 != GB_OK) {
                 return GB_FAILED;
             }

+ 33 - 29
src/kernel/interrupt.cpp

@@ -191,11 +191,10 @@ extern "C" void int14_handler(int14_data* d)
     if (unlikely(d->error_code.present == 0 && !mm_area->mapped_file))
         _int14_panic(d->v_eip, d->l_addr, d->error_code);
 
-    // copy on write
-    if (page->attr.in.cow == 1) {
+    if (page->attr & PAGE_COW) {
         // if it is a dying page
         if (*page->ref_count == 1) {
-            page->attr.in.cow = 0;
+            page->attr &= ~PAGE_COW;
             pte->in.p = 1;
             pte->in.a = 0;
             pte->in.rw = mm_area->attr.in.write;
@@ -204,15 +203,13 @@ extern "C" void int14_handler(int14_data* d)
         // duplicate the page
         page_t new_page = __alloc_raw_page();
 
-        // memory mapped
-        if (d->error_code.present == 0)
-            pte->in.p = 1;
-
-        kernel::paccess pdst(new_page), psrc(page->phys_page_id);
-        auto* new_page_data = (char*)pdst.ptr();
-        auto* src = psrc.ptr();
-        assert(new_page_data && src);
-        memcpy(new_page_data, src, PAGE_SIZE);
+        {
+            kernel::paccess pdst(new_page), psrc(page->phys_page_id);
+            auto* new_page_data = (char*)pdst.ptr();
+            auto* src = psrc.ptr();
+            assert(new_page_data && src);
+            memcpy(new_page_data, src, PAGE_SIZE);
+        }
 
         pte->in.page = new_page;
         pte->in.rw = mm_area->attr.in.write;
@@ -221,25 +218,32 @@ extern "C" void int14_handler(int14_data* d)
         --*page->ref_count;
 
         page->ref_count = types::pnew<types::kernel_ident_allocator>(page->ref_count, 1);
-        page->attr.in.cow = 0;
+        page->attr &= ~PAGE_COW;
         page->phys_page_id = new_page;
+    }
 
-        // memory mapped
-        if (d->error_code.present == 0) {
-            size_t offset = align_down<12>((uint32_t)d->l_addr);
-            offset -= (uint32_t)mm_area->start;
-
-            int n = vfs_read(
-                mm_area->mapped_file,
-                new_page_data,
-                PAGE_SIZE,
-                mm_area->file_offset + offset,
-                PAGE_SIZE);
-
-            // TODO: send SIGBUS if offset is greater than real size
-            if (n != PAGE_SIZE)
-                memset(new_page_data + n, 0x00, PAGE_SIZE - n);
-        }
+    if (page->attr & PAGE_MMAP) {
+        pte->in.p = 1;
+
+        size_t offset = align_down<12>((uint32_t)d->l_addr);
+        offset -= (uint32_t)mm_area->start;
+
+        kernel::paccess pa(page->phys_page_id);
+        auto* data = (char*)pa.ptr();
+        assert(data);
+
+        int n = vfs_read(
+            mm_area->mapped_file,
+            data,
+            PAGE_SIZE,
+            mm_area->file_offset + offset,
+            PAGE_SIZE);
+
+        // TODO: send SIGBUS if offset is greater than real size
+        if (n != PAGE_SIZE)
+            memset(data + n, 0x00, PAGE_SIZE - n);
+
+        page->attr &= ~PAGE_MMAP;
     }
 }
 

+ 20 - 13
src/kernel/mem.cpp

@@ -126,7 +126,7 @@ page allocate_page(void)
         .phys_page_id = __alloc_raw_page(),
         .ref_count = types::_new<types::kernel_ident_allocator, size_t>(0),
         .pg_pteidx = 0,
-        .attr { 0 },
+        .attr = 0,
     };
 }
 
@@ -228,7 +228,7 @@ inline void map_raw_page_to_pte(
     pte->in.page = page;
 }
 
-int mm::append_page(page* pg, bool present, bool write, bool priv, bool cow)
+int mm::append_page(page& pg, uint32_t attr, bool priv)
 {
     void* addr = this->end();
     kernel::paccess pa(this->owner->m_pd);
@@ -263,24 +263,31 @@ int mm::append_page(page* pg, bool present, bool write, bool priv, bool cow)
     int pti = v_to_pti(addr);
     pte += pti;
 
-    map_raw_page_to_pte(pte, pg->phys_page_id, present, (write && !cow), priv);
+    map_raw_page_to_pte(
+        pte,
+        pg.phys_page_id,
+        !(attr & PAGE_MMAP),
+        false,
+        priv);
 
     kernel::pfree(pt_pg);
 
-    if (unlikely(cow && !pg->attr.in.cow)) {
-        kernel::paccess pa(pg->pg_pteidx >> 12);
+    if (unlikely((attr & PAGE_COW) && !(pg.attr & PAGE_COW))) {
+        kernel::paccess pa(pg.pg_pteidx >> 12);
         auto* pg_pte = (pte_t*)pa.ptr();
         assert(pg_pte);
-        pg_pte += (pg->pg_pteidx & 0xfff);
-        pg->attr.in.cow = 1;
+        pg_pte += (pg.pg_pteidx & 0xfff);
+        pg.attr |= PAGE_COW;
         pg_pte->in.rw = 0;
         pg_pte->in.a = 0;
         invalidate_tlb(addr);
     }
-    ++*pg->ref_count;
+    ++*pg.ref_count;
 
-    auto iter = this->pgs->emplace_back(*pg);
+    auto iter = this->pgs->emplace_back(pg);
     iter->pg_pteidx = (pt_pg << 12) + pti;
+    iter->attr = attr;
+
     return GB_OK;
 }
 
@@ -316,7 +323,7 @@ int mmap(
     mm->file_offset = offset;
 
     for (size_t i = 0; i < n_pgs; ++i)
-        mm->append_page(&empty_page, false, write, priv, true);
+        mm->append_page(empty_page, PAGE_MMAP | PAGE_COW, priv);
 
     return GB_OK;
 }
@@ -331,14 +338,14 @@ void init_mem(void)
     auto heap_mm = kernel_mms->addarea(KERNEL_HEAP_START, true, true);
 
     // create empty_page struct
-    empty_page.attr.in.cow = 0;
+    empty_page.attr = 0;
     empty_page.phys_page_id = EMPTY_PAGE;
-    empty_page.ref_count = types::_new<types::kernel_ident_allocator, size_t>(1);
+    empty_page.ref_count = types::_new<types::kernel_ident_allocator, size_t>(2);
     empty_page.pg_pteidx = 0x00002000;
 
     // 0xd0000000 to 0xd4000000 or 3.5GiB, size 64MiB
     while (heap_mm->pgs->size() < 64 * 1024 * 1024 / PAGE_SIZE)
-        heap_mm->append_page(&empty_page, true, true, true, true);
+        heap_mm->append_page(empty_page, PAGE_COW, true);
 
     types::__allocator::init_kernel_heap(KERNEL_HEAP_START,
         vptrdiff(KERNEL_HEAP_LIMIT, KERNEL_HEAP_START));