Example #1
0
// check_pgfault - check correctness of pgfault handler
static void check_pgfault(void)
{
#ifdef UCONFIG_CHECK_PGFAULT
	kprintf("starting check_pgfault()\n");
	size_t nr_used_pages_store = nr_used_pages();
	size_t slab_allocated_store = slab_allocated();

	check_mm_struct = mm_create();
	assert(check_mm_struct != NULL);

	struct mm_struct *mm = check_mm_struct;
	pgd_t *pgdir = mm->pgdir = init_pgdir_get();
	assert(pgdir[PGX(TEST_PAGE)] == 0);

	struct vma_struct *vma =
	    vma_create(TEST_PAGE, TEST_PAGE + PTSIZE, VM_WRITE);
	assert(vma != NULL);

	insert_vma_struct(mm, vma);
	uintptr_t addr = TEST_PAGE + 0x100;
	assert(find_vma(mm, addr) == vma);

	int i, sum = 0;
	for (i = 0; i < 100; i++) {
		*(char *)(addr + i) = i;
		sum += i;
	}
	for (i = 0; i < 100; i++) {
		sum -= *(char *)(addr + i);
	}
	assert(sum == 0);

	page_remove(pgdir, ROUNDDOWN(addr, PGSIZE));
#if PMXSHIFT != PUXSHIFT
	free_page(pa2page(PMD_ADDR(*get_pmd(pgdir, addr, 0))));
#endif
#if PUXSHIFT != PGXSHIFT
	free_page(pa2page(PUD_ADDR(*get_pud(pgdir, addr, 0))));
#endif
	free_page(pa2page(PGD_ADDR(*get_pgd(pgdir, addr, 0))));
	pgdir[PGX(TEST_PAGE)] = 0;

	mm->pgdir = NULL;
	mm_destroy(mm);
	check_mm_struct = NULL;

	assert(nr_used_pages_store == nr_used_pages());
	assert(slab_allocated_store == slab_allocated());

	kprintf("check_pgfault() succeeded!\n");
#endif
}
Example #2
0
static void check_vma_struct(void)
{
	size_t nr_used_pages_store = nr_used_pages();
	size_t slab_allocated_store = slab_allocated();

	struct mm_struct *mm = mm_create();
	assert(mm != NULL);

	int step1 = RB_MIN_MAP_COUNT * 2, step2 = step1 * 10;
//	int step1 = 2, step2 = step1 * 10;

	int i;
	for (i = step1; i >= 1; i--) {
		struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
		assert(vma != NULL);
		insert_vma_struct(mm, vma);
	}

	for (i = step1 + 1; i <= step2; i++) {
		struct vma_struct *vma = vma_create(i * 5, i * 5 + 2, 0);
		assert(vma != NULL);
		insert_vma_struct(mm, vma);
	}

	list_entry_t *le = list_next(&(mm->mmap_list));

	for (i = 1; i <= step2; i++) {
		assert(le != &(mm->mmap_list));
		struct vma_struct *mmap = le2vma(le, list_link);
		assert(mmap->vm_start == i * 5 && mmap->vm_end == i * 5 + 2);
		le = list_next(le);
	}

	for (i = 5; i <= 5 * step2; i+=5) {
		struct vma_struct *vma1 = find_vma(mm, i);
		assert(vma1 != NULL);
		struct vma_struct *vma2 = find_vma(mm, i+1);
		assert(vma2 != NULL);
		struct vma_struct *vma3 = find_vma(mm, i+2);
		assert(vma3 == NULL);
		struct vma_struct *vma4 = find_vma(mm, i+3);
		assert(vma4 == NULL);
		struct vma_struct *vma5 = find_vma(mm, i+4);
		assert(vma5 == NULL);

		assert(vma1->vm_start == i  && vma1->vm_end == i  + 2);
		assert(vma2->vm_start == i  && vma2->vm_end == i  + 2);
	}

	for (i =4; i>=0; i--) {
		struct vma_struct *vma_below_5= find_vma(mm,i);
		assert(vma_below_5 == NULL);
	}
	mm_destroy(mm);

	__CHECK_MEMORY_LEAK();

	kprintf("check_vma_struct() succeeded!\n");
}
Example #3
0
// check_vmm - check correctness of vmm
static void check_vmm(void)
{
	size_t nr_used_pages_store = nr_used_pages();
	size_t slab_allocated_store = slab_allocated();

	check_vma_struct();
	check_pgfault();

	__CHECK_MEMORY_LEAK();

	kprintf("check_vmm() succeeded.\n");
}
Example #4
0
// init_main - the second kernel thread used to create kswapd_main & user_main kernel threads
static int
init_main(void *arg) {
    int pid;
#ifndef CONFIG_NO_SWAP
    if ((pid = kernel_thread(kswapd_main, NULL, 0)) <= 0) {
        panic("kswapd init failed.\n");
    }
    kswapd = find_proc(pid);
    set_proc_name(kswapd, "kswapd");
#else
#warning swapping disabled
#endif

    int ret;
	char root[] = "disk0:";
    if ((ret = vfs_set_bootfs(root)) != 0) {
        panic("set boot fs failed: %e.\n", ret);
    }

    size_t nr_used_pages_store = nr_used_pages();
    size_t slab_allocated_store = slab_allocated();

    unsigned int nr_process_store = nr_process;

    pid = kernel_thread(user_main, NULL, 0);
    if (pid <= 0) {
        panic("create user_main failed.\n");
    }

    while (do_wait(0, NULL) == 0) {
        if (nr_process_store == nr_process) {
            break;
        }
        schedule();
    }
#ifndef CONFIG_NO_SWAP
    assert(kswapd != NULL);
    int i;
    for (i = 0; i < 10; i ++) {
        if (kswapd->wait_state == WT_TIMER) {
            wakeup_proc(kswapd);
        }
        schedule();
    }
#endif

    mbox_cleanup();
    fs_cleanup();

    kprintf("all user-mode processes have quit, no /bin/sh?.\n");
#ifndef CONFIG_NO_SWAP
    assert(initproc->cptr == kswapd && initproc->yptr == NULL && initproc->optr == NULL);
    assert(kswapd->cptr == NULL && kswapd->yptr == NULL && kswapd->optr == NULL);
    assert(nr_process == 2 + pls_read(lcpu_count));
#else
    assert(nr_process == 1 + pls_read(lcpu_count));
#endif
    assert(nr_used_pages_store == nr_used_pages());
    assert(slab_allocated_store == slab_allocated());
    kprintf("init check memory pass.\n");
    return 0;
}
Example #5
0
// check_swap - check the correctness of swap & page replacement algorithm
static void
check_swap(void) {
    size_t nr_used_pages_store = nr_used_pages();
    size_t slab_allocated_store = slab_allocated();

    size_t offset;
    for (offset = 2; offset < max_swap_offset; offset ++) {
        mem_map[offset] = 1;
    }

    struct mm_struct *mm = mm_create();
    assert(mm != NULL);

    extern struct mm_struct *check_mm_struct;
    assert(check_mm_struct == NULL);

    check_mm_struct = mm;

    pgd_t *pgdir = mm->pgdir = init_pgdir_get();
    assert(pgdir[PGX(TEST_PAGE)] == 0);

    struct vma_struct *vma = vma_create(TEST_PAGE, TEST_PAGE + PTSIZE, VM_WRITE | VM_READ);
    assert(vma != NULL);

    insert_vma_struct(mm, vma);

    struct Page *rp0 = alloc_page(), *rp1 = alloc_page();
    assert(rp0 != NULL && rp1 != NULL);

    pte_perm_t perm;
	ptep_unmap (&perm);
	ptep_set_u_write(&perm);
    int ret = page_insert(pgdir, rp1, TEST_PAGE, perm);
    assert(ret == 0 && page_ref(rp1) == 1);

    page_ref_inc(rp1);
    ret = page_insert(pgdir, rp0, TEST_PAGE, perm);
    assert(ret == 0 && page_ref(rp1) == 1 && page_ref(rp0) == 1);

    // check try_alloc_swap_entry

    swap_entry_t entry = try_alloc_swap_entry();
    assert(swap_offset(entry) == 1);
    mem_map[1] = 1;
    assert(try_alloc_swap_entry() == 0);

    // set rp1, Swap, Active, add to hash_list, active_list

    swap_page_add(rp1, entry);
    swap_active_list_add(rp1);
    assert(PageSwap(rp1));

    mem_map[1] = 0;
    entry = try_alloc_swap_entry();
    assert(swap_offset(entry) == 1);
    assert(!PageSwap(rp1));

    // check swap_remove_entry

    assert(swap_hash_find(entry) == NULL);
    mem_map[1] = 2;
    swap_remove_entry(entry);
    assert(mem_map[1] == 1);

    swap_page_add(rp1, entry);
    swap_inactive_list_add(rp1);
    swap_remove_entry(entry);
    assert(PageSwap(rp1));
    assert(rp1->index == entry && mem_map[1] == 0);

    // check page_launder, move page from inactive_list to active_list

    assert(page_ref(rp1) == 1);
    assert(nr_active_pages == 0 && nr_inactive_pages == 1);
    assert(list_next(&(inactive_list.swap_list)) == &(rp1->swap_link));

    page_launder();
    assert(nr_active_pages == 1 && nr_inactive_pages == 0);
    assert(PageSwap(rp1) && PageActive(rp1));

    entry = try_alloc_swap_entry();
    assert(swap_offset(entry) == 1);
    assert(!PageSwap(rp1) && nr_active_pages == 0);
    assert(list_empty(&(active_list.swap_list)));

    // set rp1 inactive again

    assert(page_ref(rp1) == 1);
    swap_page_add(rp1, 0);
    assert(PageSwap(rp1) && swap_offset(rp1->index) == 1);
    swap_inactive_list_add(rp1);
    mem_map[1] = 1;
    assert(nr_inactive_pages == 1);
    page_ref_dec(rp1);

    size_t count = nr_used_pages();
    swap_remove_entry(entry);
    assert(nr_inactive_pages == 0 && nr_used_pages() == count - 1);

    // check swap_out_mm

    pte_t *ptep0 = get_pte(pgdir, TEST_PAGE, 0), *ptep1;
    assert(ptep0 != NULL && pte2page(*ptep0) == rp0);

    ret = swap_out_mm(mm, 0);
    assert(ret == 0);

    ret = swap_out_mm(mm, 10);
    assert(ret == 1 && mm->swap_address == TEST_PAGE + PGSIZE);

    ret = swap_out_mm(mm, 10);
    assert(ret == 0 && *ptep0 == entry && mem_map[1] == 1);
    assert(PageDirty(rp0) && PageActive(rp0) && page_ref(rp0) == 0);
    assert(nr_active_pages == 1 && list_next(&(active_list.swap_list)) == &(rp0->swap_link));

    // check refill_inactive_scan()

    refill_inactive_scan();
    assert(!PageActive(rp0) && page_ref(rp0) == 0);
    assert(nr_inactive_pages == 1 && list_next(&(inactive_list.swap_list)) == &(rp0->swap_link));

    page_ref_inc(rp0);
    page_launder();
    assert(PageActive(rp0) && page_ref(rp0) == 1);
    assert(nr_active_pages == 1 && list_next(&(active_list.swap_list)) == &(rp0->swap_link));

    page_ref_dec(rp0);
    refill_inactive_scan();
    assert(!PageActive(rp0));

    // save data in rp0

    int i;
    for (i = 0; i < PGSIZE; i ++) {
        ((char *)page2kva(rp0))[i] = (char)i;
    }

    page_launder();
    assert(nr_inactive_pages == 0 && list_empty(&(inactive_list.swap_list)));
    assert(mem_map[1] == 1);

    rp1 = alloc_page();
    assert(rp1 != NULL);
    ret = swapfs_read(entry, rp1);
    assert(ret == 0);

    for (i = 0; i < PGSIZE; i ++) {
        assert(((char *)page2kva(rp1))[i] == (char)i);
    }

    // page fault now

    *(char *)(TEST_PAGE) = 0xEF;

    rp0 = pte2page(*ptep0);
    assert(page_ref(rp0) == 1);
    assert(PageSwap(rp0) && PageActive(rp0));

    entry = try_alloc_swap_entry();
    assert(swap_offset(entry) == 1 && mem_map[1] == SWAP_UNUSED);
    assert(!PageSwap(rp0) && nr_active_pages == 0 && nr_inactive_pages == 0);

    // clear accessed flag

    assert(rp0 == pte2page(*ptep0));
    assert(!PageSwap(rp0));

    ret = swap_out_mm(mm, 10);
    assert(ret == 0);
    assert(!PageSwap(rp0) && ptep_present(ptep0));

    // change page table

    ret = swap_out_mm(mm, 10);
    assert(ret == 1);
    assert(*ptep0 == entry && page_ref(rp0) == 0 && mem_map[1] == 1);

    count = nr_used_pages();
    refill_inactive_scan();
    page_launder();
    assert(count - 1 == nr_used_pages());

    ret = swapfs_read(entry, rp1);
    assert(ret == 0 && *(char *)(page2kva(rp1)) == (char)0xEF);
    free_page(rp1);

    // duplictate *ptep0

    ptep1 = get_pte(pgdir, TEST_PAGE + PGSIZE, 0);
    assert(ptep1 != NULL && ptep_invalid(ptep1));
    swap_duplicate(*ptep0);
	ptep_copy(ptep1, ptep0);
	mp_tlb_invalidate (pgdir, TEST_PAGE + PGSIZE);

    // page fault again
    // update for copy on write

    *(char *)(TEST_PAGE + 1) = 0x88;
    *(char *)(TEST_PAGE + PGSIZE) = 0x8F;
    *(char *)(TEST_PAGE + PGSIZE + 1) = 0xFF;
    assert(pte2page(*ptep0) != pte2page(*ptep1));
    assert(*(char *)(TEST_PAGE) == (char)0xEF);
    assert(*(char *)(TEST_PAGE + 1) == (char)0x88);
    assert(*(char *)(TEST_PAGE + PGSIZE) == (char)0x8F);
    assert(*(char *)(TEST_PAGE + PGSIZE + 1) == (char)0xFF);

    rp0 = pte2page(*ptep0);
    rp1 = pte2page(*ptep1);
    assert(!PageSwap(rp0) && PageSwap(rp1) && PageActive(rp1));

    entry = try_alloc_swap_entry();
    assert(!PageSwap(rp0) && !PageSwap(rp1));
    assert(swap_offset(entry) == 1 && mem_map[1] == SWAP_UNUSED);
    assert(list_empty(&(active_list.swap_list)));
    assert(list_empty(&(inactive_list.swap_list)));

	ptep_set_accessed(&perm);
    page_insert(pgdir, rp0, TEST_PAGE + PGSIZE, perm);

    // check swap_out_mm

    *(char *)(TEST_PAGE) = *(char *)(TEST_PAGE + PGSIZE) = 0xEE;
    mm->swap_address = TEST_PAGE + PGSIZE * 2;
    ret = swap_out_mm(mm, 2);
    assert(ret == 0);
    assert(ptep_present(ptep0) && ! ptep_accessed(ptep0));
    assert(ptep_present(ptep1) && ! ptep_accessed(ptep1));

    ret = swap_out_mm(mm, 2);
    assert(ret == 2);
    assert(mem_map[1] == 2 && page_ref(rp0) == 0);

    refill_inactive_scan();
    page_launder();
    assert(mem_map[1] == 2 && swap_hash_find(entry) == NULL);

    // check copy entry

    swap_remove_entry(entry);
	ptep_unmap(ptep1);
    assert(mem_map[1] == 1);

    swap_entry_t store;
    ret = swap_copy_entry(entry, &store);
    assert(ret == -E_NO_MEM);
    mem_map[2] = SWAP_UNUSED;

    ret = swap_copy_entry(entry, &store);
    assert(ret == 0 && swap_offset(store) == 2 && mem_map[2] == 0);
    mem_map[2] = 1;
	ptep_copy(ptep1, &store);

    assert(*(char *)(TEST_PAGE + PGSIZE) == (char)0xEE && *(char *)(TEST_PAGE + PGSIZE + 1)== (char)0x88);

    *(char *)(TEST_PAGE + PGSIZE) = 1, *(char *)(TEST_PAGE + PGSIZE + 1) = 2;
    assert(*(char *)TEST_PAGE == (char)0xEE && *(char *)(TEST_PAGE + 1) == (char)0x88);

    ret = swap_in_page(entry, &rp0);
    assert(ret == 0);
    ret = swap_in_page(store, &rp1);
    assert(ret == 0);
    assert(rp1 != rp0);

    // free memory

    swap_list_del(rp0), swap_list_del(rp1);
    swap_page_del(rp0), swap_page_del(rp1);

    assert(page_ref(rp0) == 1 && page_ref(rp1) == 1);
    assert(nr_active_pages == 0 && list_empty(&(active_list.swap_list)));
    assert(nr_inactive_pages == 0 && list_empty(&(inactive_list.swap_list)));

    for (i = 0; i < HASH_LIST_SIZE; i ++) {
        assert(list_empty(hash_list + i));
    }

    page_remove(pgdir, TEST_PAGE);
    page_remove(pgdir, (TEST_PAGE + PGSIZE));

#if PMXSHIFT != PUXSHIFT
    free_page(pa2page(PMD_ADDR(*get_pmd(pgdir, TEST_PAGE, 0))));
#endif
#if PUXSHIFT != PGXSHIFT
    free_page(pa2page(PUD_ADDR(*get_pud(pgdir, TEST_PAGE, 0))));
#endif
    free_page(pa2page(PGD_ADDR(*get_pgd(pgdir, TEST_PAGE, 0))));
    pgdir[PGX(TEST_PAGE)] = 0;

    mm->pgdir = NULL;
    mm_destroy(mm);
    check_mm_struct = NULL;

    assert(nr_active_pages == 0 && nr_inactive_pages == 0);
    for (offset = 0; offset < max_swap_offset; offset ++) {
        mem_map[offset] = SWAP_UNUSED;
    }

    assert(nr_used_pages_store == nr_used_pages());
    assert(slab_allocated_store == slab_allocated());

    kprintf("check_swap() succeeded.\n");
}
Example #6
0
void check_mm_shm_swap(void)
{
	size_t nr_used_pages_store = nr_used_pages();
	size_t slab_allocated_store = slab_allocated();

	int ret, i;
	for (i = 0; i < max_swap_offset; i++) {
		assert(mem_map[i] == SWAP_UNUSED);
	}

	extern struct mm_struct *check_mm_struct;
	assert(check_mm_struct == NULL);

	struct mm_struct *mm0 = mm_create(), *mm1;
	assert(mm0 != NULL && list_empty(&(mm0->mmap_list)));

	struct Page *page = alloc_page();
	assert(page != NULL);
	pgd_t *pgdir = page2kva(page);
	memcpy(pgdir, init_pgdir_get(), PGSIZE);
	ptep_map(&(pgdir[PGX(VPT)]), PADDR(pgdir));
	ptep_set_s_write(&(pgdir[PGX(VPT)]));

	mm0->pgdir = pgdir;
	check_mm_struct = mm0;
	lcr3(PADDR(mm0->pgdir));

	uint32_t vm_flags = VM_WRITE | VM_READ;

	uintptr_t addr0, addr1;

	addr0 = 0;
	do {
		if ((ret = mm_map(mm0, addr0, PTSIZE * 4, vm_flags, NULL)) == 0) {
			break;
		}
		addr0 += PTSIZE;
	} while (addr0 != 0);

	assert(ret == 0 && addr0 != 0 && mm0->map_count == 1);

	ret = mm_unmap(mm0, addr0, PTSIZE * 4);
	assert(ret == 0 && mm0->map_count == 0);

	struct shmem_struct *shmem = shmem_create(PTSIZE * 2);
	assert(shmem != NULL && shmem_ref(shmem) == 0);

	// step1: check share memory

	struct vma_struct *vma;

	addr1 = addr0 + PTSIZE * 2;
	ret = mm_map_shmem(mm0, addr0, vm_flags, shmem, &vma);
	assert(ret == 0);
	assert((vma->vm_flags & VM_SHARE) && vma->shmem == shmem
	       && shmem_ref(shmem) == 1);
	ret = mm_map_shmem(mm0, addr1, vm_flags, shmem, &vma);
	assert(ret == 0);
	assert((vma->vm_flags & VM_SHARE) && vma->shmem == shmem
	       && shmem_ref(shmem) == 2);

	// page fault

	for (i = 0; i < 4; i++) {
		*(char *)(addr0 + i * PGSIZE) = (char)(i * i);
	}
	for (i = 0; i < 4; i++) {
		assert(*(char *)(addr1 + i * PGSIZE) == (char)(i * i));
	}

	for (i = 0; i < 4; i++) {
		*(char *)(addr1 + i * PGSIZE) = (char)(-i * i);
	}
	for (i = 0; i < 4; i++) {
		assert(*(char *)(addr1 + i * PGSIZE) == (char)(-i * i));
	}

	// check swap

	ret = swap_out_mm(mm0, 8) + swap_out_mm(mm0, 8);
	assert(ret == 8 && nr_active_pages == 4 && nr_inactive_pages == 0);

	refill_inactive_scan();
	assert(nr_active_pages == 0 && nr_inactive_pages == 4);

	// write & read again

	memset((void *)addr0, 0x77, PGSIZE);
	for (i = 0; i < PGSIZE; i++) {
		assert(*(char *)(addr1 + i) == (char)0x77);
	}

	// check unmap

	ret = mm_unmap(mm0, addr1, PGSIZE * 4);
	assert(ret == 0);

	addr0 += 4 * PGSIZE, addr1 += 4 * PGSIZE;
	*(char *)(addr0) = (char)(0xDC);
	assert(*(char *)(addr1) == (char)(0xDC));
	*(char *)(addr1 + PTSIZE) = (char)(0xDC);
	assert(*(char *)(addr0 + PTSIZE) == (char)(0xDC));

	kprintf("check_mm_shm_swap: step1, share memory ok.\n");

	// setup mm1

	mm1 = mm_create();
	assert(mm1 != NULL);

	page = alloc_page();
	assert(page != NULL);
	pgdir = page2kva(page);
	memcpy(pgdir, init_pgdir_get(), PGSIZE);
	ptep_map(&(pgdir[PGX(VPT)]), PADDR(pgdir));
	ptep_set_s_write(&(pgdir[PGX(VPT)]));
	mm1->pgdir = pgdir;

	ret = dup_mmap(mm1, mm0);
	assert(ret == 0 && shmem_ref(shmem) == 4);

	// switch to mm1

	check_mm_struct = mm1;
	lcr3(PADDR(mm1->pgdir));

	for (i = 0; i < 4; i++) {
		*(char *)(addr0 + i * PGSIZE) = (char)(0x57 + i);
	}
	for (i = 0; i < 4; i++) {
		assert(*(char *)(addr1 + i * PGSIZE) == (char)(0x57 + i));
	}

	check_mm_struct = mm0;
	lcr3(PADDR(mm0->pgdir));

	for (i = 0; i < 4; i++) {
		assert(*(char *)(addr0 + i * PGSIZE) == (char)(0x57 + i));
		assert(*(char *)(addr1 + i * PGSIZE) == (char)(0x57 + i));
	}

	swap_out_mm(mm1, 4);
	exit_mmap(mm1);

	free_page(kva2page(mm1->pgdir));
	mm_destroy(mm1);

	assert(shmem_ref(shmem) == 2);

	kprintf("check_mm_shm_swap: step2, dup_mmap ok.\n");

	// free memory

	check_mm_struct = NULL;
	lcr3(PADDR(init_pgdir_get()));

	exit_mmap(mm0);
	free_page(kva2page(mm0->pgdir));
	mm_destroy(mm0);

	refill_inactive_scan();
	page_launder();
	for (i = 0; i < max_swap_offset; i++) {
		assert(mem_map[i] == SWAP_UNUSED);
	}

	assert(nr_used_pages_store == nr_used_pages());
	assert(slab_allocated_store == slab_allocated());

	kprintf("check_mm_shm_swap() succeeded.\n");
}
Example #7
0
void check_mm_swap(void)
{
	size_t nr_used_pages_store = nr_used_pages();
	size_t slab_allocated_store = slab_allocated();

	int ret, i, j;
	for (i = 0; i < max_swap_offset; i++) {
		assert(mem_map[i] == SWAP_UNUSED);
	}

	extern struct mm_struct *check_mm_struct;
	assert(check_mm_struct == NULL);

	// step1: check mm_map

	struct mm_struct *mm0 = mm_create(), *mm1;
	assert(mm0 != NULL && list_empty(&(mm0->mmap_list)));

	uintptr_t addr0, addr1;

	addr0 = 0;
	do {
		ret = mm_map(mm0, addr0, PUSIZE, 0, NULL);
		assert(ret ==
		       (USER_ACCESS(addr0, addr0 + PUSIZE)) ? 0 : -E_INVAL);
		addr0 += PUSIZE;
	} while (!((addr0 >> 48) & 0xFFFF));

	addr0 = 0;
	for (i = 0; i < 1024; i++, addr0 += PUSIZE) {
		ret = mm_map(mm0, addr0, PGSIZE, 0, NULL);
		assert(ret == -E_INVAL);
	}

	mm_destroy(mm0);

	mm0 = mm_create();
	assert(mm0 != NULL && list_empty(&(mm0->mmap_list)));

	addr0 = 0, i = 0;
	do {
		ret = mm_map(mm0, addr0, PUSIZE - PGSIZE, 0, NULL);
		assert(ret ==
		       (USER_ACCESS(addr0, addr0 + PUSIZE)) ? 0 : -E_INVAL);
		if (ret == 0) {
			i++;
		}
		addr0 += PUSIZE;
	} while (!((addr0 >> 48) & 0xFFFF));

	addr0 = 0, j = 0;
	do {
		addr0 += PUSIZE - PGSIZE;
		ret = mm_map(mm0, addr0, PGSIZE, 0, NULL);
		assert(ret ==
		       (USER_ACCESS(addr0, addr0 + PGSIZE)) ? 0 : -E_INVAL);
		if (ret == 0) {
			j++;
		}
		addr0 += PGSIZE;
	} while (!((addr0 >> 48) & 0xFFFF));

	assert(j + 1 >= i);

	mm_destroy(mm0);

	assert(nr_used_pages_store == nr_used_pages());
	assert(slab_allocated_store == slab_allocated());

	kprintf("check_mm_swap: step1, mm_map ok.\n");

	// step2: check page fault

	mm0 = mm_create();
	assert(mm0 != NULL && list_empty(&(mm0->mmap_list)));

	// setup page table

	struct Page *page = alloc_page();
	assert(page != NULL);
	pgd_t *pgdir = page2kva(page);
	memcpy(pgdir, init_pgdir_get(), PGSIZE);
	ptep_map(&(pgdir[PGX(VPT)]), PADDR(pgdir));
	ptep_set_s_write(&(pgdir[PGX(VPT)]));

	// prepare for page fault

	mm0->pgdir = pgdir;
	check_mm_struct = mm0;
	lcr3(PADDR(mm0->pgdir));

	uint32_t vm_flags = VM_WRITE | VM_READ;
	struct vma_struct *vma;

	addr0 = 0;
	do {
		if ((ret = mm_map(mm0, addr0, PTSIZE, vm_flags, &vma)) == 0) {
			break;
		}
		addr0 += PUSIZE;
	} while (!((addr0 >> 48) & 0xFFFF));

	assert(ret == 0 && addr0 != 0 && mm0->map_count == 1);
	assert(vma->vm_start == addr0 && vma->vm_end == addr0 + PTSIZE);

	// check pte entry

	pte_t *ptep;
	for (addr1 = addr0; addr1 < addr0 + PTSIZE; addr1 += PGSIZE) {
		ptep = get_pte(pgdir, addr1, 0);
		assert(ptep == NULL);
	}

	memset((void *)addr0, 0xEF, PGSIZE * 2);
	ptep = get_pte(pgdir, addr0, 0);
	assert(ptep != NULL && ptep_present(ptep));
	ptep = get_pte(pgdir, addr0 + PGSIZE, 0);
	assert(ptep != NULL && ptep_present(ptep));

	ret = mm_unmap(mm0, -PTSIZE, PTSIZE);
	assert(ret == -E_INVAL);
	ret = mm_unmap(mm0, addr0 + PTSIZE, PGSIZE);
	assert(ret == 0);

	addr1 = addr0 + PTSIZE / 2;
	ret = mm_unmap(mm0, addr1, PGSIZE);
	assert(ret == 0 && mm0->map_count == 2);

	ret = mm_unmap(mm0, addr1 + 2 * PGSIZE, PGSIZE * 4);
	assert(ret == 0 && mm0->map_count == 3);

	ret = mm_map(mm0, addr1, PGSIZE * 6, 0, NULL);
	assert(ret == -E_INVAL);
	ret = mm_map(mm0, addr1, PGSIZE, 0, NULL);
	assert(ret == 0 && mm0->map_count == 4);
	ret = mm_map(mm0, addr1 + 2 * PGSIZE, PGSIZE * 4, 0, NULL);
	assert(ret == 0 && mm0->map_count == 5);

	ret = mm_unmap(mm0, addr1 + PGSIZE / 2, PTSIZE / 2 - PGSIZE);
	assert(ret == 0 && mm0->map_count == 1);

	addr1 = addr0 + PGSIZE;
	for (i = 0; i < PGSIZE; i++) {
		assert(*(char *)(addr1 + i) == (char)0xEF);
	}

	ret = mm_unmap(mm0, addr1 + PGSIZE / 2, PGSIZE / 4);
	assert(ret == 0 && mm0->map_count == 2);
	ptep = get_pte(pgdir, addr0, 0);
	assert(ptep != NULL && ptep_present(ptep));
	ptep = get_pte(pgdir, addr0 + PGSIZE, 0);
	assert(ptep != NULL && ptep_invalid(ptep));

	ret = mm_map(mm0, addr1, PGSIZE, vm_flags, NULL);
	memset((void *)addr1, 0x88, PGSIZE);
	assert(*(char *)addr1 == (char)0x88 && mm0->map_count == 3);

	for (i = 1; i < 16; i += 2) {
		ret = mm_unmap(mm0, addr0 + PGSIZE * i, PGSIZE);
		assert(ret == 0);
		if (i < 8) {
			ret = mm_map(mm0, addr0 + PGSIZE * i, PGSIZE, 0, NULL);
			assert(ret == 0);
		}
	}
	assert(mm0->map_count == 13);

	ret = mm_unmap(mm0, addr0 + PGSIZE * 2, PTSIZE - PGSIZE * 2);
	assert(ret == 0 && mm0->map_count == 2);

	ret = mm_unmap(mm0, addr0, PGSIZE * 2);
	assert(ret == 0 && mm0->map_count == 0);

	kprintf("check_mm_swap: step2, mm_unmap ok.\n");

	// step3: check exit_mmap

	ret = mm_map(mm0, addr0, PTSIZE, vm_flags, NULL);
	assert(ret == 0);

	for (i = 0, addr1 = addr0; i < 4; i++, addr1 += PGSIZE) {
		*(char *)addr1 = (char)0xFF;
	}

	exit_mmap(mm0);
	for (i = 0; i < PGX(SVBASE); i++) {
		assert(pgdir[i] == 0);
	}

	kprintf("check_mm_swap: step3, exit_mmap ok.\n");

	// step4: check dup_mmap

	for (i = 0; i < max_swap_offset; i++) {
		assert(mem_map[i] == SWAP_UNUSED);
	}

	ret = mm_map(mm0, addr0, PTSIZE, vm_flags, NULL);
	assert(ret != 0);

	addr1 = addr0;
	for (i = 0; i < 4; i++, addr1 += PGSIZE) {
		*(char *)addr1 = (char)(i * i);
	}

	ret = 0;
	ret += swap_out_mm(mm0, 10);
	ret += swap_out_mm(mm0, 10);
	assert(ret == 4);

	for (; i < 8; i++, addr1 += PGSIZE) {
		*(char *)addr1 = (char)(i * i);
	}

	// setup mm1

	mm1 = mm_create();
	assert(mm1 != NULL);

	page = alloc_page();
	assert(page != NULL);
	pgdir = page2kva(page);
	memcpy(pgdir, init_pgdir_get(), PGSIZE);
	ptep_map(&(pgdir[PGX(VPT)]), PADDR(pgdir));
	ptep_set_s_write(&(pgdir[PGX(VPT)]));
	mm1->pgdir = pgdir;

	ret = dup_mmap(mm1, mm0);
	assert(ret == 0);

	// switch to mm1

	check_mm_struct = mm1;
	lcr3(PADDR(mm1->pgdir));

	addr1 = addr0;
	for (i = 0; i < 8; i++, addr1 += PGSIZE) {
		assert(*(char *)addr1 == (char)(i * i));
		*(char *)addr1 = (char)0x88;
	}

	// switch to mm0

	check_mm_struct = mm0;
	lcr3(PADDR(mm0->pgdir));

	addr1 = addr0;
	for (i = 0; i < 8; i++, addr1 += PGSIZE) {
		assert(*(char *)addr1 == (char)(i * i));
	}

	// switch to PADDR(init_pgdir_get())

	check_mm_struct = NULL;
	lcr3(PADDR(init_pgdir_get()));

	// free memory

	exit_mmap(mm0);
	exit_mmap(mm1);

	free_page(kva2page(mm0->pgdir));
	mm_destroy(mm0);
	free_page(kva2page(mm1->pgdir));
	mm_destroy(mm1);

	kprintf("check_mm_swap: step4, dup_mmap ok.\n");

	refill_inactive_scan();
	page_launder();
	for (i = 0; i < max_swap_offset; i++) {
		assert(mem_map[i] == SWAP_UNUSED);
	}

	assert(nr_used_pages_store == nr_used_pages());
	assert(slab_allocated_store == slab_allocated());

	kprintf("check_mm_swap() succeeded.\n");
}