From be6c94d33ab080589be1a6d73f135d25318dc870 Mon Sep 17 00:00:00 2001
From: Eobard26 <1527198893@qq.com>
Date: Wed, 7 Apr 2021 22:03:08 +0800
Subject: [PATCH] rm .git

---
 codes/.dockerignore                      |   1 +
 codes/.gitignore                         |  13 +
 codes/.vscode/settings.json              |   3 +
 codes/Dockerfile                         |  40 ++
 codes/LICENSE                            | 674 ++++++++++++++++++++
 codes/Makefile                           |   8 +
 codes/README.md                          |   2 +
 codes/bootloader/rustsbi-k210.bin        | Bin 0 -> 75068 bytes
 codes/bootloader/rustsbi-qemu.bin        | Bin 0 -> 103288 bytes
 codes/easy-fs-fuse/Cargo.toml            |  12 +
 codes/easy-fs-fuse/src/main.rs           | 258 ++++++++
 codes/easy-fs/.gitignore                 |   3 +
 codes/easy-fs/Cargo.toml                 |  12 +
 codes/easy-fs/src/bitmap.rs              |  73 +++
 codes/easy-fs/src/block_cache.rs         | 134 ++++
 codes/easy-fs/src/block_dev.rs           |   6 +
 codes/easy-fs/src/efs.rs                 | 237 +++++++
 codes/easy-fs/src/layout.rs              | 610 ++++++++++++++++++
 codes/easy-fs/src/lib.rs                 |  19 +
 codes/easy-fs/src/vfs.rs                 | 430 +++++++++++++
 codes/os/.cargo/config                   |   7 +
 codes/os/Cargo.toml                      |  24 +
 codes/os/Makefile                        | 104 ++++
 codes/os/build.rs                        |   6 +
 codes/os/src/config.rs                   |  42 ++
 codes/os/src/console.rs                  |  33 +
 codes/os/src/drivers/block/mod.rs        |  30 +
 codes/os/src/drivers/block/sdcard.rs     | 755 +++++++++++++++++++++++
 codes/os/src/drivers/block/virtio_blk.rs |  76 +++
 codes/os/src/drivers/mod.rs              |   3 +
 codes/os/src/entry.asm                   |  12 +
 codes/os/src/fs/inode.rs                 | 242 ++++++++
 codes/os/src/fs/mod.rs                   |  17 +
 codes/os/src/fs/pipe.rs                  | 168 +++++
 codes/os/src/fs/stdio.rs                 |  47 ++
 codes/os/src/fs/util.rs                  |   5 +
 codes/os/src/lang_items.rs               |  19 +
 codes/os/src/linker-k210.ld              |  50 ++
 codes/os/src/linker-qemu.ld              |  50 ++
 codes/os/src/main.rs                     |  52 ++
 codes/os/src/mm/address.rs               | 209 +++++++
 codes/os/src/mm/frame_allocator.rs       | 133 ++++
 codes/os/src/mm/heap_allocator.rs        |  45 ++
 codes/os/src/mm/memory_set.rs            | 359 +++++++++++
 codes/os/src/mm/mod.rs                   |  28 +
 codes/os/src/mm/page_table.rs            | 255 ++++++++
 codes/os/src/sbi.rs                      |  43 ++
 codes/os/src/syscall/fs.rs               | 116 ++++
 codes/os/src/syscall/mod.rs              |  39 ++
 codes/os/src/syscall/process.rs          | 117 ++++
 codes/os/src/task/context.rs             |  18 +
 codes/os/src/task/manager.rs             |  34 +
 codes/os/src/task/mod.rs                 |  92 +++
 codes/os/src/task/pid.rs                 | 105 ++++
 codes/os/src/task/processor.rs           |  95 +++
 codes/os/src/task/switch.S               |  37 ++
 codes/os/src/task/switch.rs              |   8 +
 codes/os/src/task/task.rs                | 245 ++++++++
 codes/os/src/timer.rs                    |  18 +
 codes/os/src/trap/context.rs             |  37 ++
 codes/os/src/trap/mod.rs                 | 117 ++++
 codes/os/src/trap/trap.S                 |  69 +++
 codes/rust-toolchain                     |   1 +
 codes/user/.cargo/config                 |   7 +
 codes/user/.gitignore                    |   1 +
 codes/user/Cargo.toml                    |  11 +
 codes/user/Makefile                      |  23 +
 codes/user/src/bin/cat.rs                |  34 +
 codes/user/src/bin/cmdline_args.rs       |  16 +
 codes/user/src/bin/exit.rs               |  29 +
 codes/user/src/bin/fantastic_text.rs     |  44 ++
 codes/user/src/bin/filetest_simple.rs    |  38 ++
 codes/user/src/bin/forktest.rs           |  34 +
 codes/user/src/bin/forktest2.rs          |  33 +
 codes/user/src/bin/forktest_simple.rs    |  28 +
 codes/user/src/bin/forktree.rs           |  37 ++
 codes/user/src/bin/hello_world.rs        |  11 +
 codes/user/src/bin/initproc.rs           |  34 +
 codes/user/src/bin/matrix.rs             |  68 ++
 codes/user/src/bin/pipe_large_test.rs    |  69 +++
 codes/user/src/bin/pipetest.rs           |  44 ++
 codes/user/src/bin/run_pipe_test.rs      |  21 +
 codes/user/src/bin/sleep.rs              |  30 +
 codes/user/src/bin/sleep_simple.rs       |  19 +
 codes/user/src/bin/stack_overflow.rs     |  17 +
 codes/user/src/bin/user_shell.rs         | 138 +++++
 codes/user/src/bin/usertests.rs          |  40 ++
 codes/user/src/bin/yield.rs              |  17 +
 codes/user/src/console.rs                |  39 ++
 codes/user/src/lang_items.rs             |  12 +
 codes/user/src/lib.rs                    | 108 ++++
 codes/user/src/linker.ld                 |  29 +
 codes/user/src/syscall.rs                |  79 +++
 93 files changed, 7607 insertions(+)
 create mode 100644 codes/.dockerignore
 create mode 100644 codes/.gitignore
 create mode 100644 codes/.vscode/settings.json
 create mode 100644 codes/Dockerfile
 create mode 100644 codes/LICENSE
 create mode 100644 codes/Makefile
 create mode 100644 codes/README.md
 create mode 100755 codes/bootloader/rustsbi-k210.bin
 create mode 100755 codes/bootloader/rustsbi-qemu.bin
 create mode 100644 codes/easy-fs-fuse/Cargo.toml
 create mode 100644 codes/easy-fs-fuse/src/main.rs
 create mode 100644 codes/easy-fs/.gitignore
 create mode 100644 codes/easy-fs/Cargo.toml
 create mode 100644 codes/easy-fs/src/bitmap.rs
 create mode 100644 codes/easy-fs/src/block_cache.rs
 create mode 100644 codes/easy-fs/src/block_dev.rs
 create mode 100644 codes/easy-fs/src/efs.rs
 create mode 100644 codes/easy-fs/src/layout.rs
 create mode 100644 codes/easy-fs/src/lib.rs
 create mode 100644 codes/easy-fs/src/vfs.rs
 create mode 100644 codes/os/.cargo/config
 create mode 100644 codes/os/Cargo.toml
 create mode 100644 codes/os/Makefile
 create mode 100644 codes/os/build.rs
 create mode 100644 codes/os/src/config.rs
 create mode 100644 codes/os/src/console.rs
 create mode 100644 codes/os/src/drivers/block/mod.rs
 create mode 100644 codes/os/src/drivers/block/sdcard.rs
 create mode 100644 codes/os/src/drivers/block/virtio_blk.rs
 create mode 100644 codes/os/src/drivers/mod.rs
 create mode 100644 codes/os/src/entry.asm
 create mode 100644 codes/os/src/fs/inode.rs
 create mode 100644 codes/os/src/fs/mod.rs
 create mode 100644 codes/os/src/fs/pipe.rs
 create mode 100644 codes/os/src/fs/stdio.rs
 create mode 100644 codes/os/src/fs/util.rs
 create mode 100644 codes/os/src/lang_items.rs
 create mode 100644 codes/os/src/linker-k210.ld
 create mode 100644 codes/os/src/linker-qemu.ld
 create mode 100644 codes/os/src/main.rs
 create mode 100644 codes/os/src/mm/address.rs
 create mode 100644 codes/os/src/mm/frame_allocator.rs
 create mode 100644 codes/os/src/mm/heap_allocator.rs
 create mode 100644 codes/os/src/mm/memory_set.rs
 create mode 100644 codes/os/src/mm/mod.rs
 create mode 100644 codes/os/src/mm/page_table.rs
 create mode 100644 codes/os/src/sbi.rs
 create mode 100644 codes/os/src/syscall/fs.rs
 create mode 100644 codes/os/src/syscall/mod.rs
 create mode 100644 codes/os/src/syscall/process.rs
 create mode 100644 codes/os/src/task/context.rs
 create mode 100644 codes/os/src/task/manager.rs
 create mode 100644 codes/os/src/task/mod.rs
 create mode 100644 codes/os/src/task/pid.rs
 create mode 100644 codes/os/src/task/processor.rs
 create mode 100644 codes/os/src/task/switch.S
 create mode 100644 codes/os/src/task/switch.rs
 create mode 100644 codes/os/src/task/task.rs
 create mode 100644 codes/os/src/timer.rs
 create mode 100644 codes/os/src/trap/context.rs
 create mode 100644 codes/os/src/trap/mod.rs
 create mode 100644 codes/os/src/trap/trap.S
 create mode 100644 codes/rust-toolchain
 create mode 100644 codes/user/.cargo/config
 create mode 100644 codes/user/.gitignore
 create mode 100644 codes/user/Cargo.toml
 create mode 100644 codes/user/Makefile
 create mode 100644 codes/user/src/bin/cat.rs
 create mode 100644 codes/user/src/bin/cmdline_args.rs
 create mode 100644 codes/user/src/bin/exit.rs
 create mode 100644 codes/user/src/bin/fantastic_text.rs
 create mode 100644 codes/user/src/bin/filetest_simple.rs
 create mode 100644 codes/user/src/bin/forktest.rs
 create mode 100644 codes/user/src/bin/forktest2.rs
 create mode 100644 codes/user/src/bin/forktest_simple.rs
 create mode 100644 codes/user/src/bin/forktree.rs
 create mode 100644 codes/user/src/bin/hello_world.rs
 create mode 100644 codes/user/src/bin/initproc.rs
 create mode 100644 codes/user/src/bin/matrix.rs
 create mode 100644 codes/user/src/bin/pipe_large_test.rs
 create mode 100644 codes/user/src/bin/pipetest.rs
 create mode 100644 codes/user/src/bin/run_pipe_test.rs
 create mode 100644 codes/user/src/bin/sleep.rs
 create mode 100644 codes/user/src/bin/sleep_simple.rs
 create mode 100644 codes/user/src/bin/stack_overflow.rs
 create mode 100644 codes/user/src/bin/user_shell.rs
 create mode 100644 codes/user/src/bin/usertests.rs
 create mode 100644 codes/user/src/bin/yield.rs
 create mode 100644 codes/user/src/console.rs
 create mode 100644 codes/user/src/lang_items.rs
 create mode 100644 codes/user/src/lib.rs
 create mode 100644 codes/user/src/linker.ld
 create mode 100644 codes/user/src/syscall.rs

diff --git a/codes/.dockerignore b/codes/.dockerignore
new file mode 100644
index 00000000..df3359dd
--- /dev/null
+++ b/codes/.dockerignore
@@ -0,0 +1 @@
+*/*
\ No newline at end of file
diff --git a/codes/.gitignore b/codes/.gitignore
new file mode 100644
index 00000000..74b65454
--- /dev/null
+++ b/codes/.gitignore
@@ -0,0 +1,13 @@
+.idea/*
+os/target/*
+os/.idea/*
+os/src/link_app.S
+os/Cargo.lock
+user/target/*
+user/.idea/*
+user/Cargo.lock
+easy-fs/Cargo.lock
+easy-fs/target/*
+easy-fs-fuse/Cargo.lock
+easy-fs-fuse/target/*
+tools/
diff --git a/codes/.vscode/settings.json b/codes/.vscode/settings.json
new file mode 100644
index 00000000..25c729f5
--- /dev/null
+++ b/codes/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+    "python.pythonPath": "/bin/python3"
+}
\ No newline at end of file
diff --git a/codes/Dockerfile b/codes/Dockerfile
new file mode 100644
index 00000000..ac784bc2
--- /dev/null
+++ b/codes/Dockerfile
@@ -0,0 +1,40 @@
+FROM ubuntu:18.04
+LABEL maintainer="dinghao188" \
+      version="1.1" \
+      description="ubuntu 18.04 with tools for tsinghua's rCore-Tutorial-V3"
+
+#install some deps
+RUN set -x \
+    && apt-get update \
+    && apt-get install -y curl wget autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
+              gawk build-essential bison flex texinfo gperf libtool patchutils bc xz-utils \
+              zlib1g-dev libexpat-dev pkg-config  libglib2.0-dev libpixman-1-dev git tmux python3 
+
+#install rust and qemu
+RUN set -x; \
+    RUSTUP='/root/rustup.sh' \
+    && cd $HOME \
+    #install rust
+    && curl https://sh.rustup.rs -sSf > $RUSTUP && chmod +x $RUSTUP \
+    && $RUSTUP -y --default-toolchain nightly --profile minimal \
+
+    #compile qemu
+    && wget https://ftp.osuosl.org/pub/blfs/conglomeration/qemu/qemu-5.0.0.tar.xz \
+    && tar xvJf qemu-5.0.0.tar.xz \
+    && cd qemu-5.0.0 \
+    && ./configure --target-list=riscv64-softmmu,riscv64-linux-user \
+    && make -j$(nproc) install \
+    && cd $HOME && rm -rf qemu-5.0.0 qemu-5.0.0.tar.xz
+
+#for chinese network
+RUN set -x; \
+    APT_CONF='/etc/apt/sources.list'; \
+    CARGO_CONF='/root/.cargo/config'; \
+    BASHRC='/root/.bashrc' \
+    && echo 'export RUSTUP_DIST_SERVER=https://mirrors.ustc.edu.cn/rust-static' >> $BASHRC \
+    && echo 'export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup' >> $BASHRC \
+    && touch $CARGO_CONF \
+    && echo '[source.crates-io]' > $CARGO_CONF \
+    && echo "replace-with = 'ustc'" >> $CARGO_CONF \
+    && echo '[source.ustc]' >> $CARGO_CONF \
+    && echo 'registry = "git://mirrors.ustc.edu.cn/crates.io-index"' >> $CARGO_CONF
\ No newline at end of file
diff --git a/codes/LICENSE b/codes/LICENSE
new file mode 100644
index 00000000..f288702d
--- /dev/null
+++ b/codes/LICENSE
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/codes/Makefile b/codes/Makefile
new file mode 100644
index 00000000..2e339762
--- /dev/null
+++ b/codes/Makefile
@@ -0,0 +1,8 @@
+DOCKER_NAME ?= dinghao188/rcore-tutorial
+.PHONY: docker build_docker
+
+docker:
+	docker run --rm -it --mount type=bind,source=$(shell pwd),destination=/mnt ${DOCKER_NAME}
+
+build_docker: 
+	docker build -t ${DOCKER_NAME} .
diff --git a/codes/README.md b/codes/README.md
new file mode 100644
index 00000000..dd356b5f
--- /dev/null
+++ b/codes/README.md
@@ -0,0 +1,2 @@
+# rCore-Tutorial-v3
+rCore-Tutorial version 3.
\ No newline at end of file
diff --git a/codes/bootloader/rustsbi-k210.bin b/codes/bootloader/rustsbi-k210.bin
new file mode 100755
index 0000000000000000000000000000000000000000..e696137ab183ea5129e14e86ba980af53ae025d2
GIT binary patch
literal 75068
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

literal 0
HcmV?d00001

diff --git a/codes/bootloader/rustsbi-qemu.bin b/codes/bootloader/rustsbi-qemu.bin
new file mode 100755
index 0000000000000000000000000000000000000000..5ccab433b2cac6c11e47ae827904839c4275dfa7
GIT binary patch
literal 103288
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

literal 0
HcmV?d00001

diff --git a/codes/easy-fs-fuse/Cargo.toml b/codes/easy-fs-fuse/Cargo.toml
new file mode 100644
index 00000000..d7437b72
--- /dev/null
+++ b/codes/easy-fs-fuse/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "easy-fs-fuse"
+version = "0.1.0"
+authors = ["Yifan Wu <shinbokuow@163.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = "2.33.3"
+easy-fs = { path = "../easy-fs" }
+rand = "0.8.0"
diff --git a/codes/easy-fs-fuse/src/main.rs b/codes/easy-fs-fuse/src/main.rs
new file mode 100644
index 00000000..22540a50
--- /dev/null
+++ b/codes/easy-fs-fuse/src/main.rs
@@ -0,0 +1,258 @@
+use easy_fs::{
+    BlockDevice,
+    EasyFileSystem,
+    DiskInodeType,
+};
+use std::fs::{File, OpenOptions, read_dir};
+use std::io::{Read, Write, Seek, SeekFrom};
+use std::sync::Mutex;
+use std::sync::Arc;
+use clap::{Arg, App};
+
+const BLOCK_SZ: usize = 512;
+
+struct BlockFile(Mutex<File>);
+
+impl BlockDevice for BlockFile {
+    fn read_block(&self, block_id: usize, buf: &mut [u8]) {
+        let mut file = self.0.lock().unwrap();
+        file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64))
+            .expect("Error when seeking!");
+        assert_eq!(file.read(buf).unwrap(), BLOCK_SZ, "Not a complete block!");
+    }
+
+    fn write_block(&self, block_id: usize, buf: &[u8]) {
+        let mut file = self.0.lock().unwrap();
+        file.seek(SeekFrom::Start((block_id * BLOCK_SZ) as u64))
+            .expect("Error when seeking!");
+        assert_eq!(file.write(buf).unwrap(), BLOCK_SZ, "Not a complete block!");
+    }
+}
+
+fn main() {
+    //efs_test();
+    easy_fs_pack().expect("Error when packing easy-fs!");
+}
+
+fn easy_fs_pack() -> std::io::Result<()> {
+    // clap::matches 用于捕获用户输入的参数
+    // 在makefile中,命令为
+    // @cd ../easy-fs-fuse && cargo run --release \
+    // -- -s ../user/src/bin/ \
+    // -t ../user/target/riscv64gc-unknown-none-elf/release/
+    // 因此得到的参数就是两个路径
+    let matches = App::new("EasyFileSystem packer")
+        .arg(Arg::with_name("source")
+            .short("s") // 对应输入的 -s
+            .long("source")//对应输入 --source
+            .takes_value(true)
+            .help("Executable source dir(with backslash)")
+        )
+        .arg(Arg::with_name("target")
+            .short("t")
+            .long("target")
+            .takes_value(true)
+            .help("Executable target dir(with backslash)")    
+        )
+        .get_matches();
+    let src_path = matches.value_of("source").unwrap();
+    let target_path = matches.value_of("target").unwrap();
+    println!("src_path = {}\ntarget_path = {}", src_path, target_path);
+    
+    // 创建fs.img
+    let block_file = Arc::new(BlockFile(Mutex::new({
+        let f = OpenOptions::new()
+            .read(true)
+            .write(true)
+            .create(true)
+            .open(format!("{}{}", target_path, "fs.img"))?;
+        f.set_len(8192 * 512).unwrap();
+        f
+    })));
+    
+    // 4MiB, at most 4095 files
+    // 512*8 = 4095 * file_inode + 1 * root_inode
+    let efs = EasyFileSystem::create(
+        block_file.clone(),
+        8192,//只有4MB。。。
+        1,
+    );
+    let root_inode = Arc::new(EasyFileSystem::get_inode(&efs,0));
+    
+    // 从host获取应用名
+    let apps: Vec<_> = read_dir(src_path)
+        .unwrap()
+        .into_iter()
+        .map(|dir_entry| {
+            let mut name_with_ext = dir_entry.unwrap().file_name().into_string().unwrap();
+            // 丢弃后缀 从'.'到末尾(len-1)
+            name_with_ext.drain(name_with_ext.find('.').unwrap()..name_with_ext.len());
+            name_with_ext
+        })
+        .collect();
+    for app in apps {
+        // load app data from host file system
+        let mut host_file = File::open(format!("{}{}", target_path, app)).unwrap();
+        let mut all_data: Vec<u8> = Vec::new();
+        host_file.read_to_end(&mut all_data).unwrap();
+        // create a file in easy-fs
+        let inode = root_inode.create(app.as_str(), DiskInodeType::File).unwrap();
+        // write data to easy-fs
+        inode.write_at(0, all_data.as_slice());
+    }
+    // list apps
+
+    for app in root_inode.ls() {
+        println!("{}", app.0);
+    }
+    Ok(())
+}
+
+macro_rules! color_text {
+    ($text:expr, $color:expr) => {{
+        format_args!("\x1b[{}m{}\x1b[0m", $color, $text)
+    }};
+}
+
+#[test]
+fn efs_test() -> std::io::Result<()> {
+    let block_file = Arc::new(BlockFile(Mutex::new({
+        let f = OpenOptions::new()
+            .read(true)
+            .write(true)
+            .create(true)
+            .open("target/fs.img")?;
+        f.set_len(8192 * 512).unwrap();
+        f
+    })));
+    EasyFileSystem::create(
+        block_file.clone(),
+        4096,    
+        1,
+    );
+    let efs = EasyFileSystem::open(block_file.clone());
+    println!("freeblk = {}", efs.lock().free_blocks());
+    println!("freeinode = {}", efs.lock().free_inodes());
+    let root_inode = EasyFileSystem::get_inode(&efs,0);
+    root_inode.create("filea",DiskInodeType::File);
+    root_inode.create("fileb",DiskInodeType::File);
+    for name in root_inode.ls() {
+        println!("{}", name.0);
+    }
+    let (filea,_) = root_inode.find_path(vec!["filea"]).unwrap();
+    let greet_str = "Hello, world!";
+    filea.write_at(0, greet_str.as_bytes());
+    // let mut buffer = [0u8; 512];
+    let mut buffer = [0u8; 256];
+    let len = filea.read_at(0, &mut buffer);
+    assert_eq!(
+        greet_str,
+        core::str::from_utf8(&buffer[..len]).unwrap(),
+    );
+
+    // TODO:目录功能测试
+    // 0.1 根目录下文件删除
+    
+    // 1.1 根目录下目录创建
+    println!("0: rw in /dir ... start");
+    let dira_inode_id = {
+        println!("0.0: create dir");
+        let dira_inode = root_inode.create("dira",DiskInodeType::Directory).unwrap();
+        // 1.2 根目录下目录内文件创建/读写/删除
+        println!("0.1: create file in dir");
+        let filec = dira_inode.create("filec",DiskInodeType::File).unwrap();
+        println!("0.2: write file. wlen={}", filec.write_at(0, greet_str.as_bytes()));
+        let len = filec.read_at(0, &mut buffer);
+        println!("0.3: read file. rlen = {}",len);
+        assert_eq!(
+            greet_str,
+            core::str::from_utf8(&buffer[..len]).unwrap(),
+        );
+        dira_inode.get_id()
+        // 到这里,filec和dira_inode被释放
+    };
+    println!("0.4: open dir");
+    let dira_inode = Arc::new(EasyFileSystem::get_inode(&efs.clone(), dira_inode_id));
+    println!("0.5: read from file");
+    let (filec,_) = dira_inode.find_path(vec!["filec"]).unwrap();
+    let mut buffer = [0u8; 233];
+    let len = filec.read_at(0, &mut buffer);
+    assert_eq!(
+        greet_str,
+        core::str::from_utf8(&buffer[..len]).unwrap(),
+    );
+    // 打印根目录内容
+    println!("list files in root:");
+    let file_vec = root_inode.ls();
+    
+    for i in 0..file_vec.len(){
+        if file_vec[i].1 == DiskInodeType::File{
+            print!("{} ", file_vec[i].0);
+        } else {
+            // TODO: 统一配色!
+            print!("{} ", color_text!(file_vec[i].0, 96));
+        }
+    }
+    println!("");
+    println!("0: rw in /dir ... pass");
+
+    // 1.3 根目录下目录删除
+    
+    
+    // 2.1 多级目录创建
+    // 2.2 多级目录下文件创建/读写/删除
+    // 2.3 多级目录删除
+    
+    // 3.1 目录切换测试: cd ./..
+    // 3.2 目录切换测试: 绝对路径
+    // 3.3 目录切换测试: 相对路径
+
+    // 4 鲁棒性测试
+    // 4.1 尝试操作不存在的文件/目录
+    // 4.2 大小超出限制
+
+
+    // 文件数据块分配回收测试(superblock/inode的size是否及时增减)
+
+    // 随机字符串读写测试
+    println!("random str rw test ... start");
+    let mut random_str_test = |len: usize| {
+        filea.clear();
+        assert_eq!(
+            filea.read_at(0, &mut buffer),
+            0,
+        );
+        let mut str = String::new();
+        use rand;
+        // random digit
+        for _ in 0..len {
+            str.push(char::from('0' as u8 + rand::random::<u8>() % 10));
+        }
+        filea.write_at(0, str.as_bytes());
+        let mut read_buffer = [0u8; 127];
+        let mut offset = 0usize;
+        let mut read_str = String::new();
+        loop {
+            let len = filea.read_at(offset, &mut read_buffer);
+            if len == 0 {
+                break;
+            }
+            offset += len;
+            read_str.push_str(
+                core::str::from_utf8(&read_buffer[..len]).unwrap()
+            );
+        }
+        assert_eq!(str, read_str);
+    };
+
+    random_str_test(4 * BLOCK_SZ);
+    random_str_test(8 * BLOCK_SZ + BLOCK_SZ / 2);
+    random_str_test(100 * BLOCK_SZ);
+    random_str_test(70 * BLOCK_SZ + BLOCK_SZ / 7);
+    random_str_test((12 + 128) * BLOCK_SZ);
+    random_str_test(400 * BLOCK_SZ);
+    random_str_test(1000 * BLOCK_SZ);
+    random_str_test(2000 * BLOCK_SZ);
+    println!("random str rw test ... pass");
+    Ok(())
+}
\ No newline at end of file
diff --git a/codes/easy-fs/.gitignore b/codes/easy-fs/.gitignore
new file mode 100644
index 00000000..79f5db68
--- /dev/null
+++ b/codes/easy-fs/.gitignore
@@ -0,0 +1,3 @@
+.idea/
+target/
+Cargo.lock
diff --git a/codes/easy-fs/Cargo.toml b/codes/easy-fs/Cargo.toml
new file mode 100644
index 00000000..417de3f6
--- /dev/null
+++ b/codes/easy-fs/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "easy-fs"
+version = "0.1.0"
+authors = ["Yifan Wu <shinbokuow@163.com>","Haochen Gong <1527198893@qq.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+spin = "0.7.0"
+lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
+riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
diff --git a/codes/easy-fs/src/bitmap.rs b/codes/easy-fs/src/bitmap.rs
new file mode 100644
index 00000000..6485ac5f
--- /dev/null
+++ b/codes/easy-fs/src/bitmap.rs
@@ -0,0 +1,73 @@
+use alloc::sync::Arc;
+use super::{
+    BlockDevice,
+    BLOCK_SZ,
+    get_block_cache,
+};
+
+type BitmapBlock = [u64; 64];
+
+const BLOCK_BITS: usize = BLOCK_SZ * 8;
+
+pub struct Bitmap {
+    start_block_id: usize,
+    blocks: usize, //块数
+}
+
+/// Return (block_pos, bits64_pos, inner_pos)
+fn decomposition(mut bit: usize) -> (usize, usize, usize) {
+    let block_pos = bit / BLOCK_BITS;
+    bit = bit % BLOCK_BITS;
+    (block_pos, bit / 64, bit % 64)
+}
+
+impl Bitmap {
+    pub fn new(start_block_id: usize, blocks: usize) -> Self {
+        Self {
+            start_block_id,
+            blocks,
+        }
+    }
+
+    pub fn alloc(&self, block_device: &Arc<dyn BlockDevice>) -> Option<usize> {
+        for block_id in 0..self.blocks {
+            let pos = get_block_cache(
+                block_id + self.start_block_id as usize,
+                Arc::clone(block_device),
+            ).lock().modify(0, |bitmap_block: &mut BitmapBlock| {
+                if let Some((bits64_pos, inner_pos)) = bitmap_block
+                    .iter()
+                    .enumerate()
+                    .find(|(_, bits64)| **bits64 != u64::MAX)//找一个有空闲的
+                    .map(|(bits64_pos, bits64)| {
+                        (bits64_pos, bits64.trailing_ones() as usize) //找到最低的0位并置1
+                    }) {
+                    // modify cache
+                    bitmap_block[bits64_pos] |= 1u64 << inner_pos;
+                    Some(block_id * BLOCK_BITS + bits64_pos * 64 + inner_pos as usize)
+                } else {
+                    None
+                }
+            });
+            if pos.is_some() {
+                return pos;
+            }
+        }
+        None
+    }
+
+    pub fn dealloc(&self, block_device: &Arc<dyn BlockDevice>, bit: usize) {
+        let (block_pos, bits64_pos, inner_pos) = decomposition(bit);
+        get_block_cache(
+            block_pos + self.start_block_id,
+            Arc::clone(block_device)
+        ).lock().modify(0, |bitmap_block: &mut BitmapBlock| {
+            assert!(bitmap_block[bits64_pos] & (1u64 << inner_pos) > 0);
+            bitmap_block[bits64_pos] -= 1u64 << inner_pos;
+        });
+    }
+
+    pub fn maximum(&self) -> usize {
+        self.blocks * BLOCK_BITS
+    }
+}
\ No newline at end of file
diff --git a/codes/easy-fs/src/block_cache.rs b/codes/easy-fs/src/block_cache.rs
new file mode 100644
index 00000000..37bafe1f
--- /dev/null
+++ b/codes/easy-fs/src/block_cache.rs
@@ -0,0 +1,134 @@
+use super::{
+    BLOCK_SZ,
+    BlockDevice,
+};
+use alloc::collections::VecDeque;
+use alloc::sync::Arc;
+use lazy_static::*;
+use spin::Mutex;
+use riscv::register::time;
+
+pub struct BlockCache {
+    cache: [u8; BLOCK_SZ],
+    block_id: usize,
+    block_device: Arc<dyn BlockDevice>,
+    modified: bool,
+    time_stamp: usize,
+}
+
+impl BlockCache {
+    /// Load a new BlockCache from disk.
+    pub fn new(
+        block_id: usize, 
+        block_device: Arc<dyn BlockDevice>
+    ) -> Self {
+        let mut cache = [0u8; BLOCK_SZ];
+        block_device.read_block(block_id, &mut cache);
+        // TODO: 时间戳
+        //let mut time_stamp = time::read();
+        let mut time_stamp = 0;
+        Self {
+            cache,
+            block_id,
+            block_device,
+            modified: false,
+            time_stamp,
+        }
+    }
+
+    fn addr_of_offset(&self, offset: usize) -> usize {
+        &self.cache[offset] as *const _ as usize
+    }
+
+    pub fn get_ref<T>(&self, offset: usize) -> &T where T: Sized {
+        let type_size = core::mem::size_of::<T>();
+        assert!(offset + type_size <= BLOCK_SZ);
+        let addr = self.addr_of_offset(offset);
+        unsafe { &*(addr as *const T) } 
+    }
+
+    pub fn get_mut<T>(&mut self, offset: usize) -> &mut T where T: Sized {
+        let type_size = core::mem::size_of::<T>();
+        assert!(offset + type_size <= BLOCK_SZ);
+        self.modified = true;
+        let addr = self.addr_of_offset(offset);
+        unsafe { &mut *(addr as *mut T) }
+    }
+
+    pub fn read<T, V>(&self, offset: usize, f: impl FnOnce(&T) -> V) -> V {
+        f(self.get_ref(offset))
+    }
+
+    pub fn modify<T, V>(&mut self, offset:usize, f: impl FnOnce(&mut T) -> V) -> V {
+        f(self.get_mut(offset))
+    }
+
+    pub fn sync(&mut self) {
+        if self.modified {
+            self.modified = false;
+            self.block_device.write_block(self.block_id, &self.cache);
+        }
+    }
+}
+
+impl Drop for BlockCache {
+    fn drop(&mut self) {
+        self.sync()
+    }
+}
+
+const BLOCK_CACHE_SIZE: usize = 16;
+
+pub struct BlockCacheManager {
+    queue: VecDeque<(usize, Arc<Mutex<BlockCache>>)>,
+}
+
+impl BlockCacheManager {
+    pub fn new() -> Self {
+        Self { queue: VecDeque::new() }
+    }
+
+    pub fn get_block_cache(
+        &mut self,
+        block_id: usize,
+        block_device: Arc<dyn BlockDevice>,
+    ) -> Arc<Mutex<BlockCache>> {
+        if let Some(pair) = self.queue
+            .iter()
+            .find(|pair| pair.0 == block_id) {
+                Arc::clone(&pair.1)
+        } else {
+            // substitute
+            if self.queue.len() == BLOCK_CACHE_SIZE {
+                // from front to tail
+                if let Some((idx, _)) = self.queue
+                    .iter()
+                    .enumerate()
+                    .find(|(_, pair)| Arc::strong_count(&pair.1) == 1) {
+                    self.queue.drain(idx..=idx);
+                } else {
+                    panic!("Run out of BlockCache!");
+                }
+            }
+            // load block into mem and push back
+            let block_cache = Arc::new(Mutex::new(
+                BlockCache::new(block_id, Arc::clone(&block_device))
+            ));
+            self.queue.push_back((block_id, Arc::clone(&block_cache)));
+            block_cache
+        }
+    }
+}
+
+lazy_static! {
+    pub static ref BLOCK_CACHE_MANAGER: Mutex<BlockCacheManager> = Mutex::new(
+        BlockCacheManager::new()
+    );
+}
+
+pub fn get_block_cache(
+    block_id: usize,
+    block_device: Arc<dyn BlockDevice>
+) -> Arc<Mutex<BlockCache>> {
+    BLOCK_CACHE_MANAGER.lock().get_block_cache(block_id, block_device)
+}
\ No newline at end of file
diff --git a/codes/easy-fs/src/block_dev.rs b/codes/easy-fs/src/block_dev.rs
new file mode 100644
index 00000000..7a282751
--- /dev/null
+++ b/codes/easy-fs/src/block_dev.rs
@@ -0,0 +1,6 @@
+use core::any::Any;
+
+pub trait BlockDevice : Send + Sync + Any {
+    fn read_block(&self, block_id: usize, buf: &mut [u8]);
+    fn write_block(&self, block_id: usize, buf: &[u8]);
+}
diff --git a/codes/easy-fs/src/efs.rs b/codes/easy-fs/src/efs.rs
new file mode 100644
index 00000000..8176078e
--- /dev/null
+++ b/codes/easy-fs/src/efs.rs
@@ -0,0 +1,237 @@
+use alloc::sync::Arc;
+use spin::Mutex;
+use super::{
+    BlockDevice,
+    Bitmap,
+    SuperBlock,
+    DiskInode,
+    DiskInodeType,
+    Inode,
+    get_block_cache,
+    DIRENT_SZ,
+    DirEntry,
+};
+use crate::BLOCK_SZ;
+use alloc::vec::Vec;
+
+pub struct EasyFileSystem {
+    pub block_device: Arc<dyn BlockDevice>,
+    pub inode_bitmap: Bitmap,
+    pub data_bitmap: Bitmap,
+    inode_area_start_block: u32,
+    data_area_start_block: u32,
+}
+
+type DataBlock = [u8; BLOCK_SZ];
+
+impl EasyFileSystem {
+    pub fn create(
+        block_device: Arc<dyn BlockDevice>,
+        total_blocks: u32,
+        inode_bitmap_blocks: u32,
+    ) -> Arc<Mutex<Self>> {
+        // calculate block size of areas & create bitmaps
+        let inode_bitmap = Bitmap::new(1, inode_bitmap_blocks as usize);
+        let inode_num = inode_bitmap.maximum();
+        // 存放inode数组的块数(向上对齐
+        let inode_area_blocks =
+            ((inode_num * core::mem::size_of::<DiskInode>() + BLOCK_SZ - 1) / BLOCK_SZ) as u32;
+        let inode_total_blocks = inode_bitmap_blocks + inode_area_blocks;
+        let data_total_blocks = total_blocks - 1 - inode_total_blocks;
+        // 每个位图块对应512*8=4096个数据块
+        // 因此理想情况为,4096 * data_bitmap_blocks + data_bitmap_blocks = data_total_blocks
+        // 需要向上取整,否则有的数据块没有对应的位图,会被浪费!
+        let data_bitmap_blocks = (data_total_blocks + 4096) / 4097;
+        let data_area_blocks = data_total_blocks - data_bitmap_blocks;
+        let data_bitmap = Bitmap::new(
+            (1 + inode_bitmap_blocks + inode_area_blocks) as usize,
+            data_bitmap_blocks as usize,
+        );
+        let mut efs = Self {
+            block_device: Arc::clone(&block_device),
+            inode_bitmap,
+            data_bitmap,
+            inode_area_start_block: 1 + inode_bitmap_blocks,
+            data_area_start_block: 1 + inode_total_blocks + data_bitmap_blocks,
+        };
+        // clear all blocks
+        for i in 0..total_blocks {
+            get_block_cache(
+                i as usize, 
+                Arc::clone(&block_device)
+            )
+            .lock()
+            .modify(0, |data_block: &mut DataBlock| {
+                for byte in data_block.iter_mut() { *byte = 0; }
+            });
+        }
+        // initialize SuperBlock
+        get_block_cache(0, Arc::clone(&block_device))
+        .lock()
+        .modify(0, |super_block: &mut SuperBlock| {
+            super_block.initialize(
+                total_blocks,
+                inode_bitmap_blocks,
+                inode_area_blocks,
+                data_bitmap_blocks,
+                data_area_blocks,
+                inode_num as u32,
+                data_area_blocks,
+            );
+        });
+        // write back immediately
+        // create a inode for root node "/"
+        assert_eq!(efs.alloc_inode(), 0);
+        let (root_inode_block_id, root_inode_offset) = efs.get_disk_inode_pos(0);
+        get_block_cache(
+            root_inode_block_id as usize,
+            Arc::clone(&block_device)
+        )
+        .lock()
+        .modify(root_inode_offset, |disk_inode: &mut DiskInode| {
+            disk_inode.initialize(DiskInodeType::Directory);
+            // DEBUG: 创建根节点的..和.
+            let new_size = 2 * DIRENT_SZ;
+            // 分配数据块
+            let mut v: Vec<u32> = Vec::new();
+            for _ in 0..1 {
+                v.push(efs.alloc_data());
+            }
+            disk_inode.increase_size(new_size as u32, v, &block_device);
+            // root的../.均指向自身
+            let dirent_self = DirEntry::new(".", DiskInodeType::Directory,0);
+            let dirent_parent = DirEntry::new("..", DiskInodeType::Directory,0);
+            disk_inode.write_at(
+                0, 
+                dirent_self.as_bytes(), 
+                &block_device
+            );
+            disk_inode.write_at(
+                DIRENT_SZ, 
+                dirent_parent.as_bytes(), 
+                &block_device
+            );
+            ///////
+        });
+        Arc::new(Mutex::new(efs))
+    }
+
+    pub fn open(block_device: Arc<dyn BlockDevice>) -> Arc<Mutex<Self>> {
+        // read SuperBlock
+        get_block_cache(0, Arc::clone(&block_device))
+        .lock()
+        .read(0, |super_block: &SuperBlock| {
+            assert!(super_block.is_valid(), "Error loading EFS! magic = {}", super_block.magic);
+            let inode_total_blocks =
+                super_block.inode_bitmap_blocks + super_block.inode_area_blocks;
+            let efs = Self {
+                block_device,
+                inode_bitmap: Bitmap::new(
+                    1,
+                    super_block.inode_bitmap_blocks as usize
+                ),
+                data_bitmap: Bitmap::new(
+                    (1 + inode_total_blocks) as usize,
+                    super_block.data_bitmap_blocks as usize,
+                ),
+                inode_area_start_block: 1 + super_block.inode_bitmap_blocks,
+                data_area_start_block: 1 + inode_total_blocks + super_block.data_bitmap_blocks,
+            };
+            Arc::new(Mutex::new(efs))
+        })        
+    }
+
+    pub fn get_inode( efs: &Arc<Mutex<Self>>, inode_id :u32) -> Inode {
+        let block_device = Arc::clone(&efs.lock().block_device);
+        Inode::new(
+            inode_id,
+            Arc::clone(efs),
+            block_device,
+        )
+    }
+
+    pub fn get_disk_inode_pos(&self, inode_id: u32) -> (u32, usize) {
+        let inode_size = core::mem::size_of::<DiskInode>();
+        let inodes_per_block = (BLOCK_SZ / inode_size) as u32;
+        let block_id = self.inode_area_start_block + inode_id / inodes_per_block;
+        (block_id, (inode_id % inodes_per_block) as usize * inode_size)
+    }
+
+    pub fn get_data_block_id(&self, data_block_id: u32) -> u32 {
+        self.data_area_start_block + data_block_id
+    }
+
+    // Return ID in the inode area
+    pub fn alloc_inode(&mut self) -> u32 {
+        // DEGUB: 修改超级块
+        get_block_cache(0, Arc::clone(&self.block_device))
+        .lock()
+        .modify(0, |super_block: &mut SuperBlock| {
+            super_block.dec_inode();
+        });
+        self.inode_bitmap.alloc(&self.block_device).unwrap() as u32
+    }
+
+    /// Return a block ID not ID in the data area !
+    pub fn alloc_data(&mut self) -> u32 {
+        // DEGUB: 修改超级块
+        get_block_cache(0, Arc::clone(&self.block_device))
+        .lock()
+        .modify(0, |super_block: &mut SuperBlock| {
+            super_block.dec_block();
+        });
+        self.data_bitmap.alloc(&self.block_device).unwrap() as u32 + self.data_area_start_block
+    }
+
+    pub fn dealloc_data(&mut self, block_id: u32) {
+        // DEGUB: 修改超级块
+        get_block_cache(0, Arc::clone(&self.block_device))
+        .lock()
+        .modify(0, |super_block: &mut SuperBlock| {
+            super_block.add_block();
+        });
+        get_block_cache(
+            block_id as usize,
+            Arc::clone(&self.block_device)
+        )
+        .lock()
+        .modify(0, |data_block: &mut DataBlock| {
+            data_block.iter_mut().for_each(|p| { *p = 0; })
+        });
+        self.data_bitmap.dealloc(
+            &self.block_device,
+            (block_id - self.data_area_start_block) as usize
+        )
+    }
+
+    pub fn dealloc_inode(&mut self, inode_id: u32) {
+        // 回收inode  
+        // 因为初始化inode的时候会清零,所以这里没必要清零
+        // DEGUB: 修改超级块
+        get_block_cache(0, Arc::clone(&self.block_device))
+        .lock()
+        .modify(0, |super_block: &mut SuperBlock| {
+            super_block.add_inode();
+        });
+        self.inode_bitmap.dealloc(
+            &self.block_device, 
+            inode_id as usize
+        );
+    }
+
+    pub fn free_blocks(&self) -> u32{
+        get_block_cache(0, Arc::clone(&self.block_device))
+        .lock()
+        .read(0, |super_block: &SuperBlock| {
+            super_block.get_free_blocks()
+        })
+    }
+
+    pub fn free_inodes(&self) -> u32{
+        get_block_cache(0, Arc::clone(&self.block_device))
+        .lock()
+        .read(0, |super_block: &SuperBlock| {
+            super_block.get_free_inodes()
+        })
+    }
+}
\ No newline at end of file
diff --git a/codes/easy-fs/src/layout.rs b/codes/easy-fs/src/layout.rs
new file mode 100644
index 00000000..814acdfe
--- /dev/null
+++ b/codes/easy-fs/src/layout.rs
@@ -0,0 +1,610 @@
+use core::fmt::{Debug, Formatter, Result};
+use super::{
+    BLOCK_SZ,
+    BlockDevice,
+    get_block_cache,
+};
+use alloc::sync::Arc;
+use alloc::vec::Vec;
+use riscv::interrupt::free;
+
+const EFS_MAGIC: u32 = 0x3b800001;
+const INODE_DIRECT_COUNT: usize = 28;
+pub const NAME_LENGTH_LIMIT: usize = 26;//27
+const INODE_INDIRECT1_COUNT: usize = BLOCK_SZ / 4;
+const INODE_INDIRECT2_COUNT: usize = INODE_INDIRECT1_COUNT * INODE_INDIRECT1_COUNT;
+const DIRECT_BOUND: usize = INODE_DIRECT_COUNT;
+const INDIRECT1_BOUND: usize = DIRECT_BOUND + INODE_INDIRECT1_COUNT;
+#[allow(unused)]
+const INDIRECT2_BOUND: usize = INDIRECT1_BOUND + INODE_INDIRECT2_COUNT;
+
+#[repr(C)]
+pub struct SuperBlock {
+    // 可用空间:512B = 128 * u32
+    pub magic: u32,
+    pub total_blocks: u32,
+    // 各个区域的长度
+    pub inode_bitmap_blocks: u32,
+    pub inode_area_blocks: u32,
+    pub data_bitmap_blocks: u32,
+    pub data_area_blocks: u32,
+    // 余量
+    pub free_inodes: u32,
+    pub free_blocks: u32,
+}
+
+impl Debug for SuperBlock {
+    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
+        f.debug_struct("SuperBlock")
+            .field("total_blocks", &self.total_blocks)
+            .field("inode_bitmap_blocks", &self.inode_bitmap_blocks)
+            .field("inode_area_blocks", &self.inode_area_blocks)
+            .field("data_bitmap_blocks", &self.data_bitmap_blocks)
+            .field("data_area_blocks", &self.data_area_blocks)
+            .finish()
+    }
+}
+
+impl SuperBlock {
+    pub fn initialize(
+        &mut self,
+        total_blocks: u32,
+        inode_bitmap_blocks: u32,
+        inode_area_blocks: u32,
+        data_bitmap_blocks: u32,
+        data_area_blocks: u32,
+        free_inodes: u32,
+        free_blocks: u32,
+    ) {
+        *self = Self {
+            magic: EFS_MAGIC,
+            total_blocks,
+            inode_bitmap_blocks,
+            inode_area_blocks,
+            data_bitmap_blocks,
+            data_area_blocks,
+            free_inodes,
+            free_blocks,
+        }
+    }
+    pub fn is_valid(&self) -> bool {
+        self.magic == EFS_MAGIC
+    }
+
+    pub fn add_inode(&mut self) {
+        self.free_inodes += 1;
+    }
+    
+    pub fn dec_inode(&mut self) {
+        self.free_inodes -= 1;
+    }
+
+    pub fn add_block(&mut self) {
+        self.free_blocks += 1;
+    }
+    
+    pub fn dec_block(&mut self) {
+        self.free_blocks -= 1;
+    }
+
+    pub fn get_free_inodes(&self) -> u32{
+        self.free_inodes
+    }
+
+    pub fn get_free_blocks(&self) -> u32{
+        self.free_blocks
+    }
+
+}
+
+#[derive(PartialEq,Copy,Clone)]
+pub enum DiskInodeType {
+    File,
+    Directory,
+}
+
+type IndirectBlock = [u32; BLOCK_SZ / 4];
+type DataBlock = [u8; BLOCK_SZ];
+
+#[repr(C)]
+pub struct DiskInode {
+    // 最大16540个数据块
+    // 
+    pub size: u32, // Byte
+    pub direct: [u32; INODE_DIRECT_COUNT], //28*512 = 14KB
+    pub indirect1: u32,  // 128*512 = 64KB
+    pub indirect2: u32,  // 128*128*512 = 8MB
+    pub type_: DiskInodeType,
+}
+
+impl DiskInode {
+    /// indirect1 and indirect2 block are allocated only when they are needed.
+    pub fn initialize(&mut self, type_: DiskInodeType) {
+        self.size = 0;
+        self.direct.iter_mut().for_each(|v| *v = 0);
+        self.indirect1 = 0;
+        self.indirect2 = 0;
+        self.type_ = type_;
+    }
+    pub fn is_dir(&self) -> bool {
+        self.type_ == DiskInodeType::Directory
+    }
+    #[allow(unused)]
+    pub fn is_file(&self) -> bool {
+        self.type_ == DiskInodeType::File
+    }
+    /// Return block number correspond to size.
+    pub fn data_blocks(&self) -> u32 {
+        Self::_data_blocks(self.size)
+    }
+    fn _data_blocks(size: u32) -> u32 {
+        (size + BLOCK_SZ as u32 - 1) / BLOCK_SZ as u32
+    }
+    /// Return number of blocks needed include indirect1/2.
+    pub fn total_blocks(size: u32) -> u32 {
+        let data_blocks = Self::_data_blocks(size) as usize;
+        let mut total = data_blocks as usize;
+        // indirect1
+        if data_blocks > INODE_DIRECT_COUNT {
+            total += 1;
+        }
+        // indirect2
+        if data_blocks > INDIRECT1_BOUND {
+            total += 1;
+            // sub indirect1 (向上取整)
+            total += (data_blocks - INDIRECT1_BOUND + INODE_INDIRECT1_COUNT - 1) / INODE_INDIRECT1_COUNT;
+        }
+        total as u32
+    }
+    // 计算扩容至new_size需要多少索引块
+    pub fn blocks_num_needed(&self, new_size: u32) -> u32 {
+        assert!(new_size >= self.size);
+        Self::total_blocks(new_size) - Self::total_blocks(self.size)
+    }
+    pub fn get_block_id(&self, inner_id: u32, block_device: &Arc<dyn BlockDevice>) -> u32 {
+        let inner_id = inner_id as usize;
+        if inner_id < INODE_DIRECT_COUNT {
+            self.direct[inner_id]
+        } else if inner_id < INDIRECT1_BOUND {
+            get_block_cache(self.indirect1 as usize, Arc::clone(block_device))
+                .lock()
+                .read(0, |indirect_block: &IndirectBlock| {
+                    indirect_block[inner_id - INODE_DIRECT_COUNT]
+                })
+        } else {
+            let last = inner_id - INDIRECT1_BOUND;
+            let indirect1 = get_block_cache(
+                self.indirect2 as usize,
+                Arc::clone(block_device)
+            )
+            .lock()
+            .read(0, |indirect2: &IndirectBlock| {
+                indirect2[last / INODE_INDIRECT1_COUNT]
+            });
+            get_block_cache(
+                indirect1 as usize,
+                Arc::clone(block_device)
+            )
+            .lock()
+            .read(0, |indirect1: &IndirectBlock| {
+                indirect1[last % INODE_INDIRECT1_COUNT]
+            })
+        }
+    }
+
+    pub fn decrease_size(&mut self, new_size: u32, block_device: &Arc<dyn BlockDevice>) -> Option<Vec<u32>>{
+        // 查看是否有可以回收的块
+        // 有则将块号返回
+        let mut old_blocks = self.data_blocks();
+        self.size = new_size;
+        let mut current_blocks = self.data_blocks();
+        if current_blocks == old_blocks {
+            return None;
+        }
+        let mut blk_vec:Vec<u32> = Vec::new();
+        
+        // 采用和分配一样的顺序,从前往后,这样效率高
+        // 回收direct
+        let mut ltimes = old_blocks.min(INODE_DIRECT_COUNT as u32);
+        while current_blocks < ltimes {
+            blk_vec.push(self.direct[current_blocks as usize]);
+            // self.direct[blocks_needed as usize] = 0; // 没必要清零
+            current_blocks += 1;
+        }
+        
+        // 回收indirect1
+        if old_blocks > INODE_DIRECT_COUNT as u32 {
+            // 计算偏移量
+            current_blocks -= INODE_DIRECT_COUNT as u32;
+            old_blocks -= INODE_DIRECT_COUNT as u32;
+        }else{
+            return Some(blk_vec);
+        }
+        if current_blocks == 0 { // 回收一级间接块
+            blk_vec.push(self.indirect1);
+        }
+        get_block_cache(
+            self.indirect1 as usize,
+            Arc::clone(block_device)
+        )
+        .lock()
+        .modify(0, |indirect1: &mut IndirectBlock| {
+            ltimes = old_blocks.min(INODE_INDIRECT1_COUNT as u32);
+            while current_blocks < ltimes {
+                blk_vec.push(indirect1[current_blocks as usize]);
+                current_blocks += 1;
+            }
+        });
+
+        // 回收indirect2
+        if old_blocks > INODE_INDIRECT1_COUNT as u32 {
+            old_blocks -= INODE_INDIRECT1_COUNT as u32;
+            current_blocks -= INODE_INDIRECT1_COUNT as u32;
+        } else {
+            return Some(blk_vec);
+        }
+        if current_blocks == 0 { // 二级间接块需要回收
+            blk_vec.push(self.indirect2);
+        }
+        // from (a0, b0) to (a1, b1)
+        let mut a0 = current_blocks as usize / INODE_INDIRECT1_COUNT;
+        let mut b0 = current_blocks as usize % INODE_INDIRECT1_COUNT;
+        let a1 = old_blocks as usize / INODE_INDIRECT1_COUNT;
+        let b1 = old_blocks as usize % INODE_INDIRECT1_COUNT;
+        get_block_cache(
+            self.indirect2 as usize,
+            Arc::clone(block_device)
+        )
+        .lock()
+        .modify(0, |indirect2: &mut IndirectBlock| {
+            while (a0 < a1) || (a0 == a1 && b0 < b1) {
+                // TODO
+                if b0 == 0 { // 一级间接块需要回收
+                    blk_vec.push(indirect2[a0]);
+                }
+                get_block_cache( 
+                    indirect2[a0] as usize,
+                    Arc::clone(block_device)
+                )
+                .lock()
+                .modify(0,|indirect1:&mut IndirectBlock|{
+                    //修改一级间接块
+                    let ltimes = b1.min(INODE_INDIRECT1_COUNT);
+                    while b0 < ltimes{
+                        blk_vec.push(indirect1[b0]);
+                        b0 += 1;
+                    }
+                });
+                // TODO 
+                if b0 == INODE_INDIRECT1_COUNT{
+                    b0 = 0;
+                }
+                a0 += 1;  
+            } 
+        });
+        Some(blk_vec)
+    }
+    
+    // new_blocks: 保存本次容量扩充所需要块编号的向量
+    // ?: new_blocks 究竟是什么结构,里面包含了为indirect分配的块?
+    // 结论:根据blocks_num_needed得到需要的块数以确定
+    pub fn increase_size(
+        &mut self,
+        new_size: u32,
+        new_blocks: Vec<u32>,
+        block_device: &Arc<dyn BlockDevice>,
+    ) {
+        let mut current_blocks = self.data_blocks();
+        self.size = new_size;
+        let mut total_blocks = self.data_blocks();
+        let mut new_blocks = new_blocks.into_iter();
+        // fill direct
+        while current_blocks < total_blocks.min(INODE_DIRECT_COUNT as u32) {
+            self.direct[current_blocks as usize] = new_blocks.next().unwrap();
+            current_blocks += 1;
+        }
+        // alloc indirect1
+        if total_blocks > INODE_DIRECT_COUNT as u32{
+            if current_blocks == INODE_DIRECT_COUNT as u32 {
+                // 说明indirect1还未被分配,因此分配一个块
+                self.indirect1 = new_blocks.next().unwrap(); 
+            }
+            current_blocks -= INODE_DIRECT_COUNT as u32;
+            total_blocks -= INODE_DIRECT_COUNT as u32;
+        } else {
+            return;
+        }
+        // fill indirect1
+        get_block_cache(
+            self.indirect1 as usize,
+            Arc::clone(block_device)
+        )
+        .lock()
+        .modify(0, |indirect1: &mut IndirectBlock| {
+            while current_blocks < total_blocks.min(INODE_INDIRECT1_COUNT as u32) {
+                indirect1[current_blocks as usize] = new_blocks.next().unwrap();
+                current_blocks += 1;
+            }
+        });
+        // alloc indirect2
+        if total_blocks > INODE_INDIRECT1_COUNT as u32 {
+            if current_blocks == INODE_INDIRECT1_COUNT as u32 {
+                self.indirect2 = new_blocks.next().unwrap();
+            }
+            current_blocks -= INODE_INDIRECT1_COUNT as u32;
+            total_blocks -= INODE_INDIRECT1_COUNT as u32;
+        } else {
+            return;
+        }
+        // fill indirect2 from (a0, b0) -> (a1, b1)
+        let mut a0 = current_blocks as usize / INODE_INDIRECT1_COUNT;
+        let mut b0 = current_blocks as usize % INODE_INDIRECT1_COUNT;
+        let a1 = total_blocks as usize / INODE_INDIRECT1_COUNT;
+        let b1 = total_blocks as usize % INODE_INDIRECT1_COUNT;
+        // alloc low-level indirect1
+        get_block_cache(
+            self.indirect2 as usize,
+            Arc::clone(block_device)
+        )
+        .lock()
+        .modify(0, |indirect2: &mut IndirectBlock| {
+            while (a0 < a1) || (a0 == a1 && b0 < b1) {
+                if b0 == 0 {
+                    // 先分配一个一级索引块
+                    indirect2[a0] = new_blocks.next().unwrap();
+                }
+                // fill current
+                get_block_cache(
+                    indirect2[a0] as usize,
+                    Arc::clone(block_device)
+                )
+                .lock()
+                .modify(0, |indirect1: &mut IndirectBlock| {
+                    //填写一级索引块
+                    indirect1[b0] = new_blocks.next().unwrap();
+                });
+                // move to next
+                b0 += 1;
+                if b0 == INODE_INDIRECT1_COUNT {
+                    b0 = 0;
+                    a0 += 1;
+                }
+            } 
+        });
+    }
+
+    /// Clear size to zero and return blocks that should be deallocated.
+    ///
+    /// We will clear the block contents to zero later.
+    pub fn clear_size(&mut self, block_device: &Arc<dyn BlockDevice>) -> Vec<u32> {
+        let mut v: Vec<u32> = Vec::new();
+        let mut data_blocks = self.data_blocks() as usize;
+        self.size = 0;
+
+        //统计当前回收的块数
+        let mut current_blocks = 0usize;
+        // direct
+        while current_blocks < data_blocks.min(INODE_DIRECT_COUNT) {
+            v.push(self.direct[current_blocks]);
+            self.direct[current_blocks] = 0;
+            current_blocks += 1;
+        }
+        // indirect1 block
+        if data_blocks > INODE_DIRECT_COUNT {
+            v.push(self.indirect1);
+            data_blocks -= INODE_DIRECT_COUNT;
+            current_blocks = 0;
+        } else {
+            return v;
+        }
+        // indirect1
+        get_block_cache(
+            self.indirect1 as usize,
+            Arc::clone(block_device),
+        )
+        .lock()
+        .modify(0, |indirect1: &mut IndirectBlock| {
+            while current_blocks < data_blocks.min(INODE_INDIRECT1_COUNT) {
+                v.push(indirect1[current_blocks]);
+                //indirect1[current_blocks] = 0;
+                current_blocks += 1;
+            }
+        });
+        self.indirect1 = 0;
+        // indirect2 block
+        if data_blocks > INODE_INDIRECT1_COUNT {
+            v.push(self.indirect2);
+            data_blocks -= INODE_INDIRECT1_COUNT;
+        } else {
+            return v;
+        }
+        // indirect2
+        assert!(data_blocks <= INODE_INDIRECT2_COUNT);
+        let a1 = data_blocks / INODE_INDIRECT1_COUNT;
+        let b1 = data_blocks % INODE_INDIRECT1_COUNT;
+        get_block_cache(
+            self.indirect2 as usize,
+            Arc::clone(block_device),
+        )
+        .lock()
+        .modify(0, |indirect2: &mut IndirectBlock| {
+            // full indirect1 blocks
+            for i in 0..a1 {
+                v.push(indirect2[i]);
+                get_block_cache(
+                    indirect2[i] as usize,
+                    Arc::clone(block_device),
+                )
+                .lock()
+                .modify(0, |indirect1: &mut IndirectBlock| {
+                    // 前几个一级索引都是占满的
+                    // 因此每个都要回收INODE_INDIRECT1_COUNT块
+                    for j in 0..INODE_INDIRECT1_COUNT {
+                        v.push(indirect1[j]);
+                        //indirect1[j] = 0;
+                    }
+                });
+                //indirect2[i] = 0;
+            }
+            // last indirect1 block
+            if b1 > 0 {
+                // 这种情况下,实际是第a1+1个一级索引块
+                v.push(indirect2[a1]);
+                get_block_cache(
+                    indirect2[a1] as usize,
+                    Arc::clone(block_device),
+                )
+                .lock()
+                .modify(0, |indirect1: &mut IndirectBlock| {
+                    for j in 0..b1 {
+                        v.push(indirect1[j]);
+                        //indirect1[j] = 0;
+                    }
+                });
+                //indirect2[a1] = 0;
+            }
+        });
+        self.indirect2 = 0;
+        v
+    }
+    pub fn read_at(
+        &self,
+        offset: usize,
+        buf: &mut [u8],
+        block_device: &Arc<dyn BlockDevice>,
+    ) -> usize {
+        let mut start = offset;
+        //println!("size = {}",self.size);        
+        let end = (offset + buf.len()).min(self.size as usize);
+        if start >= end {
+            return 0;
+        }
+        let mut start_block = start / BLOCK_SZ;
+        let mut read_size = 0usize;
+        loop {
+            // calculate end of current block
+            // 指文件从头开始到当前块末尾的大小(实际上也是向上对齐
+            // 但是不同于前面,这里是对齐的是索引
+            // 例如前面,size = 512, 则应该算在一个块
+            // 而这里,块内地址范围为0-511,512属于下一个块
+            let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ;
+            end_current_block = end_current_block.min(end);
+
+            // read and update read size
+            let block_read_size = end_current_block - start;
+            let dst = &mut buf[read_size..read_size + block_read_size];
+            get_block_cache(
+                self.get_block_id(start_block as u32, block_device) as usize,
+                Arc::clone(block_device),
+            )
+            .lock()
+            .read(0, |data_block: &DataBlock| {
+                let src = &data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_read_size];
+                dst.copy_from_slice(src);
+            });
+            read_size += block_read_size;
+            // move to next block
+            if end_current_block == end { break; }
+            start_block += 1;
+            start = end_current_block;
+        }
+        read_size
+    }
+    /// File size must be adjusted before!
+    /// Users should call increase_size for that
+    pub fn write_at(
+        &mut self,
+        offset: usize,
+        buf: &[u8],
+        block_device: &Arc<dyn BlockDevice>,
+    ) -> usize {
+        let mut start = offset;
+        let end = (offset + buf.len()).min(self.size as usize);
+        assert!(start <= end);
+        let mut start_block = start / BLOCK_SZ;
+        let mut write_size = 0usize;
+        loop {
+            // calculate end of current block
+            let mut end_current_block = (start / BLOCK_SZ + 1) * BLOCK_SZ;
+            end_current_block = end_current_block.min(end);
+            // write and update write size
+            let block_write_size = end_current_block - start;
+            get_block_cache(
+                self.get_block_id(start_block as u32, block_device) as usize,
+                Arc::clone(block_device)
+            )
+            .lock()
+            .modify(0, |data_block: &mut DataBlock| {
+                let src = &buf[write_size..write_size + block_write_size];
+                let dst = &mut data_block[start % BLOCK_SZ..start % BLOCK_SZ + block_write_size];
+                dst.copy_from_slice(src);
+            });
+            write_size += block_write_size;
+            // move to next block
+            if end_current_block == end { break; }
+            start_block += 1;
+            start = end_current_block;
+        }
+        write_size
+    }
+}
+
+#[repr(C)]
+pub struct DirEntry {
+    // +1 is for '\0'
+    // 大小为32B
+    // 规定:如果当前目录项为空,则name[0] = 0
+    // 这样效率比较高
+    name: [u8; NAME_LENGTH_LIMIT + 1],
+    type_: DiskInodeType,
+    inode_number: u32,
+}
+
+pub const DIRENT_SZ: usize = 32;
+
+impl DirEntry {
+    pub fn empty() -> Self {
+        Self {
+            name: [0u8; NAME_LENGTH_LIMIT + 1],
+            type_: DiskInodeType::File,
+            inode_number: 0,
+        }
+    }
+    pub fn new(name: &str, type_: DiskInodeType, inode_number: u32) -> Self {
+        let mut bytes = [0u8; NAME_LENGTH_LIMIT + 1];
+        &mut bytes[..name.len()].copy_from_slice(name.as_bytes());
+        Self {
+            name: bytes,
+            type_,
+            inode_number,
+        }
+    }
+    pub fn as_bytes(&self) -> &[u8] {
+        unsafe {
+            core::slice::from_raw_parts(
+                self as *const _ as usize as *const u8,
+                DIRENT_SZ,
+            )
+        }
+    }
+    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
+        unsafe {
+            core::slice::from_raw_parts_mut(
+                self as *mut _ as usize as *mut u8,
+                DIRENT_SZ,
+            )
+        }
+    }
+    pub fn name(&self) -> &str {
+        let len = (0usize..).find(|i| self.name[*i] == 0).unwrap();
+        core::str::from_utf8(&self.name[..len]).unwrap()
+    }
+    
+    pub fn type_(&self) -> DiskInodeType {
+        self.type_
+    }
+
+    pub fn inode_number(&self) -> u32 {
+        self.inode_number
+    }
+}
\ No newline at end of file
diff --git a/codes/easy-fs/src/lib.rs b/codes/easy-fs/src/lib.rs
new file mode 100644
index 00000000..e365d9e0
--- /dev/null
+++ b/codes/easy-fs/src/lib.rs
@@ -0,0 +1,19 @@
+#![no_std]
+extern crate alloc;
+
+mod block_dev;
+mod layout;
+mod efs;
+mod bitmap;
+mod vfs;
+mod block_cache;
+
+pub const BLOCK_SZ: usize = 512;
+pub use block_dev::BlockDevice;
+pub use efs::EasyFileSystem;
+pub use vfs::Inode;
+pub use layout::DiskInodeType;
+pub use layout::NAME_LENGTH_LIMIT;
+use layout::*;
+use bitmap::Bitmap;
+use block_cache::get_block_cache;
\ No newline at end of file
diff --git a/codes/easy-fs/src/vfs.rs b/codes/easy-fs/src/vfs.rs
new file mode 100644
index 00000000..cbbd6b35
--- /dev/null
+++ b/codes/easy-fs/src/vfs.rs
@@ -0,0 +1,430 @@
+use super::{
+    BlockDevice,
+    DiskInode,
+    DiskInodeType,
+    DirEntry,
+    EasyFileSystem,
+    DIRENT_SZ,
+    get_block_cache,
+};
+use alloc::sync::Arc;
+use alloc::string::String;
+use alloc::vec::Vec;
+use spin::{Mutex, MutexGuard};
+
+#[derive(Clone)]
+pub struct Inode {
+    inode_id: u32,
+    block_id: usize,
+    block_offset: usize,  //记录inode在磁盘上的位置
+    fs: Arc<Mutex<EasyFileSystem>>,
+    block_device: Arc<dyn BlockDevice>,
+}
+
+impl Inode {
+    pub fn new(
+        inode_id: u32,
+        fs: Arc<Mutex<EasyFileSystem>>,
+        block_device: Arc<dyn BlockDevice>,
+    ) -> Self {
+        let (block_id, block_offset) = fs.lock().get_disk_inode_pos(inode_id);
+        Self {
+            inode_id,
+            block_id: block_id as usize,
+            block_offset,
+            fs,
+            block_device,
+        }
+    }
+
+    pub fn get_id(&self) -> u32{
+        self.inode_id
+    }
+
+    pub fn get_fs(&self) -> Arc<Mutex<EasyFileSystem>>{
+        self.fs.clone()
+    }
+
+    fn read_disk_inode<V>(&self, f: impl FnOnce(&DiskInode) -> V) -> V {
+        get_block_cache(
+            self.block_id,
+            Arc::clone(&self.block_device)
+        ).lock().read(self.block_offset, f)
+    }
+
+    fn modify_disk_inode<V>(&self, f: impl FnOnce(&mut DiskInode) -> V) -> V {
+        get_block_cache(
+            self.block_id,
+            Arc::clone(&self.block_device)
+        ).lock().modify(self.block_offset, f)
+    }
+
+    fn find_inode_id(
+        &self,
+        name: &str,
+        disk_inode: &DiskInode,
+    ) -> Option<(u32,u32)> {
+        // 返回inode_id和文件目录项在目录的偏移量
+        assert!(disk_inode.is_dir());
+        let file_count = (disk_inode.size as usize) / DIRENT_SZ;
+        let mut dirent = DirEntry::empty();
+        for i in 0..file_count {
+            assert_eq!(
+                disk_inode.read_at(
+                    DIRENT_SZ * i,
+                    dirent.as_bytes_mut(),
+                    &self.block_device,
+                ),
+                DIRENT_SZ,
+            );
+            if dirent.name() == name {
+                return Some((dirent.inode_number() as u32, i as u32));
+            }
+        }
+        None
+    }
+
+    fn find(&self, name: &str) -> Option<(Arc<Inode>,u32)> {
+        // 搜索当前目录下的文件/目录
+        let _ = self.fs.lock();
+        self.read_disk_inode(|disk_inode| {
+            // find_inode_id会检查是否是目录
+            self.find_inode_id(name, disk_inode)// 获取Inode编号
+            .map(|(inode_id,offset)| { //转换为Inode
+                (   Arc::new(Self::new(
+                        inode_id,
+                        self.fs.clone(),
+                        self.block_device.clone(),
+                    )),
+                    offset
+                )
+            })
+        })
+    }
+
+    pub fn find_path(&self, path: Vec<&str>) -> Option<(Arc<Inode>, u32)> {
+        // 返回inode和文件的dirent在目录中的偏移
+        // 根据路径搜索文件/目录
+        // 调用find逐级搜索
+        // shell应当保证path有内容!!!这里不对len进行检查以提高效率。
+        let len = path.len();
+        let mut curr_inode:Arc<Inode> = Arc::new(self.clone());
+        let mut curr_offset:u32 = 0;
+        for i in 0 .. len {
+            if let Some((inode,offset)) = curr_inode.find(path[i]){
+                curr_inode = inode;
+                curr_offset = offset;
+            }else{
+                return None;
+            }
+        }
+        Some((curr_inode.clone(), curr_offset))
+    }
+
+    /* 
+    pub fn ch_dir(&self, path: Vec<&str>)->Option<Vec<Arc<Inode>>>{
+        // 切换工作目录时调用,返回路径目录的所有Inode
+        // QUES: 修改策略后,似乎没用了...
+        let mut curr_inode:Arc<Inode> = Arc::new(self.clone());
+        let mut inode_vec = Vec::new();
+        let len = path.len();
+        if len == 0 {
+            return None;
+        }
+        for i in 0 .. len {
+            if let Some(inode) = curr_inode.find(path[i]){
+                inode_vec.push(inode.clone());
+                curr_inode = inode;
+            }else{
+                return None;
+            }
+        }
+        Some(inode_vec)
+    }
+    */
+
+    fn remove_file(&self)->bool{
+        // 删除当前Inode对应的文件
+        // 清空文件内容
+        self.clear();
+        // 释放inode
+        let mut fs = self.fs.lock();
+        fs.dealloc_inode(self.inode_id);
+        true
+    }
+
+    fn remove_dir(&self)->bool{
+        // 递归删除当前Inode对应的目录
+        // DEBUG
+        self.read_disk_inode(|disk_inode| {
+            assert!(disk_inode.is_dir());
+            let file_count = (disk_inode.size as usize) / DIRENT_SZ;
+            let mut dirent = DirEntry::empty();
+            // 删除目录下的每一项
+            for i in 0..file_count {
+                assert_eq!(
+                    disk_inode.read_at(
+                        DIRENT_SZ * i,
+                        dirent.as_bytes_mut(),
+                        &self.block_device,
+                    ),
+                    DIRENT_SZ,
+                );
+                let temp_inode = Inode::new(
+                    dirent.inode_number(),
+                    self.fs.clone(), 
+                    self.block_device.clone()
+                );
+                let type_:DiskInodeType =  temp_inode.read_disk_inode(
+                    |disk_inode|{
+                        disk_inode.type_
+                    }
+                );
+                let result:bool;
+                // 根据类别删除
+                if type_ == DiskInodeType::File{
+                    result = temp_inode.remove_file();
+                } else if type_ == DiskInodeType::Directory {
+                    result = temp_inode.remove_dir();
+                } else {
+                    return false;
+                }
+                if result == false{
+                    return false;
+                }
+            }
+            // 清除所有目录项
+            self.clear();
+            // 释放inode
+            let mut fs = self.fs.lock();
+            fs.dealloc_inode(self.inode_id);
+            true
+        })
+    }
+
+    pub fn remove(&self, mut path: Vec<&str>, type_: DiskInodeType)->bool{
+        // 删除文件/目录
+        let result:bool;
+        let name = path.pop().unwrap();
+        // 获取上级目录
+        if let Some((par_inode, offset)) = self.find_path(path){
+            // 搜索目标文件/目录
+            if let Some((tar_inode,_)) = par_inode.find(name){
+                if type_ == DiskInodeType::File{
+                    result = tar_inode.remove_file();
+                } else if type_ == DiskInodeType::Directory{
+                    result = tar_inode.remove_dir();
+                } else {
+                    return false;
+                }
+                if result == false {
+                    return false;
+                }
+            } else{
+                return false;
+            }
+            // DEBUG: 修改目录项,调整目录大小
+            // 对fs上锁,避免修改途中有进程对其操作
+            let mut fs = self.fs.lock();
+            // 修改目录项,调整目录大小
+            self.modify_disk_inode(|curr_inode| {
+                // append file in the dirent
+                let file_count = (curr_inode.size as usize) / DIRENT_SZ;
+                let new_size = (file_count - 1) * DIRENT_SZ;
+                // 移动目录项
+                let mut dirent = DirEntry::empty();
+                // 读出最后一个目录项
+                curr_inode.read_at(
+                    new_size,
+                    dirent.as_bytes_mut(),
+                    &self.block_device,
+                );
+                // 写入被删除的位置
+                curr_inode.write_at(
+                    offset as usize,
+                    dirent.as_bytes(),
+                    &self.block_device,
+                );
+                self.decrease_size(new_size as u32, curr_inode, &mut fs);
+            });
+            return result;
+        }else{
+            false
+        }
+    }
+
+    
+    fn decrease_size(
+        &self,
+        new_size: u32,
+        disk_inode: &mut DiskInode,
+        fs: &mut MutexGuard<EasyFileSystem>,
+    ){
+        // TODO: 删除文件后检测是否需要回收块
+        // TODO: 修改文件后也要检测是否需要回收块
+        // 因为无气泡压缩删除的设计,目录的大小很好判断
+        if new_size > disk_inode.size {
+            return;
+        }
+        // disk_inode释放不需要的块
+        if let Some(blk_vec) = disk_inode.decrease_size(new_size, &self.block_device){
+            // 控制文件系统回收块
+            for block_id in blk_vec {
+                fs.dealloc_data(block_id);
+            }
+        }        
+    }
+
+    fn increase_size(
+        &self,
+        new_size: u32,
+        disk_inode: &mut DiskInode,
+        fs: &mut MutexGuard<EasyFileSystem>,
+    ) {
+        if new_size < disk_inode.size {
+            //println!("new_size < disk_inode.size");
+            return;
+        }
+        let blocks_needed = disk_inode.blocks_num_needed(new_size);
+        let mut v: Vec<u32> = Vec::new();
+        for _ in 0..blocks_needed {
+            v.push(fs.alloc_data());
+        }
+        disk_inode.increase_size(new_size, v, &self.block_device);
+    }
+
+    pub fn create(&self, name: &str , type_: DiskInodeType) -> Option<Arc<Inode>> {
+        let mut fs = self.fs.lock();
+        if self.modify_disk_inode(|curr_inode| {
+            // assert it is a directory
+            assert!(curr_inode.is_dir());
+            // has the file been created?
+            // 文件和目录也不能同名!
+            self.find_inode_id(name, curr_inode)
+        }).is_some() {
+            return None;
+        }
+        // create a new file
+        // alloc a inode with an indirect block
+        let new_inode_id = fs.alloc_inode();
+        // initialize inode
+        let (new_inode_block_id, new_inode_block_offset) 
+            = fs.get_disk_inode_pos(new_inode_id);
+        get_block_cache(
+            new_inode_block_id as usize,
+            Arc::clone(&self.block_device)
+        ).lock().modify(new_inode_block_offset, |new_dskinode: &mut DiskInode| {
+            new_dskinode.initialize(type_.clone());
+            // TODO: 为新目录分配一个块并简历.和..两个目录项
+            // DEBUG
+            if type_ == DiskInodeType::Directory {
+                let new_size = 2 * DIRENT_SZ;
+                //println!("0.0._");
+                // get_inode/new会获取锁
+                // let new_inode = EasyFileSystem::get_inode(&self.fs.clone(), new_inode_id);
+                let (block_id, block_offset) = fs.get_disk_inode_pos(new_inode_id);
+                let new_inode = Self{
+                    inode_id: new_inode_id,
+                    block_id: block_id as usize,
+                    block_offset,
+                    fs: self.fs.clone(),
+                    block_device: self.block_device.clone(),
+                };
+                // increase_size自身不会获取锁,因此不会死锁
+                //println!("0.0.0");
+                new_inode.increase_size(new_size as u32, new_dskinode, &mut fs);
+                let dirent_self = DirEntry::new(".", DiskInodeType::Directory,new_inode_id);
+                let dirent_parent = DirEntry::new("..", DiskInodeType::Directory,self.get_id());
+                //println!("0.0.1");
+                new_dskinode.write_at(
+                    0, 
+                    dirent_self.as_bytes(), 
+                    &self.block_device
+                );
+                //println!("0.0.2");
+                new_dskinode.write_at(
+                    DIRENT_SZ, 
+                    dirent_parent.as_bytes(), 
+                    &self.block_device
+                );
+            }
+        });
+
+        // 对当前目录进行修改
+        self.modify_disk_inode(|curr_inode| {
+            // append file in the dirent
+            let file_count = (curr_inode.size as usize) / DIRENT_SZ;
+            let new_size = (file_count + 1) * DIRENT_SZ;
+            // increase size
+            self.increase_size(new_size as u32, curr_inode, &mut fs);
+            // write dirent
+            let dirent = DirEntry::new(name, type_,new_inode_id);
+            curr_inode.write_at(
+                file_count * DIRENT_SZ,
+                dirent.as_bytes(),
+                &self.block_device,
+            );
+        });
+
+        // release efs lock manually because we will acquire it again in Inode::new
+        drop(fs);
+        // return inode
+        Some(Arc::new(Self::new(
+            new_inode_id,
+            self.fs.clone(),
+            self.block_device.clone(),
+        )))
+    }
+
+    pub fn ls(&self) -> Vec<(String, DiskInodeType)> {
+        let _ = self.fs.lock();
+        self.read_disk_inode(|disk_inode| {
+            let file_count = (disk_inode.size as usize) / DIRENT_SZ;
+            let mut v: Vec<(String, DiskInodeType)> = Vec::new();
+            //let mut tv: Vec<DiskInodeType> = Vec::new();
+            for i in 0..file_count {
+                let mut dirent = DirEntry::empty();
+                assert_eq!(
+                    disk_inode.read_at(
+                        i * DIRENT_SZ,
+                        dirent.as_bytes_mut(),
+                        &self.block_device,
+                    ),
+                    DIRENT_SZ,
+                );
+                // TODO: 获取类型
+                v.push( (String::from(dirent.name()), dirent.type_()) );
+            }
+            v.sort_by(|a, b| a.0.cmp(&b.0));
+            v
+        })
+    }
+
+    pub fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize {
+        let _ = self.fs.lock();
+        self.read_disk_inode(|disk_inode| {
+            disk_inode.read_at(offset, buf, &self.block_device)
+        })
+    }
+
+    pub fn write_at(&self, offset: usize, buf: &[u8]) -> usize {
+        let mut fs = self.fs.lock();
+        self.modify_disk_inode(|disk_inode| {
+            //println!("buflen = {}",buf.len());
+            self.increase_size((offset + buf.len()) as u32, disk_inode, &mut fs);
+            disk_inode.write_at(offset, buf, &self.block_device)
+        })
+    }
+
+    pub fn clear(&self) {
+        let mut fs = self.fs.lock();
+        self.modify_disk_inode(|disk_inode| {
+            let size = disk_inode.size;
+            let data_blocks_dealloc = disk_inode.clear_size(&self.block_device);
+            assert!(data_blocks_dealloc.len() == DiskInode::total_blocks(size) as usize);
+            for data_block in data_blocks_dealloc.into_iter() {
+                fs.dealloc_data(data_block);
+            }
+        });
+    }
+}
diff --git a/codes/os/.cargo/config b/codes/os/.cargo/config
new file mode 100644
index 00000000..4275fcad
--- /dev/null
+++ b/codes/os/.cargo/config
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64gc-unknown-none-elf"
+
+[target.riscv64gc-unknown-none-elf]
+rustflags = [
+    "-Clink-arg=-Tsrc/linker.ld", "-Cforce-frame-pointers=yes"
+]
diff --git a/codes/os/Cargo.toml b/codes/os/Cargo.toml
new file mode 100644
index 00000000..bded6409
--- /dev/null
+++ b/codes/os/Cargo.toml
@@ -0,0 +1,24 @@
+[package]
+name = "os"
+version = "0.1.0"
+authors = ["Yifan Wu <shinbokuow@163.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
+lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
+buddy_system_allocator = "0.6"
+spin = "0.7.0"
+bitflags = "1.2.1"
+xmas-elf = "0.7.0"
+virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers" }
+k210-pac = { git = "https://github.com/wyfcyx/k210-pac" }
+k210-hal = { git = "https://github.com/wyfcyx/k210-hal" }
+k210-soc = { git = "https://github.com/wyfcyx/k210-soc" }
+easy-fs = { path = "../easy-fs" }
+
+[features]
+board_qemu = []
+board_k210 = []
\ No newline at end of file
diff --git a/codes/os/Makefile b/codes/os/Makefile
new file mode 100644
index 00000000..96b542f4
--- /dev/null
+++ b/codes/os/Makefile
@@ -0,0 +1,104 @@
+# Building
+TARGET := riscv64gc-unknown-none-elf
+MODE := release
+KERNEL_ELF := target/$(TARGET)/$(MODE)/os
+KERNEL_BIN := $(KERNEL_ELF).bin
+DISASM_TMP := target/$(TARGET)/$(MODE)/asm
+FS_IMG := ../user/target/$(TARGET)/$(MODE)/fs.img
+SDCARD := /dev/sdb
+APPS := ../user/src/bin/*
+
+# BOARD
+BOARD ?= qemu
+SBI ?= rustsbi
+BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin
+K210_BOOTLOADER_SIZE := 131072
+
+# KERNEL ENTRY
+ifeq ($(BOARD), qemu)
+	KERNEL_ENTRY_PA := 0x80200000
+else ifeq ($(BOARD), k210)
+	KERNEL_ENTRY_PA := 0x80020000
+endif
+
+# Run K210
+K210-SERIALPORT	= /dev/ttyUSB0
+K210-BURNER	= ../tools/kflash.py/kflash.py
+
+# Binutils
+OBJDUMP := rust-objdump --arch-name=riscv64
+OBJCOPY := rust-objcopy --binary-architecture=riscv64
+
+# Disassembly
+DISASM ?= -x
+
+build: env $(KERNEL_BIN) $(FS_IMG)
+
+env:
+	(rustup target list | grep "riscv64gc-unknown-none-elf (installed)") || rustup target add $(TARGET)
+	cargo install cargo-binutils
+	rustup component add rust-src
+	rustup component add llvm-tools-preview
+
+# dev/zero永远输出0
+sdcard: $(FS_IMG)
+	@echo "Are you sure write to $(SDCARD) ? [y/N] " && read ans && [ $${ans:-N} = y ]
+	@sudo dd if=/dev/zero of=$(SDCARD) bs=1048576 count=16
+	@sudo dd if=$(FS_IMG) of=$(SDCARD)
+
+$(KERNEL_BIN): kernel
+	@$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@
+
+$(FS_IMG): $(APPS)
+	@cd ../user && make build
+	@cd ../easy-fs-fuse && cargo run --release -- -s ../user/src/bin/ -t ../user/target/riscv64gc-unknown-none-elf/release/
+
+$(APPS):
+
+kernel:
+	@echo Platform: $(BOARD)
+	@cp src/linker-$(BOARD).ld src/linker.ld
+	@cargo build --release --features "board_$(BOARD)"
+	@rm src/linker.ld
+
+clean:
+	@cargo clean
+
+disasm: kernel
+	@$(OBJDUMP) $(DISASM) $(KERNEL_ELF) | less
+
+disasm-vim: kernel
+	@$(OBJDUMP) $(DISASM) $(KERNEL_ELF) > $(DISASM_TMP)
+	@vim $(DISASM_TMP)
+	@rm $(DISASM_TMP)
+
+run: run-inner
+
+	
+
+run-inner: build
+ifeq ($(BOARD),qemu)
+	@qemu-system-riscv64 \
+		-machine virt \
+		-nographic \
+		-bios $(BOOTLOADER) \
+		-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) \
+		-drive file=$(FS_IMG),if=none,format=raw,id=x0 \
+        -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
+else
+	(which $(K210-BURNER)) || (cd .. && git clone https://github.com/sipeed/kflash.py.git && mv kflash.py tools)
+	@cp $(BOOTLOADER) $(BOOTLOADER).copy
+	@dd if=$(KERNEL_BIN) of=$(BOOTLOADER).copy bs=$(K210_BOOTLOADER_SIZE) seek=1
+	@mv $(BOOTLOADER).copy $(KERNEL_BIN)
+	@sudo chmod 777 $(K210-SERIALPORT)
+	python3 $(K210-BURNER) -p $(K210-SERIALPORT) -b 1500000 $(KERNEL_BIN)
+	python3 -m serial.tools.miniterm --eol LF --dtr 0 --rts 0 --filter direct $(K210-SERIALPORT) 115200
+endif
+
+debug: build
+	@tmux new-session -d \
+		"qemu-system-riscv64 -machine virt -nographic -bios $(BOOTLOADER) -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) -s -S" && \
+		tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \
+		tmux -2 attach-session -d
+
+.PHONY: build env kernel clean disasm disasm-vim run-inner
diff --git a/codes/os/build.rs b/codes/os/build.rs
new file mode 100644
index 00000000..5529b4fe
--- /dev/null
+++ b/codes/os/build.rs
@@ -0,0 +1,6 @@
+static TARGET_PATH: &str = "../user/target/riscv64gc-unknown-none-elf/release/";
+
+fn main() {
+    println!("cargo:rerun-if-changed=../user/src/");
+    println!("cargo:rerun-if-changed={}", TARGET_PATH);
+}
diff --git a/codes/os/src/config.rs b/codes/os/src/config.rs
new file mode 100644
index 00000000..dce95ec4
--- /dev/null
+++ b/codes/os/src/config.rs
@@ -0,0 +1,42 @@
+#[allow(unused)]
+
+pub const USER_STACK_SIZE: usize = 4096 * 2;
+pub const KERNEL_STACK_SIZE: usize = 4096 * 2;
+pub const KERNEL_HEAP_SIZE: usize = 0x20_0000;
+pub const MEMORY_END: usize = 0x80800000;
+pub const PAGE_SIZE: usize = 0x1000;
+pub const PAGE_SIZE_BITS: usize = 0xc;
+
+pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1;
+pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE;
+
+#[cfg(feature = "board_k210")]
+pub const CLOCK_FREQ: usize = 403000000 / 62;
+
+#[cfg(feature = "board_qemu")]
+pub const CLOCK_FREQ: usize = 12500000;
+
+#[cfg(feature = "board_qemu")]
+pub const MMIO: &[(usize, usize)] = &[
+    (0x10001000, 0x1000),
+];
+
+#[cfg(feature = "board_k210")]
+pub const MMIO: &[(usize, usize)] = &[
+    // we don't need clint in S priv when running
+    // we only need claim/complete for target0 after initializing
+    (0x0C00_0000, 0x3000),      /* PLIC      */
+    (0x0C20_0000, 0x1000),      /* PLIC      */
+    (0x3800_0000, 0x1000),      /* UARTHS    */
+    (0x3800_1000, 0x1000),      /* GPIOHS    */
+    (0x5020_0000, 0x1000),      /* GPIO      */
+    (0x5024_0000, 0x1000),      /* SPI_SLAVE */
+    (0x502B_0000, 0x1000),      /* FPIOA     */
+    (0x502D_0000, 0x1000),      /* TIMER0    */
+    (0x502E_0000, 0x1000),      /* TIMER1    */
+    (0x502F_0000, 0x1000),      /* TIMER2    */
+    (0x5044_0000, 0x1000),      /* SYSCTL    */
+    (0x5200_0000, 0x1000),      /* SPI0      */
+    (0x5300_0000, 0x1000),      /* SPI1      */
+    (0x5400_0000, 0x1000),      /* SPI2      */
+];
\ No newline at end of file
diff --git a/codes/os/src/console.rs b/codes/os/src/console.rs
new file mode 100644
index 00000000..2bd55930
--- /dev/null
+++ b/codes/os/src/console.rs
@@ -0,0 +1,33 @@
+use core::fmt::{self, Write};
+use crate::sbi::console_putchar;
+
+struct Stdout;
+
+impl Write for Stdout {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        for c in s.chars() {
+            console_putchar(c as usize);
+        }
+        Ok(())
+    }
+}
+
+pub fn print(args: fmt::Arguments) {
+    Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+    }
+}
+
+#[macro_export]
+macro_rules! println {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+    }
+}
+
+
diff --git a/codes/os/src/drivers/block/mod.rs b/codes/os/src/drivers/block/mod.rs
new file mode 100644
index 00000000..f79b8b81
--- /dev/null
+++ b/codes/os/src/drivers/block/mod.rs
@@ -0,0 +1,30 @@
+mod virtio_blk;
+mod sdcard;
+
+use lazy_static::*;
+use alloc::sync::Arc;
+use easy_fs::BlockDevice;
+
+#[cfg(feature = "board_qemu")]
+type BlockDeviceImpl = virtio_blk::VirtIOBlock;
+
+#[cfg(feature = "board_k210")]
+type BlockDeviceImpl = sdcard::SDCardWrapper;
+
+lazy_static! {
+    pub static ref BLOCK_DEVICE: Arc<dyn BlockDevice> = Arc::new(BlockDeviceImpl::new());
+}
+
+#[allow(unused)]
+pub fn block_device_test() {
+    let block_device = BLOCK_DEVICE.clone();
+    let mut write_buffer = [0u8; 512];
+    let mut read_buffer = [0u8; 512];
+    for i in 0..512 {
+        for byte in write_buffer.iter_mut() { *byte = i as u8; }
+        block_device.write_block(i as usize, &write_buffer);
+        block_device.read_block(i as usize, &mut read_buffer);
+        assert_eq!(write_buffer, read_buffer);
+    }
+    println!("block device test passed!");
+}
\ No newline at end of file
diff --git a/codes/os/src/drivers/block/sdcard.rs b/codes/os/src/drivers/block/sdcard.rs
new file mode 100644
index 00000000..17ef1631
--- /dev/null
+++ b/codes/os/src/drivers/block/sdcard.rs
@@ -0,0 +1,755 @@
+#![allow(non_snake_case)]
+#![allow(non_camel_case_types)]
+#![allow(unused)]
+
+use k210_pac::{Peripherals, SPI0};
+use k210_hal::prelude::*;
+use k210_soc::{
+    //dmac::{dma_channel, DMAC, DMACExt},
+    gpio,
+    gpiohs,
+    spi::{aitm, frame_format, tmod, work_mode, SPI, SPIExt, SPIImpl},
+    fpioa::{self, io},
+    sysctl,
+    sleep::usleep,
+};
+use spin::Mutex;
+use lazy_static::*;
+use super::BlockDevice;
+use core::convert::TryInto;
+
+pub struct SDCard<SPI> {
+    spi: SPI,
+    spi_cs: u32,
+    cs_gpionum: u8,
+    //dmac: &'a DMAC,
+    //channel: dma_channel,
+}
+
+/*
+ * Start Data tokens:
+ *         Tokens (necessary because at nop/idle (and CS active) only 0xff is
+ *         on the data/command line)
+ */
+/** Data token start byte, Start Single Block Read */
+pub const SD_START_DATA_SINGLE_BLOCK_READ: u8 = 0xFE;
+/** Data token start byte, Start Multiple Block Read */
+pub const SD_START_DATA_MULTIPLE_BLOCK_READ: u8 = 0xFE;
+/** Data token start byte, Start Single Block Write */
+pub const SD_START_DATA_SINGLE_BLOCK_WRITE: u8 = 0xFE;
+/** Data token start byte, Start Multiple Block Write */
+pub const SD_START_DATA_MULTIPLE_BLOCK_WRITE: u8 = 0xFC;
+
+pub const SEC_LEN: usize = 512;
+
+/** SD commands */
+#[repr(u8)]
+#[derive(Debug, PartialEq, Eq, Copy, Clone)]
+#[allow(unused)]
+pub enum CMD {
+    /** Software reset */
+    CMD0 = 0,
+    /** Check voltage range (SDC V2) */
+    CMD8 = 8,
+    /** Read CSD register */
+    CMD9 = 9,
+    /** Read CID register */
+    CMD10 = 10,
+    /** Stop to read data */
+    CMD12 = 12,
+    /** Change R/W block size */
+    CMD16 = 16,
+    /** Read block */
+    CMD17 = 17,
+    /** Read multiple blocks */
+    CMD18 = 18,
+    /** Number of blocks to erase (SDC) */
+    ACMD23 = 23,
+    /** Write a block */
+    CMD24 = 24,
+    /** Write multiple blocks */
+    CMD25 = 25,
+    /** Initiate initialization process (SDC) */
+    ACMD41 = 41,
+    /** Leading command for ACMD* */
+    CMD55 = 55,
+    /** Read OCR */
+    CMD58 = 58,
+    /** Enable/disable CRC check */
+    CMD59 = 59,
+}
+
+#[allow(unused)]
+#[derive(Debug, Copy, Clone)]
+pub enum InitError {
+    CMDFailed(CMD, u8),
+    CardCapacityStatusNotSet([u8; 4]),
+    CannotGetCardInfo,
+}
+
+/**
+ * Card Specific Data: CSD Register
+ */
+#[derive(Debug, Copy, Clone)]
+pub struct SDCardCSD {
+    pub CSDStruct: u8,        /* CSD structure */
+    pub SysSpecVersion: u8,   /* System specification version */
+    pub Reserved1: u8,        /* Reserved */
+    pub TAAC: u8,             /* Data read access-time 1 */
+    pub NSAC: u8,             /* Data read access-time 2 in CLK cycles */
+    pub MaxBusClkFrec: u8,    /* Max. bus clock frequency */
+    pub CardComdClasses: u16, /* Card command classes */
+    pub RdBlockLen: u8,       /* Max. read data block length */
+    pub PartBlockRead: u8,    /* Partial blocks for read allowed */
+    pub WrBlockMisalign: u8,  /* Write block misalignment */
+    pub RdBlockMisalign: u8,  /* Read block misalignment */
+    pub DSRImpl: u8,          /* DSR implemented */
+    pub Reserved2: u8,        /* Reserved */
+    pub DeviceSize: u32,      /* Device Size */
+    //MaxRdCurrentVDDMin: u8,   /* Max. read current @ VDD min */
+    //MaxRdCurrentVDDMax: u8,   /* Max. read current @ VDD max */
+    //MaxWrCurrentVDDMin: u8,   /* Max. write current @ VDD min */
+    //MaxWrCurrentVDDMax: u8,   /* Max. write current @ VDD max */
+    //DeviceSizeMul: u8,        /* Device size multiplier */
+    pub EraseGrSize: u8,         /* Erase group size */
+    pub EraseGrMul: u8,          /* Erase group size multiplier */
+    pub WrProtectGrSize: u8,     /* Write protect group size */
+    pub WrProtectGrEnable: u8,   /* Write protect group enable */
+    pub ManDeflECC: u8,          /* Manufacturer default ECC */
+    pub WrSpeedFact: u8,         /* Write speed factor */
+    pub MaxWrBlockLen: u8,       /* Max. write data block length */
+    pub WriteBlockPaPartial: u8, /* Partial blocks for write allowed */
+    pub Reserved3: u8,           /* Reserded */
+    pub ContentProtectAppli: u8, /* Content protection application */
+    pub FileFormatGroup: u8,     /* File format group */
+    pub CopyFlag: u8,            /* Copy flag (OTP) */
+    pub PermWrProtect: u8,       /* Permanent write protection */
+    pub TempWrProtect: u8,       /* Temporary write protection */
+    pub FileFormat: u8,          /* File Format */
+    pub ECC: u8,                 /* ECC code */
+    pub CSD_CRC: u8,             /* CSD CRC */
+    pub Reserved4: u8,           /* always 1*/
+}
+
+/**
+ * Card Identification Data: CID Register
+ */
+#[derive(Debug, Copy, Clone)]
+pub struct SDCardCID {
+    pub ManufacturerID: u8, /* ManufacturerID */
+    pub OEM_AppliID: u16,   /* OEM/Application ID */
+    pub ProdName1: u32,     /* Product Name part1 */
+    pub ProdName2: u8,      /* Product Name part2*/
+    pub ProdRev: u8,        /* Product Revision */
+    pub ProdSN: u32,        /* Product Serial Number */
+    pub Reserved1: u8,      /* Reserved1 */
+    pub ManufactDate: u16,  /* Manufacturing Date */
+    pub CID_CRC: u8,        /* CID CRC */
+    pub Reserved2: u8,      /* always 1 */
+}
+
+/**
+ * Card information
+ */
+#[derive(Debug, Copy, Clone)]
+pub struct SDCardInfo {
+    pub SD_csd: SDCardCSD,
+    pub SD_cid: SDCardCID,
+    pub CardCapacity: u64,  /* Card Capacity */
+    pub CardBlockSize: u64, /* Card Block Size */
+}
+
+impl</*'a,*/ X: SPI> SDCard</*'a,*/ X> {
+    pub fn new(spi: X, spi_cs: u32, cs_gpionum: u8/*, dmac: &'a DMAC, channel: dma_channel*/) -> Self {
+        Self {
+            spi,
+            spi_cs,
+            cs_gpionum,
+            /*
+            dmac,
+            channel,
+             */
+        }
+    }
+
+    fn CS_HIGH(&self) {
+        gpiohs::set_pin(self.cs_gpionum, true);
+    }
+
+    fn CS_LOW(&self) {
+        gpiohs::set_pin(self.cs_gpionum, false);
+    }
+
+    fn HIGH_SPEED_ENABLE(&self) {
+        self.spi.set_clk_rate(10000000);
+    }
+
+    fn lowlevel_init(&self) {
+        gpiohs::set_direction(self.cs_gpionum, gpio::direction::OUTPUT);
+        self.spi.set_clk_rate(200000);
+    }
+
+    fn write_data(&self, data: &[u8]) {
+        self.spi.configure(
+            work_mode::MODE0,
+            frame_format::STANDARD,
+            8, /* data bits */
+            0, /* endian */
+            0, /*instruction length*/
+            0, /*address length*/
+            0, /*wait cycles*/
+            aitm::STANDARD,
+            tmod::TRANS,
+        );
+        self.spi.send_data(self.spi_cs, data);
+    }
+
+    /*
+    fn write_data_dma(&self, data: &[u32]) {
+        self.spi.configure(
+            work_mode::MODE0,
+            frame_format::STANDARD,
+            8, /* data bits */
+            0, /* endian */
+            0, /*instruction length*/
+            0, /*address length*/
+            0, /*wait cycles*/
+            aitm::STANDARD,
+            tmod::TRANS,
+        );
+        self.spi
+            .send_data_dma(self.dmac, self.channel, self.spi_cs, data);
+    }
+     */
+
+    fn read_data(&self, data: &mut [u8]) {
+        self.spi.configure(
+            work_mode::MODE0,
+            frame_format::STANDARD,
+            8, /* data bits */
+            0, /* endian */
+            0, /*instruction length*/
+            0, /*address length*/
+            0, /*wait cycles*/
+            aitm::STANDARD,
+            tmod::RECV,
+        );
+        self.spi.recv_data(self.spi_cs, data);
+    }
+
+    /*
+    fn read_data_dma(&self, data: &mut [u32]) {
+        self.spi.configure(
+            work_mode::MODE0,
+            frame_format::STANDARD,
+            8, /* data bits */
+            0, /* endian */
+            0, /*instruction length*/
+            0, /*address length*/
+            0, /*wait cycles*/
+            aitm::STANDARD,
+            tmod::RECV,
+        );
+        self.spi
+            .recv_data_dma(self.dmac, self.channel, self.spi_cs, data);
+    }
+     */
+
+    /*
+     * Send 5 bytes command to the SD card.
+     * @param  cmd: The user expected command to send to SD card.
+     * @param  arg: The command argument.
+     * @param  crc: The CRC.
+     * @retval None
+     */
+    fn send_cmd(&self, cmd: CMD, arg: u32, crc: u8) {
+        /* SD chip select low */
+        self.CS_LOW();
+        /* Send the Cmd bytes */
+        self.write_data(&[
+            /* Construct byte 1 */
+            ((cmd as u8) | 0x40),
+            /* Construct byte 2 */
+            (arg >> 24) as u8,
+            /* Construct byte 3 */
+            ((arg >> 16) & 0xff) as u8,
+            /* Construct byte 4 */
+            ((arg >> 8) & 0xff) as u8,
+            /* Construct byte 5 */
+            (arg & 0xff) as u8,
+            /* Construct CRC: byte 6 */
+            crc,
+        ]);
+    }
+
+    /* Send end-command sequence to SD card */
+    fn end_cmd(&self) {
+        /* SD chip select high */
+        self.CS_HIGH();
+        /* Send the cmd byte */
+        self.write_data(&[0xff]);
+    }
+
+    /*
+     * Returns the SD response.
+     * @param  None
+     * @retval The SD Response:
+     *         - 0xFF: Sequence failed
+     *         - 0: Sequence succeed
+     */
+    fn get_response(&self) -> u8 {
+        let result = &mut [0u8];
+        let mut timeout = 0x0FFF;
+        /* Check if response is got or a timeout is happen */
+        while timeout != 0 {
+            self.read_data(result);
+            /* Right response got */
+            if result[0] != 0xFF {
+                return result[0];
+            }
+            timeout -= 1;
+        }
+        /* After time out */
+        return 0xFF;
+    }
+
+    /*
+     * Get SD card data response.
+     * @param  None
+     * @retval The SD status: Read data response xxx0<status>1
+     *         - status 010: Data accecpted
+     *         - status 101: Data rejected due to a crc error
+     *         - status 110: Data rejected due to a Write error.
+     *         - status 111: Data rejected due to other error.
+     */
+    fn get_dataresponse(&self) -> u8 {
+        let response = &mut [0u8];
+        /* Read resonse */
+        self.read_data(response);
+        /* Mask unused bits */
+        response[0] &= 0x1F;
+        if response[0] != 0x05 {
+            return 0xFF;
+        }
+        /* Wait null data */
+        self.read_data(response);
+        while response[0] == 0 {
+            self.read_data(response);
+        }
+        /* Return response */
+        return 0;
+    }
+
+    /*
+     * Read the CSD card register
+     *         Reading the contents of the CSD register in SPI mode is a simple
+     *         read-block transaction.
+     * @param  SD_csd: pointer on an SCD register structure
+     * @retval The SD Response:
+     *         - `Err()`: Sequence failed
+     *         - `Ok(info)`: Sequence succeed
+     */
+    fn get_csdregister(&self) -> Result<SDCardCSD, ()> {
+        let mut csd_tab = [0u8; 18];
+        /* Send CMD9 (CSD register) */
+        self.send_cmd(CMD::CMD9, 0, 0);
+        /* Wait for response in the R1 format (0x00 is no errors) */
+        if self.get_response() != 0x00 {
+            self.end_cmd();
+            return Err(());
+        }
+        if self.get_response() != SD_START_DATA_SINGLE_BLOCK_READ {
+            self.end_cmd();
+            return Err(());
+        }
+        /* Store CSD register value on csd_tab */
+        /* Get CRC bytes (not really needed by us, but required by SD) */
+        self.read_data(&mut csd_tab);
+        self.end_cmd();
+        /* see also: https://cdn-shop.adafruit.com/datasheets/TS16GUSDHC6.pdf */
+        return Ok(SDCardCSD {
+            /* Byte 0 */
+            CSDStruct: (csd_tab[0] & 0xC0) >> 6,
+            SysSpecVersion: (csd_tab[0] & 0x3C) >> 2,
+            Reserved1: csd_tab[0] & 0x03,
+            /* Byte 1 */
+            TAAC: csd_tab[1],
+            /* Byte 2 */
+            NSAC: csd_tab[2],
+            /* Byte 3 */
+            MaxBusClkFrec: csd_tab[3],
+            /* Byte 4, 5 */
+            CardComdClasses: (u16::from(csd_tab[4]) << 4) | ((u16::from(csd_tab[5]) & 0xF0) >> 4),
+            /* Byte 5 */
+            RdBlockLen: csd_tab[5] & 0x0F,
+            /* Byte 6 */
+            PartBlockRead: (csd_tab[6] & 0x80) >> 7,
+            WrBlockMisalign: (csd_tab[6] & 0x40) >> 6,
+            RdBlockMisalign: (csd_tab[6] & 0x20) >> 5,
+            DSRImpl: (csd_tab[6] & 0x10) >> 4,
+            Reserved2: 0,
+            // DeviceSize: (csd_tab[6] & 0x03) << 10,
+            /* Byte 7, 8, 9 */
+            DeviceSize: ((u32::from(csd_tab[7]) & 0x3F) << 16)
+                | (u32::from(csd_tab[8]) << 8)
+                | u32::from(csd_tab[9]),
+            /* Byte 10 */
+            EraseGrSize: (csd_tab[10] & 0x40) >> 6,
+            /* Byte 10, 11 */
+            EraseGrMul: ((csd_tab[10] & 0x3F) << 1) | ((csd_tab[11] & 0x80) >> 7),
+            /* Byte 11 */
+            WrProtectGrSize: (csd_tab[11] & 0x7F),
+            /* Byte 12 */
+            WrProtectGrEnable: (csd_tab[12] & 0x80) >> 7,
+            ManDeflECC: (csd_tab[12] & 0x60) >> 5,
+            WrSpeedFact: (csd_tab[12] & 0x1C) >> 2,
+            /* Byte 12,13 */
+            MaxWrBlockLen: ((csd_tab[12] & 0x03) << 2) | ((csd_tab[13] & 0xC0) >> 6),
+            /* Byte 13 */
+            WriteBlockPaPartial: (csd_tab[13] & 0x20) >> 5,
+            Reserved3: 0,
+            ContentProtectAppli: (csd_tab[13] & 0x01),
+            /* Byte 14 */
+            FileFormatGroup: (csd_tab[14] & 0x80) >> 7,
+            CopyFlag: (csd_tab[14] & 0x40) >> 6,
+            PermWrProtect: (csd_tab[14] & 0x20) >> 5,
+            TempWrProtect: (csd_tab[14] & 0x10) >> 4,
+            FileFormat: (csd_tab[14] & 0x0C) >> 2,
+            ECC: (csd_tab[14] & 0x03),
+            /* Byte 15 */
+            CSD_CRC: (csd_tab[15] & 0xFE) >> 1,
+            Reserved4: 1,
+            /* Return the reponse */
+        });
+    }
+
+    /*
+     * Read the CID card register.
+     *         Reading the contents of the CID register in SPI mode is a simple
+     *         read-block transaction.
+     * @param  SD_cid: pointer on an CID register structure
+     * @retval The SD Response:
+     *         - `Err()`: Sequence failed
+     *         - `Ok(info)`: Sequence succeed
+     */
+    fn get_cidregister(&self) -> Result<SDCardCID, ()> {
+        let mut cid_tab = [0u8; 18];
+        /* Send CMD10 (CID register) */
+        self.send_cmd(CMD::CMD10, 0, 0);
+        /* Wait for response in the R1 format (0x00 is no errors) */
+        if self.get_response() != 0x00 {
+            self.end_cmd();
+            return Err(());
+        }
+        if self.get_response() != SD_START_DATA_SINGLE_BLOCK_READ {
+            self.end_cmd();
+            return Err(());
+        }
+        /* Store CID register value on cid_tab */
+        /* Get CRC bytes (not really needed by us, but required by SD) */
+        self.read_data(&mut cid_tab);
+        self.end_cmd();
+        return Ok(SDCardCID {
+            /* Byte 0 */
+            ManufacturerID: cid_tab[0],
+            /* Byte 1, 2 */
+            OEM_AppliID: (u16::from(cid_tab[1]) << 8) | u16::from(cid_tab[2]),
+            /* Byte 3, 4, 5, 6 */
+            ProdName1: (u32::from(cid_tab[3]) << 24)
+                | (u32::from(cid_tab[4]) << 16)
+                | (u32::from(cid_tab[5]) << 8)
+                | u32::from(cid_tab[6]),
+            /* Byte 7 */
+            ProdName2: cid_tab[7],
+            /* Byte 8 */
+            ProdRev: cid_tab[8],
+            /* Byte 9, 10, 11, 12 */
+            ProdSN: (u32::from(cid_tab[9]) << 24)
+                | (u32::from(cid_tab[10]) << 16)
+                | (u32::from(cid_tab[11]) << 8)
+                | u32::from(cid_tab[12]),
+            /* Byte 13, 14 */
+            Reserved1: (cid_tab[13] & 0xF0) >> 4,
+            ManufactDate: ((u16::from(cid_tab[13]) & 0x0F) << 8) | u16::from(cid_tab[14]),
+            /* Byte 15 */
+            CID_CRC: (cid_tab[15] & 0xFE) >> 1,
+            Reserved2: 1,
+        });
+    }
+
+    /*
+     * Returns information about specific card.
+     * @param  cardinfo: pointer to a SD_CardInfo structure that contains all SD
+     *         card information.
+     * @retval The SD Response:
+     *         - `Err(())`: Sequence failed
+     *         - `Ok(info)`: Sequence succeed
+     */
+    fn get_cardinfo(&self) -> Result<SDCardInfo, ()> {
+        let mut info = SDCardInfo {
+            SD_csd: self.get_csdregister()?,
+            SD_cid: self.get_cidregister()?,
+            CardCapacity: 0,
+            CardBlockSize: 0,
+        };
+        info.CardBlockSize = 1 << u64::from(info.SD_csd.RdBlockLen);
+        info.CardCapacity = (u64::from(info.SD_csd.DeviceSize) + 1) * 1024 * info.CardBlockSize;
+
+        Ok(info)
+    }
+
+    /*
+     * Initializes the SD/SD communication in SPI mode.
+     * @param  None
+     * @retval The SD Response info if succeeeded, otherwise Err
+     */
+    pub fn init(&self) -> Result<SDCardInfo, InitError> {
+        /* Initialize SD_SPI */
+        self.lowlevel_init();
+        /* SD chip select high */
+        self.CS_HIGH();
+        /* NOTE: this reset doesn't always seem to work if the SD access was broken off in the
+         * middle of an operation: CMDFailed(CMD0, 127). */
+
+        /* Send dummy byte 0xFF, 10 times with CS high */
+        /* Rise CS and MOSI for 80 clocks cycles */
+        /* Send dummy byte 0xFF */
+        self.write_data(&[0xff; 10]);
+        /*------------Put SD in SPI mode--------------*/
+        /* SD initialized and set to SPI mode properly */
+
+        /* Send software reset */
+        self.send_cmd(CMD::CMD0, 0, 0x95);
+        let result = self.get_response();
+        self.end_cmd();
+        if result != 0x01 {
+            return Err(InitError::CMDFailed(CMD::CMD0, result));
+        }
+
+        /* Check voltage range */
+        self.send_cmd(CMD::CMD8, 0x01AA, 0x87);
+        /* 0x01 or 0x05 */
+        let result = self.get_response();
+        let mut frame = [0u8; 4];
+        self.read_data(&mut frame);
+        self.end_cmd();
+        if result != 0x01 {
+            return Err(InitError::CMDFailed(CMD::CMD8, result));
+        }
+        let mut index = 255;
+        while index != 0 {
+            /* <ACMD> */
+            self.send_cmd(CMD::CMD55, 0, 0);
+            let result = self.get_response();
+            self.end_cmd();
+            if result != 0x01 {
+                return Err(InitError::CMDFailed(CMD::CMD55, result));
+            }
+            /* Initiate SDC initialization process */
+            self.send_cmd(CMD::ACMD41, 0x40000000, 0);
+            let result = self.get_response();
+            self.end_cmd();
+            if result == 0x00 {
+                break;
+            }
+            index -= 1;
+        }
+        if index == 0 {
+            return Err(InitError::CMDFailed(CMD::ACMD41, result));
+        }
+        index = 255;
+        let mut frame = [0u8; 4];
+        while index != 0 {
+            /* Read OCR */
+            self.send_cmd(CMD::CMD58, 0, 1);
+            let result = self.get_response();
+            self.read_data(&mut frame);
+            self.end_cmd();
+            if result == 0 {
+                break;
+            }
+            index -= 1;
+        }
+        if index == 0 {
+            return Err(InitError::CMDFailed(CMD::CMD58, result));
+        }
+        if (frame[0] & 0x40) == 0 {
+            return Err(InitError::CardCapacityStatusNotSet(frame));
+        }
+        self.HIGH_SPEED_ENABLE();
+        self.get_cardinfo()
+            .map_err(|_| InitError::CannotGetCardInfo)
+    }
+
+    /*
+     * Reads a block of data from the SD.
+     * @param  data_buf: slice that receives the data read from the SD.
+     * @param  sector: SD's internal address to read from.
+     * @retval The SD Response:
+     *         - `Err(())`: Sequence failed
+     *         - `Ok(())`: Sequence succeed
+     */
+    pub fn read_sector(&self, data_buf: &mut [u8], sector: u32) -> Result<(), ()> {
+        assert!(data_buf.len() >= SEC_LEN && (data_buf.len() % SEC_LEN) == 0);
+        /* Send CMD17 to read one block, or CMD18 for multiple */
+        let flag = if data_buf.len() == SEC_LEN {
+            self.send_cmd(CMD::CMD17, sector, 0);
+            false
+        } else {
+            self.send_cmd(CMD::CMD18, sector, 0);
+            true
+        };
+        /* Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */
+        if self.get_response() != 0x00 {
+            self.end_cmd();
+            return Err(());
+        }
+        let mut error = false;
+        //let mut dma_chunk = [0u32; SEC_LEN];
+        let mut tmp_chunk= [0u8; SEC_LEN];
+        for chunk in data_buf.chunks_mut(SEC_LEN) {
+            if self.get_response() != SD_START_DATA_SINGLE_BLOCK_READ {
+                error = true;
+                break;
+            }
+            /* Read the SD block data : read NumByteToRead data */
+            //self.read_data_dma(&mut dma_chunk);
+            //*可优化
+            self.read_data(&mut tmp_chunk);
+            /* Place the data received as u32 units from DMA into the u8 target buffer */
+            for (a, b) in chunk.iter_mut().zip(/*dma_chunk*/tmp_chunk.iter()) {
+                //*a = (b & 0xff) as u8;
+                *a = *b;
+            }
+            /* Get CRC bytes (not really needed by us, but required by SD) */
+            let mut frame = [0u8; 2];
+            self.read_data(&mut frame);
+        }
+        self.end_cmd();
+        if flag {
+            self.send_cmd(CMD::CMD12, 0, 0);
+            self.get_response();
+            self.end_cmd();
+            self.end_cmd();
+        }
+        /* It is an error if not everything requested was read */
+        if error {
+            Err(())
+        } else {
+            Ok(())
+        }
+    }
+
+    /*
+     * Writes a block to the SD
+     * @param  data_buf: slice containing the data to be written to the SD.
+     * @param  sector: address to write on.
+     * @retval The SD Response:
+     *         - `Err(())`: Sequence failed
+     *         - `Ok(())`: Sequence succeed
+     */
+    pub fn write_sector(&self, data_buf: &[u8], sector: u32) -> Result<(), ()> {
+        assert!(data_buf.len() >= SEC_LEN && (data_buf.len() % SEC_LEN) == 0);
+        let mut frame = [0xff, 0x00];
+        if data_buf.len() == SEC_LEN {
+            frame[1] = SD_START_DATA_SINGLE_BLOCK_WRITE;
+            self.send_cmd(CMD::CMD24, sector, 0);
+        } else {
+            frame[1] = SD_START_DATA_MULTIPLE_BLOCK_WRITE;
+            self.send_cmd(
+                CMD::ACMD23,
+                (data_buf.len() / SEC_LEN).try_into().unwrap(),
+                0,
+            );
+            self.get_response();
+            self.end_cmd();
+            self.send_cmd(CMD::CMD25, sector, 0);
+        }
+        /* Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */
+        if self.get_response() != 0x00 {
+            self.end_cmd();
+            return Err(());
+        }
+        //let mut dma_chunk = [0u32; SEC_LEN];
+        let mut tmp_chunk = [0u8; SEC_LEN];
+        for chunk in data_buf.chunks(SEC_LEN) {
+            /* Send the data token to signify the start of the data */
+            self.write_data(&frame);
+            /* Write the block data to SD : write count data by block */
+            for (a, &b) in /*dma_chunk*/tmp_chunk.iter_mut().zip(chunk.iter()) {
+                //*a = b.into();
+                *a = b;
+            }
+            //self.write_data_dma(&mut dma_chunk);
+            self.write_data(&mut tmp_chunk);
+            /* Put dummy CRC bytes */
+            self.write_data(&[0xff, 0xff]);
+            /* Read data response */
+            if self.get_dataresponse() != 0x00 {
+                self.end_cmd();
+                return Err(());
+            }
+        }
+        self.end_cmd();
+        self.end_cmd();
+        Ok(())
+    }
+}
+
+/** GPIOHS GPIO number to use for controlling the SD card CS pin */
+const SD_CS_GPIONUM: u8 = 7;
+/** CS value passed to SPI controller, this is a dummy value as SPI0_CS3 is not mapping to anything
+ * in the FPIOA */
+const SD_CS: u32 = 3;
+
+/** Connect pins to internal functions */
+fn io_init() {
+    fpioa::set_function(io::SPI0_SCLK, fpioa::function::SPI0_SCLK);
+    fpioa::set_function(io::SPI0_MOSI, fpioa::function::SPI0_D0);
+    fpioa::set_function(io::SPI0_MISO, fpioa::function::SPI0_D1);
+    fpioa::set_function(io::SPI0_CS0, fpioa::function::gpiohs(SD_CS_GPIONUM));
+    fpioa::set_io_pull(io::SPI0_CS0, fpioa::pull::DOWN); // GPIO output=pull down
+}
+
+lazy_static! {
+    static ref PERIPHERALS: Mutex<Peripherals> = Mutex::new(Peripherals::take().unwrap());
+}
+
+fn init_sdcard() -> SDCard<SPIImpl<SPI0>> {
+    // wait previous output
+    usleep(100000);
+    let peripherals = unsafe { Peripherals::steal() };
+    sysctl::pll_set_freq(sysctl::pll::PLL0, 800_000_000).unwrap();
+    sysctl::pll_set_freq(sysctl::pll::PLL1, 300_000_000).unwrap();
+    sysctl::pll_set_freq(sysctl::pll::PLL2, 45_158_400).unwrap();
+    let clocks = k210_hal::clock::Clocks::new();
+    peripherals.UARTHS.configure(115_200.bps(), &clocks);
+    io_init();
+
+    let spi = peripherals.SPI0.constrain();
+    let sd = SDCard::new(spi, SD_CS, SD_CS_GPIONUM);
+    let info = sd.init().unwrap();
+    let num_sectors = info.CardCapacity / 512;
+    assert!(num_sectors > 0);
+
+    println!("init sdcard!");
+    sd
+}
+
+
+pub struct SDCardWrapper(Mutex<SDCard<SPIImpl<SPI0>>>);
+
+impl SDCardWrapper {
+    pub fn new() -> Self {
+        Self(Mutex::new(init_sdcard()))
+    }
+}
+
+impl BlockDevice for SDCardWrapper {
+    fn read_block(&self, block_id: usize, buf: &mut [u8]) {
+        self.0.lock().read_sector(buf,block_id as u32).unwrap();
+    }
+    fn write_block(&self, block_id: usize, buf: &[u8]) {
+        self.0.lock().write_sector(buf,block_id as u32).unwrap();
+    }
+}
\ No newline at end of file
diff --git a/codes/os/src/drivers/block/virtio_blk.rs b/codes/os/src/drivers/block/virtio_blk.rs
new file mode 100644
index 00000000..fde3428c
--- /dev/null
+++ b/codes/os/src/drivers/block/virtio_blk.rs
@@ -0,0 +1,76 @@
+
+use virtio_drivers::{VirtIOBlk, VirtIOHeader};
+use crate::mm::{
+    PhysAddr,
+    VirtAddr,
+    frame_alloc,
+    frame_dealloc,
+    PhysPageNum,
+    FrameTracker,
+    StepByOne,
+    PageTable,
+    kernel_token,
+};
+use super::BlockDevice;
+use spin::Mutex;
+use alloc::vec::Vec;
+use lazy_static::*;
+
+#[allow(unused)]
+const VIRTIO0: usize = 0x10001000;
+
+pub struct VirtIOBlock(Mutex<VirtIOBlk<'static>>);
+
+lazy_static! {
+    static ref QUEUE_FRAMES: Mutex<Vec<FrameTracker>> = Mutex::new(Vec::new());
+}
+
+impl BlockDevice for VirtIOBlock {
+    fn read_block(&self, block_id: usize, buf: &mut [u8]) {
+        self.0.lock().read_block(block_id, buf).expect("Error when reading VirtIOBlk");
+    }
+    fn write_block(&self, block_id: usize, buf: &[u8]) {
+        self.0.lock().write_block(block_id, buf).expect("Error when writing VirtIOBlk");
+    }
+}
+
+impl VirtIOBlock {
+    #[allow(unused)]
+    pub fn new() -> Self {
+        Self(Mutex::new(VirtIOBlk::new(
+            unsafe { &mut *(VIRTIO0 as *mut VirtIOHeader) }
+        ).unwrap()))
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn virtio_dma_alloc(pages: usize) -> PhysAddr {
+    let mut ppn_base = PhysPageNum(0);
+    for i in 0..pages {
+        let frame = frame_alloc().unwrap();
+        if i == 0 { ppn_base = frame.ppn; }
+        assert_eq!(frame.ppn.0, ppn_base.0 + i);
+        QUEUE_FRAMES.lock().push(frame);
+    }
+    ppn_base.into()
+}
+
+#[no_mangle]
+pub extern "C" fn virtio_dma_dealloc(pa: PhysAddr, pages: usize) -> i32 {
+    let mut ppn_base: PhysPageNum = pa.into();
+    for _ in 0..pages {
+        frame_dealloc(ppn_base);
+        ppn_base.step();
+    }
+    0
+}
+
+#[no_mangle]
+pub extern "C" fn virtio_phys_to_virt(paddr: PhysAddr) -> VirtAddr {
+    VirtAddr(paddr.0)
+}
+
+#[no_mangle]
+pub extern "C" fn virtio_virt_to_phys(vaddr: VirtAddr) -> PhysAddr {
+    PageTable::from_token(kernel_token()).translate_va(vaddr).unwrap()
+}
diff --git a/codes/os/src/drivers/mod.rs b/codes/os/src/drivers/mod.rs
new file mode 100644
index 00000000..54c0a2cb
--- /dev/null
+++ b/codes/os/src/drivers/mod.rs
@@ -0,0 +1,3 @@
+mod block;
+
+pub use block::BLOCK_DEVICE;
\ No newline at end of file
diff --git a/codes/os/src/entry.asm b/codes/os/src/entry.asm
new file mode 100644
index 00000000..9d2ff713
--- /dev/null
+++ b/codes/os/src/entry.asm
@@ -0,0 +1,12 @@
+    .section .text.entry
+    .globl _start
+_start:
+    la sp, boot_stack_top
+    call rust_main
+
+    .section .bss.stack
+    .globl boot_stack
+boot_stack:
+    .space 4096 * 16
+    .globl boot_stack_top
+boot_stack_top:
\ No newline at end of file
diff --git a/codes/os/src/fs/inode.rs b/codes/os/src/fs/inode.rs
new file mode 100644
index 00000000..1c86f500
--- /dev/null
+++ b/codes/os/src/fs/inode.rs
@@ -0,0 +1,242 @@
+use easy_fs::{
+    EasyFileSystem,
+    Inode,
+    DiskInodeType,
+    NAME_LENGTH_LIMIT,
+};
+use crate::drivers::BLOCK_DEVICE;
+use crate::color_text;
+use alloc::sync::Arc;
+use lazy_static::*;
+use bitflags::*;
+use alloc::vec::Vec;
+use alloc::vec;
+use spin::Mutex;
+use super::File;
+use crate::mm::UserBuffer;
+
+// 此inode实际被当作文件
+pub struct OSInode {
+    readable: bool,
+    writable: bool,
+    inner: Mutex<OSInodeInner>,
+}
+
+pub struct OSDirEntry {
+    inode_id: u32, 
+    offset: usize,//dirent在目录中的偏移量
+}
+
+pub struct OSInodeInner {
+    offset: usize, // 当前读写的位置
+    inode: Arc<Inode>, // inode引用
+}
+
+impl OSInode {
+    pub fn new(
+        readable: bool,
+        writable: bool,
+        inode: Arc<Inode>,
+    ) -> Self {
+        Self {
+            readable,
+            writable,
+            inner: Mutex::new(OSInodeInner {
+                offset: 0,
+                inode,
+            }),
+        }
+    }
+    pub fn read_all(&self) -> Vec<u8> {
+        let mut inner = self.inner.lock();
+        let mut buffer = [0u8; 512];
+        let mut v: Vec<u8> = Vec::new();
+        loop {
+            let len = inner.inode.read_at(inner.offset, &mut buffer);
+            if len == 0 {
+                break;
+            }
+            inner.offset += len;
+            v.extend_from_slice(&buffer[..len]);
+        }
+        v
+    }
+}
+
+lazy_static! {
+    // 通过ROOT_INODE可以实现对efs的操作
+    pub static ref ROOT_INODE: Arc<Inode> = {
+        // 此处载入文件系统
+        let efs = EasyFileSystem::open(BLOCK_DEVICE.clone());
+        Arc::new(EasyFileSystem::get_inode(&efs, 0))
+    };
+}
+
+lazy_static! {
+    // 目录栈 
+    pub static ref DIR_STACK: Vec<Arc<Inode>> = vec![ROOT_INODE.clone()];
+}
+
+pub fn list_apps() {
+    println!("/**** APPS ****");
+    for app in ROOT_INODE.ls() {
+        println!("{}", app.0);
+    }
+    println!("**************/")
+}
+
+pub fn list_files(inode_id: u32){
+    let curr_inode = EasyFileSystem::get_inode(&ROOT_INODE.get_fs(), inode_id);
+    let file_vec = curr_inode.ls();
+    for i in 0 .. file_vec.len() {
+        if file_vec[i].1 == DiskInodeType::File{
+            print!("{} ", file_vec[i].0);
+        } else {
+            // TODO: 统一配色!
+            print!("{} ", color_text!(file_vec[i].0, 96));
+        }
+    }
+}
+
+bitflags! {
+    pub struct OpenFlags: u32 {
+        const RDONLY = 0;
+        const WRONLY = 1 << 0;
+        const RDWR = 1 << 1;
+        const CREATE = 1 << 9;
+        const TRUNC = 1 << 10;
+    }
+}
+
+impl OpenFlags {
+    /// Do not check validity for simplicity
+    /// Return (readable, writable)
+    pub fn read_write(&self) -> (bool, bool) {
+        if self.is_empty() {
+            (true, false)
+        } else if self.contains(Self::WRONLY) {
+            (false, true)
+        } else {
+            (true, true)
+        }
+    }
+}
+
+pub fn open(inode_id: u32, path: &str, flags: OpenFlags, type_: DiskInodeType) -> Option<Arc<OSInode>> {
+    // DEBUG: 相对路径
+    let cur_inode = {
+        if inode_id == 0 {
+            ROOT_INODE.clone()
+        }else{
+            Arc::new(EasyFileSystem::get_inode(
+                &ROOT_INODE.get_fs(), 
+                inode_id
+            ))
+        }
+    };
+    let mut pathv:Vec<&str> = path.split('/').collect();
+    // shell应当保证此处输入的path不为空
+    let (readable, writable) = flags.read_write();
+    if flags.contains(OpenFlags::CREATE) {
+        if let Some((inode,_)) = cur_inode.find_path(pathv.clone()) {
+            // clear size
+            inode.clear();
+            Some(Arc::new(OSInode::new(
+                readable,
+                writable,
+                inode,
+            )))
+        } else {
+            // create file
+            let name = pathv.pop().unwrap();
+            if let Some((temp_inode,_)) = cur_inode.find_path(pathv.clone()){
+                temp_inode.create( name, type_)
+                .map(|inode| {
+                    Arc::new(OSInode::new(
+                        readable,
+                        writable,
+                        inode,
+                    ))
+                })
+            }else{
+                None
+            }
+        }
+    } else {
+        cur_inode.find_path(pathv)
+            .map(|(inode, _)| {
+                if flags.contains(OpenFlags::TRUNC) {
+                    inode.clear();
+                }
+                Arc::new(OSInode::new(
+                    readable,
+                    writable,
+                    inode
+                ))
+            })
+    }
+}
+
+
+pub fn ch_dir(inode_id: u32, path: &str) -> i32{
+    // 切换工作路径
+    // 切换成功,返回inode_id,否则返回-1
+    let pathv:Vec<&str> = path.split('/').collect();
+    let cur_inode = EasyFileSystem::get_inode(
+        &ROOT_INODE.get_fs(), 
+        inode_id
+    );
+    if let Some((tar_inode,_)) = cur_inode.find_path(pathv){
+        // ! 当inode_id > 2^16 时,有溢出的可能(目前不会发生。。
+        tar_inode.get_id() as i32
+    }else{
+        -1
+    }
+}
+ 
+// TODO: 不急
+/* 
+pub fn read_dir(inode_id: u32) -> Option<Arc<OSDirEntry>> {
+    // 从目录中读取下一个目录项
+}*/
+
+
+pub fn remove(inode_id: u32, path: &str, type_: DiskInodeType)->bool{
+    // type_确认要删除的文件类型,如果是目录,递归删除
+    let curr_inode = EasyFileSystem::get_inode(
+        &ROOT_INODE.get_fs().clone(), 
+        inode_id
+    );
+    let pathv:Vec<&str> = path.split('/').collect();
+    curr_inode.remove(pathv,type_)
+}
+
+impl File for OSInode {
+    fn readable(&self) -> bool { self.readable }
+    fn writable(&self) -> bool { self.writable }
+    fn read(&self, mut buf: UserBuffer) -> usize {
+        let mut inner = self.inner.lock();
+        let mut total_read_size = 0usize;
+        for slice in buf.buffers.iter_mut() {
+            // buffer存放的元素是[u8]而不是u8
+            let read_size = inner.inode.read_at(inner.offset, *slice);
+            if read_size == 0 {
+                break;
+            }
+            inner.offset += read_size;
+            total_read_size += read_size;
+        }
+        total_read_size
+    }
+    fn write(&self, buf: UserBuffer) -> usize {
+        let mut inner = self.inner.lock();
+        let mut total_write_size = 0usize;
+        for slice in buf.buffers.iter() {
+            let write_size = inner.inode.write_at(inner.offset, *slice);
+            assert_eq!(write_size, slice.len());
+            inner.offset += write_size;
+            total_write_size += write_size;
+        }
+        total_write_size
+    }
+}
\ No newline at end of file
diff --git a/codes/os/src/fs/mod.rs b/codes/os/src/fs/mod.rs
new file mode 100644
index 00000000..6b092b25
--- /dev/null
+++ b/codes/os/src/fs/mod.rs
@@ -0,0 +1,17 @@
+mod pipe;
+mod stdio;
+mod inode;
+mod util;
+
+use crate::mm::UserBuffer;
+
+pub trait File : Send + Sync {
+    fn readable(&self) -> bool;
+    fn writable(&self) -> bool;
+    fn read(&self, buf: UserBuffer) -> usize;
+    fn write(&self, buf: UserBuffer) -> usize;
+}
+
+pub use pipe::{Pipe, make_pipe};
+pub use stdio::{Stdin, Stdout};
+pub use inode::{OSInode, open, OpenFlags, list_apps};
\ No newline at end of file
diff --git a/codes/os/src/fs/pipe.rs b/codes/os/src/fs/pipe.rs
new file mode 100644
index 00000000..ab319237
--- /dev/null
+++ b/codes/os/src/fs/pipe.rs
@@ -0,0 +1,168 @@
+use super::File;
+use alloc::sync::{Arc, Weak};
+use spin::Mutex;
+use crate::mm::{
+    UserBuffer,
+};
+use crate::task::suspend_current_and_run_next;
+
+pub struct Pipe {
+    readable: bool,
+    writable: bool,
+    buffer: Arc<Mutex<PipeRingBuffer>>,
+}
+
+impl Pipe {
+    pub fn read_end_with_buffer(buffer: Arc<Mutex<PipeRingBuffer>>) -> Self {
+        Self {
+            readable: true,
+            writable: false,
+            buffer,
+        }
+    }
+    pub fn write_end_with_buffer(buffer: Arc<Mutex<PipeRingBuffer>>) -> Self {
+        Self {
+            readable: false,
+            writable: true,
+            buffer,
+        }
+    }
+}
+
+const RING_BUFFER_SIZE: usize = 32;
+
+#[derive(Copy, Clone, PartialEq)]
+enum RingBufferStatus {
+    FULL,
+    EMPTY,
+    NORMAL,
+}
+
+pub struct PipeRingBuffer {
+    arr: [u8; RING_BUFFER_SIZE],
+    head: usize,
+    tail: usize,
+    status: RingBufferStatus,
+    write_end: Option<Weak<Pipe>>,
+}
+
+impl PipeRingBuffer {
+    pub fn new() -> Self {
+        Self {
+            arr: [0; RING_BUFFER_SIZE],
+            head: 0,
+            tail: 0,
+            status: RingBufferStatus::EMPTY,
+            write_end: None,
+        }
+    }
+    pub fn set_write_end(&mut self, write_end: &Arc<Pipe>) {
+        self.write_end = Some(Arc::downgrade(write_end));
+    }
+    pub fn write_byte(&mut self, byte: u8) {
+        self.status = RingBufferStatus::NORMAL;
+        self.arr[self.tail] = byte;
+        self.tail = (self.tail + 1) % RING_BUFFER_SIZE;
+        if self.tail == self.head {
+            self.status = RingBufferStatus::FULL;
+        }
+    }
+    pub fn read_byte(&mut self) -> u8 {
+        self.status = RingBufferStatus::NORMAL;
+        let c = self.arr[self.head];
+        self.head = (self.head + 1) % RING_BUFFER_SIZE;
+        if self.head == self.tail {
+            self.status = RingBufferStatus::EMPTY;
+        }
+        c
+    }
+    pub fn available_read(&self) -> usize {
+        if self.status == RingBufferStatus::EMPTY {
+            0
+        } else {
+            if self.tail > self.head {
+                self.tail - self.head
+            } else {
+                self.tail + RING_BUFFER_SIZE - self.head
+            }
+        }
+    }
+    pub fn available_write(&self) -> usize {
+        if self.status == RingBufferStatus::FULL {
+            0
+        } else {
+            RING_BUFFER_SIZE - self.available_read()
+        }
+    }
+    pub fn all_write_ends_closed(&self) -> bool {
+        self.write_end.as_ref().unwrap().upgrade().is_none()
+    }
+}
+
+/// Return (read_end, write_end)
+pub fn make_pipe() -> (Arc<Pipe>, Arc<Pipe>) {
+    let buffer = Arc::new(Mutex::new(PipeRingBuffer::new()));
+    // buffer仅剩两个强引用,这样读写端关闭后就会被释放
+    let read_end = Arc::new(
+        Pipe::read_end_with_buffer(buffer.clone())
+    );
+    let write_end = Arc::new(
+        Pipe::write_end_with_buffer(buffer.clone())
+    );
+    buffer.lock().set_write_end(&write_end);
+    (read_end, write_end)
+}
+
+impl File for Pipe {
+    fn readable(&self) -> bool { self.readable }
+    fn writable(&self) -> bool { self.writable }
+    fn read(&self, buf: UserBuffer) -> usize {
+        assert_eq!(self.readable(), true);
+        let mut buf_iter = buf.into_iter();
+        let mut read_size = 0usize;
+        loop {
+            let mut ring_buffer = self.buffer.lock();
+            let loop_read = ring_buffer.available_read();
+            if loop_read == 0 {
+                if ring_buffer.all_write_ends_closed() {
+                    return read_size;  //return后就ring_buffer释放了,锁自然释放
+                }
+                drop(ring_buffer);
+                suspend_current_and_run_next();
+                continue;
+            }
+            // read at most loop_read bytes
+            for _ in 0..loop_read {
+                if let Some(byte_ref) = buf_iter.next() {
+                    unsafe { *byte_ref = ring_buffer.read_byte(); }
+                    read_size += 1;
+                } else {
+                    return read_size;
+                }
+            }
+        }
+    }
+    fn write(&self, buf: UserBuffer) -> usize {
+        assert_eq!(self.writable(), true);
+        let mut buf_iter = buf.into_iter();
+        let mut write_size = 0usize;
+        loop {
+            let mut ring_buffer = self.buffer.lock();
+            let loop_write = ring_buffer.available_write();
+            if loop_write == 0 {
+                drop(ring_buffer);
+                suspend_current_and_run_next();
+                continue;
+            }
+            // write at most loop_write bytes
+            for _ in 0..loop_write {
+                if let Some(byte_ref) = buf_iter.next() {
+                    ring_buffer.write_byte(unsafe { *byte_ref });
+                    write_size += 1;
+                } else {
+                    return write_size;
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/codes/os/src/fs/stdio.rs b/codes/os/src/fs/stdio.rs
new file mode 100644
index 00000000..e8df7950
--- /dev/null
+++ b/codes/os/src/fs/stdio.rs
@@ -0,0 +1,47 @@
+use super::File;
+use crate::mm::{UserBuffer};
+use crate::sbi::console_getchar;
+use crate::task::suspend_current_and_run_next;
+
+pub struct Stdin;
+
+pub struct Stdout;
+
+impl File for Stdin {
+    fn readable(&self) -> bool { true }
+    fn writable(&self) -> bool { false }
+    fn read(&self, mut user_buf: UserBuffer) -> usize {
+        assert_eq!(user_buf.len(), 1);
+        // busy loop
+        let mut c: usize;
+        loop {
+            c = console_getchar();
+            if c == 0 {
+                suspend_current_and_run_next();
+                continue;
+            } else {
+                break;
+            }
+        }
+        let ch = c as u8;
+        unsafe { user_buf.buffers[0].as_mut_ptr().write_volatile(ch); }
+        1
+    }
+    fn write(&self, _user_buf: UserBuffer) -> usize {
+        panic!("Cannot write to stdin!");
+    }
+}
+
+impl File for Stdout {
+    fn readable(&self) -> bool { false }
+    fn writable(&self) -> bool { true }
+    fn read(&self, _user_buf: UserBuffer) -> usize{
+        panic!("Cannot read from stdout!");
+    }
+    fn write(&self, user_buf: UserBuffer) -> usize {
+        for buffer in user_buf.buffers.iter() {
+            print!("{}", core::str::from_utf8(*buffer).unwrap());
+        }
+        user_buf.len()
+    }
+}
\ No newline at end of file
diff --git a/codes/os/src/fs/util.rs b/codes/os/src/fs/util.rs
new file mode 100644
index 00000000..5cb1d76a
--- /dev/null
+++ b/codes/os/src/fs/util.rs
@@ -0,0 +1,5 @@
+use alloc::vec::Vec;
+
+pub fn str2vec(str: &str) -> Vec<&str>{
+    str.split('/').collect()
+}
\ No newline at end of file
diff --git a/codes/os/src/lang_items.rs b/codes/os/src/lang_items.rs
new file mode 100644
index 00000000..99c34cd9
--- /dev/null
+++ b/codes/os/src/lang_items.rs
@@ -0,0 +1,19 @@
+use core::panic::PanicInfo;
+use crate::sbi::shutdown;
+
+#[panic_handler]
+fn panic(info: &PanicInfo) -> ! {
+    if let Some(location) = info.location() {
+        println!("[kernel] Panicked at {}:{} {}", location.file(), location.line(), info.message().unwrap());
+    } else {
+        println!("[kernel] Panicked: {}", info.message().unwrap());
+    }
+    shutdown()
+}
+
+#[macro_export]
+macro_rules! color_text {
+    ($text:expr, $color:expr) => {{
+        format_args!("\x1b[{}m{}\x1b[0m", $color, $text)
+    }};
+}
\ No newline at end of file
diff --git a/codes/os/src/linker-k210.ld b/codes/os/src/linker-k210.ld
new file mode 100644
index 00000000..4f9d2171
--- /dev/null
+++ b/codes/os/src/linker-k210.ld
@@ -0,0 +1,50 @@
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+BASE_ADDRESS = 0x80020000;
+
+SECTIONS
+{
+    . = BASE_ADDRESS;
+    skernel = .;
+
+    stext = .;
+    .text : {
+        *(.text.entry)
+        . = ALIGN(4K);
+        strampoline = .;
+        *(.text.trampoline);
+        . = ALIGN(4K);
+        *(.text .text.*)
+    }
+
+    . = ALIGN(4K);
+    etext = .;
+    srodata = .;
+    .rodata : {
+        *(.rodata .rodata.*)
+    }
+
+    . = ALIGN(4K);
+    erodata = .;
+    sdata = .;
+    .data : {
+        *(.data .data.*)
+    }
+
+    . = ALIGN(4K);
+    edata = .;
+    sbss_with_stack = .;
+    .bss : {
+        *(.bss.stack)
+        sbss = .;
+        *(.bss .bss.*)
+    }
+
+    . = ALIGN(4K);
+    ebss = .;
+    ekernel = .;
+
+    /DISCARD/ : {
+        *(.eh_frame)
+    }
+}
\ No newline at end of file
diff --git a/codes/os/src/linker-qemu.ld b/codes/os/src/linker-qemu.ld
new file mode 100644
index 00000000..6b06e916
--- /dev/null
+++ b/codes/os/src/linker-qemu.ld
@@ -0,0 +1,50 @@
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+BASE_ADDRESS = 0x80200000;
+
+SECTIONS
+{
+    . = BASE_ADDRESS;
+    skernel = .;
+
+    stext = .;
+    .text : {
+        *(.text.entry)
+        . = ALIGN(4K);
+        strampoline = .;
+        *(.text.trampoline);
+        . = ALIGN(4K);
+        *(.text .text.*)
+    }
+
+    . = ALIGN(4K);
+    etext = .;
+    srodata = .;
+    .rodata : {
+        *(.rodata .rodata.*)
+    }
+
+    . = ALIGN(4K);
+    erodata = .;
+    sdata = .;
+    .data : {
+        *(.data .data.*)
+    }
+
+    . = ALIGN(4K);
+    edata = .;
+    sbss_with_stack = .;
+    .bss : {
+        *(.bss.stack)
+        sbss = .;
+        *(.bss .bss.*)
+    }
+
+    . = ALIGN(4K);
+    ebss = .;
+    ekernel = .;
+
+    /DISCARD/ : {
+        *(.eh_frame)
+    }
+}
\ No newline at end of file
diff --git a/codes/os/src/main.rs b/codes/os/src/main.rs
new file mode 100644
index 00000000..e9a34bb8
--- /dev/null
+++ b/codes/os/src/main.rs
@@ -0,0 +1,52 @@
+#![no_std]
+#![no_main]
+#![feature(global_asm)]
+#![feature(llvm_asm)]
+#![feature(panic_info_message)]
+#![feature(const_in_array_repeat_expressions)]
+#![feature(alloc_error_handler)]
+
+extern crate alloc;
+
+#[macro_use]
+extern crate bitflags;
+
+#[macro_use]
+mod console;
+mod lang_items;
+mod sbi;
+mod syscall;
+mod trap;
+mod config;
+mod task;
+mod timer;
+mod mm;
+mod fs;
+mod drivers;
+
+global_asm!(include_str!("entry.asm"));
+
+fn clear_bss() {
+    extern "C" {
+        fn sbss();
+        fn ebss();
+    }
+    (sbss as usize..ebss as usize).for_each(|a| {
+        unsafe { (a as *mut u8).write_volatile(0) }
+    });
+}
+
+#[no_mangle]
+pub fn rust_main() -> ! {
+    clear_bss();
+    println!("[kernel] Hello, world!");
+    mm::init();
+    mm::remap_test();
+    trap::init();
+    trap::enable_timer_interrupt();
+    timer::set_next_trigger();
+    fs::list_apps();
+    task::add_initproc();
+    task::run_tasks();
+    panic!("Unreachable in rust_main!");
+}
\ No newline at end of file
diff --git a/codes/os/src/mm/address.rs b/codes/os/src/mm/address.rs
new file mode 100644
index 00000000..d5828ed2
--- /dev/null
+++ b/codes/os/src/mm/address.rs
@@ -0,0 +1,209 @@
+use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS};
+use super::PageTableEntry;
+use core::fmt::{self, Debug, Formatter};
+
+/// Definitions
+#[repr(C)]
+#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
+pub struct PhysAddr(pub usize);
+
+#[repr(C)]
+#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
+pub struct VirtAddr(pub usize);
+
+#[repr(C)]
+#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
+pub struct PhysPageNum(pub usize);
+
+#[repr(C)]
+#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
+pub struct VirtPageNum(pub usize);
+
+/// Debugging
+
+impl Debug for VirtAddr {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.write_fmt(format_args!("VA:{:#x}", self.0))
+    }
+}
+impl Debug for VirtPageNum {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.write_fmt(format_args!("VPN:{:#x}", self.0))
+    }
+}
+impl Debug for PhysAddr {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.write_fmt(format_args!("PA:{:#x}", self.0))
+    }
+}
+impl Debug for PhysPageNum {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.write_fmt(format_args!("PPN:{:#x}", self.0))
+    }
+}
+
+/// T: {PhysAddr, VirtAddr, PhysPageNum, VirtPageNum}
+/// T -> usize: T.0
+/// usize -> T: usize.into()
+
+impl From<usize> for PhysAddr {
+    fn from(v: usize) -> Self { Self(v) }
+}
+impl From<usize> for PhysPageNum {
+    fn from(v: usize) -> Self { Self(v) }
+}
+impl From<usize> for VirtAddr {
+    fn from(v: usize) -> Self { Self(v) }
+}
+impl From<usize> for VirtPageNum {
+    fn from(v: usize) -> Self { Self(v) }
+}
+impl From<PhysAddr> for usize {
+    fn from(v: PhysAddr) -> Self { v.0 }
+}
+impl From<PhysPageNum> for usize {
+    fn from(v: PhysPageNum) -> Self { v.0 }
+}
+impl From<VirtAddr> for usize {
+    fn from(v: VirtAddr) -> Self { v.0 }
+}
+impl From<VirtPageNum> for usize {
+    fn from(v: VirtPageNum) -> Self { v.0 }
+}
+
+impl VirtAddr {
+    pub fn floor(&self) -> VirtPageNum { VirtPageNum(self.0 / PAGE_SIZE) }
+    pub fn ceil(&self) -> VirtPageNum  { VirtPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) }
+    pub fn page_offset(&self) -> usize { self.0 & (PAGE_SIZE - 1) }
+    pub fn aligned(&self) -> bool { self.page_offset() == 0 }
+}
+impl From<VirtAddr> for VirtPageNum {
+    fn from(v: VirtAddr) -> Self {
+        assert_eq!(v.page_offset(), 0);
+        v.floor()
+    }
+}
+impl From<VirtPageNum> for VirtAddr {
+    fn from(v: VirtPageNum) -> Self { Self(v.0 << PAGE_SIZE_BITS) }
+}
+impl PhysAddr {
+    pub fn floor(&self) -> PhysPageNum { PhysPageNum(self.0 / PAGE_SIZE) }
+    pub fn ceil(&self) -> PhysPageNum { PhysPageNum((self.0 - 1 + PAGE_SIZE) / PAGE_SIZE) }
+    pub fn page_offset(&self) -> usize { self.0 & (PAGE_SIZE - 1) }
+    pub fn aligned(&self) -> bool { self.page_offset() == 0 }
+}
+impl From<PhysAddr> for PhysPageNum {
+    fn from(v: PhysAddr) -> Self {
+        assert_eq!(v.page_offset(), 0);
+        v.floor()
+    }
+}
+impl From<PhysPageNum> for PhysAddr {
+    fn from(v: PhysPageNum) -> Self { Self(v.0 << PAGE_SIZE_BITS) }
+}
+
+impl VirtPageNum {
+    pub fn indexes(&self) -> [usize; 3] {
+        let mut vpn = self.0;
+        let mut idx = [0usize; 3];
+        for i in (0..3).rev() {
+            idx[i] = vpn & 511;
+            vpn >>= 9;
+        }
+        idx
+    }
+}
+
+impl PhysAddr {
+    pub fn get_ref<T>(&self) -> &'static T {
+        unsafe {
+            (self.0 as *const T).as_ref().unwrap()
+        }
+    }
+    pub fn get_mut<T>(&self) -> &'static mut T {
+        unsafe {
+            (self.0 as *mut T).as_mut().unwrap()
+        }
+    }
+}
+impl PhysPageNum {
+    pub fn get_pte_array(&self) -> &'static mut [PageTableEntry] {
+        let pa: PhysAddr = self.clone().into();
+        unsafe {
+            core::slice::from_raw_parts_mut(pa.0 as *mut PageTableEntry, 512)
+        }
+    }
+    pub fn get_bytes_array(&self) -> &'static mut [u8] {
+        let pa: PhysAddr = self.clone().into();
+        unsafe {
+            core::slice::from_raw_parts_mut(pa.0 as *mut u8, 4096)
+        }
+    }
+    pub fn get_mut<T>(&self) -> &'static mut T {
+        let pa: PhysAddr = self.clone().into();
+        pa.get_mut()
+    }
+}
+
+pub trait StepByOne {
+    fn step(&mut self);
+}
+impl StepByOne for VirtPageNum {
+    fn step(&mut self) {
+        self.0 += 1;
+    }
+}
+impl StepByOne for PhysPageNum {
+    fn step(&mut self) {
+        self.0 += 1;
+    }
+}
+
+#[derive(Copy, Clone)]
+pub struct SimpleRange<T> where
+    T: StepByOne + Copy + PartialEq + PartialOrd + Debug, {
+    l: T,
+    r: T,
+}
+impl<T> SimpleRange<T> where
+    T: StepByOne + Copy + PartialEq + PartialOrd + Debug, {
+    pub fn new(start: T, end: T) -> Self {
+        assert!(start <= end, "start {:?} > end {:?}!", start, end);
+        Self { l: start, r: end }
+    }
+    pub fn get_start(&self) -> T { self.l }
+    pub fn get_end(&self) -> T { self.r }
+}
+impl<T> IntoIterator for SimpleRange<T> where
+    T: StepByOne + Copy + PartialEq + PartialOrd + Debug, {
+    type Item = T;
+    type IntoIter = SimpleRangeIterator<T>;
+    fn into_iter(self) -> Self::IntoIter {
+        SimpleRangeIterator::new(self.l, self.r)
+    }
+}
+pub struct SimpleRangeIterator<T> where
+    T: StepByOne + Copy + PartialEq + PartialOrd + Debug, {
+    current: T,
+    end: T,
+}
+impl<T> SimpleRangeIterator<T> where
+    T: StepByOne + Copy + PartialEq + PartialOrd + Debug, {
+    pub fn new(l: T, r: T) -> Self {
+        Self { current: l, end: r, }
+    }
+}
+impl<T> Iterator for SimpleRangeIterator<T> where
+    T: StepByOne + Copy + PartialEq + PartialOrd + Debug, {
+    type Item = T;
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.current == self.end {
+            None
+        } else {
+            let t = self.current;
+            self.current.step();
+            Some(t)
+        }
+    }
+}
+pub type VPNRange = SimpleRange<VirtPageNum>;
\ No newline at end of file
diff --git a/codes/os/src/mm/frame_allocator.rs b/codes/os/src/mm/frame_allocator.rs
new file mode 100644
index 00000000..2cb6427b
--- /dev/null
+++ b/codes/os/src/mm/frame_allocator.rs
@@ -0,0 +1,133 @@
+use super::{PhysAddr, PhysPageNum};
+use alloc::vec::Vec;
+use spin::Mutex;
+use crate::config::MEMORY_END;
+use lazy_static::*;
+use core::fmt::{self, Debug, Formatter};
+
+pub struct FrameTracker {
+    pub ppn: PhysPageNum,
+}
+
+impl FrameTracker {
+    pub fn new(ppn: PhysPageNum) -> Self {
+        // page cleaning
+        let bytes_array = ppn.get_bytes_array();
+        for i in bytes_array {
+            *i = 0;
+        }
+        Self { ppn }
+    }
+}
+
+impl Debug for FrameTracker {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        f.write_fmt(format_args!("FrameTracker:PPN={:#x}", self.ppn.0))
+    }
+}
+
+impl Drop for FrameTracker {
+    fn drop(&mut self) {
+        frame_dealloc(self.ppn);
+    }
+}
+
+trait FrameAllocator {
+    fn new() -> Self;
+    fn alloc(&mut self) -> Option<PhysPageNum>;
+    fn dealloc(&mut self, ppn: PhysPageNum);
+}
+
+pub struct StackFrameAllocator {
+    current: usize,
+    end: usize,
+    recycled: Vec<usize>,
+}
+
+impl StackFrameAllocator {
+    pub fn init(&mut self, l: PhysPageNum, r: PhysPageNum) {
+        self.current = l.0;
+        self.end = r.0;
+        println!("last {} Physical Frames.", self.end - self.current);
+    }
+}
+impl FrameAllocator for StackFrameAllocator {
+    fn new() -> Self {
+        Self {
+            current: 0,
+            end: 0,
+            recycled: Vec::new(),
+        }
+    }
+    fn alloc(&mut self) -> Option<PhysPageNum> {
+        if let Some(ppn) = self.recycled.pop() {
+            Some(ppn.into())
+        } else {
+            if self.current == self.end {
+                None
+            } else {
+                self.current += 1;
+                Some((self.current - 1).into())
+            }
+        }
+    }
+    fn dealloc(&mut self, ppn: PhysPageNum) {
+        let ppn = ppn.0;
+        // validity check
+        if ppn >= self.current || self.recycled
+            .iter()
+            .find(|&v| {*v == ppn})
+            .is_some() {
+            panic!("Frame ppn={:#x} has not been allocated!", ppn);
+        }
+        // recycle
+        self.recycled.push(ppn);
+    }
+}
+
+type FrameAllocatorImpl = StackFrameAllocator;
+
+lazy_static! {
+    pub static ref FRAME_ALLOCATOR: Mutex<FrameAllocatorImpl> =
+        Mutex::new(FrameAllocatorImpl::new());
+}
+
+pub fn init_frame_allocator() {
+    extern "C" {
+        fn ekernel();
+    }
+    FRAME_ALLOCATOR
+        .lock()
+        .init(PhysAddr::from(ekernel as usize).ceil(), PhysAddr::from(MEMORY_END).floor());
+}
+
+pub fn frame_alloc() -> Option<FrameTracker> {
+    FRAME_ALLOCATOR
+        .lock()
+        .alloc()
+        .map(|ppn| FrameTracker::new(ppn))
+}
+
+pub fn frame_dealloc(ppn: PhysPageNum) {
+    FRAME_ALLOCATOR
+        .lock()
+        .dealloc(ppn);
+}
+
+#[allow(unused)]
+pub fn frame_allocator_test() {
+    let mut v: Vec<FrameTracker> = Vec::new();
+    for i in 0..5 {
+        let frame = frame_alloc().unwrap();
+        println!("{:?}", frame);
+        v.push(frame);
+    }
+    v.clear();
+    for i in 0..5 {
+        let frame = frame_alloc().unwrap();
+        println!("{:?}", frame);
+        v.push(frame);
+    }
+    drop(v);
+    println!("frame_allocator_test passed!");
+}
\ No newline at end of file
diff --git a/codes/os/src/mm/heap_allocator.rs b/codes/os/src/mm/heap_allocator.rs
new file mode 100644
index 00000000..2c7468f2
--- /dev/null
+++ b/codes/os/src/mm/heap_allocator.rs
@@ -0,0 +1,45 @@
+use buddy_system_allocator::LockedHeap;
+use crate::config::KERNEL_HEAP_SIZE;
+
+#[global_allocator]
+static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty();
+
+#[alloc_error_handler]
+pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
+    panic!("Heap allocation error, layout = {:?}", layout);
+}
+
+static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE];
+
+pub fn init_heap() {
+    unsafe {
+        HEAP_ALLOCATOR
+            .lock()
+            .init(HEAP_SPACE.as_ptr() as usize, KERNEL_HEAP_SIZE);
+    }
+}
+
+#[allow(unused)]
+pub fn heap_test() {
+    use alloc::boxed::Box;
+    use alloc::vec::Vec;
+    extern "C" {
+        fn sbss();
+        fn ebss();
+    }
+    let bss_range = sbss as usize..ebss as usize;
+    let a = Box::new(5);
+    assert_eq!(*a, 5);
+    assert!(bss_range.contains(&(a.as_ref() as *const _ as usize)));
+    drop(a);
+    let mut v: Vec<usize> = Vec::new();
+    for i in 0..500 {
+        v.push(i);
+    }
+    for i in 0..500 {
+        assert_eq!(v[i], i);
+    }
+    assert!(bss_range.contains(&(v.as_ptr() as usize)));
+    drop(v);
+    println!("heap_test passed!");
+}
diff --git a/codes/os/src/mm/memory_set.rs b/codes/os/src/mm/memory_set.rs
new file mode 100644
index 00000000..262f0852
--- /dev/null
+++ b/codes/os/src/mm/memory_set.rs
@@ -0,0 +1,359 @@
+use super::{PageTable, PageTableEntry, PTEFlags};
+use super::{VirtPageNum, VirtAddr, PhysPageNum, PhysAddr};
+use super::{FrameTracker, frame_alloc};
+use super::{VPNRange, StepByOne};
+use alloc::collections::BTreeMap;
+use alloc::vec::Vec;
+use riscv::register::satp;
+use alloc::sync::Arc;
+use lazy_static::*;
+use spin::Mutex;
+use crate::config::{
+    MEMORY_END,
+    PAGE_SIZE,
+    TRAMPOLINE,
+    TRAP_CONTEXT,
+    USER_STACK_SIZE,
+    MMIO,
+};
+
+extern "C" {
+    fn stext();
+    fn etext();
+    fn srodata();
+    fn erodata();
+    fn sdata();
+    fn edata();
+    fn sbss_with_stack();
+    fn ebss();
+    fn ekernel();
+    fn strampoline();
+}
+
+lazy_static! {
+    pub static ref KERNEL_SPACE: Arc<Mutex<MemorySet>> = Arc::new(Mutex::new(
+        MemorySet::new_kernel()
+    ));
+}
+
+pub fn kernel_token() -> usize {
+    KERNEL_SPACE.lock().token()
+}
+
+pub struct MemorySet {
+    page_table: PageTable,
+    areas: Vec<MapArea>,
+}
+
+impl MemorySet {
+    pub fn new_bare() -> Self {
+        Self {
+            page_table: PageTable::new(),
+            areas: Vec::new(),
+        }
+    }
+    pub fn token(&self) -> usize {
+        self.page_table.token()
+    }
+    /// Assume that no conflicts.
+    pub fn insert_framed_area(&mut self, start_va: VirtAddr, end_va: VirtAddr, permission: MapPermission) {
+        self.push(MapArea::new(
+            start_va,
+            end_va,
+            MapType::Framed,
+            permission,
+        ), None);
+    }
+    pub fn remove_area_with_start_vpn(&mut self, start_vpn: VirtPageNum) {
+        if let Some((idx, area)) = self.areas.iter_mut().enumerate()
+            .find(|(_, area)| area.vpn_range.get_start() == start_vpn) {
+            area.unmap(&mut self.page_table);
+            self.areas.remove(idx);
+        }
+    }
+    fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) {
+        map_area.map(&mut self.page_table);
+        if let Some(data) = data {
+            map_area.copy_data(&mut self.page_table, data);
+        }
+        self.areas.push(map_area);
+    }
+    /// Mention that trampoline is not collected by areas.
+    fn map_trampoline(&mut self) {
+        println!("*** trampoline = {:X}",TRAMPOLINE);
+        self.page_table.map(
+            VirtAddr::from(TRAMPOLINE).into(),
+            PhysAddr::from(strampoline as usize).into(),
+            PTEFlags::R | PTEFlags::X,
+        );
+    }
+    /// Without kernel stacks.
+    pub fn new_kernel() -> Self {
+        let mut memory_set = Self::new_bare();
+        // map trampoline
+        memory_set.map_trampoline();
+        // map kernel sections
+        println!(".text [{:#x}, {:#x})", stext as usize, etext as usize);
+        println!(".rodata [{:#x}, {:#x})", srodata as usize, erodata as usize);
+        println!(".data [{:#x}, {:#x})", sdata as usize, edata as usize);
+        println!(".bss [{:#x}, {:#x})", sbss_with_stack as usize, ebss as usize);
+        println!("mapping .text section");
+        memory_set.push(MapArea::new(
+            (stext as usize).into(),
+            (etext as usize).into(),
+            MapType::Identical,
+            MapPermission::R | MapPermission::X,
+        ), None);
+        println!("mapping .rodata section");
+        memory_set.push(MapArea::new(
+            (srodata as usize).into(),
+            (erodata as usize).into(),
+            MapType::Identical,
+            MapPermission::R,
+        ), None);
+        println!("mapping .data section");
+        memory_set.push(MapArea::new(
+            (sdata as usize).into(),
+            (edata as usize).into(),
+            MapType::Identical,
+            MapPermission::R | MapPermission::W,
+        ), None);
+        println!("mapping .bss section");
+        memory_set.push(MapArea::new(
+            (sbss_with_stack as usize).into(),
+            (ebss as usize).into(),
+            MapType::Identical,
+            MapPermission::R | MapPermission::W,
+        ), None);
+        println!("mapping physical memory");
+        memory_set.push(MapArea::new(
+            (ekernel as usize).into(),
+            MEMORY_END.into(),
+            MapType::Identical,
+            MapPermission::R | MapPermission::W,
+        ), None);
+        println!("mapping memory-mapped registers");
+        for pair in MMIO {
+            memory_set.push(MapArea::new(
+                (*pair).0.into(),
+                ((*pair).0 + (*pair).1).into(),
+                MapType::Identical,
+                MapPermission::R | MapPermission::W,
+            ), None);
+        }
+        memory_set
+    }
+    /// Include sections in elf and trampoline and TrapContext and user stack,
+    /// also returns user_sp and entry point.
+    pub fn from_elf(elf_data: &[u8]) -> (Self, usize, usize) {
+        let mut memory_set = Self::new_bare();
+        // map trampoline
+        memory_set.map_trampoline();
+        // map program headers of elf, with U flag
+        let elf = xmas_elf::ElfFile::new(elf_data).unwrap();
+        let elf_header = elf.header;
+        let magic = elf_header.pt1.magic;
+        assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!");
+        let ph_count = elf_header.pt2.ph_count();
+        let mut max_end_vpn = VirtPageNum(0);
+        for i in 0..ph_count {
+            let ph = elf.program_header(i).unwrap();
+            if ph.get_type().unwrap() == xmas_elf::program::Type::Load {
+                let start_va: VirtAddr = (ph.virtual_addr() as usize).into();
+                let end_va: VirtAddr = ((ph.virtual_addr() + ph.mem_size()) as usize).into();
+                let mut map_perm = MapPermission::U;
+                let ph_flags = ph.flags();
+                if ph_flags.is_read() { map_perm |= MapPermission::R; }
+                if ph_flags.is_write() { map_perm |= MapPermission::W; }
+                if ph_flags.is_execute() { map_perm |= MapPermission::X; }
+                let map_area = MapArea::new(
+                    start_va,
+                    end_va,
+                    MapType::Framed,
+                    map_perm,
+                );
+                max_end_vpn = map_area.vpn_range.get_end();
+                memory_set.push(
+                    map_area,
+                    Some(&elf.input[ph.offset() as usize..(ph.offset() + ph.file_size()) as usize])
+                );
+            }
+        }
+        // map user stack with U flags
+        let max_end_va: VirtAddr = max_end_vpn.into();
+        let mut user_stack_bottom: usize = max_end_va.into();
+        // guard page
+        user_stack_bottom += PAGE_SIZE;
+        let user_stack_top = user_stack_bottom + USER_STACK_SIZE;
+        memory_set.push(MapArea::new(
+            user_stack_bottom.into(),
+            user_stack_top.into(),
+            MapType::Framed,
+            MapPermission::R | MapPermission::W | MapPermission::U,
+        ), None);
+        // map TrapContext
+        memory_set.push(MapArea::new(
+            TRAP_CONTEXT.into(),
+            TRAMPOLINE.into(),
+            MapType::Framed,
+            MapPermission::R | MapPermission::W,
+        ), None);
+        (memory_set, user_stack_top, elf.header.pt2.entry_point() as usize)
+    }
+    pub fn from_existed_user(user_space: &MemorySet) -> MemorySet {
+        let mut memory_set = Self::new_bare();
+        // map trampoline
+        memory_set.map_trampoline();
+        // copy data sections/trap_context/user_stack
+        for area in user_space.areas.iter() {
+            let new_area = MapArea::from_another(area);
+            memory_set.push(new_area, None);
+            // copy data from another space
+            for vpn in area.vpn_range {
+                let src_ppn = user_space.translate(vpn).unwrap().ppn();
+                let dst_ppn = memory_set.translate(vpn).unwrap().ppn();
+                dst_ppn.get_bytes_array().copy_from_slice(src_ppn.get_bytes_array());
+            }
+        }
+        memory_set
+    }
+    pub fn activate(&self) {
+        let satp = self.page_table.token();
+        unsafe {
+            satp::write(satp);
+            llvm_asm!("sfence.vma" :::: "volatile");
+        }
+    }
+    pub fn translate(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
+        self.page_table.translate(vpn)
+    }
+    pub fn recycle_data_pages(&mut self) {
+        //*self = Self::new_bare();
+        self.areas.clear();
+    }
+}
+
+pub struct MapArea {
+    vpn_range: VPNRange,
+    data_frames: BTreeMap<VirtPageNum, FrameTracker>,
+    map_type: MapType,
+    map_perm: MapPermission,
+}
+
+impl MapArea {
+    pub fn new(
+        start_va: VirtAddr,
+        end_va: VirtAddr,
+        map_type: MapType,
+        map_perm: MapPermission
+    ) -> Self {
+        let start_vpn: VirtPageNum = start_va.floor();
+        let end_vpn: VirtPageNum = end_va.ceil();
+        Self {
+            vpn_range: VPNRange::new(start_vpn, end_vpn),
+            data_frames: BTreeMap::new(),
+            map_type,
+            map_perm,
+        }
+    }
+    pub fn from_another(another: &MapArea) -> Self {
+        Self {
+            vpn_range: VPNRange::new(another.vpn_range.get_start(), another.vpn_range.get_end()),
+            data_frames: BTreeMap::new(),
+            map_type: another.map_type,
+            map_perm: another.map_perm,
+        }
+    }
+    pub fn map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) {
+        let ppn: PhysPageNum;
+        match self.map_type {
+            MapType::Identical => {
+                ppn = PhysPageNum(vpn.0);
+            }
+            MapType::Framed => {
+                let frame = frame_alloc().unwrap();
+                ppn = frame.ppn;
+                self.data_frames.insert(vpn, frame);
+            }
+        }
+        let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap();
+        page_table.map(vpn, ppn, pte_flags);
+    }
+    pub fn unmap_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) {
+        match self.map_type {
+            MapType::Framed => {
+                self.data_frames.remove(&vpn);
+            }
+            _ => {}
+        }
+        page_table.unmap(vpn);
+    }
+    pub fn map(&mut self, page_table: &mut PageTable) {
+        for vpn in self.vpn_range {
+            self.map_one(page_table, vpn);
+        }
+    }
+    pub fn unmap(&mut self, page_table: &mut PageTable) {
+        for vpn in self.vpn_range {
+            self.unmap_one(page_table, vpn);
+        }
+    }
+    /// data: start-aligned but maybe with shorter length
+    /// assume that all frames were cleared before
+    pub fn copy_data(&mut self, page_table: &mut PageTable, data: &[u8]) {
+        assert_eq!(self.map_type, MapType::Framed);
+        let mut start: usize = 0;
+        let mut current_vpn = self.vpn_range.get_start();
+        let len = data.len();
+        loop {
+            let src = &data[start..len.min(start + PAGE_SIZE)];
+            let dst = &mut page_table
+                .translate(current_vpn)
+                .unwrap()
+                .ppn()
+                .get_bytes_array()[..src.len()];
+            dst.copy_from_slice(src);
+            start += PAGE_SIZE;
+            if start >= len {
+                break;
+            }
+            current_vpn.step();
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum MapType {
+    Identical,
+    Framed,
+}
+
+bitflags! {
+    pub struct MapPermission: u8 {
+        const R = 1 << 1;
+        const W = 1 << 2;
+        const X = 1 << 3;
+        const U = 1 << 4;
+    }
+}
+
+#[allow(unused)]
+pub fn remap_test() {
+    let mut kernel_space = KERNEL_SPACE.lock();
+    let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into();
+    let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into();
+    let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into();
+    assert_eq!(
+        kernel_space.page_table.translate(mid_text.floor()).unwrap().writable(),
+        false
+    );
+    assert_eq!(
+        kernel_space.page_table.translate(mid_rodata.floor()).unwrap().writable(),
+        false,
+    );
+    assert_eq!(
+        kernel_space.page_table.translate(mid_data.floor()).unwrap().executable(),
+        false,
+    );
+    println!("remap_test passed!");
+}
\ No newline at end of file
diff --git a/codes/os/src/mm/mod.rs b/codes/os/src/mm/mod.rs
new file mode 100644
index 00000000..dbd47fe7
--- /dev/null
+++ b/codes/os/src/mm/mod.rs
@@ -0,0 +1,28 @@
+mod heap_allocator;
+mod address;
+mod frame_allocator;
+mod page_table;
+mod memory_set;
+
+use page_table::PTEFlags;
+use address::VPNRange;
+pub use address::{PhysAddr, VirtAddr, PhysPageNum, VirtPageNum, StepByOne};
+pub use frame_allocator::{FrameTracker, frame_alloc, frame_dealloc,};
+pub use page_table::{
+    PageTable,
+    PageTableEntry,
+    translated_byte_buffer,
+    translated_str,
+    translated_ref,
+    translated_refmut,
+    UserBuffer,
+    UserBufferIterator,
+};
+pub use memory_set::{MemorySet, KERNEL_SPACE, MapPermission, kernel_token};
+pub use memory_set::remap_test;
+
+pub fn init() {
+    heap_allocator::init_heap();
+    frame_allocator::init_frame_allocator();
+    KERNEL_SPACE.lock().activate();
+}
\ No newline at end of file
diff --git a/codes/os/src/mm/page_table.rs b/codes/os/src/mm/page_table.rs
new file mode 100644
index 00000000..b1a227fa
--- /dev/null
+++ b/codes/os/src/mm/page_table.rs
@@ -0,0 +1,255 @@
+use super::{
+    frame_alloc,
+    PhysPageNum,
+    FrameTracker,
+    VirtPageNum,
+    VirtAddr,
+    PhysAddr,
+    StepByOne
+};
+use alloc::vec::Vec;
+use alloc::vec;
+use alloc::string::String;
+use bitflags::*;
+
+bitflags! {
+    pub struct PTEFlags: u8 {
+        const V = 1 << 0;
+        const R = 1 << 1;
+        const W = 1 << 2;
+        const X = 1 << 3;
+        const U = 1 << 4;
+        const G = 1 << 5;
+        const A = 1 << 6;
+        const D = 1 << 7;
+    }
+}
+
+#[derive(Copy, Clone)]
+#[repr(C)]
+pub struct PageTableEntry {
+    pub bits: usize,
+}
+
+impl PageTableEntry {
+    pub fn new(ppn: PhysPageNum, flags: PTEFlags) -> Self {
+        PageTableEntry {
+            bits: ppn.0 << 10 | flags.bits as usize,
+        }
+    }
+    pub fn empty() -> Self {
+        PageTableEntry {
+            bits: 0,
+        }
+    }
+    pub fn ppn(&self) -> PhysPageNum {
+        (self.bits >> 10 & ((1usize << 44) - 1)).into()
+    }
+    pub fn flags(&self) -> PTEFlags {
+        PTEFlags::from_bits(self.bits as u8).unwrap()
+    }
+    pub fn is_valid(&self) -> bool {
+        (self.flags() & PTEFlags::V) != PTEFlags::empty()
+    }
+    pub fn readable(&self) -> bool {
+        (self.flags() & PTEFlags::R) != PTEFlags::empty()
+    }
+    pub fn writable(&self) -> bool {
+        (self.flags() & PTEFlags::W) != PTEFlags::empty()
+    }
+    pub fn executable(&self) -> bool {
+        (self.flags() & PTEFlags::X) != PTEFlags::empty()
+    }
+}
+
+pub struct PageTable {
+    root_ppn: PhysPageNum,
+    frames: Vec<FrameTracker>,
+}
+
+/// Assume that it won't oom when creating/mapping.
+impl PageTable {
+    pub fn new() -> Self {
+        let frame = frame_alloc().unwrap();
+        PageTable {
+            root_ppn: frame.ppn,
+            frames: vec![frame],
+        }
+    }
+    /// Temporarily used to get arguments from user space.
+    pub fn from_token(satp: usize) -> Self {
+        Self {
+            root_ppn: PhysPageNum::from(satp & ((1usize << 44) - 1)),
+            frames: Vec::new(),
+        }
+    }
+    fn find_pte_create(&mut self, vpn: VirtPageNum) -> Option<&mut PageTableEntry> {
+        let idxs = vpn.indexes();
+        let mut ppn = self.root_ppn;
+        let mut result: Option<&mut PageTableEntry> = None;
+        for i in 0..3 {
+            let pte = &mut ppn.get_pte_array()[idxs[i]];
+            if i == 2 {
+                result = Some(pte);
+                break;
+            }
+            if !pte.is_valid() {
+                let frame = frame_alloc().unwrap();
+                *pte = PageTableEntry::new(frame.ppn, PTEFlags::V);
+                self.frames.push(frame);
+            }
+            ppn = pte.ppn();
+        }
+        result
+    }
+    fn find_pte(&self, vpn: VirtPageNum) -> Option<&PageTableEntry> {
+        let idxs = vpn.indexes();
+        let mut ppn = self.root_ppn;
+        let mut result: Option<&PageTableEntry> = None;
+        for i in 0..3 {
+            let pte = &ppn.get_pte_array()[idxs[i]];
+            if i == 2 {
+                result = Some(pte);
+                break;
+            }
+            if !pte.is_valid() {
+                return None;
+            }
+            ppn = pte.ppn();
+        }
+        result
+    }
+    #[allow(unused)]
+    pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) {
+        let pte = self.find_pte_create(vpn).unwrap();
+        assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn);
+        *pte = PageTableEntry::new(ppn, flags | PTEFlags::V);
+    }
+    #[allow(unused)]
+    pub fn unmap(&mut self, vpn: VirtPageNum) {
+        let pte = self.find_pte_create(vpn).unwrap();
+        assert!(pte.is_valid(), "vpn {:?} is invalid before unmapping", vpn);
+        *pte = PageTableEntry::empty();
+    }
+    pub fn translate(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
+        self.find_pte(vpn)
+            .map(|pte| {pte.clone()})
+    }
+    pub fn translate_va(&self, va: VirtAddr) -> Option<PhysAddr> {
+        self.find_pte(va.clone().floor())
+            .map(|pte| {
+                let aligned_pa: PhysAddr = pte.ppn().into();
+                let offset = va.page_offset();
+                let aligned_pa_usize: usize = aligned_pa.into();
+                (aligned_pa_usize + offset).into()
+            })
+    }
+    pub fn token(&self) -> usize {
+        8usize << 60 | self.root_ppn.0
+    }
+}
+
+pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> {
+    let page_table = PageTable::from_token(token);
+    let mut start = ptr as usize;
+    let end = start + len;
+    let mut v = Vec::new();
+    while start < end {
+        let start_va = VirtAddr::from(start);
+        let mut vpn = start_va.floor();
+        let ppn = page_table
+            .translate(vpn)
+            .unwrap()
+            .ppn();
+        vpn.step();
+        let mut end_va: VirtAddr = vpn.into();
+        end_va = end_va.min(VirtAddr::from(end));
+        if end_va.page_offset() == 0 {
+            v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..]);
+        } else {
+            v.push(&mut ppn.get_bytes_array()[start_va.page_offset()..end_va.page_offset()]);
+        }
+        start = end_va.into();
+    }
+    v
+}
+
+/// Load a string from other address spaces into kernel space without an end `\0`.
+pub fn translated_str(token: usize, ptr: *const u8) -> String {
+    let page_table = PageTable::from_token(token);
+    let mut string = String::new();
+    let mut va = ptr as usize;
+    loop {
+        let ch: u8 = *(page_table.translate_va(VirtAddr::from(va)).unwrap().get_mut());
+        if ch == 0 {
+            break;
+        }
+        string.push(ch as char);
+        va += 1;
+    }
+    string
+}
+
+pub fn translated_ref<T>(token: usize, ptr: *const T) -> &'static T {
+    let page_table = PageTable::from_token(token);
+    page_table.translate_va(VirtAddr::from(ptr as usize)).unwrap().get_ref()
+}
+
+pub fn translated_refmut<T>(token: usize, ptr: *mut T) -> &'static mut T {
+    let page_table = PageTable::from_token(token);
+    let va = ptr as usize;
+    page_table.translate_va(VirtAddr::from(va)).unwrap().get_mut()
+}
+
+pub struct UserBuffer {
+    pub buffers: Vec<&'static mut [u8]>,
+}
+
+impl UserBuffer {
+    pub fn new(buffers: Vec<&'static mut [u8]>) -> Self {
+        Self { buffers }
+    }
+    pub fn len(&self) -> usize {
+        let mut total: usize = 0;
+        for b in self.buffers.iter() {
+            total += b.len();
+        }
+        total
+    }
+}
+
+impl IntoIterator for UserBuffer {
+    type Item = *mut u8;
+    type IntoIter = UserBufferIterator;
+    fn into_iter(self) -> Self::IntoIter {
+        UserBufferIterator {
+            buffers: self.buffers,
+            current_buffer: 0,
+            current_idx: 0,
+        }
+    }
+}
+
+pub struct UserBufferIterator {
+    buffers: Vec<&'static mut [u8]>,
+    current_buffer: usize,
+    current_idx: usize,
+}
+
+impl Iterator for UserBufferIterator {
+    type Item = *mut u8;
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.current_buffer >= self.buffers.len() {
+            None
+        } else {
+            let r = &mut self.buffers[self.current_buffer][self.current_idx] as *mut _;
+            if self.current_idx + 1 == self.buffers[self.current_buffer].len() {
+                self.current_idx = 0;
+                self.current_buffer += 1;
+            } else {
+                self.current_idx += 1;
+            }
+            Some(r)
+        }
+    }
+}
\ No newline at end of file
diff --git a/codes/os/src/sbi.rs b/codes/os/src/sbi.rs
new file mode 100644
index 00000000..36e15fcc
--- /dev/null
+++ b/codes/os/src/sbi.rs
@@ -0,0 +1,43 @@
+#![allow(unused)]
+
+const SBI_SET_TIMER: usize = 0;
+const SBI_CONSOLE_PUTCHAR: usize = 1;
+const SBI_CONSOLE_GETCHAR: usize = 2;
+const SBI_CLEAR_IPI: usize = 3;
+const SBI_SEND_IPI: usize = 4;
+const SBI_REMOTE_FENCE_I: usize = 5;
+const SBI_REMOTE_SFENCE_VMA: usize = 6;
+const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
+const SBI_SHUTDOWN: usize = 8;
+
+#[inline(always)]
+fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
+    let mut ret;
+    unsafe {
+        llvm_asm!("ecall"
+            : "={x10}" (ret)
+            : "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which)
+            : "memory"
+            : "volatile"
+        );
+    }
+    ret
+}
+
+pub fn set_timer(timer: usize) {
+    sbi_call(SBI_SET_TIMER, timer, 0, 0);
+}
+
+pub fn console_putchar(c: usize) {
+    sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0);
+}
+
+pub fn console_getchar() -> usize {
+    sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
+}
+
+pub fn shutdown() -> ! {
+    sbi_call(SBI_SHUTDOWN, 0, 0, 0);
+    panic!("It should shutdown!");
+}
+
diff --git a/codes/os/src/syscall/fs.rs b/codes/os/src/syscall/fs.rs
new file mode 100644
index 00000000..6599b405
--- /dev/null
+++ b/codes/os/src/syscall/fs.rs
@@ -0,0 +1,116 @@
+use crate::mm::{
+    UserBuffer,
+    translated_byte_buffer,
+    translated_refmut,
+    translated_str,
+};
+use crate::task::{current_user_token, current_task};
+use crate::fs::{make_pipe, OpenFlags, open};
+use alloc::sync::Arc;
+use alloc::vec;
+use easy_fs::DiskInodeType;
+
+pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
+    let token = current_user_token();
+    let task = current_task().unwrap();
+    let inner = task.acquire_inner_lock();
+    if fd >= inner.fd_table.len() {
+        return -1;
+    }
+    if let Some(file) = &inner.fd_table[fd] {
+        if !file.writable() {
+            return -1;
+        }
+        let file = file.clone();
+        // release Task lock manually to avoid deadlock
+        drop(inner);
+        file.write(
+            UserBuffer::new(translated_byte_buffer(token, buf, len))
+        ) as isize
+    } else {
+        -1
+    }
+}
+
+pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize {
+    let token = current_user_token();
+    let task = current_task().unwrap();
+    let inner = task.acquire_inner_lock();
+    if fd >= inner.fd_table.len() {
+        return -1;
+    }
+    if let Some(file) = &inner.fd_table[fd] {
+        let file = file.clone();
+        if !file.readable() {
+            return -1;
+        }
+        // release Task lock manually to avoid deadlock
+        drop(inner);
+        file.read(
+            UserBuffer::new(translated_byte_buffer(token, buf, len))
+        ) as isize
+    } else {
+        -1
+    }
+}
+
+pub fn sys_open(path: *const u8, flags: u32) -> isize {
+    let task = current_task().unwrap();
+    let token = current_user_token();
+    // 这里传入的地址为用户的虚地址,因此要使用用户的虚地址进行映射
+    let path = translated_str(token, path);
+    if let Some(inode) = open(
+        0,
+        path.as_str(),
+        OpenFlags::from_bits(flags).unwrap(),
+        DiskInodeType::File
+    ) {
+        let mut inner = task.acquire_inner_lock();
+        let fd = inner.alloc_fd();
+        inner.fd_table[fd] = Some(inode);
+        fd as isize
+    } else {
+        -1
+    }
+}
+
+pub fn sys_close(fd: usize) -> isize {
+    let task = current_task().unwrap();
+    let mut inner = task.acquire_inner_lock();
+    if fd >= inner.fd_table.len() {
+        return -1;
+    }
+    if inner.fd_table[fd].is_none() {
+        return -1;
+    }
+    inner.fd_table[fd].take();
+    0
+}
+
+pub fn sys_pipe(pipe: *mut usize) -> isize {
+    let task = current_task().unwrap();
+    let token = current_user_token();
+    let mut inner = task.acquire_inner_lock();
+    let (pipe_read, pipe_write) = make_pipe();
+    let read_fd = inner.alloc_fd();
+    inner.fd_table[read_fd] = Some(pipe_read);
+    let write_fd = inner.alloc_fd();
+    inner.fd_table[write_fd] = Some(pipe_write);
+    *translated_refmut(token, pipe) = read_fd;
+    *translated_refmut(token, unsafe { pipe.add(1) }) = write_fd;
+    0
+}
+
+pub fn sys_dup(fd: usize) -> isize {
+    let task = current_task().unwrap();
+    let mut inner = task.acquire_inner_lock();
+    if fd >= inner.fd_table.len() {
+        return -1;
+    }
+    if inner.fd_table[fd].is_none() {
+        return -1;
+    }
+    let new_fd = inner.alloc_fd();
+    inner.fd_table[new_fd] = Some(Arc::clone(inner.fd_table[fd].as_ref().unwrap()));
+    new_fd as isize
+}
\ No newline at end of file
diff --git a/codes/os/src/syscall/mod.rs b/codes/os/src/syscall/mod.rs
new file mode 100644
index 00000000..4683d055
--- /dev/null
+++ b/codes/os/src/syscall/mod.rs
@@ -0,0 +1,39 @@
+const SYSCALL_DUP: usize = 24;
+const SYSCALL_OPEN: usize = 56;
+const SYSCALL_CLOSE: usize = 57;
+const SYSCALL_PIPE: usize = 59;
+const SYSCALL_READ: usize = 63;
+const SYSCALL_WRITE: usize = 64;
+const SYSCALL_EXIT: usize = 93;
+const SYSCALL_YIELD: usize = 124;
+const SYSCALL_GET_TIME: usize = 169;
+const SYSCALL_GETPID: usize = 172;
+const SYSCALL_FORK: usize = 220;
+const SYSCALL_EXEC: usize = 221;
+const SYSCALL_WAITPID: usize = 260;
+
+mod fs;
+mod process;
+
+use fs::*;
+use process::*;
+
+pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
+    match syscall_id {
+        SYSCALL_DUP=> sys_dup(args[0]),
+        SYSCALL_OPEN => sys_open(args[0] as *const u8, args[1] as u32),
+        SYSCALL_CLOSE => sys_close(args[0]),
+        SYSCALL_PIPE => sys_pipe(args[0] as *mut usize),
+        SYSCALL_READ => sys_read(args[0], args[1] as *const u8, args[2]),
+        SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]),
+        SYSCALL_EXIT => sys_exit(args[0] as i32),
+        SYSCALL_YIELD => sys_yield(),
+        SYSCALL_GET_TIME => sys_get_time(),
+        SYSCALL_GETPID => sys_getpid(),
+        SYSCALL_FORK => sys_fork(),
+        SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize),
+        SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32),
+        _ => panic!("Unsupported syscall_id: {}", syscall_id),
+    }
+}
+
diff --git a/codes/os/src/syscall/process.rs b/codes/os/src/syscall/process.rs
new file mode 100644
index 00000000..36e420e2
--- /dev/null
+++ b/codes/os/src/syscall/process.rs
@@ -0,0 +1,117 @@
+use crate::task::{
+    suspend_current_and_run_next,
+    exit_current_and_run_next,
+    current_task,
+    current_user_token,
+    add_task,
+};
+use crate::timer::get_time_ms;
+use crate::mm::{
+    translated_str,
+    translated_refmut,
+    translated_ref,
+};
+use crate::fs::{
+    open,
+    OpenFlags,
+};
+use alloc::sync::Arc;
+use alloc::vec::Vec;
+use alloc::vec;
+use alloc::string::String;
+use easy_fs::DiskInodeType;
+
+pub fn sys_exit(exit_code: i32) -> ! {
+    exit_current_and_run_next(exit_code);
+    panic!("Unreachable in sys_exit!");
+}
+
+pub fn sys_yield() -> isize {
+    suspend_current_and_run_next();
+    0
+}
+
+pub fn sys_get_time() -> isize {
+    get_time_ms() as isize
+}
+
+pub fn sys_getpid() -> isize {
+    current_task().unwrap().pid.0 as isize
+}
+
+pub fn sys_fork() -> isize {
+    let current_task = current_task().unwrap();
+    let new_task = current_task.fork();
+    let new_pid = new_task.pid.0;
+    // modify trap context of new_task, because it returns immediately after switching
+    let trap_cx = new_task.acquire_inner_lock().get_trap_cx();
+    // we do not have to move to next instruction since we have done it before
+    // for child process, fork returns 0
+    trap_cx.x[10] = 0;
+    // add new task to scheduler
+    add_task(new_task);
+    new_pid as isize
+}
+
+pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize {
+    let token = current_user_token();
+    let path = translated_str(token, path);
+    let mut args_vec: Vec<String> = Vec::new();
+    loop {
+        let arg_str_ptr = *translated_ref(token, args);
+        if arg_str_ptr == 0 {
+            break;
+        }
+        args_vec.push(translated_str(token, arg_str_ptr as *const u8));
+        unsafe { args = args.add(1); }
+    }
+    if let Some(app_inode) = open(0,path.as_str(), OpenFlags::RDONLY, DiskInodeType::File) {
+        let all_data = app_inode.read_all();
+        let task = current_task().unwrap();
+        let argc = args_vec.len();
+        task.exec(all_data.as_slice(), args_vec);
+        // return argc because cx.x[10] will be covered with it later
+        argc as isize
+    } else {
+        -1
+    }
+}
+
+/// If there is not a child process whose pid is same as given, return -1.
+/// Else if there is a child process but it is still running, return -2.
+pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize {
+    let task = current_task().unwrap();
+    // find a child process
+
+    // ---- hold current PCB lock
+    let mut inner = task.acquire_inner_lock();
+    if inner.children
+        .iter()
+        .find(|p| {pid == -1 || pid as usize == p.getpid()})
+        .is_none() {
+        return -1;
+        // ---- release current PCB lock
+    }
+    let pair = inner.children
+        .iter()
+        .enumerate()
+        .find(|(_, p)| {
+            // ++++ temporarily hold child PCB lock
+            p.acquire_inner_lock().is_zombie() && (pid == -1 || pid as usize == p.getpid())
+            // ++++ release child PCB lock
+        });
+    if let Some((idx, _)) = pair {
+        let child = inner.children.remove(idx);
+        // confirm that child will be deallocated after being removed from children list
+        assert_eq!(Arc::strong_count(&child), 1);
+        let found_pid = child.getpid();
+        // ++++ temporarily hold child lock
+        let exit_code = child.acquire_inner_lock().exit_code;
+        // ++++ release child PCB lock
+        *translated_refmut(inner.memory_set.token(), exit_code_ptr) = exit_code;
+        found_pid as isize
+    } else {
+        -2
+    }
+    // ---- release current PCB lock automatically
+}
\ No newline at end of file
diff --git a/codes/os/src/task/context.rs b/codes/os/src/task/context.rs
new file mode 100644
index 00000000..a91f45db
--- /dev/null
+++ b/codes/os/src/task/context.rs
@@ -0,0 +1,18 @@
+use crate::trap::trap_return;
+
+#[derive(Debug)]
+#[repr(C)]
+pub struct TaskContext {
+    ra: usize,
+    s: [usize; 12],
+}
+
+impl TaskContext {
+    pub fn goto_trap_return() -> Self {
+        Self {
+            ra: trap_return as usize,
+            s: [0; 12],
+        }
+    }
+}
+
diff --git a/codes/os/src/task/manager.rs b/codes/os/src/task/manager.rs
new file mode 100644
index 00000000..ed223916
--- /dev/null
+++ b/codes/os/src/task/manager.rs
@@ -0,0 +1,34 @@
+use super::TaskControlBlock;
+use alloc::collections::VecDeque;
+use alloc::sync::Arc;
+use spin::Mutex;
+use lazy_static::*;
+
+pub struct TaskManager {
+    ready_queue: VecDeque<Arc<TaskControlBlock>>,
+}
+
+/// A simple FIFO scheduler.
+impl TaskManager {
+    pub fn new() -> Self {
+        Self { ready_queue: VecDeque::new(), }
+    }
+    pub fn add(&mut self, task: Arc<TaskControlBlock>) {
+        self.ready_queue.push_back(task);
+    }
+    pub fn fetch(&mut self) -> Option<Arc<TaskControlBlock>> {
+        self.ready_queue.pop_front()
+    }
+}
+
+lazy_static! {
+    pub static ref TASK_MANAGER: Mutex<TaskManager> = Mutex::new(TaskManager::new());
+}
+
+pub fn add_task(task: Arc<TaskControlBlock>) {
+    TASK_MANAGER.lock().add(task);
+}
+
+pub fn fetch_task() -> Option<Arc<TaskControlBlock>> {
+    TASK_MANAGER.lock().fetch()
+}
\ No newline at end of file
diff --git a/codes/os/src/task/mod.rs b/codes/os/src/task/mod.rs
new file mode 100644
index 00000000..07ead764
--- /dev/null
+++ b/codes/os/src/task/mod.rs
@@ -0,0 +1,92 @@
+mod context;
+mod switch;
+mod task;
+mod manager;
+mod processor;
+mod pid;
+
+use crate::fs::{open, OpenFlags};
+use easy_fs::DiskInodeType;
+use switch::__switch;
+use task::{TaskControlBlock, TaskStatus};
+use alloc::sync::Arc;
+use alloc::vec;
+use manager::fetch_task;
+use lazy_static::*;
+pub use context::TaskContext;
+
+pub use processor::{
+    run_tasks,
+    current_task,
+    current_user_token,
+    current_trap_cx,
+    take_current_task,
+    schedule,
+};
+pub use manager::add_task;
+pub use pid::{PidHandle, pid_alloc, KernelStack};
+
+pub fn suspend_current_and_run_next() {
+    // There must be an application running.
+    let task = take_current_task().unwrap();
+
+    // ---- hold current PCB lock
+    let mut task_inner = task.acquire_inner_lock();
+    //task_inner.print_cx();
+    let task_cx_ptr2 = task_inner.get_task_cx_ptr2();
+    // Change status to Ready
+    task_inner.task_status = TaskStatus::Ready;
+    drop(task_inner);
+    // ---- release current PCB lock
+
+    // push back to ready queue.
+    add_task(task);
+
+    // jump to scheduling cycle
+    schedule(task_cx_ptr2);
+}
+
+pub fn exit_current_and_run_next(exit_code: i32) {
+    // take from Processor
+    let task = take_current_task().unwrap();
+    // **** hold current PCB lock
+    let mut inner = task.acquire_inner_lock();
+    // Change status to Zombie
+    inner.task_status = TaskStatus::Zombie;
+    // Record exit code
+    inner.exit_code = exit_code;
+    // do not move to its parent but under initproc
+
+    // ++++++ hold initproc PCB lock here
+    {
+        let mut initproc_inner = INITPROC.acquire_inner_lock();
+        for child in inner.children.iter() {
+            child.acquire_inner_lock().parent = Some(Arc::downgrade(&INITPROC));
+            initproc_inner.children.push(child.clone());
+        }
+    }
+    // ++++++ release parent PCB lock here
+
+    inner.children.clear();
+    // deallocate user space
+    inner.memory_set.recycle_data_pages();
+    drop(inner);
+    // **** release current PCB lock
+    // drop task manually to maintain rc correctly
+    drop(task);
+    // we do not have to save task context
+    let _unused: usize = 0;
+    schedule(&_unused as *const _);
+}
+
+lazy_static! {
+    pub static ref INITPROC: Arc<TaskControlBlock> = Arc::new({
+        let inode = open(0,"initproc", OpenFlags::RDONLY, DiskInodeType::File).unwrap();
+        let v = inode.read_all();
+        TaskControlBlock::new(v.as_slice())
+    });
+}
+
+pub fn add_initproc() {
+    add_task(INITPROC.clone());
+}
diff --git a/codes/os/src/task/pid.rs b/codes/os/src/task/pid.rs
new file mode 100644
index 00000000..7cfdb83d
--- /dev/null
+++ b/codes/os/src/task/pid.rs
@@ -0,0 +1,105 @@
+use alloc::vec::Vec;
+use lazy_static::*;
+use spin::Mutex;
+use crate::mm::{KERNEL_SPACE, MapPermission, VirtAddr};
+use crate::config::{
+    PAGE_SIZE,
+    TRAMPOLINE,
+    KERNEL_STACK_SIZE,
+};
+
+struct PidAllocator {
+    current: usize,
+    recycled: Vec<usize>,
+}
+
+impl PidAllocator {
+    pub fn new() -> Self {
+        PidAllocator {
+            current: 0,
+            recycled: Vec::new(),
+        }
+    }
+    pub fn alloc(&mut self) -> PidHandle {
+        if let Some(pid) = self.recycled.pop() {
+            PidHandle(pid)
+        } else {
+            self.current += 1;
+            PidHandle(self.current - 1)
+        }
+    }
+    pub fn dealloc(&mut self, pid: usize) {
+        assert!(pid < self.current);
+        assert!(
+            self.recycled.iter().find(|ppid| **ppid == pid).is_none(),
+            "pid {} has been deallocated!", pid
+        );
+        self.recycled.push(pid);
+    }
+}
+
+lazy_static! {
+    static ref PID_ALLOCATOR : Mutex<PidAllocator> = Mutex::new(PidAllocator::new());
+}
+
+pub struct PidHandle(pub usize);
+
+impl Drop for PidHandle {
+    fn drop(&mut self) {
+        //println!("drop pid {}", self.0);
+        PID_ALLOCATOR.lock().dealloc(self.0);
+    }
+}
+
+pub fn pid_alloc() -> PidHandle {
+    PID_ALLOCATOR.lock().alloc()
+}
+
+/// Return (bottom, top) of a kernel stack in kernel space.
+pub fn kernel_stack_position(app_id: usize) -> (usize, usize) {
+    let top = TRAMPOLINE - app_id * (KERNEL_STACK_SIZE + PAGE_SIZE);
+    let bottom = top - KERNEL_STACK_SIZE;
+    (bottom, top)
+}
+
+pub struct KernelStack {
+    pid: usize,
+}
+
+impl KernelStack {
+    pub fn new(pid_handle: &PidHandle) -> Self {
+        let pid = pid_handle.0;
+        let (kernel_stack_bottom, kernel_stack_top) = kernel_stack_position(pid);
+        KERNEL_SPACE
+            .lock()
+            .insert_framed_area(
+                kernel_stack_bottom.into(),
+                kernel_stack_top.into(),
+                MapPermission::R | MapPermission::W,
+            );
+        KernelStack {
+            pid: pid_handle.0,
+        }
+    }
+    pub fn push_on_top<T>(&self, value: T) -> *mut T where
+        T: Sized, {
+        let kernel_stack_top = self.get_top();
+        let ptr_mut = (kernel_stack_top - core::mem::size_of::<T>()) as *mut T;
+        unsafe { *ptr_mut = value; }
+        ptr_mut
+    }
+    pub fn get_top(&self) -> usize {
+        let (_, kernel_stack_top) = kernel_stack_position(self.pid);
+        kernel_stack_top
+    }
+}
+
+impl Drop for KernelStack {
+    fn drop(&mut self) {
+        let (kernel_stack_bottom, _) = kernel_stack_position(self.pid);
+        let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into();
+        KERNEL_SPACE
+            .lock()
+            .remove_area_with_start_vpn(kernel_stack_bottom_va.into());
+    }
+}
\ No newline at end of file
diff --git a/codes/os/src/task/processor.rs b/codes/os/src/task/processor.rs
new file mode 100644
index 00000000..9cdced4e
--- /dev/null
+++ b/codes/os/src/task/processor.rs
@@ -0,0 +1,95 @@
+use super::TaskControlBlock;
+use alloc::sync::Arc;
+use core::{borrow::Borrow, cell::RefCell};
+use lazy_static::*;
+use super::{fetch_task, TaskStatus};
+use super::__switch;
+use crate::trap::TrapContext;
+
+pub struct Processor {
+    inner: RefCell<ProcessorInner>,
+}
+
+unsafe impl Sync for Processor {}
+
+struct ProcessorInner {
+    current: Option<Arc<TaskControlBlock>>,
+    idle_task_cx_ptr: usize,
+}
+
+impl Processor {
+    pub fn new() -> Self {
+        Self {
+            inner: RefCell::new(ProcessorInner {
+                current: None,
+                idle_task_cx_ptr: 0,
+            }),
+        }
+    }
+    fn get_idle_task_cx_ptr2(&self) -> *const usize {
+        let inner = self.inner.borrow();
+        &inner.idle_task_cx_ptr as *const usize
+    }
+    pub fn run(&self) {
+        loop {
+            if let Some(task) = fetch_task() {
+                let idle_task_cx_ptr2 = self.get_idle_task_cx_ptr2();
+                // acquire
+                let mut task_inner = task.acquire_inner_lock();
+                let next_task_cx_ptr2 = task_inner.get_task_cx_ptr2();
+                task_inner.task_status = TaskStatus::Running;
+                drop(task_inner);
+                // release
+                self.inner.borrow_mut().current = Some(task);
+                unsafe {
+                    __switch(
+                        idle_task_cx_ptr2,
+                        next_task_cx_ptr2,
+                    );
+                }
+            }
+        }
+    }
+    pub fn take_current(&self) -> Option<Arc<TaskControlBlock>> {
+        self.inner.borrow_mut().current.take()
+    }
+    pub fn current(&self) -> Option<Arc<TaskControlBlock>> {
+        self.inner.borrow().current.as_ref().map(|task| Arc::clone(task))
+    }
+}
+
+lazy_static! {
+    pub static ref PROCESSOR: Processor = Processor::new();
+}
+
+pub fn run_tasks() {
+    PROCESSOR.run();
+}
+
+pub fn take_current_task() -> Option<Arc<TaskControlBlock>> {
+    PROCESSOR.take_current()
+}
+
+pub fn current_task() -> Option<Arc<TaskControlBlock>> {
+    PROCESSOR.current()
+}
+
+pub fn current_user_token() -> usize {
+    let task = current_task().unwrap();
+    let token = task.acquire_inner_lock().get_user_token();
+    token
+}
+
+pub fn current_trap_cx() -> &'static mut TrapContext {
+    current_task().unwrap().acquire_inner_lock().get_trap_cx()
+}
+
+pub fn schedule(switched_task_cx_ptr2: *const usize) {
+    let idle_task_cx_ptr2 = PROCESSOR.get_idle_task_cx_ptr2();
+    unsafe {
+        __switch(
+            switched_task_cx_ptr2,
+            idle_task_cx_ptr2,
+        );
+    }
+}
diff --git a/codes/os/src/task/switch.S b/codes/os/src/task/switch.S
new file mode 100644
index 00000000..262511fe
--- /dev/null
+++ b/codes/os/src/task/switch.S
@@ -0,0 +1,37 @@
+.altmacro
+.macro SAVE_SN n
+    sd s\n, (\n+1)*8(sp)
+.endm
+.macro LOAD_SN n
+    ld s\n, (\n+1)*8(sp)
+.endm
+    .section .text
+    .globl __switch
+__switch:
+    # __switch(
+    #     current_task_cx_ptr2: &*const TaskContext,
+    #     next_task_cx_ptr2: &*const TaskContext
+    # )
+    # push TaskContext to current sp and save its address to where a0 points to
+    addi sp, sp, -13*8
+    sd sp, 0(a0)
+    # fill TaskContext with ra & s0-s11
+    sd ra, 0(sp)
+    .set n, 0
+    .rept 12
+        SAVE_SN %n
+        .set n, n + 1
+    .endr
+    # ready for loading TaskContext a1 points to
+    ld sp, 0(a1)
+    # load registers in the TaskContext
+    ld ra, 0(sp)
+    .set n, 0
+    .rept 12
+        LOAD_SN %n
+        .set n, n + 1
+    .endr
+    # pop TaskContext
+    addi sp, sp, 13*8
+    ret
+
diff --git a/codes/os/src/task/switch.rs b/codes/os/src/task/switch.rs
new file mode 100644
index 00000000..867fcb1e
--- /dev/null
+++ b/codes/os/src/task/switch.rs
@@ -0,0 +1,8 @@
+global_asm!(include_str!("switch.S"));
+
+extern "C" {
+    pub fn __switch(
+        current_task_cx_ptr2: *const usize,
+        next_task_cx_ptr2: *const usize
+    );
+}
diff --git a/codes/os/src/task/task.rs b/codes/os/src/task/task.rs
new file mode 100644
index 00000000..c583d5d6
--- /dev/null
+++ b/codes/os/src/task/task.rs
@@ -0,0 +1,245 @@
+use crate::{console::print, mm::{
+    MemorySet,
+    PhysPageNum,
+    KERNEL_SPACE, 
+    VirtAddr,
+    translated_refmut,
+}};
+use crate::trap::{TrapContext, trap_handler};
+use crate::config::{TRAP_CONTEXT};
+use super::TaskContext;
+use super::{PidHandle, pid_alloc, KernelStack};
+use alloc::sync::{Weak, Arc};
+use alloc::vec;
+use alloc::vec::Vec;
+use alloc::string::String;
+use spin::{Mutex, MutexGuard};
+use crate::fs::{File, Stdin, Stdout};
+
+pub struct TaskControlBlock {
+    // immutable
+    pub pid: PidHandle,
+    pub kernel_stack: KernelStack,
+    // mutable
+    inner: Mutex<TaskControlBlockInner>,
+}
+
+pub struct TaskControlBlockInner {
+    pub trap_cx_ppn: PhysPageNum,
+    pub base_size: usize,
+    pub task_cx_ptr: usize,
+    pub task_status: TaskStatus,
+    pub memory_set: MemorySet,
+    pub parent: Option<Weak<TaskControlBlock>>,
+    pub children: Vec<Arc<TaskControlBlock>>,
+    pub exit_code: i32,
+    pub fd_table: Vec<Option<Arc<dyn File + Send + Sync>>>,
+}
+
+impl TaskControlBlockInner {
+    pub fn get_task_cx_ptr2(&self) -> *const usize {
+        &self.task_cx_ptr as *const usize
+    }
+    pub fn get_trap_cx(&self) -> &'static mut TrapContext {
+        self.trap_cx_ppn.get_mut()
+    }
+    pub fn get_user_token(&self) -> usize {
+        self.memory_set.token()
+    }
+    fn get_status(&self) -> TaskStatus {
+        self.task_status
+    }
+    pub fn is_zombie(&self) -> bool {
+        self.get_status() == TaskStatus::Zombie
+    }
+    pub fn alloc_fd(&mut self) -> usize {
+        if let Some(fd) = (0..self.fd_table.len())
+            .find(|fd| self.fd_table[*fd].is_none()) {
+            fd
+        } else {
+            self.fd_table.push(None);
+            self.fd_table.len() - 1
+        }
+    }
+    pub fn print_cx(&self) {
+        unsafe{ 
+            println!("task_cx = {:?}", *(self.task_cx_ptr as *const TaskContext) );
+        }
+    }
+}
+
+
+
+impl TaskControlBlock {
+    pub fn acquire_inner_lock(&self) -> MutexGuard<TaskControlBlockInner> {
+        self.inner.lock()
+    }
+    pub fn new(elf_data: &[u8]) -> Self {
+        // memory_set with elf program headers/trampoline/trap context/user stack
+        let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data);
+        let trap_cx_ppn = memory_set
+            .translate(VirtAddr::from(TRAP_CONTEXT).into())
+            .unwrap()
+            .ppn();
+        // alloc a pid and a kernel stack in kernel space
+        let pid_handle = pid_alloc();
+        let kernel_stack = KernelStack::new(&pid_handle);
+        let kernel_stack_top = kernel_stack.get_top();
+        // push a task context which goes to trap_return to the top of kernel stack
+        let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return());
+        let task_control_block = Self {
+            pid: pid_handle,
+            kernel_stack,
+            inner: Mutex::new(TaskControlBlockInner {
+                trap_cx_ppn,
+                base_size: user_sp,
+                task_cx_ptr: task_cx_ptr as usize,
+                task_status: TaskStatus::Ready,
+                memory_set,
+                parent: None,
+                children: Vec::new(),
+                exit_code: 0,
+                fd_table: vec![
+                    // 0 -> stdin
+                    Some(Arc::new(Stdin)),
+                    // 1 -> stdout
+                    Some(Arc::new(Stdout)),
+                    // 2 -> stderr
+                    Some(Arc::new(Stdout)),
+                ],
+            }),
+        };
+        // prepare TrapContext in user space
+        let trap_cx = task_control_block.acquire_inner_lock().get_trap_cx();
+        *trap_cx = TrapContext::app_init_context(
+            entry_point,
+            user_sp,
+            KERNEL_SPACE.lock().token(),
+            kernel_stack_top,
+            trap_handler as usize,
+        );
+        task_control_block
+    }
+    pub fn exec(&self, elf_data: &[u8], args: Vec<String>) {
+        // memory_set with elf program headers/trampoline/trap context/user stack
+        let (memory_set, mut user_sp, entry_point) = MemorySet::from_elf(elf_data);
+        let trap_cx_ppn = memory_set
+            .translate(VirtAddr::from(TRAP_CONTEXT).into())
+            .unwrap()
+            .ppn();
+        // push arguments on user stack
+        // sp减小[参数个数*usize],用于存放参数地址 
+        user_sp -= (args.len() + 1) * core::mem::size_of::<usize>();
+        let argv_base = user_sp;
+        
+        let mut argv: Vec<_> = (0..=args.len())
+            .map(|arg| {
+                translated_refmut(
+                    memory_set.token(),
+                    (argv_base + arg * core::mem::size_of::<usize>()) as *mut usize
+                )
+            })
+            .collect();
+
+        // argv的类型实际为 Vec<&'static mut T>
+        // 所以这里直接往对应的地址写数据
+        *argv[args.len()] = 0;
+        for i in 0..args.len() {
+            // sp继续向下增长,留空间给每个参数
+            // +1是因为需要'\0'
+            user_sp -= args[i].len() + 1;
+            *argv[i] = user_sp;
+            let mut p = user_sp;
+            // 将字符写入栈 [user_sp, user_sp + len]
+            for c in args[i].as_bytes() {
+                *translated_refmut(memory_set.token(), p as *mut u8) = *c;
+                p += 1;
+            }
+            *translated_refmut(memory_set.token(), p as *mut u8) = 0;
+        }
+        // make the user_sp aligned to 8B for k210 platform
+        user_sp -= user_sp % core::mem::size_of::<usize>();
+
+        // **** hold current PCB lock
+        let mut inner = self.acquire_inner_lock();
+        // substitute memory_set
+        inner.memory_set = memory_set;
+        // update trap_cx ppn
+        inner.trap_cx_ppn = trap_cx_ppn;
+        // initialize trap_cx
+        let mut trap_cx = TrapContext::app_init_context(
+            entry_point,
+            user_sp,
+            KERNEL_SPACE.lock().token(),
+            self.kernel_stack.get_top(),
+            trap_handler as usize,
+        );
+        trap_cx.x[10] = args.len();
+        trap_cx.x[11] = argv_base;
+        *inner.get_trap_cx() = trap_cx;
+        // **** release current PCB lock
+    }
+    pub fn fork(self: &Arc<TaskControlBlock>) -> Arc<TaskControlBlock> {
+        // ---- hold parent PCB lock
+        let mut parent_inner = self.acquire_inner_lock();
+        // copy user space(include trap context)
+        let memory_set = MemorySet::from_existed_user(
+            &parent_inner.memory_set
+        );
+        let trap_cx_ppn = memory_set
+            .translate(VirtAddr::from(TRAP_CONTEXT).into())
+            .unwrap()
+            .ppn();
+        // alloc a pid and a kernel stack in kernel space
+        let pid_handle = pid_alloc();
+        let kernel_stack = KernelStack::new(&pid_handle);
+        let kernel_stack_top = kernel_stack.get_top();
+        // push a goto_trap_return task_cx on the top of kernel stack
+        let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return());
+        // copy fd table
+        let mut new_fd_table: Vec<Option<Arc<dyn File + Send + Sync>>> = Vec::new();
+        for fd in parent_inner.fd_table.iter() {
+            if let Some(file) = fd {
+                new_fd_table.push(Some(file.clone()));
+            } else {
+                new_fd_table.push(None);
+            }
+        }
+        let task_control_block = Arc::new(TaskControlBlock {
+            pid: pid_handle,
+            kernel_stack,
+            inner: Mutex::new(TaskControlBlockInner {
+                trap_cx_ppn,
+                base_size: parent_inner.base_size,
+                task_cx_ptr: task_cx_ptr as usize,
+                task_status: TaskStatus::Ready,
+                memory_set,
+                parent: Some(Arc::downgrade(self)),
+                children: Vec::new(),
+                exit_code: 0,
+                fd_table: new_fd_table,
+            }),
+        });
+        // add child
+        parent_inner.children.push(task_control_block.clone());
+        // modify kernel_sp in trap_cx
+        // **** acquire child PCB lock
+        let trap_cx = task_control_block.acquire_inner_lock().get_trap_cx();
+        // **** release child PCB lock
+        trap_cx.kernel_sp = kernel_stack_top;
+        // return
+        task_control_block
+        // ---- release parent PCB lock
+    }
+    pub fn getpid(&self) -> usize {
+        self.pid.0
+    }
+
+}
+
+#[derive(Copy, Clone, PartialEq)]
+pub enum TaskStatus {
+    Ready,
+    Running,
+    Zombie,
+}
\ No newline at end of file
diff --git a/codes/os/src/timer.rs b/codes/os/src/timer.rs
new file mode 100644
index 00000000..92d50e3a
--- /dev/null
+++ b/codes/os/src/timer.rs
@@ -0,0 +1,18 @@
+use riscv::register::time;
+use crate::sbi::set_timer;
+use crate::config::CLOCK_FREQ;
+
+const TICKS_PER_SEC: usize = 100;
+const MSEC_PER_SEC: usize = 1000;
+
+pub fn get_time() -> usize {
+    time::read()
+}
+
+pub fn get_time_ms() -> usize {
+    time::read() / (CLOCK_FREQ / MSEC_PER_SEC)
+}
+
+pub fn set_next_trigger() {
+    set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
+}
\ No newline at end of file
diff --git a/codes/os/src/trap/context.rs b/codes/os/src/trap/context.rs
new file mode 100644
index 00000000..16f81415
--- /dev/null
+++ b/codes/os/src/trap/context.rs
@@ -0,0 +1,37 @@
+use riscv::register::sstatus::{Sstatus, self, SPP};
+
+#[repr(C)]
+#[derive(Debug)]
+pub struct TrapContext {
+    pub x: [usize; 32],
+    pub sstatus: Sstatus,
+    pub sepc: usize,
+    pub kernel_satp: usize,
+    pub kernel_sp: usize,
+    pub trap_handler: usize,
+}
+
+impl TrapContext {
+    pub fn set_sp(&mut self, sp: usize) { self.x[2] = sp; }
+    pub fn app_init_context(
+        entry: usize,
+        sp: usize,
+        kernel_satp: usize,
+        kernel_sp: usize,
+        trap_handler: usize,
+    ) -> Self {
+        let mut sstatus = sstatus::read();
+        // set CPU privilege to User after trapping back
+        sstatus.set_spp(SPP::User);
+        let mut cx = Self {
+            x: [0; 32],
+            sstatus,
+            sepc: entry,
+            kernel_satp,
+            kernel_sp,
+            trap_handler,
+        };
+        cx.set_sp(sp);
+        cx
+    }
+}
diff --git a/codes/os/src/trap/mod.rs b/codes/os/src/trap/mod.rs
new file mode 100644
index 00000000..f83560b2
--- /dev/null
+++ b/codes/os/src/trap/mod.rs
@@ -0,0 +1,117 @@
+mod context;
+
+use riscv::register::{
+    mtvec::TrapMode,
+    stvec,
+    scause::{
+        self,
+        Trap,
+        Exception,
+        Interrupt,
+    },
+    stval,
+    sie,
+};
+use crate::syscall::syscall;
+use crate::task::{
+    exit_current_and_run_next,
+    suspend_current_and_run_next,
+    current_user_token,
+    current_trap_cx,
+};
+use crate::timer::set_next_trigger;
+use crate::config::{TRAP_CONTEXT, TRAMPOLINE};
+
+global_asm!(include_str!("trap.S"));
+
+pub fn init() {
+    set_kernel_trap_entry();
+}
+
+fn set_kernel_trap_entry() {
+    unsafe {
+        stvec::write(trap_from_kernel as usize, TrapMode::Direct);
+    }
+}
+
+fn set_user_trap_entry() {
+    unsafe {
+        stvec::write(TRAMPOLINE as usize, TrapMode::Direct);
+    }
+}
+
+pub fn enable_timer_interrupt() {
+    unsafe { sie::set_stimer(); }
+}
+
+#[no_mangle]
+pub fn trap_handler() -> ! {
+    set_kernel_trap_entry();
+    let scause = scause::read();
+    let stval = stval::read();
+    match scause.cause() {
+        Trap::Exception(Exception::UserEnvCall) => {
+            // jump to next instruction anyway
+            let mut cx = current_trap_cx();
+            cx.sepc += 4;
+            // get system call return value
+            let result = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]);
+            // cx is changed during sys_exec, so we have to call it again
+            cx = current_trap_cx();
+            cx.x[10] = result as usize;
+        }
+        Trap::Exception(Exception::StoreFault) |
+        Trap::Exception(Exception::StorePageFault) |
+        Trap::Exception(Exception::InstructionFault) |
+        Trap::Exception(Exception::InstructionPageFault) |
+        Trap::Exception(Exception::LoadFault) |
+        Trap::Exception(Exception::LoadPageFault) => {
+            println!(
+                "[kernel] {:?} in application, bad addr = {:#x}, bad instruction = {:#x}, core dumped.",
+                scause.cause(),
+                stval,
+                current_trap_cx().sepc,
+            );
+            // page fault exit code
+            exit_current_and_run_next(-2);
+        }
+        Trap::Exception(Exception::IllegalInstruction) => {
+            println!("[kernel] IllegalInstruction in application, core dumped.");
+            // illegal instruction exit code
+            exit_current_and_run_next(-3);
+        }
+        Trap::Interrupt(Interrupt::SupervisorTimer) => {
+            set_next_trigger();
+            suspend_current_and_run_next();
+        }
+        _ => {
+            panic!("Unsupported trap {:?}, stval = {:#x}!", scause.cause(), stval);
+        }
+    }
+    //println!("before trap_return");
+    trap_return();
+}
+
+#[no_mangle]
+pub fn trap_return() -> ! {
+    set_user_trap_entry();
+    let trap_cx_ptr = TRAP_CONTEXT;
+    let user_satp = current_user_token();
+    extern "C" {
+        fn __alltraps();
+        fn __restore();
+    }
+    let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
+    unsafe {
+        llvm_asm!("fence.i" :::: "volatile");
+        llvm_asm!("jr $0" :: "r"(restore_va), "{a0}"(trap_cx_ptr), "{a1}"(user_satp) :: "volatile");
+    }
+    panic!("Unreachable in back_to_user!");
+}
+
+#[no_mangle]
+pub fn trap_from_kernel() -> ! {
+    panic!("a trap {:?} from kernel!", scause::read().cause());
+}
+
+pub use context::{TrapContext};
diff --git a/codes/os/src/trap/trap.S b/codes/os/src/trap/trap.S
new file mode 100644
index 00000000..c0e2d153
--- /dev/null
+++ b/codes/os/src/trap/trap.S
@@ -0,0 +1,69 @@
+.altmacro
+.macro SAVE_GP n
+    sd x\n, \n*8(sp)
+.endm
+.macro LOAD_GP n
+    ld x\n, \n*8(sp)
+.endm
+    .section .text.trampoline
+    .globl __alltraps
+    .globl __restore
+    .align 2
+__alltraps:
+    csrrw sp, sscratch, sp
+    # now sp->*TrapContext in user space, sscratch->user stack
+    # save other general purpose registers
+    sd x1, 1*8(sp)
+    # skip sp(x2), we will save it later
+    sd x3, 3*8(sp)
+    # skip tp(x4), application does not use it
+    # save x5~x31
+    .set n, 5
+    .rept 27
+        SAVE_GP %n
+        .set n, n+1
+    .endr
+    # we can use t0/t1/t2 freely, because they have been saved in TrapContext
+    csrr t0, sstatus
+    csrr t1, sepc
+    sd t0, 32*8(sp)
+    sd t1, 33*8(sp)
+    # read user stack from sscratch and save it in TrapContext
+    csrr t2, sscratch
+    sd t2, 2*8(sp)
+    # load kernel_satp into t0
+    ld t0, 34*8(sp)
+    # load trap_handler into t1
+    ld t1, 36*8(sp)
+    # move to kernel_sp
+    ld sp, 35*8(sp)
+    # switch to kernel space
+    csrw satp, t0
+    sfence.vma
+    # jump to trap_handler
+    jr t1
+
+__restore:
+    # a0: *TrapContext in user space(Constant); a1: user space token
+    # switch to user space
+    csrw satp, a1
+    sfence.vma
+    csrw sscratch, a0
+    mv sp, a0
+    # now sp points to TrapContext in user space, start restoring based on it
+    # restore sstatus/sepc
+    ld t0, 32*8(sp)
+    ld t1, 33*8(sp)
+    csrw sstatus, t0
+    csrw sepc, t1
+    # restore general purpose registers except x0/sp/tp
+    ld x1, 1*8(sp)
+    ld x3, 3*8(sp)
+    .set n, 5
+    .rept 27
+        LOAD_GP %n
+        .set n, n+1
+    .endr
+    # back to user stack
+    ld sp, 2*8(sp)
+    sret
diff --git a/codes/rust-toolchain b/codes/rust-toolchain
new file mode 100644
index 00000000..a08f00d1
--- /dev/null
+++ b/codes/rust-toolchain
@@ -0,0 +1 @@
+nightly-2021-01-30
diff --git a/codes/user/.cargo/config b/codes/user/.cargo/config
new file mode 100644
index 00000000..e5ded8a1
--- /dev/null
+++ b/codes/user/.cargo/config
@@ -0,0 +1,7 @@
+[build]
+target = "riscv64gc-unknown-none-elf"
+
+[target.riscv64gc-unknown-none-elf]
+rustflags = [
+    "-Clink-args=-Tsrc/linker.ld",
+]
diff --git a/codes/user/.gitignore b/codes/user/.gitignore
new file mode 100644
index 00000000..e420ee4b
--- /dev/null
+++ b/codes/user/.gitignore
@@ -0,0 +1 @@
+target/*
diff --git a/codes/user/Cargo.toml b/codes/user/Cargo.toml
new file mode 100644
index 00000000..d50a0ba0
--- /dev/null
+++ b/codes/user/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "user_lib"
+version = "0.1.0"
+authors = ["Yifan Wu <shinbokuow@163.com>"]
+edition = "2018"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+buddy_system_allocator = "0.6"
+bitflags = "1.2.1"
\ No newline at end of file
diff --git a/codes/user/Makefile b/codes/user/Makefile
new file mode 100644
index 00000000..e2eaf994
--- /dev/null
+++ b/codes/user/Makefile
@@ -0,0 +1,23 @@
+TARGET := riscv64gc-unknown-none-elf
+MODE := release
+APP_DIR := src/bin
+TARGET_DIR := target/$(TARGET)/$(MODE)
+APPS := $(wildcard $(APP_DIR)/*.rs)
+ELFS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%, $(APPS))
+BINS := $(patsubst $(APP_DIR)/%.rs, $(TARGET_DIR)/%.bin, $(APPS))
+
+OBJDUMP := rust-objdump --arch-name=riscv64
+OBJCOPY := rust-objcopy --binary-architecture=riscv64
+
+elf: $(APPS)
+	@cargo build --release
+
+binary: elf
+	$(foreach elf, $(ELFS), $(OBJCOPY) $(elf) --strip-all -O binary $(patsubst $(TARGET_DIR)/%, $(TARGET_DIR)/%.bin, $(elf));)
+
+build: binary
+
+clean:
+	@cargo clean
+
+.PHONY: elf binary build clean
diff --git a/codes/user/src/bin/cat.rs b/codes/user/src/bin/cat.rs
new file mode 100644
index 00000000..988164d3
--- /dev/null
+++ b/codes/user/src/bin/cat.rs
@@ -0,0 +1,34 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+extern crate alloc;
+
+use user_lib::{
+    open,
+    OpenFlags,
+    close,
+    read,
+};
+use alloc::string::String;
+
+#[no_mangle]
+pub fn main(argc: usize, argv: &[&str]) -> i32 {
+    assert!(argc == 2);
+    let fd = open(argv[1], OpenFlags::RDONLY);
+    if fd == -1 {
+        panic!("Error occured when opening file");
+    }
+    let fd = fd as usize;
+    let mut buf = [0u8; 16];
+    let mut s = String::new();
+    loop {
+        let size = read(fd, &mut buf) as usize;
+        if size == 0 { break; }
+        s.push_str(core::str::from_utf8(&buf[..size]).unwrap());
+    }
+    println!("{}", s);
+    close(fd);
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/cmdline_args.rs b/codes/user/src/bin/cmdline_args.rs
new file mode 100644
index 00000000..b49ec332
--- /dev/null
+++ b/codes/user/src/bin/cmdline_args.rs
@@ -0,0 +1,16 @@
+#![no_std]
+#![no_main]
+
+extern crate alloc;
+
+#[macro_use]
+extern crate user_lib;
+
+#[no_mangle]
+pub fn main(argc: usize, argv: &[&str]) -> i32 {
+    println!("argc = {}", argc);
+    for i in 0..argc {
+        println!("argv[{}] = {}", i, argv[i]);
+    }
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/exit.rs b/codes/user/src/bin/exit.rs
new file mode 100644
index 00000000..5bde550b
--- /dev/null
+++ b/codes/user/src/bin/exit.rs
@@ -0,0 +1,29 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+use user_lib::{fork, yield_, waitpid, exit, wait};
+
+const MAGIC: i32 = -0x10384;
+
+#[no_mangle]
+pub fn main() -> i32 {
+    println!("I am the parent. Forking the child...");
+    let pid = fork();
+    if pid == 0 {
+        println!("I am the child.");
+        for _ in 0..7 { yield_(); }
+        exit(MAGIC);
+    } else {
+        println!("I am parent, fork a child pid {}", pid);
+    }
+    println!("I am the parent, waiting now..");
+    let mut xstate: i32 = 0;
+    assert!(waitpid(pid as usize, &mut xstate) == pid && xstate == MAGIC);
+    assert!(waitpid(pid as usize, &mut xstate) < 0 && wait(&mut xstate) <= 0);
+    println!("waitpid {} ok.", pid);
+    println!("exit pass.");
+    0
+}
+
diff --git a/codes/user/src/bin/fantastic_text.rs b/codes/user/src/bin/fantastic_text.rs
new file mode 100644
index 00000000..bb51db30
--- /dev/null
+++ b/codes/user/src/bin/fantastic_text.rs
@@ -0,0 +1,44 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+macro_rules! color_text {
+    ($text:expr, $color:expr) => {{
+        format_args!("\x1b[{}m{}\x1b[0m", $color, $text)
+    }};
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+    println!(
+        "{}{}{}{}{} {}{}{}{} {}{}{}{}{}{}",
+        color_text!("H", 31),
+        color_text!("e", 32),
+        color_text!("l", 33),
+        color_text!("l", 34),
+        color_text!("o", 35),
+        color_text!("R", 36),
+        color_text!("u", 37),
+        color_text!("s", 90),
+        color_text!("t", 91),
+        color_text!("u", 92),
+        color_text!("C", 93),
+        color_text!("o", 94),
+        color_text!("r", 95),
+        color_text!("e", 96),
+        color_text!("!", 97),
+    );
+
+    let text =
+        "reguler \x1b[4munderline\x1b[24m \x1b[7mreverse\x1b[27m \x1b[9mstrikethrough\x1b[29m";
+    println!("\x1b[47m{}\x1b[0m", color_text!(text, 30));
+    for i in 31..38 {
+        println!("{}", color_text!(text, i));
+    }
+    for i in 90..98 {
+        println!("{}", color_text!(text, i));
+    }
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/filetest_simple.rs b/codes/user/src/bin/filetest_simple.rs
new file mode 100644
index 00000000..60fda6aa
--- /dev/null
+++ b/codes/user/src/bin/filetest_simple.rs
@@ -0,0 +1,38 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{
+    open,
+    close,
+    read,
+    write,
+    OpenFlags,
+};
+
+#[no_mangle]
+pub fn main() -> i32 {
+    let test_str = "Hello, world!";
+    let filea = "filea\0";
+    let fd = open(filea, OpenFlags::CREATE | OpenFlags::WRONLY);
+    assert!(fd > 0);
+    let fd = fd as usize;
+    write(fd, test_str.as_bytes());
+    close(fd);
+
+    let fd = open(filea, OpenFlags::RDONLY);
+    assert!(fd > 0);
+    let fd = fd as usize;
+    let mut buffer = [0u8; 100];
+    let read_len = read(fd, &mut buffer) as usize;
+    close(fd);
+
+    assert_eq!(
+        test_str,
+        core::str::from_utf8(&buffer[..read_len]).unwrap(),
+    );
+    println!("file_test passed!");
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/forktest.rs b/codes/user/src/bin/forktest.rs
new file mode 100644
index 00000000..fea6967c
--- /dev/null
+++ b/codes/user/src/bin/forktest.rs
@@ -0,0 +1,34 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, wait, exit};
+
+const MAX_CHILD: usize = 40;
+
+#[no_mangle]
+pub fn main() -> i32 {
+    for i in 0..MAX_CHILD {
+        let pid = fork();
+        if pid == 0 {
+            println!("I am child {}", i);
+            exit(0);
+        } else {
+            println!("forked child pid = {}", pid);
+        }
+        assert!(pid > 0);
+    }
+    let mut exit_code: i32 = 0;
+    for _ in 0..MAX_CHILD {
+        if wait(&mut exit_code) <= 0 {
+            panic!("wait stopped early");
+        }
+    }
+    if wait(&mut exit_code) > 0 {
+        panic!("wait got too many");
+    }
+    println!("forktest pass.");
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/forktest2.rs b/codes/user/src/bin/forktest2.rs
new file mode 100644
index 00000000..d08a4120
--- /dev/null
+++ b/codes/user/src/bin/forktest2.rs
@@ -0,0 +1,33 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, wait, getpid, exit, sleep, get_time};
+
+static NUM: usize = 30;
+
+#[no_mangle]
+pub fn main() -> i32 {
+    for _ in 0..NUM {
+        let pid = fork();
+        if pid == 0 {
+            let current_time = get_time();
+            let sleep_length = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000 + 1000;
+            println!("pid {} sleep for {} ms", getpid(), sleep_length);
+            sleep(sleep_length as usize);
+            println!("pid {} OK!", getpid());
+            exit(0);
+        }
+    }
+
+    let mut exit_code: i32 = 0;
+    for _ in 0..NUM {
+        assert!(wait(&mut exit_code) > 0);
+        assert_eq!(exit_code, 0);
+    }
+    assert!(wait(&mut exit_code) < 0);
+    println!("forktest2 test passed!");
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/forktest_simple.rs b/codes/user/src/bin/forktest_simple.rs
new file mode 100644
index 00000000..821fba64
--- /dev/null
+++ b/codes/user/src/bin/forktest_simple.rs
@@ -0,0 +1,28 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, getpid, wait};
+
+#[no_mangle]
+pub fn main() -> i32 {
+    assert_eq!(wait(&mut 0i32), -1);
+    println!("sys_wait without child process test passed!");
+    println!("parent start, pid = {}!", getpid());
+    let pid = fork();
+    if pid == 0 {
+        // child process
+        println!("hello child process!");
+        100
+    } else {
+        // parent process
+        let mut exit_code: i32 = 0;
+        println!("ready waiting on parent process!");
+        assert_eq!(pid, wait(&mut exit_code));
+        assert_eq!(exit_code, 100);
+        println!("child process pid = {}, exit code = {}", pid, exit_code);
+        0
+    }
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/forktree.rs b/codes/user/src/bin/forktree.rs
new file mode 100644
index 00000000..26954b7a
--- /dev/null
+++ b/codes/user/src/bin/forktree.rs
@@ -0,0 +1,37 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{sleep, getpid, fork, exit, yield_};
+
+const DEPTH: usize = 4;
+
+fn fork_child(cur: &str, branch: char) {
+    let mut next = [0u8; DEPTH + 1];
+    let l = cur.len();
+    if l >= DEPTH {
+        return;
+    }
+    &mut next[..l].copy_from_slice(cur.as_bytes());
+    next[l] = branch as u8;
+    if fork() == 0 {
+        fork_tree(core::str::from_utf8(&next[..l + 1]).unwrap());
+        yield_();
+        exit(0);
+    }
+}
+
+fn fork_tree(cur: &str) {
+    println!("pid{}: {}", getpid(), cur);
+    fork_child(cur, '0');
+    fork_child(cur, '1');
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+    fork_tree("");
+    sleep(3000);
+    0
+}
diff --git a/codes/user/src/bin/hello_world.rs b/codes/user/src/bin/hello_world.rs
new file mode 100644
index 00000000..de4a6a92
--- /dev/null
+++ b/codes/user/src/bin/hello_world.rs
@@ -0,0 +1,11 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+#[no_mangle]
+pub fn main() -> i32 {
+    println!("Hello world from user mode program!");
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/initproc.rs b/codes/user/src/bin/initproc.rs
new file mode 100644
index 00000000..99563f47
--- /dev/null
+++ b/codes/user/src/bin/initproc.rs
@@ -0,0 +1,34 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{
+    fork,
+    wait,
+    exec,
+    yield_,
+};
+
+#[no_mangle]
+fn main() -> i32 {
+    if fork() == 0 {
+        exec("user_shell\0", &[0 as *const u8]);
+    } else {
+        loop {
+            let mut exit_code: i32 = 0;
+            let pid = wait(&mut exit_code);
+            if pid == -1 {
+                yield_();
+                continue;
+            }
+            println!(
+                "[initproc] Released a zombie process, pid={}, exit_code={}",
+                pid,
+                exit_code,
+            );
+        }
+    }
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/matrix.rs b/codes/user/src/bin/matrix.rs
new file mode 100644
index 00000000..8ef2c044
--- /dev/null
+++ b/codes/user/src/bin/matrix.rs
@@ -0,0 +1,68 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, wait, yield_, exit, getpid, get_time};
+
+static NUM: usize = 35;
+const N: usize = 10;
+static P: i32 = 10007;
+type Arr = [[i32; N]; N];
+
+fn work(times: isize) {
+    let mut a: Arr = Default::default();
+    let mut b: Arr = Default::default();
+    let mut c: Arr = Default::default();
+    for i in 0..N {
+        for j in 0..N {
+            a[i][j] = 1;
+            b[i][j] = 1;
+        }
+    }
+    yield_();
+    println!("pid {} is running ({} times)!.", getpid(), times);
+    for _ in 0..times {
+        for i in 0..N {
+            for j in 0..N {
+                c[i][j] = 0;
+                for k in 0..N {
+                    c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % P;
+                }
+            }
+        }
+        for i in 0..N {
+            for j in 0..N {
+                a[i][j] = c[i][j];
+                b[i][j] = c[i][j];
+            }
+        }
+    }
+    println!("pid {} done!.", getpid());
+    exit(0);
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+    for _ in 0..NUM {
+        let pid = fork();
+        if pid == 0 {
+            let current_time = get_time();
+            let times = (current_time as i32 as isize) * (current_time as i32 as isize) % 1000;
+            work(times * 10);
+        }
+    }
+
+    println!("fork ok.");
+
+    let mut exit_code: i32 = 0;
+    for _ in 0..NUM {
+        if wait(&mut exit_code) < 0 {
+            panic!("wait failed.");
+        }
+    }
+    assert!(wait(&mut exit_code) < 0);
+    println!("matrix passed.");
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/pipe_large_test.rs b/codes/user/src/bin/pipe_large_test.rs
new file mode 100644
index 00000000..121987be
--- /dev/null
+++ b/codes/user/src/bin/pipe_large_test.rs
@@ -0,0 +1,69 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+extern crate alloc;
+
+use user_lib::{fork, close, pipe, read, write, wait, get_time};
+use alloc::format;
+
+const LENGTH: usize = 3000;
+#[no_mangle]
+pub fn main() -> i32 {
+    // create pipes
+    // parent write to child
+    let mut down_pipe_fd = [0usize; 2];
+    // child write to parent
+    let mut up_pipe_fd = [0usize; 2];
+    pipe(&mut down_pipe_fd);
+    pipe(&mut up_pipe_fd);
+    let mut random_str = [0u8; LENGTH];
+    if fork() == 0 {
+        // close write end of down pipe
+        close(down_pipe_fd[1]);
+        // close read end of up pipe
+        close(up_pipe_fd[0]);
+        assert_eq!(read(down_pipe_fd[0], &mut random_str) as usize, LENGTH);
+        close(down_pipe_fd[0]);
+        let sum: usize = random_str.iter().map(|v| *v as usize).sum::<usize>();
+        println!("sum = {}(child)", sum);
+        let sum_str = format!("{}", sum);
+        write(up_pipe_fd[1], sum_str.as_bytes());
+        close(up_pipe_fd[1]);
+        println!("Child process exited!");
+        0
+    } else {
+        // close read end of down pipe
+        close(down_pipe_fd[0]);
+        // close write end of up pipe
+        close(up_pipe_fd[1]);
+        // generate a long random string
+        for i in 0..LENGTH {
+            random_str[i] = get_time() as u8;
+        }
+        // send it
+        assert_eq!(write(down_pipe_fd[1], &random_str) as usize, random_str.len());
+        // close write end of down pipe
+        close(down_pipe_fd[1]);
+        // calculate sum(parent)
+        let sum: usize = random_str.iter().map(|v| *v as usize).sum::<usize>();
+        println!("sum = {}(parent)", sum);
+        // recv sum(child)
+        let mut child_result = [0u8; 32];
+        let result_len = read(up_pipe_fd[0], &mut child_result) as usize;
+        close(up_pipe_fd[0]);
+        // check
+        assert_eq!(
+            sum,
+            str::parse::<usize>(
+                core::str::from_utf8(&child_result[..result_len]).unwrap()
+            ).unwrap()
+        );
+        let mut _unused: i32 = 0;
+        wait(&mut _unused);
+        println!("pipe_large_test passed!");
+        0
+    }
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/pipetest.rs b/codes/user/src/bin/pipetest.rs
new file mode 100644
index 00000000..c151fbdd
--- /dev/null
+++ b/codes/user/src/bin/pipetest.rs
@@ -0,0 +1,44 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, close, pipe, read, write, wait};
+
+static STR: &str = "Hello, world!";
+
+#[no_mangle]
+pub fn main() -> i32 {
+    // create pipe
+    let mut pipe_fd = [0usize; 2];
+    pipe(&mut pipe_fd);
+    // read end
+    assert_eq!(pipe_fd[0], 3);
+    // write end
+    assert_eq!(pipe_fd[1], 4);
+    if fork() == 0 {
+        // child process, read from parent
+        // close write_end
+        close(pipe_fd[1]);
+        let mut buffer = [0u8; 32];
+        let len_read = read(pipe_fd[0], &mut buffer) as usize;
+        // close read_end
+        close(pipe_fd[0]);
+        assert_eq!(core::str::from_utf8(&buffer[..len_read]).unwrap(), STR);
+        println!("Read OK, child process exited!");
+        0
+    } else {
+        // parent process, write to child
+        // close read end
+        close(pipe_fd[0]);
+        assert_eq!(write(pipe_fd[1], STR.as_bytes()), STR.len() as isize);
+        // close write end
+        close(pipe_fd[1]);
+        let mut child_exit_code: i32 = 0;
+        wait(&mut child_exit_code);
+        assert_eq!(child_exit_code, 0);
+        println!("pipetest passed!");
+        0
+    }
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/run_pipe_test.rs b/codes/user/src/bin/run_pipe_test.rs
new file mode 100644
index 00000000..000b82d5
--- /dev/null
+++ b/codes/user/src/bin/run_pipe_test.rs
@@ -0,0 +1,21 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{fork, exec, wait};
+
+#[no_mangle]
+pub fn main() -> i32 {
+    for i in 0..1000 {
+        if fork() == 0 {
+            exec("pipe_large_test\0", &[0 as *const u8]);
+        } else {
+            let mut _unused: i32 = 0;
+            wait(&mut _unused);
+            println!("Iter {} OK.", i);
+        }
+    }
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/sleep.rs b/codes/user/src/bin/sleep.rs
new file mode 100644
index 00000000..bd1e2204
--- /dev/null
+++ b/codes/user/src/bin/sleep.rs
@@ -0,0 +1,30 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{sleep, exit, get_time, fork, waitpid};
+
+fn sleepy() {
+    let time: usize = 1000;
+    for i in 0..5 {
+        sleep(time);
+        println!("sleep {} x {} msecs.", i + 1, time);
+    }
+    exit(0);
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+    let current_time = get_time();
+    let pid = fork();
+    let mut exit_code: i32 = 0;
+    if pid == 0 {
+        sleepy();
+    }
+    assert!(waitpid(pid as usize, &mut exit_code) == pid && exit_code == 0);
+    println!("use {} msecs.", get_time() - current_time);
+    println!("sleep pass.");
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/sleep_simple.rs b/codes/user/src/bin/sleep_simple.rs
new file mode 100644
index 00000000..4c058f87
--- /dev/null
+++ b/codes/user/src/bin/sleep_simple.rs
@@ -0,0 +1,19 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+use user_lib::{get_time, sleep};
+
+#[no_mangle]
+pub fn main() -> i32 {
+    println!("into sleep test!");
+    let start = get_time();
+    println!("current time_msec = {}", start);
+    sleep(100);
+    let end = get_time();
+    println!("time_msec = {} after sleeping 100 ticks, delta = {}ms!", end, end - start);
+    println!("r_sleep passed!");
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/stack_overflow.rs b/codes/user/src/bin/stack_overflow.rs
new file mode 100644
index 00000000..e0ea471d
--- /dev/null
+++ b/codes/user/src/bin/stack_overflow.rs
@@ -0,0 +1,17 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+fn f(d: usize) {
+    println!("d = {}",d);
+    f(d + 1);
+}
+
+#[no_mangle]
+pub fn main() -> i32 {
+    println!("It should trigger segmentation fault!");
+    f(0);
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/user_shell.rs b/codes/user/src/bin/user_shell.rs
new file mode 100644
index 00000000..b2c33f2d
--- /dev/null
+++ b/codes/user/src/bin/user_shell.rs
@@ -0,0 +1,138 @@
+#![no_std]
+#![no_main]
+
+extern crate alloc;
+
+#[macro_use]
+extern crate user_lib;
+
+const LF: u8 = 0x0au8;
+const CR: u8 = 0x0du8;
+const DL: u8 = 0x7fu8;
+const BS: u8 = 0x08u8;
+
+use alloc::string::String;
+use alloc::vec::Vec;
+use user_lib::{
+    fork,
+    exec,
+    waitpid,
+    open,
+    OpenFlags,
+    close,
+    dup,
+};
+use user_lib::console::getchar;
+
+#[no_mangle]
+pub fn main() -> i32 {
+    println!("Rust user shell");
+    let mut line: String = String::new();
+    print!(">> ");
+    loop {
+        let c = getchar();
+        match c {
+            LF | CR => {
+                println!("");
+                if !line.is_empty() {
+                    let args: Vec<_> = line.as_str().split(' ').collect();
+                    let mut args_copy: Vec<String> = args
+                    .iter()
+                    .map(|&arg| {
+                        let mut string = String::new();
+                        string.push_str(arg);
+                        string
+                    })
+                    .collect();
+
+                    args_copy
+                    .iter_mut()
+                    .for_each(|string| {
+                        string.push('\0');
+                    });
+
+                    // redirect input
+                    let mut input = String::new();
+                    if let Some((idx, _)) = args_copy
+                    .iter()
+                    .enumerate()
+                    .find(|(_, arg)| arg.as_str() == "<\0") {
+                        input = args_copy[idx + 1].clone();
+                        args_copy.drain(idx..=idx + 1);
+                    }
+
+                    // redirect output
+                    let mut output = String::new();
+                    if let Some((idx, _)) = args_copy
+                    .iter()
+                    .enumerate()
+                    .find(|(_, arg)| arg.as_str() == ">\0") {
+                        output = args_copy[idx + 1].clone();
+                        args_copy.drain(idx..=idx + 1);
+                    }
+
+                    let mut args_addr: Vec<*const u8> = args_copy
+                        .iter()
+                        .map(|arg| arg.as_ptr())
+                        .collect();
+                    args_addr.push(0 as *const u8);
+                    let pid = fork();
+                    if pid == 0 {
+                        // input redirection
+                        if !input.is_empty() {
+                            let input_fd = open(input.as_str(), OpenFlags::RDONLY);
+                            if input_fd == -1 {
+                                println!("Error when opening file {}", input);
+                                return -4;
+                            }
+                            let input_fd = input_fd as usize;
+                            close(0);
+                            assert_eq!(dup(input_fd), 0);
+                            close(input_fd);
+                        }
+                        // output redirection
+                        if !output.is_empty() {
+                            let output_fd = open(
+                                output.as_str(),
+                                OpenFlags::CREATE | OpenFlags::WRONLY
+                            );
+                            if output_fd == -1 {
+                                println!("Error when opening file {}", output);
+                                return -4;
+                            }
+                            let output_fd = output_fd as usize;
+                            close(1);
+                            assert_eq!(dup(output_fd), 1);
+                            close(output_fd);
+                        }
+                        // child process
+                        if exec(args_copy[0].as_str(), args_addr.as_slice()) == -1 {
+                            println!("Error when executing!");
+                            return -4;
+                        }
+                        unreachable!();
+                    } else {
+                        let mut exit_code: i32 = 0;
+                        let exit_pid = waitpid(pid as usize, &mut exit_code);
+                        assert_eq!(pid, exit_pid);
+                        println!("Shell: Process {} exited with code {}", pid, exit_code);
+                    }
+                    line.clear();
+                }
+                print!(">> ");
+            }
+            BS | DL => {
+                if !line.is_empty() {
+                    print!("{}", BS as char);
+                    print!(" ");
+                    print!("{}", BS as char);
+                    line.pop();
+                }
+            }
+            _ => {
+                print!("{}", c as char);
+                line.push(c as char);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/usertests.rs b/codes/user/src/bin/usertests.rs
new file mode 100644
index 00000000..e8be6c45
--- /dev/null
+++ b/codes/user/src/bin/usertests.rs
@@ -0,0 +1,40 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+
+static TESTS: &[&str] = &[
+    "exit\0",
+    "fantastic_text\0",
+    "forktest\0",
+    "forktest2\0",
+    "forktest_simple\0",
+    "hello_world\0",
+    "matrix\0",
+    "sleep\0",
+    "sleep_simple\0",
+    "stack_overflow\0",
+    "yield\0",
+];
+
+use user_lib::{exec, fork, waitpid};
+
+#[no_mangle]
+pub fn main() -> i32 {
+    for test in TESTS {
+        println!("Usertests: Running {}", test);
+        let pid = fork();
+        if pid == 0 {
+            exec(*test, &[0 as *const u8]);
+            panic!("unreachable!");
+        } else {
+            let mut exit_code: i32 = Default::default();
+            let wait_pid = waitpid(pid as usize, &mut exit_code);
+            assert_eq!(pid, wait_pid);
+            println!("\x1b[32mUsertests: Test {} in Process {} exited with code {}\x1b[0m", test, pid, exit_code);
+        }
+    }
+    println!("Usertests passed!");
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/bin/yield.rs b/codes/user/src/bin/yield.rs
new file mode 100644
index 00000000..55032e40
--- /dev/null
+++ b/codes/user/src/bin/yield.rs
@@ -0,0 +1,17 @@
+#![no_std]
+#![no_main]
+
+#[macro_use]
+extern crate user_lib;
+use user_lib::{getpid, yield_};
+
+#[no_mangle]
+pub fn main() -> i32 {
+    println!("Hello, I am process {}.", getpid());
+    for i in 0..5 {
+        yield_();
+        println!("Back in process {}, iteration {}.", getpid(), i);
+    }
+    println!("yield pass.");
+    0
+}
\ No newline at end of file
diff --git a/codes/user/src/console.rs b/codes/user/src/console.rs
new file mode 100644
index 00000000..810ebba1
--- /dev/null
+++ b/codes/user/src/console.rs
@@ -0,0 +1,39 @@
+use core::fmt::{self, Write};
+
+const STDIN: usize = 0;
+const STDOUT: usize = 1;
+
+use super::{read, write};
+
+struct Stdout;
+
+impl Write for Stdout {
+    fn write_str(&mut self, s: &str) -> fmt::Result {
+        write(STDOUT, s.as_bytes());
+        Ok(())
+    }
+}
+
+pub fn print(args: fmt::Arguments) {
+    Stdout.write_fmt(args).unwrap();
+}
+
+#[macro_export]
+macro_rules! print {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!($fmt $(, $($arg)+)?));
+    }
+}
+
+#[macro_export]
+macro_rules! println {
+    ($fmt: literal $(, $($arg: tt)+)?) => {
+        $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
+    }
+}
+
+pub fn getchar() -> u8 {
+    let mut c = [0u8; 1];
+    read(STDIN, &mut c);
+    c[0]
+}
diff --git a/codes/user/src/lang_items.rs b/codes/user/src/lang_items.rs
new file mode 100644
index 00000000..b5b98e08
--- /dev/null
+++ b/codes/user/src/lang_items.rs
@@ -0,0 +1,12 @@
+use super::exit;
+
+#[panic_handler]
+fn panic_handler(panic_info: &core::panic::PanicInfo) -> ! {
+    let err = panic_info.message().unwrap();
+    if let Some(location) = panic_info.location() {
+        println!("Panicked at {}:{}, {}", location.file(), location.line(), err);
+    } else {
+        println!("Panicked: {}", err);
+    }
+    exit(-1);
+}
\ No newline at end of file
diff --git a/codes/user/src/lib.rs b/codes/user/src/lib.rs
new file mode 100644
index 00000000..0ef03bfb
--- /dev/null
+++ b/codes/user/src/lib.rs
@@ -0,0 +1,108 @@
+#![no_std]
+#![feature(llvm_asm)]
+#![feature(linkage)]
+#![feature(panic_info_message)]
+#![feature(alloc_error_handler)]
+
+#[macro_use]
+pub mod console;
+mod syscall;
+mod lang_items;
+
+extern crate alloc;
+#[macro_use]
+extern crate bitflags;
+
+use syscall::*;
+use buddy_system_allocator::LockedHeap;
+use alloc::vec::Vec;
+
+const USER_HEAP_SIZE: usize = 32768;
+
+static mut HEAP_SPACE: [u8; USER_HEAP_SIZE] = [0; USER_HEAP_SIZE];
+
+#[global_allocator]
+static HEAP: LockedHeap = LockedHeap::empty();
+
+#[alloc_error_handler]
+pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
+    panic!("Heap allocation error, layout = {:?}", layout);
+}
+
+#[no_mangle]
+#[link_section = ".text.entry"]
+pub extern "C" fn _start(argc: usize, argv: usize) -> ! {
+    unsafe {
+        HEAP.lock()
+            .init(HEAP_SPACE.as_ptr() as usize, USER_HEAP_SIZE);
+    }
+    let mut v: Vec<&'static str> = Vec::new();
+    for i in 0..argc {
+        let str_start = unsafe {
+            ((argv + i * core::mem::size_of::<usize>()) as *const usize).read_volatile()
+        };
+        let len = (0usize..).find(|i| unsafe {
+            ((str_start + *i) as *const u8).read_volatile() == 0
+        }).unwrap();
+        v.push(
+            core::str::from_utf8(unsafe {
+                core::slice::from_raw_parts(str_start as *const u8, len)
+            }).unwrap()
+        );
+    }
+    exit(main(argc, v.as_slice()));
+}
+
+#[linkage = "weak"]
+#[no_mangle]
+fn main(_argc: usize, _argv: &[&str]) -> i32 {
+    panic!("Cannot find main!");
+}
+
+bitflags! {
+    pub struct OpenFlags: u32 {
+        const RDONLY = 0;
+        const WRONLY = 1 << 0;
+        const RDWR = 1 << 1;
+        const CREATE = 1 << 9;
+        const TRUNC = 1 << 10;
+    }
+}
+
+pub fn dup(fd: usize) -> isize { sys_dup(fd) }
+pub fn open(path: &str, flags: OpenFlags) -> isize { sys_open(path, flags.bits) }
+pub fn close(fd: usize) -> isize { sys_close(fd) }
+pub fn pipe(pipe_fd: &mut [usize]) -> isize { sys_pipe(pipe_fd) }
+pub fn read(fd: usize, buf: &mut [u8]) -> isize { sys_read(fd, buf) }
+pub fn write(fd: usize, buf: &[u8]) -> isize { sys_write(fd, buf) }
+pub fn exit(exit_code: i32) -> ! { sys_exit(exit_code); }
+pub fn yield_() -> isize { sys_yield() }
+pub fn get_time() -> isize { sys_get_time() }
+pub fn getpid() -> isize { sys_getpid() }
+pub fn fork() -> isize { sys_fork() }
+pub fn exec(path: &str, args: &[*const u8]) -> isize { sys_exec(path, args) }
+pub fn wait(exit_code: &mut i32) -> isize {
+    loop {
+        match sys_waitpid(-1, exit_code as *mut _) {
+            -2 => { yield_(); }
+            // -1 or a real pid
+            exit_pid => return exit_pid,
+        }
+    }
+}
+
+pub fn waitpid(pid: usize, exit_code: &mut i32) -> isize {
+    loop {
+        match sys_waitpid(pid as isize, exit_code as *mut _) {
+            -2 => { yield_(); }
+            // -1 or a real pid
+            exit_pid => return exit_pid,
+        }
+    }
+}
+pub fn sleep(period_ms: usize) {
+    let start = sys_get_time();
+    while sys_get_time() < start + period_ms as isize {
+        sys_yield();
+    }
+}
\ No newline at end of file
diff --git a/codes/user/src/linker.ld b/codes/user/src/linker.ld
new file mode 100644
index 00000000..e05a98ba
--- /dev/null
+++ b/codes/user/src/linker.ld
@@ -0,0 +1,29 @@
+
+OUTPUT_ARCH(riscv)
+ENTRY(_start)
+
+BASE_ADDRESS = 0x0;
+
+SECTIONS
+{
+    . = BASE_ADDRESS;
+    .text : {
+        *(.text.entry)
+        *(.text .text.*)
+    }
+    . = ALIGN(4K);
+    .rodata : {
+        *(.rodata .rodata.*)
+    }
+    . = ALIGN(4K);
+    .data : {
+        *(.data .data.*)
+    }
+    .bss : {
+        *(.bss .bss.*)
+    }
+    /DISCARD/ : {
+        *(.eh_frame)
+        *(.debug*)
+    }
+}
\ No newline at end of file
diff --git a/codes/user/src/syscall.rs b/codes/user/src/syscall.rs
new file mode 100644
index 00000000..1cd30f84
--- /dev/null
+++ b/codes/user/src/syscall.rs
@@ -0,0 +1,79 @@
+const SYSCALL_DUP: usize = 24;
+const SYSCALL_OPEN: usize = 56;
+const SYSCALL_CLOSE: usize = 57;
+const SYSCALL_PIPE: usize = 59;
+const SYSCALL_READ: usize = 63;
+const SYSCALL_WRITE: usize = 64;
+const SYSCALL_EXIT: usize = 93;
+const SYSCALL_YIELD: usize = 124;
+const SYSCALL_GET_TIME: usize = 169;
+const SYSCALL_GETPID: usize = 172;
+const SYSCALL_FORK: usize = 220;
+const SYSCALL_EXEC: usize = 221;
+const SYSCALL_WAITPID: usize = 260;
+
+fn syscall(id: usize, args: [usize; 3]) -> isize {
+    let mut ret: isize;
+    unsafe {
+        llvm_asm!("ecall"
+            : "={x10}" (ret)
+            : "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
+            : "memory"
+            : "volatile"
+        );
+    }
+    ret
+}
+
+pub fn sys_dup(fd: usize) -> isize {
+    syscall(SYSCALL_DUP, [fd, 0, 0])
+}
+
+pub fn sys_open(path: &str, flags: u32) -> isize {
+    syscall(SYSCALL_OPEN, [path.as_ptr() as usize, flags as usize, 0])
+}
+
+pub fn sys_close(fd: usize) -> isize {
+    syscall(SYSCALL_CLOSE, [fd, 0, 0])
+}
+
+pub fn sys_pipe(pipe: &mut [usize]) -> isize {
+    syscall(SYSCALL_PIPE, [pipe.as_mut_ptr() as usize, 0, 0])
+}
+
+pub fn sys_read(fd: usize, buffer: &mut [u8]) -> isize {
+    syscall(SYSCALL_READ, [fd, buffer.as_mut_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_write(fd: usize, buffer: &[u8]) -> isize {
+    syscall(SYSCALL_WRITE, [fd, buffer.as_ptr() as usize, buffer.len()])
+}
+
+pub fn sys_exit(exit_code: i32) -> ! {
+    syscall(SYSCALL_EXIT, [exit_code as usize, 0, 0]);
+    panic!("sys_exit never returns!");
+}
+
+pub fn sys_yield() -> isize {
+    syscall(SYSCALL_YIELD, [0, 0, 0])
+}
+
+pub fn sys_get_time() -> isize {
+    syscall(SYSCALL_GET_TIME, [0, 0, 0])
+}
+
+pub fn sys_getpid() -> isize {
+    syscall(SYSCALL_GETPID, [0, 0, 0])
+}
+
+pub fn sys_fork() -> isize {
+    syscall(SYSCALL_FORK, [0, 0, 0])
+}
+
+pub fn sys_exec(path: &str, args: &[*const u8]) -> isize {
+    syscall(SYSCALL_EXEC, [path.as_ptr() as usize, args.as_ptr() as usize, 0])
+}
+
+pub fn sys_waitpid(pid: isize, exit_code: *mut i32) -> isize {
+    syscall(SYSCALL_WAITPID, [pid as usize, exit_code as usize, 0])
+}
\ No newline at end of file
-- 
GitLab