From c50b9a23b03c677efa3842536c363d368542e30b Mon Sep 17 00:00:00 2001 From: Minep Date: Sun, 20 Nov 2022 22:28:38 +0000 Subject: [PATCH] feat: (vm) memory mapping support: mmap/munmap fix: (vm_region) ensure the ordering of regions. fix: (hal/rnd) correct the clobbered register name. --- README.md | 4 +- lunaix-os/includes/hal/rnd.h | 2 +- lunaix-os/includes/lunaix/ds/llist.h | 6 ++ lunaix-os/includes/lunaix/mm/mm.h | 10 +- lunaix-os/includes/lunaix/mm/mmap.h | 19 ++++ lunaix-os/includes/lunaix/mm/page.h | 8 +- lunaix-os/includes/lunaix/mm/region.h | 13 +-- lunaix-os/includes/lunaix/process.h | 2 +- lunaix-os/includes/lunaix/spike.h | 2 +- lunaix-os/includes/lunaix/syscall.h | 16 +++ lunaix-os/kernel/asm/x86/pfault.c | 36 ++++++- lunaix-os/kernel/asm/x86/syscall.S | 2 + lunaix-os/kernel/mm/mmap.c | 141 ++++++++++++++++++++++++++ lunaix-os/kernel/mm/region.c | 34 +++++-- lunaix-os/kernel/process/process.c | 2 +- lunaix-os/kernel/process/sched.c | 4 +- 16 files changed, 265 insertions(+), 36 deletions(-) create mode 100644 lunaix-os/includes/lunaix/mm/mmap.h create mode 100644 lunaix-os/kernel/mm/mmap.c diff --git a/README.md b/README.md index da02130..c3c7cdd 100644 --- 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自有** diff --git a/lunaix-os/includes/hal/rnd.h b/lunaix-os/includes/hal/rnd.h index 8ab544a..1b4a8e3 100644 --- a/lunaix-os/includes/hal/rnd.h +++ b/lunaix-os/includes/hal/rnd.h @@ -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 diff --git a/lunaix-os/includes/lunaix/ds/llist.h b/lunaix-os/includes/lunaix/ds/llist.h index a4797c3..65ba474 100644 --- a/lunaix-os/includes/lunaix/ds/llist.h +++ b/lunaix-os/includes/lunaix/ds/llist.h @@ -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) { diff --git a/lunaix-os/includes/lunaix/mm/mm.h b/lunaix-os/includes/lunaix/mm/mm.h index 6585916..07d3d79 100644 --- a/lunaix-os/includes/lunaix/mm/mm.h +++ b/lunaix-os/includes/lunaix/mm/mm.h @@ -3,6 +3,8 @@ #include #include +#include +#include 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 index 0000000..78c12d6 --- /dev/null +++ b/lunaix-os/includes/lunaix/mm/mmap.h @@ -0,0 +1,19 @@ +#ifndef __LUNAIX_MMAP_H +#define __LUNAIX_MMAP_H + +#include +#include + +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 */ diff --git a/lunaix-os/includes/lunaix/mm/page.h b/lunaix-os/includes/lunaix/mm/page.h index f2c45de..37496a3 100644 --- a/lunaix-os/includes/lunaix/mm/page.h +++ b/lunaix-os/includes/lunaix/mm/page.h @@ -46,10 +46,10 @@ #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 diff --git a/lunaix-os/includes/lunaix/mm/region.h b/lunaix-os/includes/lunaix/mm/region.h index 9978d72..c1c8074 100644 --- a/lunaix-os/includes/lunaix/mm/region.h +++ b/lunaix-os/includes/lunaix/mm/region.h @@ -3,19 +3,16 @@ #include -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 */ diff --git a/lunaix-os/includes/lunaix/process.h b/lunaix-os/includes/lunaix/process.h index 7674a92..1eacf12 100644 --- a/lunaix-os/includes/lunaix/process.h +++ b/lunaix-os/includes/lunaix/process.h @@ -28,7 +28,7 @@ struct proc_mm { heap_context_t u_heap; - struct mm_region regions; + struct llist_header regions; }; struct proc_sigstate diff --git a/lunaix-os/includes/lunaix/spike.h b/lunaix-os/includes/lunaix/spike.h index 298593d..91c92fc 100644 --- a/lunaix-os/includes/lunaix/spike.h +++ b/lunaix-os/includes/lunaix/spike.h @@ -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) diff --git a/lunaix-os/includes/lunaix/syscall.h b/lunaix-os/includes/lunaix/syscall.h index 82fc4b9..04ae40b 100644 --- a/lunaix-os/includes/lunaix/syscall.h +++ b/lunaix-os/includes/lunaix/syscall.h @@ -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), ...) \ diff --git a/lunaix-os/kernel/asm/x86/pfault.c b/lunaix-os/kernel/asm/x86/pfault.c index fcd52cc..6498619 100644 --- a/lunaix-os/kernel/asm/x86/pfault.c +++ b/lunaix-os/kernel/asm/x86/pfault.c @@ -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, diff --git a/lunaix-os/kernel/asm/x86/syscall.S b/lunaix-os/kernel/asm/x86/syscall.S index 93ea1c7..bd51976 100644 --- a/lunaix-os/kernel/asm/x86/syscall.S +++ b/lunaix-os/kernel/asm/x86/syscall.S @@ -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 index 0000000..8eb6b20 --- /dev/null +++ b/lunaix-os/kernel/mm/mmap.c @@ -0,0 +1,141 @@ +#include +#include +#include +#include +#include +#include + +#include + +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, ®ion->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 diff --git a/lunaix-os/kernel/mm/region.c b/lunaix-os/kernel/mm/region.c index bd3b26e..0e0982d 100644 --- a/lunaix-os/kernel/mm/region.c +++ b/lunaix-os/kernel/mm/region.c @@ -1,8 +1,8 @@ #include #include -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(®ions->head, ®ion->head); + if (llist_empty(lead)) { + llist_append(lead, ®ion->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, ®ion->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, ®ions->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, ®ions->head, head) + llist_for_each(pos, n, lead, head) { if (pos->start <= vaddr && vaddr < pos->end) { return pos; diff --git a/lunaix-os/kernel/process/process.c b/lunaix-os/kernel/process/process.c index 74c9380..8df3f2c 100644 --- a/lunaix-os/kernel/process/process.c +++ b/lunaix-os/kernel/process/process.c @@ -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)) { diff --git a/lunaix-os/kernel/process/sched.c b/lunaix-os/kernel/process/sched.c index f0c0a98..d219616 100644 --- a/lunaix-os/kernel/process/sched.c +++ b/lunaix-os/kernel/process/sched.c @@ -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); } -- 2.27.0