diff --git a/kernel/mm/internal.h b/kernel/mm/internal.h index 1b97e77a..f50902d2 100644 --- a/kernel/mm/internal.h +++ b/kernel/mm/internal.h @@ -2,6 +2,10 @@ #include "mm.h" + +// 当vma被成功合并后的返回值 +#define __VMA_MERGED 1 + /** * @brief 将vma结构体插入mm_struct的链表之中 * @@ -69,4 +73,7 @@ int __anon_vma_del(struct vm_area_struct *vma); * @param paddr 物理地址 * @return struct Page* 创建成功的page */ -struct Page* __create_mmio_page_struct(uint64_t paddr); \ No newline at end of file +struct Page* __create_mmio_page_struct(uint64_t paddr); + +// 判断给定的两个值是否跨越了2M边界 +#define CROSS_2M_BOUND(val1, val2) ((val1 & PAGE_2M_MASK) != (val2 & PAGE_2M_MASK)) \ No newline at end of file diff --git a/kernel/mm/mm.c b/kernel/mm/mm.c index d73efac2..87825538 100644 --- a/kernel/mm/mm.c +++ b/kernel/mm/mm.c @@ -615,7 +615,8 @@ uint64_t mm_do_brk(uint64_t old_brk_end_addr, int64_t offset) { struct vm_area_struct *vma = NULL; mm_create_vma(current_pcb->mm, i, PAGE_2M_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma); - mm_map_vma(vma, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys); + mm_map(current_pcb->mm, i, PAGE_2M_SIZE, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys); + // mm_map_vma(vma, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, 0, PAGE_2M_SIZE); } current_pcb->mm->brk_end = end_addr; } diff --git a/kernel/mm/mm.h b/kernel/mm/mm.h index bd7709a2..75ea7c37 100644 --- a/kernel/mm/mm.h +++ b/kernel/mm/mm.h @@ -462,9 +462,11 @@ int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flag * * @param vma 要进行映射的VMA结构体 * @param paddr 起始物理地址 + * @param offset 要映射的起始位置在vma中的偏移量 + * @param length 要映射的长度 * @return int 错误码 */ -int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr); +int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr, uint64_t offset, uint64_t length); /** * @brief 在页表中映射物理地址到指定的虚拟地址(需要页表中已存在对应的vma) diff --git a/kernel/mm/mmap.c b/kernel/mm/mmap.c index 34329e76..d39c45ca 100644 --- a/kernel/mm/mmap.c +++ b/kernel/mm/mmap.c @@ -2,6 +2,7 @@ #include "slab.h" #include "internal.h" #include +#include extern uint64_t mm_total_2M_pages; @@ -319,6 +320,10 @@ void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_sta int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flags_t vm_flags, struct vm_operations_t *vm_ops, struct vm_area_struct **res_vma) { int retval = 0; + // 输入的地址如果不是4K对齐,则报错 + if (unlikely(vaddr & (PAGE_4K_SIZE - 1))) + return -EINVAL; + struct vm_area_struct *vma = vm_area_alloc(mm); if (unlikely(vma == NULL)) return -ENOMEM; @@ -328,12 +333,16 @@ int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flag vma->vm_end = vaddr + length; // 将VMA加入mm的链表 retval = vma_insert(mm, vma); - if (retval == -EEXIST) // 之前已经存在了相同的vma,直接返回 + if (retval == -EEXIST || retval == __VMA_MERGED) // 之前已经存在了相同的vma,直接返回 { *res_vma = vma_find(mm, vma->vm_start); kfree(vma); - return -EEXIST; + if (retval == -EEXIST) + return -EEXIST; + else + return 0; } + if (res_vma != NULL) *res_vma = vma; return 0; @@ -344,11 +353,16 @@ int mm_create_vma(struct mm_struct *mm, uint64_t vaddr, uint64_t length, vm_flag * * @param vma 要进行映射的VMA结构体 * @param paddr 起始物理地址 + * @param offset 要映射的起始位置在vma中的偏移量 + * @param length 要映射的长度 * @return int 错误码 */ -int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr) +int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr, uint64_t offset, uint64_t length) { - int retval = 0; + int retval = 0; + uint64_t mapped = 0; + BUG_ON((offset & (PAGE_4K_SIZE - 1)) != 0); + length = PAGE_4K_ALIGN(length); // 将length按照4K进行对齐 // 获取物理地址对应的页面 struct Page *pg; uint64_t page_flags = 0; @@ -374,17 +388,39 @@ int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr) // 将anon vma与vma进行绑定 __anon_vma_add(pg->anon_vma, vma); barrier(); - - uint64_t length = vma->vm_end - vma->vm_start; - // ==== 将地址映射到页表 ==== - uint64_t len_4k = length % PAGE_2M_SIZE; - uint64_t len_2m = length - len_4k; + // 长度超过界限 + BUG_ON(vma->vm_start + offset + length > vma->vm_end); /* todo: 限制页面的读写权限 */ - // kdebug("len2m=%d", len_2m); - // 先映射2M页 + + // ==== 将地址映射到页表 ==== + uint64_t len_4k, len_2m; + // 将地址使用4k页填补,使得地址按照2M对齐 + len_4k = PAGE_2M_ALIGN(vma->vm_start + offset) - (vma->vm_start + offset); + if (len_4k > 0) + len_4k = (len_4k > length) ? length : len_4k; + if (len_4k) + { + if (vma->vm_flags & VM_USER) + page_flags |= PAGE_USER_4K_PAGE; + else + page_flags |= PAGE_KERNEL_4K_PAGE; + + // 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正 + retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + offset, paddr, len_4k, page_flags, false, false, true); + if (unlikely(retval != 0)) + goto failed; + + mapped += len_4k; + length -= len_4k; + } + + len_4k = length % PAGE_2M_SIZE; + len_2m = length / PAGE_2M_SIZE; + + // 映射连续的2M页 if (likely(len_2m > 0)) { if (vma->vm_flags & VM_USER) @@ -392,31 +428,32 @@ int mm_map_vma(struct vm_area_struct *vma, uint64_t paddr) else page_flags |= PAGE_KERNEL_PAGE; // 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正 - retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start, paddr, len_2m, page_flags, false, false, false); + retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + offset + mapped, paddr + mapped, len_2m, page_flags, false, false, false); + if (unlikely(retval != 0)) goto failed; + mapped += len_2m; } - + // 最后再使用4K页填补 if (likely(len_4k > 0)) { - len_4k = ALIGN(len_4k, PAGE_4K_SIZE); if (vma->vm_flags & VM_USER) page_flags |= PAGE_USER_4K_PAGE; else page_flags |= PAGE_KERNEL_4K_PAGE; + // 这里直接设置user标志位为false,因为该函数内部会对其进行自动校正 - retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + len_2m, paddr + len_2m, len_4k, page_flags, false, false, true); + retval = mm_map_proc_page_table((uint64_t)vma->vm_mm->pgd, true, vma->vm_start + offset + mapped, paddr + mapped, len_4k, page_flags, false, false, true); + if (unlikely(retval != 0)) goto failed; + mapped += len_4k; } if (vma->vm_flags & VM_IO) vma->page_offset = 0; - else - { // 计算当前vma的起始地址在对应的物理页中的偏移量 - vma->page_offset = paddr - (paddr & PAGE_2M_MASK); - } + flush_tlb(); return 0; failed:; @@ -436,6 +473,7 @@ failed:; int mm_map(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t paddr) { int retval = 0; + uint64_t offset = 0; for (uint64_t mapped = 0; mapped < length;) { @@ -446,17 +484,20 @@ int mm_map(struct mm_struct *mm, uint64_t vaddr, uint64_t length, uint64_t paddr return -EINVAL; } - if (unlikely(vma->vm_start != (vaddr + mapped))) - { - kerror("Map addr failed: addr_start is not equal to current: %#018lx.", vaddr + mapped); - return -EINVAL; - } + // if (unlikely(vma->vm_start != (vaddr + mapped))) + // { + // kerror("Map addr failed: addr_start is not equal to current: %#018lx.", vaddr + mapped); + // return -EINVAL; + // } - retval = mm_map_vma(vma, paddr + mapped); + offset = vaddr + mapped - vma->vm_start; + uint64_t m_len = vma->vm_end - vma->vm_start - offset; + // kdebug("start=%#018lx, offset=%ld", vma->vm_start, offset); + retval = mm_map_vma(vma, paddr + mapped, offset, m_len); if (unlikely(retval != 0)) goto failed; - mapped += vma->vm_end - vma->vm_start; + mapped += m_len; } return 0; failed:; diff --git a/kernel/mm/slab.c b/kernel/mm/slab.c index 785e3661..15d3d894 100644 --- a/kernel/mm/slab.c +++ b/kernel/mm/slab.c @@ -708,6 +708,6 @@ unsigned long kfree(void *address) } while (slab_obj_ptr != kmalloc_cache_group[i].cache_pool_entry); } - kBUG("kfree(): Can't free memory."); + kBUG("kfree(): Can't free memory. address=%#018lx", address); return ECANNOT_FREE_MEM; } \ No newline at end of file diff --git a/kernel/mm/vma.c b/kernel/mm/vma.c index 9e887582..54ed527c 100644 --- a/kernel/mm/vma.c +++ b/kernel/mm/vma.c @@ -122,21 +122,35 @@ int vma_insert(struct mm_struct *mm, struct vm_area_struct *vma) { struct vm_area_struct *prev; + prev = vma_find(mm, vma->vm_start); + if (prev && prev->vm_start <= vma->vm_start && prev->vm_end >= vma->vm_end) { // 已经存在了相同的vma return -EEXIST; } - else if (prev && (prev->vm_start == vma->vm_start || prev->vm_end == vma->vm_end)) // 暂时不支持扩展vma + // todo: bugfix: 这里的第二种情况貌似从来不会满足 + else if (prev && ((vma->vm_start >= prev->vm_start && vma->vm_start <= prev->vm_end) || (prev->vm_start <= vma->vm_end && prev->vm_start >= vma->vm_start))) { - kwarn("Not support: expand vma"); - return -ENOTSUP; + //部分重叠 + if ((!CROSS_2M_BOUND(vma->vm_start, prev->vm_start)) && (!CROSS_2M_BOUND(vma->vm_end, prev->vm_end))&& vma->vm_end) + { + //合并vma 并改变链表vma的范围 + kdebug("before combining vma:vm_start = %#018lx, vm_end = %#018lx\n", vma->vm_start, vma->vm_end); + + prev->vm_start = (vma->vm_start < prev->vm_start )? vma->vm_start : prev->vm_start; + prev->vm_end = (vma->vm_end > prev->vm_end) ? vma->vm_end : prev->vm_end; + // 计算page_offset + prev->page_offset = prev->vm_start - (prev->vm_start & PAGE_2M_MASK); + kdebug("combined vma:vm_start = %#018lx, vm_end = %#018lx\nprev:vm_start = %018lx, vm_end = %018lx\n", vma->vm_start, vma->vm_end, prev->vm_start, prev->vm_end); + kinfo("vma has same part\n"); + return __VMA_MERGED; + } } - prev = vma_find(mm, vma->vm_end); - if (prev) - prev = prev->vm_prev; + // prev = vma_find(mm, vma->vm_start); + if (prev == NULL) // 要将当前vma插入到链表的尾部 { struct vm_area_struct *ptr = mm->vmas; @@ -151,6 +165,8 @@ int vma_insert(struct mm_struct *mm, struct vm_area_struct *vma) } } } + else + prev = prev->vm_prev; __vma_link_list(mm, vma, prev); return 0; } diff --git a/kernel/process/process.c b/kernel/process/process.c index 5359c30e..c748507a 100644 --- a/kernel/process/process.c +++ b/kernel/process/process.c @@ -259,7 +259,7 @@ static int process_load_elf_file(struct pt_regs *regs, char *path) pos = phdr->p_offset; uint64_t virt_base = 0; - uint64_t beginning_offset = 0; // 由于页表映射导致的virtbase与实际的p_vaddr之间的偏移量 + uint64_t beginning_offset = 0; // 由于页表映射导致的virtbase与实际的p_vaddr之间的偏移量 if (remain_mem_size >= PAGE_2M_SIZE) // 接下来存在映射2M页的情况,因此将vaddr按2M向下对齐 virt_base = phdr->p_vaddr & PAGE_2M_MASK; @@ -278,11 +278,13 @@ static int process_load_elf_file(struct pt_regs *regs, char *path) uint64_t pa = alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys; struct vm_area_struct *vma = NULL; int ret = mm_create_vma(current_pcb->mm, virt_base, PAGE_2M_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma); + // 防止内存泄露 if (ret == -EEXIST) free_pages(Phy_to_2M_Page(pa), 1); else - mm_map_vma(vma, pa); + mm_map(current_pcb->mm, virt_base, PAGE_2M_SIZE, pa); + // mm_map_vma(vma, pa, 0, PAGE_2M_SIZE); io_mfence(); memset((void *)virt_base, 0, PAGE_2M_SIZE); map_size = PAGE_2M_SIZE; @@ -298,10 +300,12 @@ static int process_load_elf_file(struct pt_regs *regs, char *path) struct vm_area_struct *vma = NULL; int val = mm_create_vma(current_pcb->mm, virt_base + off, PAGE_4K_SIZE, VM_USER | VM_ACCESS_FLAGS, NULL, &vma); + // kdebug("virt_base=%#018lx", virt_base + off); if (val == -EEXIST) kfree(phys_2_virt(paddr)); else - mm_map_vma(vma, paddr); + mm_map(current_pcb->mm, virt_base + off, PAGE_4K_SIZE, paddr); + // mm_map_vma(vma, paddr, 0, PAGE_4K_SIZE); io_mfence(); memset((void *)(virt_base + off), 0, PAGE_4K_SIZE); } @@ -335,7 +339,7 @@ static int process_load_elf_file(struct pt_regs *regs, char *path) if (val == -EEXIST) free_pages(Phy_to_2M_Page(pa), 1); else - mm_map_vma(vma, pa); + mm_map_vma(vma, pa, 0, PAGE_2M_SIZE); } // 清空栈空间 @@ -957,7 +961,7 @@ uint64_t process_copy_mm(uint64_t clone_flags, struct process_control_block *pcb if (unlikely(ret == -EEXIST)) free_pages(Phy_to_2M_Page(pa), 1); else - mm_map_vma(new_vma, pa); + mm_map_vma(new_vma, pa, 0, PAGE_2M_SIZE); memcpy((void *)phys_2_virt(pa), (void *)(vma->vm_start + i * PAGE_2M_SIZE), (vma_size >= PAGE_2M_SIZE) ? PAGE_2M_SIZE : vma_size); vma_size -= PAGE_2M_SIZE; @@ -974,7 +978,7 @@ uint64_t process_copy_mm(uint64_t clone_flags, struct process_control_block *pcb if (unlikely(ret == -EEXIST)) kfree((void *)va); else - mm_map_vma(new_vma, virt_2_phys(va)); + mm_map_vma(new_vma, virt_2_phys(va), 0, map_size); memcpy((void *)va, (void *)vma->vm_start, vma_size); }