feat: (vm) memory mapping support: mmap/munmap
authorMinep <zelong56@gmail.com>
Sun, 20 Nov 2022 22:28:38 +0000 (22:28 +0000)
committerMinep <zelong56@gmail.com>
Sun, 20 Nov 2022 22:28:38 +0000 (22:28 +0000)
fix: (vm_region) ensure the ordering of regions.
fix: (hal/rnd) correct the clobbered register name.

16 files changed:
README.md
lunaix-os/includes/hal/rnd.h
lunaix-os/includes/lunaix/ds/llist.h
lunaix-os/includes/lunaix/mm/mm.h
lunaix-os/includes/lunaix/mm/mmap.h [new file with mode: 0644]
lunaix-os/includes/lunaix/mm/page.h
lunaix-os/includes/lunaix/mm/region.h
lunaix-os/includes/lunaix/process.h
lunaix-os/includes/lunaix/spike.h
lunaix-os/includes/lunaix/syscall.h
lunaix-os/kernel/asm/x86/pfault.c
lunaix-os/kernel/asm/x86/syscall.S
lunaix-os/kernel/mm/mmap.c [new file with mode: 0644]
lunaix-os/kernel/mm/region.c
lunaix-os/kernel/process/process.c
lunaix-os/kernel/process/sched.c

index da0213069a5c74659dbf41eadc1730c54dc6ef60..c3c7cddcaf3c5286219b63aecf5d15a7977338bd 100644 (file)
--- a/README.md
+++ b/README.md
@@ -23,7 +23,7 @@ LunaixOS - 一个简单的,详细的,POSIX兼容的(但愿!),带有
 + 内存管理与按需分页(Demand Paging)
 + 键盘输入
 + 多进程
-+ 47个常见的Linux/POSIX系统调用([附录1](#appendix1))
++ 49个常见的Linux/POSIX系统调用([附录1](#appendix1))
 + 用户模式
 + 信号机制
 + PCI 3.0
@@ -216,6 +216,8 @@ qemu-img create -f vdi machine/disk0.vdi 128M
 2. `ioctl(2)`
 2. `getpgid(2)`
 2. `setpgid(2)`
+2. `mmap(2)`※
+2. `munmap(2)`※
 
 **LunaixOS自有**
 
index 8ab544a1e92d53a449803d193677af89daa1edaf..1b4a8e3b3b6a65042d0d93653c114427f9319cf4 100644 (file)
@@ -13,7 +13,7 @@ rnd_fill(void* data, size_t len)
                  "subl $4, %1\n"
                  "jnz 1b" ::"r"((ptr_t)data),
                  "r"((len & ~0x3))
-                 : "%al");
+                 : "%eax");
 }
 
 int
index a4797c34d3f889ab9c0e970ed693607ee7953fbe..65ba474d750328d80ff3c08eba0611c9cb076f30 100644 (file)
@@ -50,6 +50,12 @@ llist_prepend(struct llist_header* head, struct llist_header* elem)
     __llist_add(elem, head, head->next);
 }
 
+static inline void
+llist_insert_after(struct llist_header* head, struct llist_header* elem)
+{
+    __llist_add(elem, head, head->next);
+}
+
 static inline void
 llist_delete(struct llist_header* elem)
 {
index 6585916e4136d48cb2c0afa96ac341c4babfb65f..07d3d791df3f12fa05c6bbc52a6a7c11d5bc1243 100644 (file)
@@ -3,6 +3,8 @@
 
 #include <lunaix/ds/llist.h>
 #include <lunaix/ds/mutex.h>
+#include <lunaix/fs.h>
+#include <lunaix/types.h>
 
 typedef struct
 {
@@ -48,9 +50,11 @@ typedef struct
 struct mm_region
 {
     struct llist_header head;
-    unsigned long start;
-    unsigned long end;
-    unsigned int attr;
+    struct v_file* mfile;
+    u32_t offset;
+    ptr_t start;
+    ptr_t end;
+    u32_t attr;
 };
 
 #endif /* __LUNAIX_MM_H */
diff --git a/lunaix-os/includes/lunaix/mm/mmap.h b/lunaix-os/includes/lunaix/mm/mmap.h
new file mode 100644 (file)
index 0000000..78c12d6
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef __LUNAIX_MMAP_H
+#define __LUNAIX_MMAP_H
+
+#include <lunaix/fs.h>
+#include <lunaix/types.h>
+
+void*
+mem_map(ptr_t pd_ref,
+        struct llist_header* regions,
+        void* addr,
+        struct v_file* file,
+        u32_t offset,
+        size_t length,
+        u32_t attrs);
+
+void*
+mem_unmap(ptr_t mnt, struct llist_header* regions, void* addr, size_t length);
+
+#endif /* __LUNAIX_MMAP_H */
index f2c45de00bcfd0e81b365759cf5c5e818a81b14a..37496a384a63583b465325041001e6f56f612012 100644 (file)
 #define HAS_FLAGS(entry, flags) ((PG_ENTRY_FLAGS(entry) & (flags)) == flags)
 #define CONTAINS_FLAGS(entry, flags) (PG_ENTRY_FLAGS(entry) & (flags))
 
-#define PG_PREM_R PG_PRESENT
-#define PG_PREM_RW PG_PRESENT | PG_WRITE
-#define PG_PREM_UR PG_PRESENT | PG_ALLOW_USER
-#define PG_PREM_URW PG_PRESENT | PG_WRITE | PG_ALLOW_USER
+#define PG_PREM_R (PG_PRESENT)
+#define PG_PREM_RW (PG_PRESENT | PG_WRITE)
+#define PG_PREM_UR (PG_PRESENT | PG_ALLOW_USER)
+#define PG_PREM_URW (PG_PRESENT | PG_WRITE | PG_ALLOW_USER)
 
 // 用于对PD进行循环映射,因为我们可能需要对PD进行频繁操作,我们在这里禁用TLB缓存
 #define T_SELF_REF_PERM PG_PREM_RW | PG_DISABLE_CACHE | PG_WRITE_THROUGH
index 9978d728f808e60d6a23fe312fadce315d4595e0..c1c8074e756c096b4d50ffcfc8db0f07f62a8e0c 100644 (file)
@@ -3,19 +3,16 @@
 
 #include <lunaix/mm/mm.h>
 
-void
-region_add(struct mm_region* proc,
-           unsigned long start,
-           unsigned long end,
-           unsigned int attr);
+struct mm_region*
+region_add(struct llist_header* lead, ptr_t start, ptr_t end, u32_t attr);
 
 void
-region_release_all(struct mm_region* proc);
+region_release_all(struct llist_header* lead);
 
 struct mm_region*
-region_get(struct mm_region* proc, unsigned long vaddr);
+region_get(struct llist_header* lead, unsigned long vaddr);
 
 void
-region_copy(struct mm_region* src, struct mm_region* dest);
+region_copy(struct llist_header* src, struct llist_header* dest);
 
 #endif /* __LUNAIX_REGION_H */
index 7674a925ab182dc7b90bfa9f41cff7c59f3d663a..1eacf127a28e31ea567a42f12b8b680e69533f8a 100644 (file)
@@ -28,7 +28,7 @@
 struct proc_mm
 {
     heap_context_t u_heap;
-    struct mm_region regions;
+    struct llist_header regions;
 };
 
 struct proc_sigstate
index 298593d5d3015e540f43650fb1ebabe5a948538b..91c92fcaa47bdc597ddffe89ff46337be6fc0f56 100644 (file)
@@ -11,7 +11,7 @@
 // 除法 v/(2^k) 向下取整
 #define FLOOR(v, k) ((v) >> (k))
 
-// 获取v最近的最大k倍数
+// 获取v最近的最大k倍数 (k=2^n)
 #define ROUNDUP(v, k) (((v) + (k)-1) & ~((k)-1))
 
 // 获取v最近的最小k倍数 (k=2^m)
index 82fc4b9b6bc96f0998809f2f36b27682b28819f1..04ae40bc88964e7d1a1d242da69ef561a8dd4514 100644 (file)
@@ -60,6 +60,9 @@
 
 #define __SYSCALL_syslog 51
 
+#define __SYSCALL_mmap 52
+#define __SYSCALL_munmap 53
+
 #define __SYSCALL_MAX 0x100
 
 #ifndef __ASM__
@@ -93,6 +96,11 @@ syscall_install();
     asmlinkage rettype __lxsys_##name(                                         \
       __PARAM_MAP4(t1, p1, t2, p2, t3, p3, t4, p4))
 
+#define __DEFINE_LXSYSCALL5(                                                   \
+  rettype, name, t1, p1, t2, p2, t3, p3, t4, p4, t5, p5)                       \
+    asmlinkage rettype __lxsys_##name(                                         \
+      __PARAM_MAP5(t1, p1, t2, p2, t3, p3, t4, p4, t5, p5))
+
 #define __SYSCALL_INTERRUPTIBLE(code)                                          \
     asm("sti");                                                                \
     { code };                                                                  \
@@ -137,6 +145,14 @@ syscall_install();
         ___DOINT33(__SYSCALL_##name, rettype)                                  \
     }
 
+#define __LXSYSCALL5(rettype, name, t1, p1, t2, p2, t3, p3, t4, p4, t5, p5)    \
+    static rettype name(__PARAM_MAP5(t1, p1, t2, p2, t3, p3, t4, p4, t5, p5))  \
+    {                                                                          \
+        asm("movl %0, %%ebp\n" ::"r"(p5), "b"(p1), "c"(p2), "d"(p3), "D"(p4)   \
+            : "%ebp");                                                         \
+        ___DOINT33(__SYSCALL_##name, rettype)                                  \
+    }
+
 #define __LXSYSCALL2_VARG(rettype, name, t1, p1, t2, p2)                       \
     __attribute__((noinline)) static rettype name(                             \
       __PARAM_MAP2(t1, p1, t2, p2), ...)                                       \
index fcd52cc26480058f3a83b2144e8b46ee0b2c949a..64986190bddf58c85cc5c2cc3c792267d6730151 100644 (file)
@@ -66,10 +66,10 @@ intr_routine_page_fault(const isr_param* param)
         goto segv_term;
     }
 
-    if (!(*pte)) {
-        // Invalid location
-        goto segv_term;
-    }
+    // if (!(*pte)) {
+    //     // Invalid location
+    //     goto segv_term;
+    // }
 
     uintptr_t loc = *pte & ~0xfff;
 
@@ -78,7 +78,33 @@ intr_routine_page_fault(const isr_param* param)
     if ((hit_region->attr & REGION_WRITE) && (*pte & 0xfff) && !loc) {
         cpu_invplg(pte);
         uintptr_t pa = pmm_alloc_page(__current->pid, 0);
+        if (!pa) {
+            goto oom;
+        }
+
+        *pte = *pte | pa | PG_PRESENT;
+        goto resolved;
+    }
+
+    // if mfile is set, then it is a mem map
+    if (hit_region->mfile) {
+        struct v_file* file = hit_region->mfile;
+        u32_t offset =
+          (ptr - hit_region->start) & (PG_SIZE - 1) + hit_region->offset;
+        uintptr_t pa = pmm_alloc_page(__current->pid, 0);
+
+        if (!pa) {
+            goto oom;
+        }
+
+        cpu_invplg(pte);
         *pte = *pte | pa | PG_PRESENT;
+        int errno = file->ops->read_page(
+          file->inode, ptr & (PG_SIZE - 1), PG_SIZE, offset);
+        if (errno < 0) {
+            kprintf(KERROR "fail to read page (%d)\n", errno);
+            goto segv_term;
+        }
         goto resolved;
     }
 
@@ -87,6 +113,8 @@ intr_routine_page_fault(const isr_param* param)
     while (1)
         ;
 
+oom:
+    kprintf(KERROR "out of memory\n");
 segv_term:
     kprintf(KERROR "(pid: %d) Segmentation fault on %p (%p:%p)\n",
             __current->pid,
index 93ea1c7bf63230b1faf7c330c4c71d4142b1d483..bd51976d64fc2a5f9801dba7e7fde6cb8a3ef44d 100644 (file)
@@ -59,6 +59,8 @@
         .long __lxsys_getpgid
         .long __lxsys_setpgid       /* 50 */
         .long __lxsys_syslog
+        .long __lxsys_mmap
+        .long __lxsys_munmap
         2:
         .rept __SYSCALL_MAX - (2b - 1b)/4
             .long 0
diff --git a/lunaix-os/kernel/mm/mmap.c b/lunaix-os/kernel/mm/mmap.c
new file mode 100644 (file)
index 0000000..8eb6b20
--- /dev/null
@@ -0,0 +1,141 @@
+#include <lunaix/mm/mmap.h>
+#include <lunaix/mm/pmm.h>
+#include <lunaix/mm/region.h>
+#include <lunaix/mm/valloc.h>
+#include <lunaix/mm/vmm.h>
+#include <lunaix/spike.h>
+
+#include <lunaix/syscall.h>
+
+void*
+mem_map(ptr_t mnt,
+        struct llist_header* regions,
+        void* addr,
+        struct v_file* file,
+        u32_t offset,
+        size_t length,
+        u32_t proct)
+{
+    if (!length || (length & (PG_SIZE - 1)) || (offset & (PG_SIZE - 1))) {
+        __current->k_status = EINVAL;
+        return (void*)-1;
+    }
+
+    // read_page is not supported
+    if (!file->ops->read_page) {
+        __current->k_status = ENODEV;
+        return (void*)-1;
+    }
+
+    ptr_t last_end = USER_START;
+    struct mm_region *pos, *n;
+    llist_for_each(pos, n, regions, head)
+    {
+        if (pos->start - last_end >= length && last_end >= addr) {
+            goto found;
+        }
+        last_end = pos->end;
+    }
+
+    __current->k_status = ENOMEM;
+    return (void*)-1;
+
+found:
+    addr = last_end;
+    ptr_t end = addr + length;
+
+    struct mm_region* region = region_add(regions, addr, end, proct);
+    region->mfile = file;
+    region->offset = offset;
+
+    u32_t attr = PG_ALLOW_USER;
+    if ((proct & REGION_WRITE)) {
+        attr |= PG_WRITE;
+    }
+
+    for (u32_t i = 0; i < length; i += PG_SIZE) {
+        vmm_set_mapping(mnt, addr + i, 0, attr, 0);
+    }
+
+    return addr;
+}
+
+void*
+mem_unmap(ptr_t mnt, struct llist_header* regions, void* addr, size_t length)
+{
+    length = ROUNDUP(length, PG_SIZE);
+    ptr_t cur_addr = ROUNDDOWN((ptr_t)addr, PG_SIZE);
+    struct mm_region *pos, *n;
+
+    llist_for_each(pos, n, regions, head)
+    {
+        if (pos->start >= cur_addr) {
+            break;
+        }
+    }
+
+    while (&pos->head != regions && cur_addr > pos->start) {
+        u32_t l = pos->end - cur_addr;
+        pos->end = cur_addr;
+
+        if (l > length) {
+            // unmap cause discontinunity in a memory region -  do split
+            struct mm_region* region = valloc(sizeof(struct mm_region));
+            *region = *pos;
+            region->start = cur_addr + length;
+            llist_insert_after(&pos->head, &region->head);
+            l = length;
+        }
+
+        for (size_t i = 0; i < l; i += PG_SIZE) {
+            ptr_t pa = vmm_del_mapping(mnt, cur_addr + i);
+            if (pa) {
+                pmm_free_page(__current->pid, pa);
+            }
+        }
+
+        n = container_of(pos->head.next, typeof(*pos), head);
+        if (pos->end == pos->start) {
+            llist_delete(&pos->head);
+            vfree(pos);
+        }
+
+        pos = n;
+        length -= l;
+        cur_addr += length;
+    }
+}
+
+__DEFINE_LXSYSCALL5(void*,
+                    mmap,
+                    void*,
+                    addr,
+                    size_t,
+                    length,
+                    int,
+                    proct,
+                    int,
+                    fd,
+                    size_t,
+                    offset)
+{
+    int errno = 0;
+    struct v_fd* vfd;
+    if ((errno = vfs_getfd(fd, &vfd))) {
+        __current->k_status = errno;
+        return (void*)-1;
+    }
+
+    return mem_map(PD_REFERENCED,
+                   &__current->mm.regions,
+                   addr,
+                   vfd->file,
+                   offset,
+                   length,
+                   proct);
+}
+
+__DEFINE_LXSYSCALL2(void, munmap, void*, addr, size_t, length)
+{
+    return mem_unmap(PD_REFERENCED, &__current->mm.regions, addr, length);
+}
\ No newline at end of file
index bd3b26ec0d5fc1009b0e9cf424869934816d2c03..0e0982d7d2d9b0915ed6065c7fb90deb79c000e0 100644 (file)
@@ -1,8 +1,8 @@
 #include <lunaix/mm/region.h>
 #include <lunaix/mm/valloc.h>
 
-void
-region_add(struct mm_region* regions,
+struct mm_region*
+region_add(struct llist_header* lead,
            unsigned long start,
            unsigned long end,
            unsigned int attr)
@@ -11,22 +11,36 @@ region_add(struct mm_region* regions,
 
     *region = (struct mm_region){ .attr = attr, .end = end, .start = start };
 
-    llist_append(&regions->head, &region->head);
+    if (llist_empty(lead)) {
+        llist_append(lead, &region->head);
+        return region;
+    }
+
+    struct mm_region *pos, *n;
+    llist_for_each(pos, n, lead, head)
+    {
+        if (start >= pos->end && end <= n->start) {
+            break;
+        }
+    }
+
+    llist_insert_after(&pos->head, &region->head);
+    return region;
 }
 
 void
-region_release_all(struct mm_region* regions)
+region_release_all(struct llist_header* lead)
 {
     struct mm_region *pos, *n;
 
-    llist_for_each(pos, n, &regions->head, head)
+    llist_for_each(pos, n, lead, head)
     {
         vfree(pos);
     }
 }
 
 void
-region_copy(struct mm_region* src, struct mm_region* dest)
+region_copy(struct llist_header* src, struct llist_header* dest)
 {
     if (!src) {
         return;
@@ -34,22 +48,22 @@ region_copy(struct mm_region* src, struct mm_region* dest)
 
     struct mm_region *pos, *n;
 
-    llist_for_each(pos, n, &src->head, head)
+    llist_for_each(pos, n, src, head)
     {
         region_add(dest, pos->start, pos->end, pos->attr);
     }
 }
 
 struct mm_region*
-region_get(struct mm_region* regions, unsigned long vaddr)
+region_get(struct llist_header* lead, unsigned long vaddr)
 {
-    if (!regions) {
+    if (llist_empty(lead)) {
         return NULL;
     }
 
     struct mm_region *pos, *n;
 
-    llist_for_each(pos, n, &regions->head, head)
+    llist_for_each(pos, n, lead, head)
     {
         if (pos->start <= vaddr && vaddr < pos->end) {
             return pos;
index 74c93807f57115ac85f582d60c5acdd58996d313..8df3f2c9ef8090422000c7dbf60116220730ccbe 100644 (file)
@@ -211,7 +211,7 @@ dup_proc()
     // 根据 mm_region 进一步配置页表
 
     struct mm_region *pos, *n;
-    llist_for_each(pos, n, &pcb->mm.regions.head, head)
+    llist_for_each(pos, n, &pcb->mm.regions, head)
     {
         // 如果写共享,则不作处理。
         if ((pos->attr & REGION_WSHARED)) {
index f0c0a988c97d59c9f30bdff81266ac7cac4aae44..d2196166b6b2bfc37ced58f303dbe31a15d2e490 100644 (file)
@@ -330,7 +330,7 @@ alloc_process()
     proc->fxstate =
       vzalloc_dma(512); // FXSAVE需要十六位对齐地址,使用DMA块(128位对齐)
 
-    llist_init_head(&proc->mm.regions.head);
+    llist_init_head(&proc->mm.regions);
     llist_init_head(&proc->tasks);
     llist_init_head(&proc->children);
     llist_init_head(&proc->grp_member);
@@ -401,7 +401,7 @@ destroy_process(pid_t pid)
     vfree_dma(proc->fxstate);
 
     struct mm_region *pos, *n;
-    llist_for_each(pos, n, &proc->mm.regions.head, head)
+    llist_for_each(pos, n, &proc->mm.regions, head)
     {
         vfree(pos);
     }