diff --git a/.vscode/bookmarks.json b/.vscode/bookmarks.json
index 2bfe4576bab5890ce1aec88ec848f9f5a048aee9..77c02dea45a03f229f6ab6f38f057b18bd163f4c 100644
--- a/.vscode/bookmarks.json
+++ b/.vscode/bookmarks.json
@@ -4,12 +4,12 @@
 			"path": "src/kernel/syscall.c",
 			"bookmarks": [
 				{
-					"line": 94,
+					"line": 93,
 					"column": 0,
 					"label": ""
 				},
 				{
-					"line": 99,
+					"line": 98,
 					"column": 0,
 					"label": ""
 				}
diff --git a/.vscode/launch.json b/.vscode/launch.json
index e0139668505735c721e7795f27b5b22a7e63d28c..7af94967bcf8a0d2b05f00f08f9bb4ad225e473f 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -8,25 +8,29 @@
             "name": "(gdb) 启动",
             "type": "cppdbg",
             "request": "launch",
-            "program": "${workspaceFolder}/test/buddy/buddy",
+            "program": "${workspaceFolder}/build/kernel",
             "args": [],
             "stopAtEntry": false,
             "cwd": "${fileDirname}",
             "environment": [],
             "externalConsole": false,
             "MIMode": "gdb",
+            // "miDebuggerPath": "/opt/kendryte-toolchain/bin/riscv64-unknown-elf-gdb",
+            "miDebuggerPath": "/usr/bin/gdb-multiarch",
+            "miDebuggerServerAddress": "192.168.137.100:26000",
             "setupCommands": [
                 {
                     "description": "为 gdb 启用整齐打印",
                     "text": "-enable-pretty-printing",
                     "ignoreFailures": true
                 },
-                {
-                    "description":  "将反汇编风格设置为 Intel",
-                    "text": "-gdb-set disassembly-flavor intel",
-                    "ignoreFailures": true
-                }
-            ]
+                // {
+                //     "description":  "将反汇编风格设置为 Intel",
+                //     "text": "-gdb-set disassembly-flavor intel",
+                //     "ignoreFailures": true
+                // }
+            ],
+            
         }
 
     ]
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 22785168ad0258fb1b0f126f539b6a61f8616913..1341037c335bb5b638a1ecc62572eae522b09231 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -98,7 +98,8 @@
         "syscall_ids.h": "c",
         "syscall_arch.h": "c",
         "profile_gen.h": "c",
-        "profile.h": "c"
+        "profile.h": "c",
+        "mmap.h": "c"
     },
     "C_Cpp.errorSquiggles": "Enabled",
     "todohighlight.keywords": [
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000000000000000000000000000000000000..55f32aeda69e88054c38fa65c70ec79e0814282b
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,13 @@
+{
+    // See https://go.microsoft.com/fwlink/?LinkId=733558
+    // for the documentation about the tasks.json format
+    "version": "2.0.0",
+    "tasks": [
+        {
+            "label": "run qemu",
+            "type": "shell",
+            "command": "./run-gdb.sh",
+            "args": [],
+        }
+    ]
+}
\ No newline at end of file
diff --git a/entry/syscall.tbl b/entry/syscall.tbl
index 19a48f257a1c257a73ed8ea0de0f17cd25a01f5d..4571cc256ffd121b7a9aace8c977b2ba030c1ba1 100644
--- a/entry/syscall.tbl
+++ b/entry/syscall.tbl
@@ -30,8 +30,8 @@
 80     fstat     sys_fstat  
 ##############MM################
 214     sbrk      sys_sbrk  
-215     munmap      sys_munmap
-222     mmap      sys_mmap
+#215     munmap      sys_munmap
+#222     mmap      sys_mmap
 ############Others##############
 153     times           sys_times
 160     uname           sys_uname
diff --git a/include/fs/fcntl.h b/include/fs/fcntl.h
index 949c66064e664b108b67e61780cb29aa446bf305..9733c1f842a42c78ec8076ef67ebe64c4e838119 100644
--- a/include/fs/fcntl.h
+++ b/include/fs/fcntl.h
@@ -8,13 +8,13 @@
 #define O_DIRECTORY 0x0200000
 
 // mmap
-#define PROT_NONE       0x0
-#define PROT_READ       0x1
-#define PROT_WRITE      0x2
-#define PROT_EXEC       0x4
+// #define PROT_NONE       0x0
+// #define PROT_READ       0x1
+// #define PROT_WRITE      0x2
+// #define PROT_EXEC       0x4
 
-#define MAP_SHARED      0x01
-#define MAP_PRIVATE     0x02
+// #define MAP_SHARED      (1UL << 0)
+// #define MAP_PRIVATE     (1UL << 1)
 
 #define AT_FDCWD -100
 
diff --git a/include/kernel/proc.h b/include/kernel/proc.h
index 8796c52f30c87a3092086ec649c0638f694c407c..932779b21e5d6fc374eda876b9c46d7d9b99c9d9 100644
--- a/include/kernel/proc.h
+++ b/include/kernel/proc.h
@@ -3,6 +3,7 @@
 
 #include "atomic/spinlock.h"
 #include "platform.h"
+#include "mm/mmap.h"
 
 // Saved registers for kernel context switches.
 struct context {
@@ -36,72 +37,13 @@ struct cpu {
 
 extern struct cpu cpus[NUM_CORES];
 
-// per-process data for the trap handling code in trampoline.S.
-// sits in a page by itself just under the trampoline page in the
-// user page table. not specially mapped in the kernel page table.
-// the sscratch register points here.
-// uservec in trampoline.S saves user registers in the trapframe,
-// then initializes registers from the trapframe's
-// kernel_sp, kernel_hartid, kernel_satp, and jumps to kernel_trap.
-// usertrapret() and userret in trampoline.S set up
-// the trapframe's kernel_*, restore user registers from the
-// trapframe, switch to the user page table, and enter user space.
-// the trapframe includes callee-saved user registers like s0-s11 because the
-// return-to-user path via usertrapret() doesn't return through
-// the entire kernel call stack.
-struct trapframe {
-  /*   0 @depercated */ uint64 kernel_satp;   // kernel page table 
-  /*   8 */ uint64 kernel_sp;     // top of process's kernel stack
-  /*  16 */ uint64 kernel_trap;   // usertrap()
-  /*  24 */ uint64 epc;           // saved user program counter
-  /*  32 */ uint64 kernel_hartid; // saved kernel tp
-  /*  40 */ uint64 ra;
-  /*  48 */ uint64 sp;
-  /*  56 */ uint64 gp;
-  /*  64 */ uint64 tp;
-  /*  72 */ uint64 t0;
-  /*  80 */ uint64 t1;
-  /*  88 */ uint64 t2;
-  /*  96 */ uint64 s0;
-  /* 104 */ uint64 s1;
-  /* 112 */ uint64 a0;
-  /* 120 */ uint64 a1;
-  /* 128 */ uint64 a2;
-  /* 136 */ uint64 a3;
-  /* 144 */ uint64 a4;
-  /* 152 */ uint64 a5;
-  /* 160 */ uint64 a6;
-  /* 168 */ uint64 a7;
-  /* 176 */ uint64 s2;
-  /* 184 */ uint64 s3;
-  /* 192 */ uint64 s4;
-  /* 200 */ uint64 s5;
-  /* 208 */ uint64 s6;
-  /* 216 */ uint64 s7;
-  /* 224 */ uint64 s8;
-  /* 232 */ uint64 s9;
-  /* 240 */ uint64 s10;
-  /* 248 */ uint64 s11;
-  /* 256 */ uint64 t3;
-  /* 264 */ uint64 t4;
-  /* 272 */ uint64 t5;
-  /* 280 */ uint64 t6;
-};
 
 enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
 
-#define VMA_NUM 16
-struct vma{
-  uint64 addr;
-  uint64 end;//aligned to pages
-  uint64 len;
-  int prot;
-  int flags;
-  uint64 off;
-
-  struct file *map_file;
-  enum{VMA_UNUSED, VMA_USED} state;
-};
+
+#define PROC_KSTACK(proc) ((proc)->memlayout.kstack->addr)
+#define PROC_TRAPFRAME(proc) ((struct trapframe *)((proc)->memlayout.trapframe->addr))
+#define PROC_VMA_HEAD(proc) ((proc)->memlayout.vma_head)
 
 struct fat_entry;
 // Per-process state
@@ -119,10 +61,11 @@ struct proc {
   struct proc *parent;         // Parent process
 
   // these are private to the process, so p->lock need not be held.
-  uint64 kstack;               // Virtual address of kernel stack
-  uint64 sz;                   // Size of process memory (bytes)
-  pagetable_t pagetable;       // User page table
-  struct trapframe *trapframe; // data page for trampoline.S
+  // uint64 kstack;               // Virtual address of kernel stack
+  // uint64 sz;                   // Size of process memory (bytes)
+  mm_t *mm;
+
+  // struct trapframe *trapframe; // data page for trampoline.S
   struct context context;      // swtch() here to run process
   struct file *ofile[NOFILE];  // Open files
   struct file **ext_ofile;
@@ -131,8 +74,8 @@ struct proc {
   // struct inode *cwd;           // Current directory
   char name[16];               // Process name (debugging)
   uint64 ktrap_fp;
-
-  struct vma vma[VMA_NUM];
+  // todo: mmap
+  // struct vma vma[VMA_NUM];
   uint64 cur_mmap_sz;
 };
 
@@ -141,7 +84,7 @@ typedef struct proc proc_t;
 
 void            exit(int);
 int             do_clone(uint64_t stack);
-int             growproc(int);
+uint64          growproc(int);
 void            proc_mapstacks();
 pagetable_t     proc_pagetable(struct proc *);
 void            proc_freepagetable(pagetable_t, uint64);
@@ -158,4 +101,8 @@ int             waitpid(int cid, uint64 addr);
 void            wakeup(void*);
 void            yield(void);
 void            procdump(void);
+void            proc_setmm(proc_t *p, mm_t *newmm);
+
+
+
 #endif
\ No newline at end of file
diff --git a/include/list.h b/include/list.h
new file mode 100644
index 0000000000000000000000000000000000000000..f9b0868b8b7d574e081fa43cde6b90c650fc2997
--- /dev/null
+++ b/include/list.h
@@ -0,0 +1,356 @@
+#ifndef _H_LINKED_LIST
+#define _H_LINKED_LIST
+
+
+#include"types.h"
+
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON0  ((void *) 0x00100100)
+#define LIST_POISON1  ((void *) 0x00200200)
+
+struct list_head{
+  struct list_head *next, *prev;
+};
+
+typedef struct list_head list_head_t;
+
+
+#define LIST_HEAD_INIT(name) {&(name), &(name)}
+
+#define LIST_HEAD(name) \
+        struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/**
+ * list_is_head - tests whether @list is the list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_head(const struct list_head *list, const struct list_head *head)
+{
+	return list == head;
+}
+
+/*
+ * Insert a pnew entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *pnew,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = pnew;
+	pnew->next = next;
+	pnew->prev = prev;
+	prev->next = pnew;
+}
+/**
+ * list_add - add a pnew entry
+ * @pnew: pnew entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a pnew entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *pnew, struct list_head *head)
+{
+	__list_add(pnew, head, head->next);
+}
+
+/**
+ * list_add_tail - add a pnew entry
+ * @pnew: pnew entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a pnew entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *pnew, struct list_head *head)
+{
+	__list_add(pnew, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head * prev, struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+static inline void __list_del_entry(struct list_head *entry)
+{
+	// if (!__list_del_entry_valid(entry))
+	// 	return;
+
+	__list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	__list_del_entry(entry);
+	// entry->next = LIST_POISON1;
+	// entry->prev = LIST_POISON2;
+	entry->next = (list_head_t *)NULL;
+	entry->prev = (list_head_t *)NULL;
+}
+
+/**
+ * list_replace - replace old entry by pnew one
+ * @old : the element to be replaced
+ * @pnew : the pnew element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+				struct list_head *pnew)
+{
+	pnew->next = old->next;
+	pnew->next->prev = pnew;
+	pnew->prev = old->prev;
+	pnew->prev->next = pnew;
+}
+
+
+/**
+ * list_replace_init - replace old entry by pnew one and initialize the old one
+ * @old : the element to be replaced
+ * @pnew : the pnew element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace_init(struct list_head *old,
+				     struct list_head *pnew)
+{
+	list_replace(old, pnew);
+	INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_swap - replace entry1 with entry2 and re-add entry1 at entry2's position
+ * @entry1: the location to place entry2
+ * @entry2: the location to place entry1
+ */
+static inline void list_swap(struct list_head *entry1,
+			     struct list_head *entry2)
+{
+	struct list_head *pos = entry2->prev;
+
+	list_del(entry2);
+	list_replace(entry1, entry2);
+	if (pos == entry1)
+		pos = entry2;
+	list_add(entry1, pos);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del_entry(entry);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+        __list_del(list->prev, list->next);
+        list_add(list, head);
+}
+
+
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+	list_entry((ptr)->next, type, member)
+
+/**
+ * list_last_entry - get the last element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_head within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_last_entry(ptr, type, member) \
+	list_entry((ptr)->prev, type, member)
+
+
+
+/**
+ * list_next_entry - get the next element in list
+ * @pos:	the type * to cursor
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_next_entry(pos, member) \
+	list_entry((pos)->member.next, typeof(*(pos)), member)
+
+/**
+ * list_prev_entry - get the prev element in list
+ * @pos:	the type * to cursor
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_prev_entry(pos, member) \
+	list_entry((pos)->member.prev, typeof(*(pos)), member)
+
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next)
+
+/**
+ * list_entry_is_head - test if the entry points to the head of the list
+ * @pos:	the type * to cursor
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_entry_is_head(pos, head, member)				\
+	(&pos->member == (head))
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_first_entry(head, typeof(*pos), member);	\
+	     !list_entry_is_head(pos, head, member);			\
+	     pos = list_next_entry(pos, member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_head within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_last_entry(head, typeof(*pos), member);		\
+	     !list_entry_is_head(pos, head, member); 			\
+	     pos = list_prev_entry(pos, member))
+
+
+
+
+
+
+// // hash list
+// #define HLIST_HEAD_INIT {.first = NULL}
+// #define HLIST_HEAD(name) struct hlist_head name = {.first = NULL}
+// #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+
+// static inline void INIT_HLIST_NODE(struct hlist_node *h){
+//   h->next = NULL;
+//   h->pprev = NULL;
+// }
+
+
+
+
+// /**
+//  * hlist_add_head - add a pnew entry at the beginning of the hlist
+//  * @n: pnew entry to be added
+//  * @h: hlist head to add it after
+//  *
+//  * Insert a pnew entry after the specified head.
+//  * This is good for implementing stacks.
+//  */
+// static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+// {
+// 	struct hlist_node *first = h->first;
+// 	n->next = first;
+// 	if (first)
+// 		first->pprev = &n->next;
+// 	h->first = n;
+// 	n->pprev = &h->first;
+// }
+
+// static __inline__ void __hlist_del(struct hlist_node *n) 
+// {
+// 	struct hlist_node *next = n->next;
+// 	struct hlist_node **pprev = n->pprev;
+// 	*pprev = next;  
+// 	if (next) 
+// 		next->pprev = pprev;
+// }  
+
+// /**
+//  * hlist_del_rcu - deletes entry from hash list without re-initialization
+//  * @n: the element to delete from the hash list.
+//  *
+//  * Note: list_unhashed() on entry does not return true after this, 
+//  * the entry is in an undefined state. It is useful for RCU based
+//  * lockfree traversal.
+//  *
+//  * In particular, it means that we can not poison the forward
+//  * pointers that may still be used for walking the hash list.
+//  */
+// static inline void hlist_del_rcu(struct hlist_node *n)
+// {
+// 	__hlist_del(n);
+// 	n->pprev = LIST_POISON2;
+// }
+
+
+#endif
\ No newline at end of file
diff --git a/include/memlayout.h b/include/memlayout.h
index 1aa4fdc56013b1b58b67eec964d5c9252b4b2d75..4c1d9071c27cb0dac69b42614d5a16d9292f9e95 100644
--- a/include/memlayout.h
+++ b/include/memlayout.h
@@ -8,7 +8,8 @@
 
 #define IO_BASE_ADDRESS 0x1F00000000
 #define KSTACK_SZ PGSIZE
-#define KSTACK (MAXVA - KSTACK_SZ)
+#define TRAPFRAME (MAXVA - PGSIZE)
+#define KSTACK (TRAPFRAME - KSTACK_SZ)
 #define MMAP_BASE 0x60000000
 
 #endif
\ No newline at end of file
diff --git a/include/mm/mmap.h b/include/mm/mmap.h
new file mode 100644
index 0000000000000000000000000000000000000000..68b47e9db5ee7019d8aa35f3b2d5977152ff7dd0
--- /dev/null
+++ b/include/mm/mmap.h
@@ -0,0 +1,115 @@
+#ifndef _H_MM_
+#define _H_MM_
+#include "list.h"
+#include "common.h"
+// #include "mm/vm.h"
+// #include "proc.h"
+
+#define USERSPACE_END (0x80000000) // 2GB
+#define PROGRAM_BREAK(mm) ((mm)->uheap->addr + (mm)->uheap->addr)
+
+
+struct trapframe {
+  /*   0 @depercated */ uint64 kernel_satp;   // kernel page table 
+  /*   8 */ uint64 kernel_sp;     // top of process's kernel stack
+  /*  16 */ uint64 kernel_trap;   // usertrap()
+  /*  24 */ uint64 epc;           // saved user program counter
+  /*  32 */ uint64 kernel_hartid; // saved kernel tp
+  /*  40 */ uint64 ra;
+  /*  48 */ uint64 sp;
+  /*  56 */ uint64 gp;
+  /*  64 */ uint64 tp;
+  /*  72 */ uint64 t0;
+  /*  80 */ uint64 t1;
+  /*  88 */ uint64 t2;
+  /*  96 */ uint64 s0;
+  /* 104 */ uint64 s1;
+  /* 112 */ uint64 a0;
+  /* 120 */ uint64 a1;
+  /* 128 */ uint64 a2;
+  /* 136 */ uint64 a3;
+  /* 144 */ uint64 a4;
+  /* 152 */ uint64 a5;
+  /* 160 */ uint64 a6;
+  /* 168 */ uint64 a7;
+  /* 176 */ uint64 s2;
+  /* 184 */ uint64 s3;
+  /* 192 */ uint64 s4;
+  /* 200 */ uint64 s5;
+  /* 208 */ uint64 s6;
+  /* 216 */ uint64 s7;
+  /* 224 */ uint64 s8;
+  /* 232 */ uint64 s9;
+  /* 240 */ uint64 s10;
+  /* 248 */ uint64 s11;
+  /* 256 */ uint64 t3;
+  /* 264 */ uint64 t4;
+  /* 272 */ uint64 t5;
+  /* 280 */ uint64 t6;
+};
+
+#define VMA_NUM 16
+/* 用于描述特定一段内存区域(段) */
+struct vma{
+  uint64 addr;
+  uint64 len;
+  uint64_t pa;
+  uint offset; /* 目前用来记录申请时给定地址与其所在页面的偏移量 */
+
+  int flags;
+  int prot;
+
+  list_head_t head; /* 用于串联VMA结构 */
+
+  struct file *map_file;
+};
+
+typedef struct vma vma_t;
+
+// flags
+#define MAP_ANONYMOUS (1L << 31) // 匿名,为0表示FILE
+#define MAP_SHARED    (1L << 30) // 共享,为0表示私有
+#define MAP_STACK (1L << 29) // 表示向下扩展的栈
+// prot(与pte同)
+#define MAP_PROT_READ (PTE_R)
+#define MAP_PROT_WRITE (PTE_W)
+#define MAP_PROT_EXEC (PTE_X)
+#define MAP_PROT_USER (PTE_U)
+
+/* 描述进程的内存段 */
+struct mmlayout
+{
+    uint64_t kstack; /* 内核栈 */
+    uint64_t trapframe; /* 陷入栈帧 */
+
+    vma_t *ustack; /* 用户栈 */
+    vma_t *uheap;  /* 用户堆 */
+
+    list_head_t vma_head; /* 其他内存区域 */
+
+    pagetable_t pagetable;       // User page table
+    // proc_t *owner;
+};
+
+typedef struct mmlayout mm_t;
+
+
+int do_mmap(mm_t *mm, struct file *fp, uint64_t addr, uint64_t len, int flags, int prot);
+int do_mmap_alloc(mm_t *mm, struct file *fp, uint64_t addr, uint64_t len, int flags, int prot);
+void do_unmap(mm_t *mm, uint64_t addr, int do_free);
+int mmap_init(mm_t *mm, mm_t *oldmm);
+void mmap_free(mm_t **pmm);
+vma_t *vma_find(mm_t *mm, uint64 addr);
+vma_t *vma_exist(mm_t *mm, uint64_t addr, uint64_t len);
+struct trapframe *get_trapframe(mm_t *mm);
+uint64_t get_kstack(mm_t *mm);
+int mmap_dup(mm_t *newm, mm_t *oldm);
+int mmap_ext_heap(mm_t *mm, int newsize);
+vma_t *__vma_find_strict(mm_t *mm, uint64 addr);
+void mmap_print_vmas(mm_t *mm);
+void mmap_print_vma(vma_t *vma);
+
+void switchuvm(mm_t *mm);
+void switchkvm();
+
+#endif
\ No newline at end of file
diff --git a/include/mm/vm.h b/include/mm/vm.h
index e50862dd43c771937e016037145318fc20cbf1be..6e75ed000e0df6afaf4c764a9dc93d34d85059f5 100644
--- a/include/mm/vm.h
+++ b/include/mm/vm.h
@@ -1,6 +1,7 @@
-#ifndef _H_MM_
-#define _H_MM_
+#ifndef _H_VM_
+#define _H_VM_
 
+#include "mm/mmap.h"
 #include "mm/page.h"
 #include "mm/alloc.h"
 
@@ -23,27 +24,27 @@ pagetable_t uvmcreate(void);
 void        uvminit(pagetable_t, uchar *, uint);
 uint64      uvmalloc(pagetable_t, uint64, uint64);
 uint64      uvmdealloc(pagetable_t, uint64, uint64);
-int         uvmcopy(pagetable_t, pagetable_t, uint64);
+int         uvmcopy(pagetable_t old, pagetable_t new, uint64 addr, uint64_t len);
 void        uvmfree(pagetable_t, uint64);
 void        uvmclear(pagetable_t, uint64);
 uint64      _walkaddr(pagetable_t pagetable, uint64 va, int pg_spec);
-int         copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len);
 
+void        __copyout(mm_t *mm, uint64_t dstva, char *src, uint64 len, int walk);
+int         copyout(uint64 dstva, char *src, uint64 len);
 /** @deprecated use copy_from_user instead */
-int         copyin(pagetable_t, char *, uint64, uint64);
-int         copyinstr(pagetable_t, char *, uint64, uint64);
-int         cow_copy(pagetable_t pagetable, uint64_t va, pte_t **pppte);
+int         copyin(char *, uint64, uint64);
+int         copyinstr(char *, uint64, uint64);
+int         cow_copy(uint64_t va, pte_t *pte);
 int         copy_from_user(void *to, void *from, size_t n);
 int         either_copyout(int user_dst, uint64 dst, void *src, uint64 len);
 int         either_copyin(void *dst, int user_src, uint64 src, uint64 len);
 
+void        vmprint(pagetable_t pagetable);
+void        print_map(struct _kmap_t map);
+
 int         setupkvm(pagetable_t pagetable);
 void        erasekvm(pagetable_t pagetable);
 
-#include "kernel/proc.h"
-void switchuvm(struct proc *p);
-void switchkvm();
-
 #define walkaddr(pagetable, va) \
     _walkaddr(pagetable, va, PGSPEC_NORMAL)
 
diff --git a/include/param.h b/include/param.h
index 75dde76fd6a317757184f593522ccc01490a3afc..f08199a620b16707b7b4dc697da85ebd390cae70 100644
--- a/include/param.h
+++ b/include/param.h
@@ -14,7 +14,7 @@
 
 #define BSIZE 512
 #define NENTRY 40
-#define MAX_FILE_NAME 20
+#define MAX_FILE_NAME 128
 
 #define USTACKSIZE 8192
 
diff --git a/include/riscv.h b/include/riscv.h
index 97f5c321026b17e692ae492e1118e1cba280dbf9..aa2e7e4f93d9e52f05a1c3a9887dc33cac108dca 100644
--- a/include/riscv.h
+++ b/include/riscv.h
@@ -25,6 +25,8 @@ r_mhartid()
 #define EXCP_LOAD_PAGE_FAULT (EXCEPTION  + 13)
 #define EXCP_STORE_PAGE_FAULT (EXCEPTION  + 15)
 
+#define IS_INTR(scause) ((scause) & INTERRUPT)
+
 static inline uint64
 r_mstatus()
 {
@@ -242,7 +244,7 @@ static inline uint64
 r_fp()
 {
   uint64 x;
-  asm volatile("mv %0, fp" : "=r" (x) );
+  asm volatile("mv %0, s0" : "=r" (x) );
   return x;
 }
 
diff --git a/run-gdb.sh b/run-gdb.sh
index cbefad6b7bc8a3f3dff139b748fccdaf3c46ebc5..f782430789962849590cb73ad09d41251c025ce2 100755
--- a/run-gdb.sh
+++ b/run-gdb.sh
@@ -1,2 +1,2 @@
 #!/bin/sh
-make run platform=qemu EXTRA_QEMUOPTS='-S -gdb tcp::26000' CPUS=1
\ No newline at end of file
+make run platform=qemu debug=on EXTRA_QEMUOPTS='-S -gdb tcp::26000' CPUS=1
\ No newline at end of file
diff --git a/src/fs/fs.c b/src/fs/fs.c
index 46e177bf384b8d7efe084c530904dbbdc631516c..05793b9f0cfcdb4fe182bb9dc2d98483954755fe 100644
--- a/src/fs/fs.c
+++ b/src/fs/fs.c
@@ -8,7 +8,7 @@
 #include "str.h"
 #include "profile.h"
 
-#define QUIET
+// #define QUIET
 #define __MODULE_NAME__ FS
 #include "debug.h"
 
diff --git a/src/fs/pipe.c b/src/fs/pipe.c
index 39c9bd78998edca933e9dda69dcfb6edc94af60d..796836ea3e5fcf2c45bd7c16a5077e209e4b4c44 100644
--- a/src/fs/pipe.c
+++ b/src/fs/pipe.c
@@ -91,7 +91,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n)
       sleep(&pi->nwrite, &pi->lock);
     } else {
       char ch;
-      if(copyin(pr->pagetable, &ch, addr + i, 1) == -1)
+      if(copyin(&ch, addr + i, 1) == -1)
         break;
       pi->data[pi->nwrite++ % PIPESIZE] = ch;
       i++;
@@ -122,7 +122,7 @@ piperead(struct pipe *pi, uint64 addr, int n)
     if(pi->nread == pi->nwrite)
       break;
     ch = pi->data[pi->nread++ % PIPESIZE];
-    if(copyout(pr->pagetable, addr + i, &ch, 1) == -1)
+    if(copyout(addr + i, &ch, 1) == -1)
       break;
   }
   wakeup(&pi->nwrite);  //DOC: piperead-wakeup
diff --git a/src/include/common.h b/src/include/common.h
index 37fd5a322cc114ce32337a6e1aaba186831cd967..a48ead4cf2b727fc35e8f766cd0944205c53ae15 100644
--- a/src/include/common.h
+++ b/src/include/common.h
@@ -12,6 +12,12 @@
 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
 #endif
 
+#ifndef container_of
+#define container_of(ptr, type, member) ({                      \
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
 #define UNUSED(x) x __attribute__((__unused__))
 
 #define likely(x) __builtin_expect(!!(x), 1)
diff --git a/src/include/utils.h b/src/include/utils.h
index 9b972f5bec41de2e5c9e46ce878702e219544ade..280f95428218829994ef4548c38a7d4b56a1724e 100644
--- a/src/include/utils.h
+++ b/src/include/utils.h
@@ -2,7 +2,6 @@
 #define _H_UTILS_
 
 #include "printf.h"
-#include "mm/vm.h"
 #include "sbi.h"
 
 #define LOOP() {while(1) continue;}
@@ -51,10 +50,9 @@ static inline uint32_t get_gpio_bit(volatile uint32_t *bits, size_t offset)
     }
 
 struct dir_item;
+struct proc;
 
-void vmprint(pagetable_t pagetable);
-void backtrace(proc_t *p);
-void print_map(kmap_t map);
+void backtrace(struct proc *p);
 void print_sbiret(sbiret_t ret);
 int  luaO_log2 (unsigned int x);
 void print_page(int pgnum);
diff --git a/src/kernel/exec.c b/src/kernel/exec.c
index 52c60289e9c57e3ac56019428a4e3af836e95d87..3fce60e45f204690de821c5ee10165a131683401 100644
--- a/src/kernel/exec.c
+++ b/src/kernel/exec.c
@@ -14,24 +14,68 @@
 #define __MODULE_NAME__ EXEC
 #include "debug.h"
 
-static int loadseg(pde_t *pgdir, uint64 addr, entry_t *ip, uint offset, uint sz);
+// Load a program segment into pagetable at virtual address va.
+// va must be page-aligned
+// and the pages from va to va+sz must already be mapped.
+// Returns 0 on success, -1 on failure.
+static int loadseg(mm_t *mm, uint64 va, entry_t *ip, uint offset, uint sz) {
+  uint i, n;
+  uint64 pa;
+
+  for(i = 0; i < sz; i += PGSIZE) {
+    pa = walkaddr(mm->pagetable, va + i);
+    if(pa == 0)
+      panic("loadseg: address should exist");
+    if(sz - i < PGSIZE)
+      n = sz - i;
+    else
+      n = PGSIZE;
+    if(reade(ip, 0, (uint64)pa, offset+i, n) != n)
+      return -1;
+  }
+  
+  return 0;
+}
 
-int
-exec(char *path, char **argv)
-{
+int exec(char *path, char **argv) {
   char *s, *last;
   int i, off;
-  uint64 argc, sz = 0, sp, ustack[MAXARG + 1], stackbase;
+  uint64 argc;
+  uint64 ustack, ustackbase;
   struct elfhdr elf;
   entry_t *ep;
   struct proghdr ph;
-  pagetable_t pagetable = 0, oldpagetable;
   struct proc *p = myproc();
+  mm_t *newmm;
+  /* alloc */
+  if((newmm = kzalloc(sizeof(mm_t))) == NULL) {
+    debug("newmm alloc failure");
+    return -1;
+  }
 
+  if((ustackbase = (uint64)kmalloc(USTACKSIZE)) == 0) {
+    debug("stack alloc failure");
+    kfree(newmm);
+    return -1;
+  }
+
+  ustack = ustackbase + USTACKSIZE;
 
   if((ep = namee(NULL, path)) == 0){
+    debug("entry acquire failure");
+    kfree(newmm);
+    kfree((void *)ustackbase);
+    return -1;
+  }
+
+  /* load */
+  if(mmap_init(newmm, p->mm) == -1) {
+    debug("newmm init fail");
+    kfree(newmm);
+    kfree((void *)ustackbase);
     return -1;
   }
+
   elock(ep);
 
   // Check ELF header
@@ -40,11 +84,6 @@ exec(char *path, char **argv)
   if(elf.magic != ELF_MAGIC)
     goto bad;
 
-  if((pagetable = proc_pagetable(p)) == 0)
-    goto bad;
-
-
-  // vmprint(pagetable);
   // Load program into memory.
   for(i=0, off=elf.phoff; i<elf.phnum; i++, off+=sizeof(ph)){
     if(reade(ep, 0, (uint64)&ph, off, sizeof(ph)) != sizeof(ph)){
@@ -56,62 +95,51 @@ exec(char *path, char **argv)
       goto bad;
     if(ph.vaddr + ph.memsz < ph.vaddr)
       goto bad;
-    uint64 sz1;
-    if((sz1 = uvmalloc(pagetable, sz, ph.vaddr + ph.memsz)) == 0)
-      goto bad;
-    sz = sz1;
-    if((ph.vaddr % PGSIZE) != 0)
+    if(do_mmap_alloc(newmm, NULL, ph.vaddr, ph.memsz, 0, MAP_PROT_READ|MAP_PROT_WRITE|MAP_PROT_EXEC|MAP_PROT_USER) == -1)
       goto bad;
-    if(loadseg(pagetable, ph.vaddr, ep, ph.off, ph.filesz) < 0)
+    if(loadseg(newmm, ph.vaddr, ep, ph.off, ph.filesz) < 0)
       goto bad;
   }
 
-  // printf("ph: %d\n", ph.vaddr + ph.memsz);
-  // vmprint(pagetable);
   eunlockput(ep);
   ep = NULL;
 
   p = myproc();
-  uint64 oldsz = p->sz;
 
-  // alloc for user stack
-  sz = PGROUNDUP(sz);
-  uint64 sz1;
-  if((sz1 = uvmalloc(pagetable, sz, sz + PGSIZE + USTACKSIZE)) == 0)
+  /* stack */
+  if(do_mmap(newmm, NULL, USERSPACE_END - USTACKSIZE, USTACKSIZE, MAP_STACK, MAP_PROT_READ|MAP_PROT_WRITE|MAP_PROT_USER) == -1)
     goto bad;
-  sz = sz1;
-  uvmclear(pagetable, sz - (PGSIZE + USTACKSIZE));
-  sp = sz;
-  stackbase = sp - USTACKSIZE;
-
-  // Push argument strings, prepare rest of stack in ustack.
   
+  uint64 argcv[MAXARG + 1];
+  uint64 *argvs = argcv + 1;
+  // Push argument strings, prepare rest of stack in ustack.
   for(argc = 0; argv[argc]; argc++) {
     if(argc >= MAXARG)
       goto bad;
-    sp -= strlen(argv[argc]) + 1;
-    sp -= sp % 16; // riscv sp must be 16-byte aligned
-    if(sp < stackbase)
-      goto bad;
-    if(copyout(pagetable, sp, argv[argc], strlen(argv[argc]) + 1) < 0)
+    ustack -= strlen(argv[argc]) + 1;
+    ustack -= ustack % 16; // riscv sp must be 16-byte aligned
+    if(ustack < ustackbase)
       goto bad;
-    ustack[argc + 1] = sp;
+    memcpy((void *)ustack, argv[argc], strlen(argv[argc]) + 1);
+    argvs[argc] = ustack;
   }
-  ustack[0]= argc;
-  ustack[argc + 1] = 0;
+  argcv[0] = argc;
+  argvs[argc] = 0;
 
-  // push the array of argv[] pointers.
-  sp -= (argc+1+1) * sizeof(uint64);
-  sp -= sp % 16;
-  if(sp < stackbase)
-    goto bad;
-  if(copyout(pagetable, sp, (char *)ustack, (argc+1+1)*sizeof(uint64)) < 0)
+  ustack -= sizeof(uint64) * (1 + 1 + argc); // argc + agrv + 0
+  ustack -= ustack % 16;
+  if(ustack < ustackbase)
     goto bad;
 
+  memcpy((void *)ustack, argcv, sizeof(uint64) * (1 + 1 + argc));
   // arguments to user main(argc, argv)
   // argc is returned via the system call return
   // value, which goes in a0.
-  p->trapframe->a1 = sp;
+  get_trapframe(newmm)->sp = USERSPACE_END - USTACKSIZE + ustack - ustackbase;
+
+  if(mappages(newmm->pagetable, USERSPACE_END - USTACKSIZE, USTACKSIZE, ustackbase, MAP_PROT_READ|MAP_PROT_WRITE|MAP_PROT_USER) == -1) {
+    goto bad;
+  }
 
   // Save program name for debugging.
   for(last=s=path; *s; s++)
@@ -119,48 +147,14 @@ exec(char *path, char **argv)
       last = s+1;
   safestrcpy(p->name, last, sizeof(p->name));
     
-  // Commit to the user image.
-  oldpagetable = p->pagetable;
-  p->pagetable = pagetable;
-  p->sz = sz;
-  // printf(rd("sz: %x\n"), sz);
-  p->trapframe->epc = elf.entry;  // initial program counter = main
-  // debug("entry addr is %lx", elf.entry);
-  p->trapframe->sp = sp; // initial stack pointer
-  switchuvm(p);
-  proc_freepagetable(oldpagetable, oldsz);
+  get_trapframe(newmm)->epc = elf.entry;  // initial program counter = main
+  proc_setmm(p, newmm);
   return argc; // this ends up in a0, the first argument to main(argc, argv)
 
  bad:
-  if(pagetable)
-    proc_freepagetable(pagetable, sz);
-  if(ep){
-    eunlockput(ep);
-  }
+  // TODO:
+  panic("bad");
   return -1;
 }
 
-// Load a program segment into pagetable at virtual address va.
-// va must be page-aligned
-// and the pages from va to va+sz must already be mapped.
-// Returns 0 on success, -1 on failure.
-static int
-loadseg(pagetable_t pagetable, uint64 va, entry_t *ip, uint offset, uint sz)
-{
-  uint i, n;
-  uint64 pa;
 
-  for(i = 0; i < sz; i += PGSIZE){
-    pa = walkaddr(pagetable, va + i);
-    if(pa == 0)
-      panic("loadseg: address should exist");
-    if(sz - i < PGSIZE)
-      n = sz - i;
-    else
-      n = PGSIZE;
-    if(reade(ip, 0, (uint64)pa, offset+i, n) != n)
-      return -1;
-  }
-  
-  return 0;
-}
diff --git a/src/kernel/initcode.c b/src/kernel/initcode.c
index 40e1292b6767207151248e0b4116f547ff09a631..75f33b71902bf39a8f4927c9e29e5b695f8b3308 100644
--- a/src/kernel/initcode.c
+++ b/src/kernel/initcode.c
@@ -1,3 +1,199 @@
+// #include "usys.h"
+// #include "stdarg.h"
+
+// void printf(const char *fmt, ...);
+// void read_test();
+
+// // FS
+// // char *fs_testcase[] = { "mkdir_","openat", "dup2","close", "unlink", "getcwd", "getdents",
+// //                       "chdir", "dup", "pipe", "open", "read", "write", "fstat",
+// //                       "mount", "umount", "test_echo"};
+// // //
+// // char *proc_testcase[] = { "getppid", "getpid",
+// //                        "clone", "wait", "waitpid",
+// //                       "yield", "fork",  "execve", "exit", "sleep"};
+
+// // char *mm_testcase[] = {"brk", "mmap", "munmap"};
+
+// // char *other_testcase[] = {"gettimeofday", "times", "uname"};
+// // //  单项测试
+// // char* prog_name[] = {"ls"};
+
+// // void readcase(int fd, char buf[]) {
+// //   while(read(fd, buf, ))
+// // }
+
+
+// void run(char *testcases[], int cnt);
+// #define run(cases) run(cases, sizeof(cases)/sizeof(cases[0]))
+// __attribute__((section(".startup"))) 
+// void main() {
+//     memuse();
+//     // run(fs_testcase);
+//     // run(proc_testcase);
+//     // run(mm_testcase);
+//     // run(other_testcase);
+//     // run(prog_name);
+//     // int fd = openat(AT_FDCWD, "run_static.sh", O_RDONLY);
+//     char *argv[5];
+//     argv[0] = "runtest.exe";
+//     argv[1] = "-w";
+//     argv[2] = "entry-static.exe";
+//     argv[3] = "argv";
+//     argv[4] = 0;
+//     printf("ready to run %s\n", argv[3]);
+//     int npid = fork();
+//     if(npid < 0) {
+//         printf("fork failed");
+//         for(;;);
+//     }
+//     if (npid == 0) { //子进程
+//         int ret = exec(argv[0], argv);
+//         printf("exec fail with %d\n", ret);
+//     } else {          // 父进程
+//         int status;
+//         wait(&status);
+//         printf("child exit with %d\n", status);
+//     }
+//     memuse();
+//     for(;;);
+// }
+// #undef run
+
+// void run(char *testcases[], int cnt) {
+//   char *argv[2];
+//   argv[1] = 0;
+//   for (int t = 0; t < cnt; t++) {
+//       printf("ready to run %s\n", testcases[t]);
+//       int npid = fork();
+//       if(npid < 0) {
+//           printf("fork failed");
+//           for(;;);
+//       }
+//       if (npid == 0) { //子进程
+//           argv[0] = testcases[t];
+//           int ret = exec(argv[0], argv);
+//           printf("exec fail with %d\n", ret);
+//       } else {          // 父进程
+//           int status;
+//           wait(&status);
+//           printf("child exit with %d\n", status);
+//       }
+//   }
+// }
+
+
+// static char digits[] = "0123456789ABCDEF";
+
+// static void
+// putc(int fd, char c)
+// {
+//   write(fd, &c, 1);
+// }
+
+// static void
+// printint(int fd, int xx, int base, int sgn)
+// {
+//   char buf[16];
+//   int i, neg;
+//   uint x;
+
+//   neg = 0;
+//   if(sgn && xx < 0){
+//     neg = 1;
+//     x = -xx;
+//   } else {
+//     x = xx;
+//   }
+
+//   i = 0;
+//   do{
+//     buf[i++] = digits[x % base];
+//   }while((x /= base) != 0);
+//   if(neg)
+//     buf[i++] = '-';
+
+//   while(--i >= 0)
+//     putc(fd, buf[i]);
+// }
+
+// static void
+// printptr(int fd, uint64 x) {
+//   int i;
+//   putc(fd, '0');
+//   putc(fd, 'x');
+//   for (i = 0; i < (sizeof(uint64) * 2); i++, x <<= 4)
+//     putc(fd, digits[x >> (sizeof(uint64) * 8 - 4)]);
+// }
+
+// // Print to the given fd. Only understands %d, %x, %p, %s.
+// void
+// vprintf(int fd, const char *fmt, va_list ap)
+// {
+//   char *s;
+//   int c, i, state;
+
+//   state = 0;
+//   for(i = 0; fmt[i]; i++){
+//     c = fmt[i] & 0xff;
+//     if(state == 0){
+//       if(c == '%'){
+//         state = '%';
+//       } else {
+//         putc(fd, c);
+//       }
+//     } else if(state == '%'){
+//       if(c == 'd'){
+//         printint(fd, va_arg(ap, int), 10, 1);
+//       } else if(c == 'l') {
+//         printint(fd, va_arg(ap, uint64), 10, 0);
+//       } else if(c == 'x') {
+//         printint(fd, va_arg(ap, int), 16, 0);
+//       } else if(c == 'p') {
+//         printptr(fd, va_arg(ap, uint64));
+//       } else if(c == 's'){
+//         s = va_arg(ap, char*);
+//         if(s == 0)
+//           s = "(null)";
+//         while(*s != 0){
+//           putc(fd, *s);
+//           s++;
+//         }
+//       } else if(c == 'c'){
+//         putc(fd, va_arg(ap, uint));
+//       } else if(c == '%'){
+//         putc(fd, c);
+//       } else {
+//         // Unknown % sequence.  Print it to draw attention.
+//         putc(fd, '%');
+//         putc(fd, c);
+//       }
+//       state = 0;
+//     }
+//   }
+// }
+
+// void
+// fprintf(int fd, const char *fmt, ...)
+// {
+//   va_list ap;
+
+//   va_start(ap, fmt);
+//   vprintf(fd, fmt, ap);
+// }
+
+// void
+// printf(const char *fmt, ...)
+// {
+//   va_list ap;
+
+//   va_start(ap, fmt);
+//   vprintf(1, fmt, ap);
+// }
+
+
+
+////////////////////////////////////////////////////
 #include "usys.h"
 #include "stdarg.h"
 
@@ -5,24 +201,19 @@ void printf(const char *fmt, ...);
 void read_test();
 
 // FS
-// char *fs_testcase[] = { "mkdir_","openat", "dup2","close", "unlink", "getcwd", "getdents",
-//                       "chdir", "dup", "pipe", "open", "read", "write", "fstat",
-//                       "mount", "umount", "test_echo"};
-// //
-// char *proc_testcase[] = { "getppid", "getpid",
-//                        "clone", "wait", "waitpid",
-//                       "yield", "fork",  "execve", "exit", "sleep"};
-
-// char *mm_testcase[] = {"brk", "mmap", "munmap"};
-
-// char *other_testcase[] = {"gettimeofday", "times", "uname"};
-// //  单项测试
-// char* prog_name[] = {"ls"};
-
-// void readcase(int fd, char buf[]) {
-//   while(read(fd, buf, ))
-// }
+char *fs_testcase[] = { "mkdir_","openat", "dup2","close", "unlink", "getcwd", "getdents",
+                      "chdir", "dup", "pipe", "open", "read", "write", "fstat",
+                      "mount", "umount", "test_echo"};
+//
+char *proc_testcase[] = { "getppid", "getpid",
+                       "clone", "wait", "waitpid",
+                      "yield", "fork",  "execve", "exit", "sleep"};
+
+char *mm_testcase[] = {"brk", "mmap", "munmap"};
 
+char *other_testcase[] = {"gettimeofday", "times", "uname"};
+//  单项测试
+char* prog_name[] = {"ls"};
 
 void run(char *testcases[], int cnt);
 #define run(cases) run(cases, sizeof(cases)/sizeof(cases[0]))
@@ -33,30 +224,9 @@ void main() {
     // run(proc_testcase);
     // run(mm_testcase);
     // run(other_testcase);
-    // run(prog_name);
-    // int fd = openat(AT_FDCWD, "run_static.sh", O_RDONLY);
-    char *argv[5];
-    argv[0] = "runtest.exe";
-    argv[1] = "-w";
-    argv[2] = "entry-static.exe";
-    argv[3] = "argv";
-    argv[4] = 0;
-    printf("ready to run %s\n", argv[3]);
-    int npid = fork();
-    if(npid < 0) {
-        printf("fork failed");
-        for(;;);
-    }
-    if (npid == 0) { //子进程
-        int ret = exec(argv[0], argv);
-        printf("exec fail with %d\n", ret);
-    } else {          // 父进程
-        int status;
-        wait(&status);
-        printf("child exit with %d\n", status);
-    }
+    run(prog_name);
     memuse();
-    for(;;);
+  for(;;);
 }
 #undef run
 
@@ -70,6 +240,7 @@ void run(char *testcases[], int cnt) {
           printf("fork failed");
           for(;;);
       }
+      printf("fork done\n");
       if (npid == 0) { //子进程
           argv[0] = testcases[t];
           int ret = exec(argv[0], argv);
diff --git a/src/kernel/pagefault.c b/src/kernel/pagefault.c
index e7d70acc8812d2bec6478f94d12a122dce5ce9e7..c6b214baf0c67469d5aa2d32a41a541a30a95928 100644
--- a/src/kernel/pagefault.c
+++ b/src/kernel/pagefault.c
@@ -15,61 +15,61 @@
  * 
  * @return int 
  */
-int mmap_fetch(){
-    struct proc *p = myproc();
-    uint64 va = r_stval(), pa;
-
-    struct vma *v = 0;
-    int i, j;
-
-    for(i = 0; i < VMA_NUM; i++){
-      v = &(p->vma[i]);
-      if(v->addr <= va && va < v->addr + v->len)
-        break;
-    }
-
-    if(i == VMA_NUM) {
-      return -1; // cannot find
-    }
-
-    for (j = 0; j * PGSIZE < v->len; j++) {
-        if (v->addr + j * PGSIZE <= va && va < v->addr + (j + 1) * PGSIZE) {
-            break;
-            pa = (uint64)kalloc();
-
-            memset((void*)pa, 0, PGSIZE);
-            if (mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, pa,
-                         PTE_R | PTE_W | PTE_X | PTE_U) == -1) {
-                panic("map page failed!");
-            }
-
-            if (reade(v->map_file->ep, 1, PGROUNDDOWN(va), v->off + j * PGSIZE,
-                      min(PGSIZE, v->end - PGROUNDDOWN(va))) == -1) {
-                panic("read file failed!");
-            }
-
-        } else {
-            p->killed = 1;
-            panic("va not find in vma!! lazy allocation is not implemented!");
-        }
-    }
-
-    pa = (uint64)kalloc();
-    memset((void*)pa, 0, PGSIZE);
-    if (mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, pa,
-                 PTE_R | PTE_W | PTE_X | PTE_U) == -1) {
-        panic("map page failed!");
-    }
-
-    // if(reade(v->map_file->ep, 1, PGROUNDDOWN(va), j*PGSIZE, PGSIZE) == -1){
-    if (reade(v->map_file->ep, 1, PGROUNDDOWN(va), v->off + j * PGSIZE,
-              PGSIZE) == -1) {
-        // printf("%d\n", r);
-        panic("read file failed!");
-    }
-
-    return 0;
-}
+// int mmap_fetch(){
+//     struct proc *p = myproc();
+//     uint64 va = r_stval(), pa;
+
+//     struct vma *v = 0;
+//     int i, j;
+
+//     for(i = 0; i < VMA_NUM; i++){
+//       v = &(p->vma[i]);
+//       if(v->addr <= va && va < v->addr + v->len)
+//         break;
+//     }
+
+//     if(i == VMA_NUM) {
+//       return -1; // cannot find
+//     }
+
+//     for (j = 0; j * PGSIZE < v->len; j++) {
+//         if (v->addr + j * PGSIZE <= va && va < v->addr + (j + 1) * PGSIZE) {
+//             break;
+//             pa = (uint64)kalloc();
+
+//             memset((void*)pa, 0, PGSIZE);
+//             if (mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, pa,
+//                          PTE_R | PTE_W | PTE_X | PTE_U) == -1) {
+//                 panic("map page failed!");
+//             }
+
+//             if (reade(v->map_file->ep, 1, PGROUNDDOWN(va), v->off + j * PGSIZE,
+//                       min(PGSIZE, v->end - PGROUNDDOWN(va))) == -1) {
+//                 panic("read file failed!");
+//             }
+
+//         } else {
+//             p->killed = 1;
+//             panic("va not find in vma!! lazy allocation is not implemented!");
+//         }
+//     }
+
+//     pa = (uint64)kalloc();
+//     memset((void*)pa, 0, PGSIZE);
+//     if (mappages(p->pagetable, PGROUNDDOWN(va), PGSIZE, pa,
+//                  PTE_R | PTE_W | PTE_X | PTE_U) == -1) {
+//         panic("map page failed!");
+//     }
+
+//     // if(reade(v->map_file->ep, 1, PGROUNDDOWN(va), j*PGSIZE, PGSIZE) == -1){
+//     if (reade(v->map_file->ep, 1, PGROUNDDOWN(va), v->off + j * PGSIZE,
+//               PGSIZE) == -1) {
+//         // printf("%d\n", r);
+//         panic("read file failed!");
+//     }
+
+//     return 0;
+// }
 
 /**
  * @return -1 if exception unhandled else 0
@@ -77,17 +77,19 @@ int mmap_fetch(){
 int handle_pagefault(uint64_t scause) {
     // printf(grn("%d\n"), scause);
     proc_t *p = myproc();
+    vma_t *vma;
     uint64_t va = read_csr(stval);
 
     // illegal address
-    if(va >= p->cur_mmap_sz) 
-        goto bad;
+    // if(va >= p->cur_mmap_sz) 
+    //     goto bad;
 
     // 地址翻译与访问顺序为:VMA ---> MMU ---> PMA ---> PMP ---> ACCESSED
     // 在特权级1.12下,所有与MMU相关的错误都将触发xx_page_fault
     // 而对于PMP(Physical Memory Protection)相关的错误,都将触发xx_access_fault。
     // 在特权级1.9下,由于没有pagefault(ref: p51),因此SBI帮我在底层做了一下转换
     // 对于缺页的错误,它将非缺页以及非PMP访问的异常都归结到了xx_access_fault中,因此这里需要加一层宏判断
+    // 这似乎违背了SBI的宗旨
     
     #if PRIVILEGE_VERSION == PRIVILEGE_VERSION_1_12
     if(scause == EXCP_STORE_PAGE_FAULT)
@@ -97,26 +99,61 @@ int handle_pagefault(uint64_t scause) {
     if(0)
     #endif
     { // store page fault
-        // cow
-        if(cow_copy(p->pagetable, va, NULL) == -1){
-            if(mmap_fetch() == -1)
-              goto bad;
+        if((vma = __vma_find_strict(p->mm, va)) == NULL) {
+            goto bad;
         }
+        mmap_print_vma(vma);
+        if(vma->prot & MAP_PROT_WRITE) {
+            pte_t *pte = walk(p->mm->pagetable, va, 1);
+            // lazy
+            if((*pte & PTE_V) == 0) {
+                uint64_t newpage = (uint64)kzalloc(PGSIZE);
+                if(newpage == 0 || mappages(p->mm->pagetable, va, PGSIZE, newpage, vma->prot) == -1)
+                    goto bad;
+                return 0;
+            }
+            // cow
+            if(*pte & PTE_COW) {
+                debug("cow trigger...");
+                if(cow_copy(va, pte) == -1){
+                    goto bad;
+                } else {
+                    return 0;
+                }
+            }
+        } else {
+            goto bad;
+        }
+        
         return 0;
     }
     
-    
 
     if(scause == EXCP_LOAD_PAGE_FAULT)
     { 
-        mmap_fetch();
-        return 0;
+        // mmap_fetch();
+        if((vma = __vma_find_strict(p->mm, va)) == NULL) {
+            mmap_print_vmas(p->mm);
+            goto bad;
+        }
+        if(vma->prot & MAP_PROT_READ) {
+            pte_t *pte = walk(p->mm->pagetable, va, 1);
+            // lazy
+            if((*pte & PTE_V) == 0) {
+                uint64_t newpage = (uint64)kzalloc(PGSIZE);
+                if(newpage == 0 || mappages(p->mm->pagetable, va, PGSIZE, newpage, vma->prot) == -1)
+                    goto bad;
+                return 0;
+            }
+        } else {
+            goto bad;
+        }
     }
 
     return -1;
 
     bad:
-    debug("page fault va is %lx sepc is %lx", va, r_sepc());
-    p->killed = 1;
+    debug("page fault va is %lx sepc is %lx scause %lx", va, r_sepc(), scause);
+    myproc()->killed = 1;
     return 0;
 }
\ No newline at end of file
diff --git a/src/kernel/proc.c b/src/kernel/proc.c
index 0bebf4982e5a4ee37230d5e35d805fee76503477..e004708d97a76ea5a87ce2cf36c5d71edfd651c7 100644
--- a/src/kernel/proc.c
+++ b/src/kernel/proc.c
@@ -8,14 +8,14 @@
 #include "defs.h"
 #include "mm/vm.h"
 
-#include "debug.h"
+
 #include "fs/fcntl.h"
 #include "fs/stat.h"
 #include "fs/fs.h"
 #include "fs/file.h"
 
-#define max(a, b) ((a) > (b) ? (a) : (b))
-#define min(a, b) ((a) < (b) ? (a) : (b))
+#define __MODULE_NAME__ PROC
+#include "debug.h"
 
 struct cpu cpus[NUM_CORES];
 
@@ -48,7 +48,7 @@ procinit(void)
   for(p = proc; p < &proc[NPROC]; p++) {
       initlock(&p->lock, "proc");
       p->state = UNUSED;
-      // p->kstack = KSTACK((int) (p - proc));
+      memset(&p->mm, 0, sizeof(mm_t));
       for(int i = 0; i < NOFILE; i++) {
         p->ofile[i] = NULL;
       }
@@ -115,23 +115,10 @@ found:
   p->state = USED;
   p->nfd = NOFILE;
   p->ext_ofile = NULL;
+  p->mm = (mm_t *)kzalloc(sizeof(mm_t));
 
-  // 申请Trapframe
-  if((p->trapframe = (struct trapframe *)kalloc()) == 0){
-    freeproc(p);
-    release(&p->lock);
-    return 0;
-  }
-  // 申请内核栈
-  if((p->kstack = (uint64_t)kmalloc(KSTACK_SZ)) == 0){
-    freeproc(p);
-    release(&p->lock);
-    return 0;
-  }
-
-  // 获取空页表(已经映射了内核地址空间,trapfram以及内核栈)
-  p->pagetable = proc_pagetable(p);
-  if(p->pagetable == 0){
+  if(mmap_init(p->mm, NULL) == -1){
+    debug("mmap_init failure");
     freeproc(p);
     release(&p->lock);
     return 0;
@@ -141,44 +128,39 @@ found:
   // which returns to user space.
   memset(&p->context, 0, sizeof(p->context));
   p->context.ra = (uint64)forkret;
-  p->context.sp = p->kstack + PGSIZE;
+  p->context.sp = get_kstack(p->mm) + KSTACK_SZ;
 
-  p->cur_mmap_sz = MMAP_BASE;
+  // p->cur_mmap_sz = MMAP_BASE;
   return p;
 }
 
-void
-free_vma(struct proc *p){
-  pte_t *pte;
-  struct vma *v;
-  int i;
-  uint64 a;
-
-  // for(;;);
-  for(i=0; i < VMA_NUM; i++){
-    v = &(p->vma[i]);
-    // v->state is cleared in sys_munmap
-    if(v->state == VMA_USED){
-      for(a = PGROUNDDOWN(v->addr); a <= PGROUNDDOWN(v->end); a+=PGSIZE){
-      if ((pte = walk(p->pagetable, a, 0)) == 0)
-        continue;
-      if ((*pte & PTE_V) == 0)//this include the case *pte == 0; or *pte not 0, but *pte &  PTE_V == 0
-        continue;
-
-      // printf(ylw("a: %p\n"), a);
-      //may have bug here!
-      if (v->flags & MAP_SHARED)
-      {
-        // printf(ylw("PGMASK & : %p\n"), (v->end - a));
-        // writee(v->map_file->ep, 1, a, v->off + (a - v->addr), min(PGSIZE, (v->end - a)));
-      }
-      // printf(ylw("free_vam a: %p\n"), a); 
-      uvmunmap(p->pagetable, a, 1, 1);
-
-      }
-    }
-  }
-}
+// void
+// free_vma(struct proc *p){
+//   pte_t *pte;
+//   struct vma *v;
+//   int i;
+//   uint64 a;
+
+//   // for(;;);
+//   list_for_each_entry(v, &PROC_VMA_HEAD(p), head) {
+//     // v->state is cleared in sys_munmap
+//     for(a = PGROUNDDOWN(v->addr); a <= PGROUNDDOWN(v->end); a+=PGSIZE){
+//       if ((pte = walk(p->pagetable, a, 0)) == 0)
+//         continue;
+//       if ((*pte & PTE_V) == 0)//this include the case *pte == 0; or *pte not 0, but *pte &  PTE_V == 0
+//         continue;
+//       //may have bug here!
+//       if (v->flags & MAP_SHARED)
+//       {
+//         // printf(ylw("PGMASK & : %p\n"), (v->end - a));
+//         // writee(v->map_file->ep, 1, a, v->off + (a - v->addr), min(PGSIZE, (v->end - a)));
+//       }
+//       // printf(ylw("free_vam a: %p\n"), a); 
+//       uvmunmap(p->pagetable, a, 1, 1);
+//     }
+    
+//   }
+// }
 
 // free a proc structure and the data hanging from it,
 // including user pages.
@@ -186,23 +168,9 @@ free_vma(struct proc *p){
 static void
 freeproc(struct proc *p)
 {
-  if(p->trapframe)
-    kfree((void*)p->trapframe);
-  if(p->kstack)
-    kfree((void*)p->kstack);
-  if(p->ext_ofile)
-    kfree((void *)p->ext_ofile);
-  p->trapframe = 0;
-  p->kstack = 0;
-
-  free_vma(p);
-
-  // for(;;);
-  if(p->pagetable){
-    proc_freepagetable(p->pagetable, p->sz);
-    p->pagetable = 0;
-  } 
-  p->sz = 0;
+  mmap_free(&p->mm);
+
+  // p->sz = 0;
   p->pid = 0;
   p->parent = 0;
   p->name[0] = 0;
@@ -214,35 +182,35 @@ freeproc(struct proc *p)
 
 // Create a user page table for a given process,
 // with no user memory, but with trampoline pages.
-pagetable_t
-proc_pagetable(struct proc *p)
-{
-  pagetable_t pagetable;
-
-  // An empty page table with kernel page table copy.
-  pagetable = uvmcreate();
-  if(pagetable == 0)
-    return 0;
-  // 映射内核栈
-  if(mappages(pagetable, KSTACK, KSTACK_SZ,
-              (uint64)(p->kstack), PTE_R | PTE_W) < 0){
-    erasekvm(pagetable);
-    uvmfree(pagetable, 0);
-    return 0;
-  }
-
-  return pagetable;
-}
+// pagetable_t
+// proc_pagetable(struct proc *p)
+// {
+//   pagetable_t pagetable;
+
+//   // An empty page table with kernel page table copy.
+//   pagetable = uvmcreate();
+//   if(pagetable == 0)
+//     return 0;
+//   // 映射内核栈
+//   if(mappages(pagetable, KSTACK, KSTACK_SZ,
+//               (uint64)(p->kstack), PTE_R | PTE_W) < 0){
+//     erasekvm(pagetable);
+//     uvmfree(pagetable, 0);
+//     return 0;
+//   }
+
+//   return pagetable;
+// }
 
 // Free a process's page table, and free the
 // physical memory it refers to.
-void
-proc_freepagetable(pagetable_t pagetable, uint64 sz)
-{
-  uvmunmap(pagetable, KSTACK, 1, 0);
-  erasekvm(pagetable);
-  uvmfree(pagetable, sz);
-}
+// void
+// proc_freepagetable(proc_t *p)
+// {
+
+//   erasekvm(p->mm.pagetable);
+//   uvmfree(pagetable, sz);
+// }
 
 // a user program that calls exec("/init")
 // od -t xC initcode
@@ -279,21 +247,31 @@ void
 userinit(void)
 {
   struct proc *p;
-
   p = allocproc();
   initproc = p;
 
-  // allocate one user page and copy init's instructions
-  // and data into it.
-  uvminit(p->pagetable, initcode, sizeof(initcode));
-  p->sz = PGSIZE;
+  if(sizeof(initcode) >= PGSIZE)
+    panic("inituvm: more than a page");
+
+  switchuvm(p->mm);
+
+  if(do_mmap_alloc(p->mm, NULL, 0, PGSIZE, MAP_ANONYMOUS, MAP_PROT_WRITE|MAP_PROT_READ|MAP_PROT_EXEC|MAP_PROT_USER) == -1) {
+    panic("mmap alloc failure");
+  }
+
+  #if PRIVILEGE_VERSION == PRIVILEGE_VERSION_1_12
+  enable_sum();
+  #endif
+  memmove(0, initcode, sizeof(initcode));
+  #if PRIVILEGE_VERSION == PRIVILEGE_VERSION_1_12
+  disable_sum();
+  #endif
 
   // prepare for the very first "return" from kernel to user.
-  p->trapframe->epc = 0;      // user program counter
-  p->trapframe->sp = PGSIZE;  // user stack pointer
+  get_trapframe(p->mm)->epc = 0;      // user program counter
+  get_trapframe(p->mm)->sp = PGSIZE;  // user stack pointer
 
   safestrcpy(p->name, "initcode", sizeof(p->name));
-    // p->cwd = namei("/");
 
   init_std(p);
 
@@ -303,22 +281,16 @@ userinit(void)
 }
 
 
-int
-growproc(int n)
-{
-  uint sz;
+uint64 growproc(int n) {
   struct proc *p = myproc();
-
-  sz = p->sz;
-  if(n > 0){
-    if((sz = uvmalloc(p->pagetable, sz, sz + n)) == 0) {
-      return -1;
-    }
-  } else if(n < 0){
-    sz = uvmdealloc(p->pagetable, sz, sz + n);
+  if(n == 0) {
+    return PROGRAM_BREAK(p->mm);
   }
-  p->sz = sz;
-  return 0;
+  if(mmap_ext_heap(p->mm, n) == -1) {
+    return -1;
+  }
+  
+  return PROGRAM_BREAK(p->mm);
 }
 
 // Create a new process, copying the parent.
@@ -336,22 +308,18 @@ do_clone(uint64_t stack)
   }
 
   // 拷贝内存布局(如果开启了COW,那么仅仅是复制页表)
-  if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
+  if(mmap_dup(np->mm, p->mm) == -1){
     freeproc(np);
     release(&np->lock);
     return -1;
   }
-  np->sz = p->sz;
-
-  // 复制trapframe
-  *(np->trapframe) = *(p->trapframe);
 
-  // 将返回地址重置为0
-  np->trapframe->a0 = 0;
+  // 将返回值置为0
+  get_trapframe(np->mm)->a0 = 0;
 
   // 如果指定了栈,那么重设sp
   if(stack)
-    np->trapframe->sp = stack;
+    get_trapframe(np->mm)->sp = stack;
 
   // 增加文件描述符引用
   for(i = 0; i < NOFILE; i++)
@@ -374,7 +342,7 @@ do_clone(uint64_t stack)
   release(&np->lock);
 
 
-  np->cur_mmap_sz = p->cur_mmap_sz;
+  // np->cur_mmap_sz = p->cur_mmap_sz;
   return pid;
 }
 
@@ -469,7 +437,7 @@ waitpid(int cid, uint64 addr)
         if(np->state == ZOMBIE){
           // Found one.
           
-          if(addr != 0 && copyout(p->pagetable, addr, (char *)&np->xstate,
+          if(addr != 0 && copyout(addr, (char *)&np->xstate,
                                   sizeof(np->xstate)) < 0) {
             release(&np->lock);
             release(&wait_lock);
@@ -521,7 +489,7 @@ scheduler(void)
         // before jumping back to us.
         p->state = RUNNING;
         c->proc = p;
-        switchuvm(p);
+        switchuvm(p->mm);
         swtch(&c->context, &p->context);
         switchkvm();
         // Process is done running for now.
@@ -578,9 +546,9 @@ void
 forkret(void)
 {
   static int first = 1;
-
+  proc_t *p = myproc();
   // Still holding p->lock from scheduler.
-  release(&myproc()->lock);
+  release(&p->lock);
 
   if (first) {
     // File system initialization must be run in the context of a
@@ -589,10 +557,10 @@ forkret(void)
     first = 0;
 
     extern fat32_t *fat;
-    printf("ready to mount\n");
+    debug("ready to mount\n");
     fat_mount(ROOTDEV, &fat);
-    printf("mount done\n");
-    myproc()->cwd = namee(NULL, "/");
+    debug("mount done\n");
+    p->cwd = namee(NULL, "/");
     // LOOP();
     // fsinit(ROOTDEV);
 
@@ -683,9 +651,8 @@ kill(int pid)
 int
 either_copyout(int user_dst, uint64 dst, void *src, uint64 len)
 {
-  struct proc *p = myproc();
   if(user_dst){
-    return copyout(p->pagetable, dst, src, len);
+    return copyout(dst, src, len);
   } else {
     memmove((char *)dst, src, len);
     return 0;
@@ -698,15 +665,22 @@ either_copyout(int user_dst, uint64 dst, void *src, uint64 len)
 int
 either_copyin(void *dst, int user_src, uint64 src, uint64 len)
 {
-  struct proc *p = myproc();
   if(user_src){
-    return copyin(p->pagetable, dst, src, len);
+    return copyin(dst, src, len);
   } else {
     memmove(dst, (char*)src, len);
     return 0;
   }
 }
 
+
+void proc_setmm(proc_t *p, mm_t *newmm) {
+  mm_t *oldmm = p->mm;
+  p->mm = newmm;
+  switchuvm(p->mm);
+  mmap_free(&oldmm);
+}
+
 // Print a process listing to console.  For debugging.
 // Runs when user types ^P on console.
 // No lock to avoid wedging a stuck machine further.
diff --git a/src/kernel/sys.c b/src/kernel/sys.c
index 5d43a8f9bb8714b3ac6383d41c6581d581b0402e..8e2d7759272b31f6dba1b9eb83d6d6d496da5380 100644
--- a/src/kernel/sys.c
+++ b/src/kernel/sys.c
@@ -3,6 +3,7 @@
 #include "defs.h"
 #include "profile.h"
 #include "mm/buddy.h"
+#include "mm/vm.h"
 
 typedef struct {
     uint64_t hit;
@@ -34,7 +35,7 @@ uint64 sys_bio_cache(void) {
   extern uint64_t bio_cache_hit, bio_cache_miss;
   rate.hit = bio_cache_hit;
   rate.miss = bio_cache_miss;
-  return copyout(myproc()->pagetable, addr, (char *)&rate, sizeof(rate));
+  return copyout(addr, (char *)&rate, sizeof(rate));
   #endif
 
 
@@ -54,7 +55,7 @@ uint64_t sys_times(void) {
   if(argaddr(0, &addr) < 0) 
     return -1;
   
-  if(copyout(myproc()->pagetable, addr, (char *)&ticks, sizeof(ticks)) == -1)  
+  if(copyout(addr, (char *)&ticks, sizeof(ticks)) == -1)  
     ret = -1;
 
   return ret;
@@ -67,7 +68,7 @@ uint64_t sys_uname(void) {
     return -1;
   }
 
-  return copyout(myproc()->pagetable, addr, (char *)&sysname, sizeof(utsname_t));
+  return copyout(addr, (char *)&sysname, sizeof(utsname_t));
 }
 uint64_t sys_gettimeofday(void) {
   timespec_t time;
@@ -79,7 +80,7 @@ uint64_t sys_gettimeofday(void) {
 
   time = TICK2TIMESPEC(ticks);
 
-  if(copyout(myproc()->pagetable, addr, (char *)&time, sizeof(time)) == -1) {
+  if(copyout(addr, (char *)&time, sizeof(time)) == -1) {
     ret = -1;
   } 
   return ret;
diff --git a/src/kernel/syscall.c b/src/kernel/syscall.c
index 4b1dc85b5e5818d0a6f569d02b993cec8a75029d..56aa08b6ba84a933c5d486e8276fd4c435f36cb0 100644
--- a/src/kernel/syscall.c
+++ b/src/kernel/syscall.c
@@ -13,9 +13,9 @@ int
 fetchaddr(uint64 addr, uint64 *ip)
 {
   struct proc *p = myproc();
-  if(addr >= p->sz || addr+sizeof(uint64) > p->sz)
+  if(__vma_find_strict(p->mm, addr) == NULL)
     return -1;
-  if(copyin(p->pagetable, (char *)ip, addr, sizeof(*ip)) != 0)
+  if(copyin((char *)ip, addr, sizeof(*ip)) != 0)
     return -1;
   return 0;
 }
@@ -25,8 +25,7 @@ fetchaddr(uint64 addr, uint64 *ip)
 int
 fetchstr(uint64 addr, char *buf, int max)
 {
-  struct proc *p = myproc();
-  int err = copyinstr(p->pagetable, buf, addr, max);
+  int err = copyinstr(buf, addr, max);
   if(err < 0)
     return err;
   return strlen(buf);
@@ -38,17 +37,17 @@ argraw(int n)
   struct proc *p = myproc();
   switch (n) {
   case 0:
-    return p->trapframe->a0;
+    return get_trapframe(p->mm)->a0;
   case 1:
-    return p->trapframe->a1;
+    return get_trapframe(p->mm)->a1;
   case 2:
-    return p->trapframe->a2;
+    return get_trapframe(p->mm)->a2;
   case 3:
-    return p->trapframe->a3;
+    return get_trapframe(p->mm)->a3;
   case 4:
-    return p->trapframe->a4;
+    return get_trapframe(p->mm)->a4;
   case 5:
-    return p->trapframe->a5;
+    return get_trapframe(p->mm)->a5;
   }
   panic("argraw");
   return -1;
@@ -109,12 +108,12 @@ syscall(void)
   int num;
   struct proc *p = myproc();
 
-  num = p->trapframe->a7;
+  num = get_trapframe(p->mm)->a7;
   if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
-    p->trapframe->a0 = syscalls[num]();
+    get_trapframe(p->mm)->a0 = syscalls[num]();
   } else {
     printf("%d %s: unknown sys call %d\n",
             p->pid, p->name, num);
-    p->trapframe->a0 = -1;
+    get_trapframe(p->mm)->a0 = -1;
   }
 }
diff --git a/src/kernel/sysfile.c b/src/kernel/sysfile.c
index 44240dc3f61a2ccd65c5a7d3f9793a26659bd271..1df2bfaa6cd848b50e5f68005e7de265b234c46a 100644
--- a/src/kernel/sysfile.c
+++ b/src/kernel/sysfile.c
@@ -12,12 +12,12 @@
 #include "fs/fs.h"
 #include "fs/stat.h"
 #include "kernel/proc.h"
+#include "mm/mmap.h"
 #include "mm/vm.h"
 #include "param.h"
 #include "riscv.h"
-#include "types.h"
 
-#define QUIET
+// #define QUIET
 #define __MODULE_NAME__ SYS_FILE
 #include "debug.h"
 
@@ -174,7 +174,6 @@ uint64 sys_close(void) {
 }
 
 uint64 sys_fstat(void) {
-    proc_t* p = myproc();
     struct file* f;
     struct kstat stat;
     entry_t* entry;
@@ -188,7 +187,7 @@ uint64 sys_fstat(void) {
     estat(entry, &stat);
     eunlock(entry);
 
-    return copyout(p->pagetable, addr, (char*)&stat, sizeof(stat));
+    return copyout(addr, (char*)&stat, sizeof(stat));
 }
 
 uint64 sys_getcwd(void) {
@@ -204,7 +203,7 @@ uint64 sys_getcwd(void) {
     char* end = getcwd(p->cwd, buf);
     assert(*end == '\0');
     // debug("%s", buf);
-    if (copyout(p->pagetable, addr, buf, size) == -1) {
+    if (copyout(addr, buf, size) == -1) {
         return 0;
     }
 
@@ -248,7 +247,6 @@ uint64 sys_unlinkat(void) {
 }
 
 uint64 sys_getdents64(void) {
-    proc_t* p = myproc();
     struct file* f;
     uint64_t addr;
     int len;
@@ -261,7 +259,7 @@ uint64 sys_getdents64(void) {
     int ret = read_dents(f->ep, &f->off, buf, len);
     eunlock(f->ep);
 
-    if (copyout(p->pagetable, addr, buf, len) == -1) {
+    if (copyout(addr, buf, len) == -1) {
         kfree(buf);
         return -1;
     }
@@ -399,7 +397,7 @@ uint64 sys_exec(void) {
         }
         argv[i] = kalloc();
         if (argv[i] == 0)
-            goto bad;
+            goto bad;   
         if (fetchstr(uarg, argv[i], PGSIZE) < 0)
             goto bad;
     }
@@ -436,8 +434,8 @@ uint64 sys_pipe2(void) {
         fileclose(wf);
         return -1;
     }
-    if (copyout(p->pagetable, fdarray, (char*)&fd0, sizeof(fd0)) < 0 ||
-        copyout(p->pagetable, fdarray + sizeof(fd0), (char*)&fd1, sizeof(fd1)) <
+    if (copyout(fdarray, (char*)&fd0, sizeof(fd0)) < 0 ||
+        copyout(fdarray + sizeof(fd0), (char*)&fd1, sizeof(fd1)) <
             0) {
         p->ofile[fd0] = 0;
         p->ofile[fd1] = 0;
@@ -449,141 +447,110 @@ uint64 sys_pipe2(void) {
 }
 
 // todo: fd换file
-uint64 sys_mmap(void) {
-    uint64 addr, length, offset;
-    int prot, flags, fd, i;
-    struct proc* p = myproc();
-
-    if (argaddr(0, &addr) < 0 || argaddr(1, &length) < 0 ||
-        argint(2, &prot) < 0 || argint(3, &flags) < 0 || argint(4, &fd) < 0 ||
-        argaddr(5, &offset) < 0)
-        return -1;
-
-    if ((get_file(fd)->writable == 0) && (prot & PROT_WRITE) &&
-        (flags & MAP_SHARED))
-        return -1;
-
-    uint64 old_addr = p->cur_mmap_sz;
+// uint64 sys_mmap(void) {
+//     uint64 addr, length, offset;
+//     int prot, flags, fd, i;
+//     struct proc* p = myproc();
+
+//     if (argaddr(0, &addr) < 0 || argaddr(1, &length) < 0 ||
+//         argint(2, &prot) < 0 || argint(3, &flags) < 0 || argint(4, &fd) < 0 ||
+//         argaddr(5, &offset) < 0)
+//         return -1;
+//     struct file *fp = get_file(fd);
+//     if (fp && (fp->writable == 0) && (prot & PROT_WRITE) &&
+//         (flags & MAP_SHARED))
+//         return -1;
+
+//     if(do_file_mmap(&p->mm, fp, offset, addr, length, flags)) {
+//         return addr;
+//     } else {
+//         return addr;
+//     }
+
+// }
+
+// /**
+//  * @brief 一个最简单的版本的munmap,因为v->addr都是页对齐的,所以
+//  * 要求va是页对齐的,并且len为PGSIZE的整数倍,或者等于区域的总长度
+//  * 否则需要考虑区域的分割等问题。
+//  *
+//  * @return uint64
+//  */
+// uint64 sys_munmap(void) {
+//     uint64 va, len;
+//     if (argaddr(0, &va) < 0 || argaddr(1, &len) < 0)
+//         return -1;
+
+//     struct proc* p = myproc();
+//     struct vma* v = 0;
+//     int i;
+//     pte_t* pte;
 
-    //有个问题,一个文件似乎必须占据整个页,如果两个文件映射到同一个页,那么
-    //第一个触发页错误的文件会导致第二个无法触发页错误,从而第二个文件可能读写
-    //第一个文件的mmap区域。
-    p->cur_mmap_sz += PGROUNDUP(length);
-    for (i = 0; i < VMA_NUM; i++) {  // lock?
-        if (p->vma[i].state == VMA_UNUSED) {
-            p->vma[i].state = VMA_USED;
-            if (addr == 0)
-                p->vma[i].addr = old_addr;
-            else
-                p->vma[i].addr = addr;
-            p->vma[i].len = length;
-            p->vma[i].prot = prot;
-            p->vma[i].flags = flags;
-            p->vma[i].off = offset;
-            p->vma[i].end = (old_addr + length);
 
-            filedup(p->ofile[fd]);
-            p->vma[i].map_file = p->ofile[fd];
-
-            break;
-        }
-    }
-    if (i == VMA_NUM)
-        panic("vma is full!");
-
-    return old_addr;
-}
-
-/**
- * @brief 一个最简单的版本的munmap,因为v->addr都是页对齐的,所以
- * 要求va是页对齐的,并且len为PGSIZE的整数倍,或者等于区域的总长度
- * 否则需要考虑区域的分割等问题。
- *
- * @return uint64
- */
-uint64 sys_munmap(void) {
-    uint64 va, len;
-    if (argaddr(0, &va) < 0 || argaddr(1, &len) < 0)
-        return -1;
-
-    struct proc* p = myproc();
-    struct vma* v = 0;
-    int i;
-    pte_t* pte;
-
-    for (i = 0; i < VMA_NUM; i++) {
-        v = &(p->vma[i]);
-        if (v->state == VMA_USED && v->addr == va)
-            break;
-    }
-    if (i == VMA_NUM)  // ummap null
-        panic("munmap: va is not equal to one of v->addr!");
-    // return -1;
-
-    if (len % PGSIZE != 0 && len != v->len)
-        panic("munmap: length is not a multiple of PGSIZE or vma length!");
-
-    va = PGROUNDUP(va);
-    // if a virtual address has been mapped to physic address,
-    // unmap it, otherwise do noting.
-    if (va <= PGROUNDDOWN(va + len))
-        for (int a = va; a <= PGROUNDDOWN(va + len); a += PGSIZE) {
-            // printf(rd("a: %p\n"), a);
-            if ((pte = walk(p->pagetable, a, 0)) == 0)
-                continue;
-            if ((*pte & PTE_V) == 0)
-                continue;
-
-            // write back to disk
-            if (v->flags & MAP_SHARED) {
-                writee(v->map_file->ep, 1, a, v->off + (a - v->addr),
-                       min(PGSIZE, (v->end - a)));
-            }
-            uvmunmap(p->pagetable, a, 1, 1);
-        }
-    if (i == VMA_NUM)  // ummap null
-        return -1;
-
-    va = PGROUNDUP(va);
-    // if a virtual address has been mapped to physic address,
-    // unmap it, otherwise do noting.
-    if (va < PGROUNDDOWN(va + len))
-        for (int a = va; a < PGROUNDDOWN(va + len); a += PGSIZE) {
-            if ((pte = walk(p->pagetable, a, 0)) == 0)
-                continue;
-            if ((*pte & PTE_V) == 0)
-                continue;
-
-            // write back to disk
-            if (v->flags & MAP_SHARED) {
-                filewrite(v->map_file, va, PGSIZE);
-            }
-            uvmunmap(p->pagetable, a, 1, 1);
-        }
-
-    // free the entire vma
-    if (va == v->addr && len == v->len) {
-        v->state = VMA_UNUSED;
-        fileclose(v->map_file);
-    }
-
-    // for a simple version of mmap, we assume it's unmap from the head
-    // of a vma, namely va == v->addr
-    // if we unmap in the middle of a vma, the vma is split into two
-    v->addr += len;
-    v->len -= len;
-
-    // free the entire vma
-    if (va == v->addr && len == v->len) {
-        // v->state = VMA_UNUSED;
-        // fileclose(v->map_file);
-    }
-
-    // for a simple version of mmap, we assume it's unmap from the head
-    // of a vma, namely va == v->addr
-    // if we unmap in the middle of a vma, the vma is split into two
-    v->addr += len;
-    v->len -= len;
+    
 
-    return 0;
-}
\ No newline at end of file
+//     if (len % PGSIZE != 0 && len != v->len)
+//         panic("munmap: length is not a multiple of PGSIZE or vma length!");
+
+//     va = PGROUNDUP(va);
+//     // if a virtual address has been mapped to physic address,
+//     // unmap it, otherwise do noting.
+//     if (va <= PGROUNDDOWN(va + len))
+//         for (int a = va; a <= PGROUNDDOWN(va + len); a += PGSIZE) {
+//             // printf(rd("a: %p\n"), a);
+//             if ((pte = walk(p->pagetable, a, 0)) == 0)
+//                 continue;
+//             if ((*pte & PTE_V) == 0)
+//                 continue;
+
+//             // write back to disk
+//             if (v->flags & MAP_SHARED) {
+//                 writee(v->map_file->ep, 1, a, v->off + (a - v->addr),
+//                        min(PGSIZE, (v->end - a)));
+//             }
+//             uvmunmap(p->pagetable, a, 1, 1);
+//         }
+
+//     va = PGROUNDUP(va);
+//     // if a virtual address has been mapped to physic address,
+//     // unmap it, otherwise do noting.
+//     if (va < PGROUNDDOWN(va + len))
+//         for (int a = va; a < PGROUNDDOWN(va + len); a += PGSIZE) {
+//             if ((pte = walk(p->pagetable, a, 0)) == 0)
+//                 continue;
+//             if ((*pte & PTE_V) == 0)
+//                 continue;
+
+//             // write back to disk
+//             if (v->flags & MAP_SHARED) {
+//                 filewrite(v->map_file, va, PGSIZE);
+//             }
+//             uvmunmap(p->pagetable, a, 1, 1);
+//         }
+
+//     // free the entire vma
+//     if (va == v->addr && len == v->len) {
+//         v->state = VMA_UNUSED;
+//         fileclose(v->map_file);
+//     }
+
+//     // for a simple version of mmap, we assume it's unmap from the head
+//     // of a vma, namely va == v->addr
+//     // if we unmap in the middle of a vma, the vma is split into two
+//     v->addr += len;
+//     v->len -= len;
+
+//     // free the entire vma
+//     if (va == v->addr && len == v->len) {
+//         // v->state = VMA_UNUSED;
+//         // fileclose(v->map_file);
+//     }
+
+//     // for a simple version of mmap, we assume it's unmap from the head
+//     // of a vma, namely va == v->addr
+//     // if we unmap in the middle of a vma, the vma is split into two
+//     v->addr += len;
+//     v->len -= len;
+
+//     return 0;
+// }
\ No newline at end of file
diff --git a/src/kernel/sysproc.c b/src/kernel/sysproc.c
index 0908d26e313d6a10b90dcb067de837e8f79ab32a..e1668930e869ff1444f98fb5067c95b961af9690 100644
--- a/src/kernel/sysproc.c
+++ b/src/kernel/sysproc.c
@@ -8,6 +8,7 @@
 #include "kernel/proc.h"
 #include "common.h"
 #include "kernel/time.h"
+#include "mm/vm.h"
 
 #define QUIET
 #define __MODULE_NAME__ SYS_PROC
@@ -84,20 +85,12 @@ sys_wait4(void)
 uint64
 sys_sbrk(void)
 {
-  int addr;
   int n;
 
   if(argint(0, &n) < 0)
     return -1;
-  addr = myproc()->sz;
-
-  if(n == 0)
-    return addr;
-  
-  if(growproc(n - addr) < 0)
-    return -1;
     
-  return addr;
+  return growproc(n);
 }
 
 uint64
diff --git a/src/kernel/trap.c b/src/kernel/trap.c
index f673bd241de212a64be47d1dd1b18066b195e512..116ebcc7c89cfd7cf4c19a07f5f3c767ca5bb966 100644
--- a/src/kernel/trap.c
+++ b/src/kernel/trap.c
@@ -54,6 +54,11 @@ usertrap(void)
     printf("sepc=%p stval=%p\n", read_csr(sepc), read_csr(stval));
     panic("usertrap: not from user mode");
   }
+
+  // if(!IS_INTR(scause)) {
+  //   debug("sepc is %lx scause is %lx stval is %lx intr is %d", r_sepc(), scause, r_stval(), intr_get());
+  //   LOOP();
+  // }
   // send interrupts and exceptions to kerneltrap(),
   // since we're now in the kernel.
   // w_stvec((uint64)kernelvec);
@@ -62,8 +67,7 @@ usertrap(void)
   struct proc *p = myproc();
 
   // save user program counter.
-  // p->trapframe->epc = r_sepc();
-  p->trapframe->epc = read_csr(sepc);
+  get_trapframe(p->mm)->epc = read_csr(sepc);
 
   if (scause == EXCP_SYSCALL) {
     if(p->killed) {
@@ -71,7 +75,7 @@ usertrap(void)
     }
     // sepc points to the ecall instruction,
     // but we want to return to the next instruction.
-    p->trapframe->epc += 4;
+    get_trapframe(p->mm)->epc += 4;
     // an interrupt will change sstatus &c registers,
     // so don't enable until done with those registers.
     // debug("usertrap: proc is %s syscall num is %d", p->name, p->trapframe->a7);
@@ -98,7 +102,7 @@ usertrap(void)
 //
 // return to user space
 //
-void userret(struct trapframe *trapfram);
+void userret(uint64 trapfram);
 void uservec();
 void
 usertrapret(void)
@@ -113,9 +117,9 @@ usertrapret(void)
 
   // set up trapframe values that uservec will need when
   // the process next re-enters the kernel.        // kernel page table
-  p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
-  p->trapframe->kernel_trap = (uint64)usertrap;
-  p->trapframe->kernel_hartid = r_tp();         // hartid for cpuid()
+  get_trapframe(p->mm)->kernel_sp = get_kstack(p->mm) + KSTACK_SZ; // process's kernel stack
+  get_trapframe(p->mm)->kernel_trap = (uint64)usertrap;
+  get_trapframe(p->mm)->kernel_hartid = r_tp();         // hartid for cpuid()
 
   // set up the registers that trampoline.S's sret will use
   // to get to user space.
@@ -127,14 +131,14 @@ usertrapret(void)
   w_sstatus(x);
 
   // set S Exception Program Counter to the saved user pc.
-  w_sepc(p->trapframe->epc);
+  w_sepc(get_trapframe(p->mm)->epc);
   // tell trampoline.S the user page table to switch to.
   // uint64 satp = MAKE_SATP(p->pagetable);
 
   // jump to trampoline.S at the top of memory, which 
   // switches to the user page table, restores user registers,
   // and switches to user mode with sret.
-  userret(p->trapframe);
+  userret((uint64)get_trapframe(p->mm));
 }
 
 // interrupts and exceptions from kernel code go here via kernelvec,
@@ -145,12 +149,19 @@ kerneltrap()
   uint64 sepc = r_sepc();
   uint64 sstatus = r_sstatus();
   uint64 scause = r_scause();
-  // debug("kerneltrap: sepc is %lx scause is %lx stval is %lx intr is %d", r_sepc(), scause, r_stval(), intr_get());
-
+  
+  
   if(myproc()) {
     myproc()->ktrap_fp = *(uint64*)(r_fp()-16);
   }
 
+  if(!IS_INTR(scause)) {
+    debug("sepc is %lx scause is %lx stval is %lx intr is %d", r_sepc(), scause, r_stval(), intr_get());
+    debug("kstack: %lx", myproc()->mm->kstack);
+    // backtrace(myproc());
+    LOOP();
+  }
+
   if((sstatus & SSTATUS_SPP) == 0)
     panic("kerneltrap: not from supervisor mode");
   if(intr_get() != 0)
@@ -158,6 +169,8 @@ kerneltrap()
 
   if(devintr(scause) == 0) {
     // ok
+  } else if(handle_pagefault(scause) == 0) {
+    // ok
   } else{
     printf("scause %p\n", scause);
     printf("sepc=%p stval=%p\n", r_sepc(), r_stval());
diff --git a/src/main.c b/src/main.c
index d21faafb8d62e58a7ddd276b8b39de1cf692c9d1..2d736f68b63f7bb41601db2b710f5042b4c7b72c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,6 +5,7 @@
 #include "defs.h"
 #include "mm/vm.h"
 #include "platform.h"
+#include "kernel/proc.h"
 #include "driver/plic.h"
 #include "test.h"
 #include "fs/blk_device.h"
@@ -54,6 +55,7 @@ main()
     fs_init();
     
     userinit();      // first user process
+    printf("user init success\n");
     __sync_synchronize();
     started = 1;
   } else {
diff --git a/src/mm/Makefile b/src/mm/Makefile
index 18f7d49a1ece954d8230b09dd2556aa973e65e49..f234cbc5b226228be58f5a049f30dafbe7ee37b5 100644
--- a/src/mm/Makefile
+++ b/src/mm/Makefile
@@ -5,4 +5,5 @@ obj-y += copy.o
 obj-y += page.o
 obj-y += buddy.o
 obj-y += alloc.o
+obj-y += mmap.o
 obj-y += slob.o
\ No newline at end of file
diff --git a/src/mm/alloc.c b/src/mm/alloc.c
index c6ef2540f9ed37688453f1807b171df72ebb6d88..08ee39e59b6445395392f2650931a45a0b47c409 100644
--- a/src/mm/alloc.c
+++ b/src/mm/alloc.c
@@ -53,7 +53,7 @@ void *kzalloc(size_t size) {
  * @deprecated use kmalloc(PGSIZE) instead
 */
 void *kalloc(void) {
-    return kmalloc(PGSIZE);
+    return kzalloc(PGSIZE);
 }
 
 /**
diff --git a/src/mm/copy.c b/src/mm/copy.c
index c4e4e04c0cdde0d199a2e0a3bdacb037d232b9b0..784c9e9fd4df6ba4c7a6deb0a23966b7c906b58e 100644
--- a/src/mm/copy.c
+++ b/src/mm/copy.c
@@ -1,7 +1,12 @@
 #include "utils.h"
 #include "mm/vm.h"
+#include "mm/mmap.h"
 #include "defs.h"
 #include "platform.h"
+#include "kernel/proc.h"
+
+#define __MODULE_NAME__ COPY
+#include "debug.h"
 
 /* 复制COW页 */
 static inline int __cow_copy(uint64_t va, pte_t *pte) {
@@ -30,79 +35,93 @@ static inline int __cow_copy(uint64_t va, pte_t *pte) {
   return 0;
 }
 
-int cow_copy(pagetable_t pagetable, uint64_t va, pte_t **pppte) {
-  pte_t *pte;
-  if(va >= MAXVA)
-    return -1;
-  pte = walk(pagetable, va, 0);
-  if(pte == 0 || (*pte & PTE_U) == 0 || (*pte & PTE_V) == 0 || (*pte & PTE_COW) == 0)
-    return -1;
+int cow_copy(uint64_t va, pte_t *pte) {
+  return __cow_copy(va, pte);
+}
 
-  if(pppte) 
-    *pppte = pte;
+void __copyout(mm_t *mm, uint64_t dstva, char *src, uint64 len, int walk) {
+  if(walk) {
+    pte_t *pte;
+    uint64 va0, n, pa0;
+    while(len > 0){
+      va0 = PGROUNDDOWN(dstva);
+      pte = walk(mm->pagetable, va0, 0);
+      if(pte == NULL || (*pte & PTE_V) == 0) 
+        panic("unmapped");
+    
+      n = PGSIZE - (dstva - va0);
+      if(n > len) n = len;
+      pa0 = PTE2PA(*pte);
+      // 由于是直接访问实地址,因此不需要设置SUM状态位
+      memmove((void *)(pa0 + (dstva - va0)), src, n);
 
-  return __cow_copy(va, pte);
+      len -= n;
+      src += n;
+      dstva = va0 + PGSIZE;
+    }
+  } else {
+      #if PRIVILEGE_VERSION == PRIVILEGE_VERSION_1_12
+      enable_sum();
+      #endif
+      memmove((void *)dstva, src, len);
+      #if PRIVILEGE_VERSION == PRIVILEGE_VERSION_1_12
+      disable_sum();
+      #endif
+  }
+
+  
 }
 
 // Copy from kernel to user.
 // Copy len bytes from src to virtual address dstva in a given page table.
 // Return 0 on success, -1 on error.
 int
-copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len)
+copyout(uint64 dstva, char *src, uint64 len)
 {
-  uint64 n, va0, pa0;
-  pte_t *pte;
-  while(len > 0){
-    va0 = PGROUNDDOWN(dstva);
-    if(va0 >= MAXVA) 
-      return -1;
-    pte = walk(pagetable, va0, 0);
-    if(pte == 0 || (*pte & PTE_U) == 0 || (*pte & PTE_V) == 0)
-      return -1;
-    
-    #ifdef COW
-    if((*pte & PTE_COW)) {
-      if(__cow_copy(va0, pte))
-        return -1;
-    }
-    #endif
-    
-    n = PGSIZE - (dstva - va0);
-    if(n > len) n = len;
-    pa0 = PTE2PA(*pte);
-    memmove((void *)(pa0 + (dstva - va0)), src, n);
-    // printf("fd=%d\n", *(int *)src);
-    len -= n;
-    src += n;
-    dstva = va0 + PGSIZE;
+  vma_t *vma;
+  proc_t *p = myproc();
+  if(!p)
+    panic("copyout: no process ctx");
+  
+  // 1. 首先确定目标段是否存在
+  if((vma = vma_exist(p->mm, (uint64)dstva, len)) == NULL) {
+    return -1;
   }
+  // 2. 是否是用户段
+  if((vma->prot & MAP_PROT_USER) == 0) {
+    return -1;
+  }
+  // 3. 由于存在映射,直接拷贝 
+  __copyout(p->mm, dstva, src, len, 0);
+
   return 0;
 }
 
-#define check_range(va, n, limit, mmap_limit) \
-  (((uint64_t)(va) + (uint64_t)(n) > (uint64_t)(va)) && \
-  (((uint64_t)(va) + (uint64_t)(n) <= (uint64_t)(limit)) || \
-  (((uint64_t)(va) + (uint64_t)(n) >= MMAP_BASE) && ((uint64_t)(va) + (uint64_t)(n) <= mmap_limit))))
+
 
 // Copy from user to kernel.
 // Copy len bytes to dst from virtual address srcva in a given page table.
 // Return 0 on success, -1 on error.
 int
-copyin(pagetable_t pagetable, char *dst, uint64 srcva, uint64 len)
+copyin(char *dst, uint64 srcva, uint64 len)
 {
   return copy_from_user(dst, (void *)srcva, len);
 }
 
-/* We always think kernel address is legal... */
+/* We trust that kernel address is legal... */
 int copy_from_user(void *to, void *from, size_t n) {
   proc_t *p = myproc();
   if(!p)
     panic("copy_from_user: no process ctx");
 
-  if(!check_range(from, n, p->sz, p->cur_mmap_sz))
-    return -1;
+  // if(!check_range(from, n, p->sz, p->cur_mmap_sz))
+  //   return -1;
   // todo: more checks, such as: guard pages, **mmap**...
-  
+  if(vma_exist(p->mm, (uint64)from, n) == NULL) {
+    debug("not exist");
+    return -1;
+  }
+
   /* 在特权级1.9版本中,SUM位为PUM为,其功能位与SUM作用相反 */
   #if PRIVILEGE_VERSION == PRIVILEGE_VERSION_1_12
   enable_sum();
@@ -119,18 +138,24 @@ int copy_from_user(void *to, void *from, size_t n) {
 // until a '\0', or max.
 // Return 0 on success, -1 on error.
 int
-copyinstr(pagetable_t pagetable, char *dst, uint64 srcva, uint64 max)
+copyinstr(char *dst, uint64 srcva, uint64 max)
 {
   int got_null = 0;
   proc_t *proc = myproc();
+  vma_t *vma;
   if(!proc)
     panic("copyinstr: no process context");
+  if((vma = __vma_find_strict(proc->mm, srcva)) == NULL) {
+    return -1;
+  }
+  max = min(max, vma->len - (srcva - vma->addr));
+
   #if PRIVILEGE_VERSION == PRIVILEGE_VERSION_1_12
   enable_sum();
   #endif
   char *p = (char *)srcva;
   // no consider wrap
-  while(max > 0 && (uint64_t)p < proc->sz){
+  while(max > 0){
     *dst = *p;
     if(*p == '\0'){
       got_null = 1;
diff --git a/src/mm/mmap.c b/src/mm/mmap.c
new file mode 100644
index 0000000000000000000000000000000000000000..e3f5789b1458b4655eeaf5b455affbcc655256c1
--- /dev/null
+++ b/src/mm/mmap.c
@@ -0,0 +1,373 @@
+#include "mm/mmap.h"
+#include "fs/fs.h"
+#include "str.h"
+#include "mm/vm.h"
+
+#define __MODULE_NAME__ MMAP
+#include "debug.h"
+
+#define VMA_LEN(vma) ((vma)->len)
+#define VMA_PAGES(vma) (((vma)->len) / PGSIZE)
+#define PAGETABLE(mm) ((mm)->owner->pagetable)
+
+#define KSTACK_VMA(mm) (&((mm)->kstack))
+
+
+#define check_range(va, len, limit) \
+  (((uint64_t)(va) + (uint64_t)(len) > (uint64_t)(va)) && \
+  (((uint64_t)(va) + (uint64_t)(len) <= (uint64_t)(limit))))
+
+
+static int check_flags(int flags1, int flags2) {
+    return flags1 == flags2;
+}
+
+// TODO: 检查flag是否兼容(目前只是简单的比较是否相等)
+static int check_prot(int prot1, int prot2) {
+    return prot1 == prot2;
+}
+
+vma_t *__vma_find(mm_t *mm, uint64 addr) {
+    vma_t *ans;
+    list_for_each_entry(ans, &mm->vma_head, head) {
+        if(addr < ans->addr + ans->len) {
+            return ans;
+        }
+    }
+    return NULL;
+}
+
+vma_t *__vma_find_strict(mm_t *mm, uint64 addr) {
+    vma_t *ans = __vma_find(mm, addr);
+    return ans && ans->addr <= addr ? ans : NULL;
+}
+
+void vma_remove(mm_t *mm, vma_t *vma) {
+    list_del(&vma->head);
+}
+
+vma_t *vma_previous(mm_t *mm, vma_t *vma) {
+    return list_prev_entry(vma, head);
+}
+
+void vma_insert(mm_t *mm, vma_t *vma) {
+    vma_t *pre;
+    list_for_each_entry(pre, &mm->vma_head, head) {
+        if(vma->addr >= pre->addr + pre->len) {
+            list_add(&vma->head, &pre->head);
+            return;
+        }
+    }
+    debug("no entry found, insert directly");
+    list_add(&vma->head, &mm->vma_head);
+}
+
+int __vmaS_merge(mm_t *mm, vma_t *start, vma_t *end) {
+    if(start == NULL || end == NULL) return 0;
+    vma_t *tmp = start;
+    // 合并检查
+    while(tmp != end) {
+        vma_t *next = list_next_entry(tmp, head);
+        if(!check_prot(tmp->prot, next->prot)) return -1;
+        if(!check_flags(tmp->flags, next->flags)) return -1;
+        tmp = next;
+    }
+    // 执行合并
+    while(1) {
+        vma_t *next = list_next_entry(tmp, head);
+        start->len = next->addr + next->len - start->addr;
+        vma_remove(mm, next);
+        kfree(next);
+        if(next == end) {
+            break;
+        }
+    }
+    return 0;
+}
+
+
+int __do_mmap(mm_t *mm, struct file *fp, int file_offset, uint64_t addr, uint64_t len, int flags, int prot) {
+    uint64_t end = addr + len;
+    // 寻找到第一个结束地址大于addr的vma(可以不包含)
+    vma_t *vma = __vma_find(mm, addr);
+    // 寻找到第一个结束地址大于end的vma
+    vma_t *vma_end = __vma_find(mm, end);\
+    if(vma_end && vma_end != vma && end < vma_end->addr) {
+        vma_end = vma_previous(mm, vma_end);
+    }
+    // 经过上述处理完,找到的vma布局如下所示:
+    // (addr) vma0] [vma1] [vma2 (end)
+    // 处理边界问题
+    if(vma) { // 当存在相交vma
+        if(check_flags(vma->flags, flags) == 0 || check_prot(vma->prot, prot) == 0) return -1;
+        if(__vmaS_merge(mm, vma, vma_end) == -1) return -1;
+        if(vma->addr > addr) {
+            vma->addr = PGROUNDDOWN(addr);
+        }
+        if(vma->addr + vma->len < end) {
+            vma->len = end - vma->addr;
+        }
+    } else { // 不存在相交vma
+        vma = (vma_t *)kzalloc(sizeof(vma_t));
+        vma->addr = PGROUNDDOWN(addr);
+        vma->len = len;
+        vma->flags = flags;
+        vma->map_file = fp;
+        vma->prot = prot;
+        vma->offset = file_offset;
+        vma_insert(mm, vma);
+    }
+    return 0;
+}
+
+// int do_file_mmap(mm_t *mm, struct file *fp, int offset, uint64_t addr, uint64_t len, int flags, int prot) {
+//     if(fp == NULL) return -1;
+//     if(__do_mmap(mm, fp, offset, addr, len, flags, prot) == -1) return -1;
+// }
+
+int do_mmap(mm_t *mm, struct file *fp, uint64_t addr, uint64_t len, int flags, int prot) {
+    flags |= (fp ? 0 : MAP_ANONYMOUS);
+    if(__do_mmap(mm, NULL, 0, addr, len, flags, prot) == -1)
+        return -1;
+    
+    return 0;
+}
+
+void do_unmap(mm_t *mm, uint64_t addr, int do_free) {
+    vma_t *vma = __vma_find(mm, addr);
+    if(vma) {
+        // TODO: file map
+        uvmunmap(mm->pagetable, vma->addr, ROUND_COUNT(vma->len), do_free);
+        vma_remove(mm, vma);
+        kfree(vma);
+    }
+}
+
+int do_mmap_alloc(mm_t *mm, struct file *fp, uint64_t addr, uint64_t len, int flags, int prot) {
+    char *mem;
+    uint64_t a;
+
+    if(do_mmap(mm, fp, addr, len, flags, prot) == -1) {
+        return -1;
+    }
+
+    for(a = PGROUNDDOWN(addr); a < addr + len; a += PGSIZE){
+        mem = kzalloc(PGSIZE);
+        if(mem == 0){
+            goto bad;
+        }
+        if(mappages(mm->pagetable, a, PGSIZE, (uint64)mem, prot) != 0){
+            kfree(mem);
+            goto bad;
+        }
+    }
+
+    
+    return 0;
+
+  bad:
+    do_unmap(mm, addr, 0);
+    for(uint64_t i = PGROUNDDOWN(addr); i < a; i += PGSIZE) {
+        uvmunmap(mm->pagetable, i, 1, 1);
+    }
+    return -1;
+}
+
+static int map_kstack(mm_t *mm) {
+    if((mm->kstack = (uint64)kmalloc(KSTACK_SZ)) == 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static void unmap_kstack(mm_t *mm) {
+    if(mm->kstack) {
+        kfree((void *)mm->kstack);
+        mm->kstack = 0;
+    }
+}
+
+static void unmap_vmas(mm_t *mm) {
+    vma_t *vma, *next;
+    list_for_each_entry_safe(vma, next, &mm->vma_head, head) {
+        uvmunmap(mm->pagetable, vma->addr, ROUND_COUNT(vma->len), 1);
+        vma_remove(mm, vma);
+        kfree(vma);
+    }
+}
+
+
+static int map_trapframe(mm_t *mm) {
+    if((mm->trapframe = (uint64)kmalloc(PGSIZE)) == 0) {
+        return -1;
+    }
+    return 0;
+}
+
+static void unmap_trapframe(mm_t *mm) {
+    if(mm->trapframe) {
+        kfree((void *)mm->trapframe);
+        mm->trapframe = 0;
+    }
+}
+
+int mmap_init(mm_t *mm, mm_t *old) {
+    mm->pagetable = kzalloc(PGSIZE);
+    INIT_LIST_HEAD(&mm->vma_head);
+    if(mm->pagetable == NULL) {
+        debug("stub1");
+    }
+    
+    if(map_kstack(mm) == -1) {
+        kfree(mm->pagetable);
+        return -1;
+    }
+    if(map_trapframe(mm) == -1) {
+        unmap_kstack(mm);
+        kfree(mm->pagetable);
+        return -1;
+    }
+
+    if(setupkvm(mm->pagetable) == -1) {
+        unmap_kstack(mm);
+        unmap_trapframe(mm);
+        kfree(mm->pagetable);
+        return -1;
+    }
+
+    // for exec
+    if(old) {
+        memcpy((void *)mm->kstack, (void *)old->kstack, KSTACK_SZ);
+    }
+    
+    return 0;
+}
+
+void mmap_free(mm_t **pmm) {
+    mm_t *mm = *pmm;
+    if(mm == NULL) 
+        panic("nullpointer");
+
+    unmap_vmas(mm);
+    unmap_kstack(mm);
+    unmap_trapframe(mm);
+    erasekvm(mm->pagetable);
+    kfree(mm->pagetable);
+    kfree(mm);
+    *pmm = NULL;
+}
+
+static vma_t *vma_dup(vma_t *vma) {
+    vma_t *dup;
+    if((dup = (vma_t *)kmalloc(sizeof(vma_t))) == NULL) {
+        return NULL;
+    }
+    memcpy(dup, vma, sizeof(vma_t));
+    return dup;
+}
+
+
+int mmap_dup(mm_t *newm, mm_t *oldm) {
+    vma_t *vma;
+    //TODO: file
+    list_for_each_entry(vma, &oldm->vma_head, head) {
+        vma_t *dup = vma_dup(vma);
+        if(dup == NULL) {
+            unmap_vmas(newm);
+            return -1;
+        }
+        if(uvmcopy(oldm->pagetable, newm->pagetable, vma->addr, vma->len) == -1) {
+            kfree(dup);
+            unmap_vmas(newm);
+            return -1;
+        }
+        vma_insert(newm, dup);
+    }
+    memcpy((void *)newm->kstack, (void *)oldm->kstack, KSTACK_SZ);
+    memcpy((void *)newm->trapframe, (void *)oldm->trapframe, PGSIZE);
+
+    return 0;
+}
+
+int mmap_ext_heap(mm_t *mm, int newsize) {
+    if(mm->uheap == NULL) return -1;
+    int cursize = mm->uheap->len;
+    if(cursize == newsize) {
+        return 0;
+    } else if(cursize < newsize) {
+        uvmunmap(mm->pagetable, 
+            PGROUNDUP(mm->uheap->addr + newsize), 
+            ROUND_COUNT(mm->uheap->addr + mm->uheap->len) - ROUND_COUNT(mm->uheap->addr + newsize), 1);
+    } else {
+        if(__vma_find_strict(mm, mm->uheap->addr + newsize)) {
+            return -1;
+        }
+    }
+    mm->uheap->len = newsize;
+    return 0;
+}
+
+int mmap_ext_stack(mm_t *mm) {
+    // TODO:
+    return -1;
+}
+
+
+struct trapframe *get_trapframe(mm_t *mm) {
+    return (struct trapframe *)mm->trapframe;
+}
+
+uint64_t get_kstack(mm_t *mm) {
+    return mm->kstack;
+}
+
+vma_t *vma_exist(mm_t *mm, uint64_t addr, uint64_t len) {
+    // if(!check_range(addr, len, USERSPACE_END)) return NULL;
+    vma_t *ans = __vma_find_strict(mm, addr);
+    if(ans && addr -  ans->addr <= ans->len - len) {
+        return ans;
+    }
+
+    return NULL;
+}
+
+void mmap_print_vma(vma_t *vma) {
+    if(vma == NULL) {
+        printf("vma: NULL\n");
+    } else {
+        char perm[5];
+        perm[4] = '\0';
+        perm[0] = vma->prot & MAP_PROT_READ ? 'r' : '-';
+        perm[1] = vma->prot & MAP_PROT_WRITE ? 'w' : '-';
+        perm[2] = vma->prot & MAP_PROT_EXEC ? 'x' : '-';
+        perm[3] = vma->prot & MAP_PROT_USER ? 'u' : '-';
+        
+        printf("vma: %lx----%lx len: %ld %s\n", vma->addr, vma->addr + vma->len, vma->len, perm);
+    }
+}
+
+void mmap_print_vmas(mm_t *mm) {
+    vma_t *vma;
+    int id = 1;
+    list_for_each_entry(vma, &mm->vma_head, head) {
+        printf("%d. ", id++);
+        mmap_print_vma(vma);
+    }
+}
+
+
+void switchuvm(mm_t *mm) {
+  if(mm->kstack == 0)
+    panic("switchuvm: no kstack");
+  if(mm->pagetable == 0)
+    panic("switchuvm: no pgdir");
+
+  write_csr(satp, MAKE_SATP(mm->pagetable));
+  sfence_vma();
+}
+
+extern pagetable_t kernel_pagetable;
+void switchkvm() {
+  write_csr(satp, MAKE_SATP(kernel_pagetable));
+  sfence_vma();
+}
diff --git a/src/mm/page.c b/src/mm/page.c
index 85097f982ca7a8ed73e4792523037594b7b35d0c..eb1be95453c9d297dc4afe2b53305cb84c050bad 100644
--- a/src/mm/page.c
+++ b/src/mm/page.c
@@ -121,12 +121,10 @@ _uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free, int spec
 
   for(a = va; a < va + npages*pgsize; a += pgsize){
     if((pte = _walk(pagetable, a, 0, spec)) == 0){
-      panic("uvmunmap: walk");
-      // continue;
+      continue;
     }
     if((*pte & PTE_V) == 0){
-      panic("uvmunmap: not mapped");
-      // continue;
+      continue;
     }
     if((*pte & (PTE_R | PTE_W | PTE_X)) == 0)
       panic("uvmunmap: not a leaf");
diff --git a/src/mm/slob.c b/src/mm/slob.c
index dde2cc44ef770110de12efcf77a8c26ae4ec880d..65536520aa6f2f1464181ce3f6567a86b6e30580 100644
--- a/src/mm/slob.c
+++ b/src/mm/slob.c
@@ -1,5 +1,6 @@
 #include "atomic/spinlock.h"
 #include "common.h"
+#include "mm/page.h"
 
 #define QUIET
 #define __MODULE_NAME__ SLOB
diff --git a/src/mm/vm.c b/src/mm/vm.c
index 176109f9257e22f93336d3865b0d96a82b677403..08abe4b3bfa107a5f98a3f4e68e4f86a988e7dea 100644
--- a/src/mm/vm.c
+++ b/src/mm/vm.c
@@ -25,7 +25,6 @@ extern char etext[];  // kernel.ld sets this to end of kernel code.
 
 // extern char trampoline[]; // trampoline.S
 
-void freewalk(pagetable_t pagetable);
 
 // Initialize the one kernel_pagetable
 void
@@ -143,111 +142,85 @@ void erasekvm(pagetable_t pagetable) {
   }
 }
 
-// create an empty user page table.
-// returns 0 if out of memory.
-pagetable_t
-uvmcreate()
-{
-  pagetable_t pagetable;
-  pagetable = (pagetable_t) kalloc();
-  if(pagetable == 0)
-    return 0;
-  memset(pagetable, 0, PGSIZE);
-  if(setupkvm(pagetable) == -1) {
-    uvmfree(pagetable, 0);
-    return 0;
-  }
-  return pagetable;
-}
 
 // Load the user initcode into address 0 of pagetable,
 // for the very first process.
 // sz must be less than a page.
-void
-uvminit(pagetable_t pagetable, uchar *src, uint sz)
-{
-  char *mem;
-
-  if(sz >= PGSIZE)
-    panic("inituvm: more than a page");
-  mem = kalloc();
-  memset(mem, 0, PGSIZE);
-  mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
-  memmove(mem, src, sz);
-}
+// void
+// uvminit(pagetable_t pagetable, uchar *src, uint sz)
+// {
+//   char *mem;
+
+//   if(sz >= PGSIZE)
+//     panic("inituvm: more than a page");
+//   mem = kalloc();
+//   memset(mem, 0, PGSIZE);
+//   mappages(pagetable, 0, PGSIZE, (uint64)mem, PTE_W|PTE_R|PTE_X|PTE_U);
+//   memmove(mem, src, sz);
+// }
 
 // Allocate PTEs and physical memory to grow process from oldsz to
 // newsz, which need not be page aligned.  Returns new size or 0 on error.
-uint64
-uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
-{
-  char *mem;
-  uint64 a;
-
-  if(newsz < oldsz)
-    return oldsz;
-
-  oldsz = PGROUNDUP(oldsz);
-  for(a = oldsz; a < newsz; a += PGSIZE){
-    mem = kalloc();
-    if(mem == 0){
-      uvmdealloc(pagetable, a, oldsz);
-      return 0;
-    }
-    memset(mem, 0, PGSIZE);
-    if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
-      kfree(mem);
-      uvmdealloc(pagetable, a, oldsz);
-      return 0;
-    }
-  }
-  return newsz;
-}
+// uint64
+// uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
+// {
+//   char *mem;
+//   uint64 a;
+
+//   if(newsz < oldsz)
+//     return oldsz;
+
+//   oldsz = PGROUNDUP(oldsz);
+//   for(a = oldsz; a < newsz; a += PGSIZE){
+//     mem = kalloc();
+//     if(mem == 0){
+//       uvmdealloc(pagetable, a, oldsz);
+//       return 0;
+//     }
+//     memset(mem, 0, PGSIZE);
+//     if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
+//       kfree(mem);
+//       uvmdealloc(pagetable, a, oldsz);
+//       return 0;
+//     }
+//   }
+//   return newsz;
+// }
 
 // Deallocate user pages to bring the process size from oldsz to
 // newsz.  oldsz and newsz need not be page-aligned, nor does newsz
 // need to be less than oldsz.  oldsz can be larger than the actual
 // process size.  Returns the new process size.
-uint64
-uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
-{
-  if(newsz >= oldsz)
-    return oldsz;
+// uint64
+// uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
+// {
+//   if(newsz >= oldsz)
+//     return oldsz;
 
-  if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){
-    int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
-    uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
-  }
+//   if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){
+//     int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE;
+//     uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1);
+//   }
 
-  return newsz;
-}
+//   return newsz;
+// }
 
 
-// Free user memory pages,
-// then free page-table pages.
-void
-uvmfree(pagetable_t pagetable, uint64 sz)
-{
-  if(sz > 0)
-    uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1);
-  freewalk(pagetable);
-}
-
 // Given a parent process's page table, copy
 // its memory into a child's page table.
 // Copies both the page table and the
 // physical memory.
 // returns 0 on success, -1 on failure.
 // frees any allocated pages on failure.'
-#include "kernel/proc.h"
-int
-uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
+// #include "kernel/proc.h"
+// int
+int uvmcopy(pagetable_t old, pagetable_t new, uint64 addr, uint64_t len)
 {
   pte_t *pte;
   uint64 pa, i;
   uint flags;
 
-  for(i = 0; i < sz; i += PGSIZE){
+  for(i = addr; i < PGROUNDUP(addr + len); i += PGSIZE) {
     if((pte = walk(old, i, 0)) == 0)
       panic("uvmcopy: pte should exist");
     if((*pte & PTE_V) == 0)
@@ -286,52 +259,71 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
 
 // mark a PTE invalid for user access.
 // used by exec for the user stack guard page.
-void
-uvmclear(pagetable_t pagetable, uint64 va)
-{
-  pte_t *pte;
+// void
+// uvmclear(pagetable_t pagetable, uint64 va)
+// {
+//   pte_t *pte;
   
-  pte = walk(pagetable, va, 0);
-  if(pte == 0)
-    panic("uvmclear");
-  *pte &= ~PTE_U;
-}
+//   pte = walk(pagetable, va, 0);
+//   if(pte == 0)
+//     panic("uvmclear");
+//   *pte &= ~PTE_U;
+// }
 
 // Recursively free page-table pages.
 // All leaf mappings must already have been removed.
+// void
+// freewalk(pagetable_t pagetable)
+// {
+//   // there are 2^9 = 512 PTEs in a page table.
+//   for(int i = 0; i < 512; i++){
+//     pte_t pte = pagetable[i];
+//     if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){ // 目录节点
+//       // this PTE points to a lower-level page table.
+//       uint64 child = PTE2PA(pte);
+//       freewalk((pagetable_t)child);
+//       pagetable[i] = 0;
+//     } else if(pte & PTE_V){ // 叶子节点
+//       printf("\nnormal pa: %p\n", PTE2PA(pte));
+//       panic("freewalk: leaf");
+//     }
+//   }
+//   kfree((void*)pagetable);
+// }
+
+static char* indents[] = {
+  ".. .. ..",
+  ".. ..",
+  "..",
+};
+
+
 void
-freewalk(pagetable_t pagetable)
-{
-  // there are 2^9 = 512 PTEs in a page table.
+_vmprint(pagetable_t pagetable, int level, int ignore_level) {
+  if(level == ignore_level) return;
+  char *indent = indents[level];
   for(int i = 0; i < 512; i++){
     pte_t pte = pagetable[i];
-    if((pte & PTE_V) && (pte & (PTE_R|PTE_W|PTE_X)) == 0){ // 目录节点
-      // this PTE points to a lower-level page table.
-      uint64 child = PTE2PA(pte);
-      freewalk((pagetable_t)child);
-      pagetable[i] = 0;
-    } else if(pte & PTE_V){ // 叶子节点
-      printf("\nnormal pa: %p\n", PTE2PA(pte));
-      panic("freewalk: leaf");
+    pagetable_t pa = (pagetable_t)PTE2PA(pte);
+    if(pte & PTE_V){  // 存在
+      if((pte & (PTE_R|PTE_W|PTE_X)) > 0) // 打印叶节点
+        printf("%s %-3d: pte[LEAF] %p pa %p\n", indent, i, pte, PTE2PA(pte));
+      else {// 打印下级页表地址 
+        printf("%s %-3d: pte %p pa %p\n", indent, i, pte, pa);
+        _vmprint(pa, level - 1, ignore_level);
+      }
     }
   }
-  kfree((void*)pagetable);
 }
-
-void switchuvm(struct proc *p) {
-  if(p == 0)
-    panic("switchuvm: no process");
-  if(p->kstack == 0)
-    panic("switchuvm: no kstack");
-  if(p->pagetable == 0)
-    panic("switchuvm: no pgdir");
-
-  write_csr(satp, MAKE_SATP(p->pagetable));
-  sfence_vma();
+void 
+vmprint(pagetable_t pagetable) {
+  printf("page table %p\n", pagetable);
+  _vmprint(pagetable, 2, -1);
 }
 
-void switchkvm() {
-  write_csr(satp, MAKE_SATP(kernel_pagetable));
-  sfence_vma();
+void
+print_map(kmap_t map) {
+  printf("map:%p => %p, size: %#x type: %d\n", map.pa, map.va, map.size, map.pg_spec);
 }
 
+
diff --git a/src/platform/k210/driver/dmac.c b/src/platform/k210/driver/dmac.c
index 292049f638d78f5ef16df8c76fa65a2f4ecd74f0..49fec0807812f7abc880751e39bf4e28ac871f84 100644
--- a/src/platform/k210/driver/dmac.c
+++ b/src/platform/k210/driver/dmac.c
@@ -20,6 +20,8 @@
 #include "utils.h"
 #include "driver/plic.h"
 #include "mm/io.h"
+#include "kernel/proc.h"
+#include "mm/vm.h"
 
 volatile dmac_t *dmac;
 
@@ -584,25 +586,6 @@ void dmac_init(void)
     dmac_enable();
 }
 
-static void list_add(struct list_head_t *new, struct list_head_t *prev,
-        struct list_head_t *next)
-{
-    next->prev = new;
-    new->next = next;
-    new->prev = prev;
-    prev->next = new;
-}
-
-void list_add_tail(struct list_head_t *new, struct list_head_t *head)
-{
-    list_add(new, head->prev, head);
-}
-
-void INIT_LIST_HEAD(struct list_head_t *list)
-{
-    list->next = list;
-    list->prev = list;
-}
 
 void dmac_link_list_item(dmac_channel_number_t channel_num,
     uint8_t LLI_row_num, int8_t LLI_last_row,
diff --git a/src/platform/k210/driver/spi.c b/src/platform/k210/driver/spi.c
index c77abcd3d9424d432d473b90fb5863b86809fb04..f70c61e5359ad90a87086c718fabbd0d30804f6f 100644
--- a/src/platform/k210/driver/spi.c
+++ b/src/platform/k210/driver/spi.c
@@ -22,6 +22,7 @@
 #include "defs.h"
 #include "atomic/spinlock.h"
 #include "mm/io.h"
+#include "mm/vm.h"
 #include "printf.h"
 
 uint64_t spi_pa[4] =
diff --git a/src/tests/Makefile b/src/tests/Makefile
index 440c4d7bd742f8989a19f4ff710b1c133f9ac42f..01cf480cf2079d8f306e7588a33eb5991539253a 100644
--- a/src/tests/Makefile
+++ b/src/tests/Makefile
@@ -1 +1 @@
-obj-y+=mm.o
\ No newline at end of file
+obj-y+=mmtest.o
\ No newline at end of file
diff --git a/src/tests/mm.c b/src/tests/mmtest.c
similarity index 100%
rename from src/tests/mm.c
rename to src/tests/mmtest.c
diff --git a/src/utils.c b/src/utils.c
index e3311bdb1cd81a7d4d3e3c6d72c36b80e97925e2..db542104d9868833d509183cd6850eb0a1d0ef9d 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -3,53 +3,8 @@
 #include "mm/page.h"
 #include "fs/fat.h"
 #include "str.h"
+#include "kernel/proc.h"
 
-static char* indents[] = {
-  ".. .. ..",
-  ".. ..",
-  "..",
-};
-
-// void
-// _vmprint(pagetable_t pagetable, int level, int ignore_level) {
-//   if(level == ignore_level) return;
-//   char *indent = indents[level];
-//   for(int i = 0; i < 512; i++){
-//     pte_t pte = pagetable[i];
-//     pagetable_t pa = (pagetable_t)PTE2PA(pte);
-//     if(pte & PTE_V){  // 存在
-//       if((pte & (PTE_R|PTE_W|PTE_X)) > 0) // 打印叶节点
-//         printf("%s %-3d: pte[LEAF] %p pa %p\n", indent, i, pte, PTE2PA_SPEC(pte, level));
-//       else {// 打印下级页表地址 
-//         printf("%s %-3d: pte %p pa %p\n", indent, i, pte, pa);
-//         _vmprint(pa, level - 1, ignore_level);
-//       }
-//     }
-//   }
-// }
-
-void
-_vmprint(pagetable_t pagetable, int level, int ignore_level) {
-  if(level == ignore_level) return;
-  char *indent = indents[level];
-  for(int i = 0; i < 512; i++){
-    pte_t pte = pagetable[i];
-    pagetable_t pa = (pagetable_t)PTE2PA(pte);
-    if(pte & PTE_V){  // 存在
-      if((pte & (PTE_R|PTE_W|PTE_X)) > 0) // 打印叶节点
-        printf("%s %-3d: pte[LEAF] %p pa %p\n", indent, i, pte, PTE2PA(pte));
-      else {// 打印下级页表地址 
-        printf("%s %-3d: pte %p pa %p\n", indent, i, pte, pa);
-        _vmprint(pa, level - 1, ignore_level);
-      }
-    }
-  }
-}
-void 
-vmprint(pagetable_t pagetable) {
-  printf("page table %p\n", pagetable);
-  _vmprint(pagetable, 2, -1);
-}
 
 void 
 backtrace(proc_t *p) {
@@ -70,10 +25,7 @@ print_block(uint8_t *b) {
   }
 }
 
-void
-print_map(kmap_t map) {
-  printf("map:%p => %p, size: %#x type: %d\n", map.pa, map.va, map.size, map.pg_spec);
-}
+
 
 void
 print_sbiret(sbiret_t ret) {