diff --git a/articles/20220504-riscv-privileged.md b/articles/20220504-riscv-privileged.md
new file mode 100644
index 0000000000000000000000000000000000000000..0d334be840f5a5ed7ffbd13b94b0ee8fd01b746c
--- /dev/null
+++ b/articles/20220504-riscv-privileged.md
@@ -0,0 +1,115 @@
+> Author: Pingbo Wen
+> Date: 2022/05/04
+> Project: [RISC-V Linux 内核剖析](https://gitee.com/tinylab/riscv-linux)
+
+# RISC-V 特权指令
+
+RISCV ISA Spec 分为两部分,一个是非特权指令,另外一个就是特权指令。非特权指令主要是用于通用计算,站在操作系统的角度来看,可以理解为用户态(低权限模式)能够运行的指令。而特权指令,是为了能够运行像 Linux/Windows 现代操作系统而设定的。现代操作系统主要强调对资源的管控,这就需要硬件上提供额外的权限管理机制,从而能够限制普通应用代码的行为。
+
+## 特权等级
+
+在 ARM64 中,分为 el0-el3 特权等级。RISCV 同样有类似的设定,具体定义如下:
+
+特权等级 | 编码 | 名称 | 缩写
+--------|------|-----|------
+0 | 00 | 用户模式 | U
+1 | 01 | 监管者模式 | S
+2 | 10 | Reserved
+3 | 11 | 机器模式 | M
+
+M 模式是权限最高的特权等级,也是 RISCV Spec 中明确规定必须要实现的特权等级,其他三个特权等级是可选的。芯片厂商可以根据实际应用场景来决定需要实现哪些特权等级。特权等级的实现组合有如下几种:
+
+- M, 简单嵌入式系统(单片机)
+- M + U, 安全嵌入式系统(带保护)
+- M + S + U, 现代操作系统(Windows/Linux)
+
+其中保留的特权等级 2 是留给虚拟化用的。在 H 扩展(Hypervisor Extension)中,把 S 模式扩展成 HS 模式(Hypervisor-Extended Supervisor mode),具体可以参考 Spec。
+
+RISCV 手册中有提到过一个 Debug Mode,可以理解为比 M 权限更高的特权等级,用于支持芯片调试。但目前没有看到更多关于这个模式的资料。
+
+在一个典型 Linux 系统中,用户态应用程序跑在 U 模式,内核跑在 S 模式,而 M 模式一般是 opensbi/uboot 等 bootloader 在用。
+
+## 异常处理
+
+有了特权等级,相应的需要提供进入退出特权等级的方法,以及控制机制。和 ARM 类似,RISCV 也是通过异常切换不同特权等级,这个地方你可以把异常理解成一种中断。严格来讲,中断也只是异常中的一种而已。 以 M 模式处理异常为例,当 U 或者 S 模式发生异常后,处理器会自动做如下处理:
+
+1. 处理器保存异常指令 PC 到 MEPC 中
+2. 根据发生的异常类型设置 MCAUSE,并更新 MTVAL 为出错的取指地址、存储/加载地址或者指令码
+3. 将 MSTATUS 的中断使能位域 MIE 保存到 MPIE 域中,将 MIE 域的值清零,禁止响应中断
+4. 将发生异常之前的权限模式保存到 MSTATUS 的 MPP 域中,切换到机器模式(没有做异常降级响应处理的话)
+5. 根据 MTVEC 中的基址和模式,得到异常服务程序入口地址。处理器从异常服务程序的第一条指令处开始执行,进行异常的处理
+
+如果是 S 模式处理异常,相应操作的寄存器就是 SEPC/SCAUSE/STVAL/SIE/SSTATUS 等。而读写这些寄存器主要是通过 CSR 指令,这跟 ARM 中的 MSR/MRS 指令类似。CSR 指令具体定义如下:
+
+CSR 指令 | 格式 | 说明
+---------|------|-----
+CSRRC | csrrc rd, csr, rs1 | 控制寄存器清零,rd = csr,csr &= ~rs1
+CSRRCI | csrrci rd, csr, imm | 控制寄存器立即数清零,rd = csr, csr &= ~imm
+CSRRS | csrrs rd, csr, rs1 | 控制寄存器置位,rd = csr, csr \|= rs1
+CSRRSI | csrrsi rd, csr, imm | 控制寄存器立即数置位,rd = csr, csr \|= imm
+CSRRW | csrrw rd, csr, rs1 | 控制寄存器读写,rd = csr, csr = rs1
+CSRRWI | csrrwi rd, csr, imm | 控制寄存器立即数读写,rd = csr, csr = imm
+
+这些 CSR 指令配合 x0 寄存器,就组成了很多我们常见的伪指令:
+
+CSR 伪指令 | 格式 | 说明
+---------|------|-----
+CSRC | csrc csr, rs | 对应基础指令 csrrc x0, csr, rs
+CSRCI | csrci csr, imm | 对应基础指令 csrrci x0, csr, imm
+CSRS | csrs csr, rs | 对应基础指令 csrrs x0, csr, rs
+CSRSI | csrsi csr, imm | 对应基础指令 csrrsi x0, csr, imm
+CSRR | csrr rd, csr | 对应基础指令 csrrs rd, csr, x0
+CSRW | csrw csr, rs | 对应基础指令 csrrw x0, csr, rs
+CSRWI | csrwi csr, imm | 对应基础指令 csrrw x0, csr, imm
+
+除了硬件上的中断,以及非法指令等异常外,RISCV 还提供 ECALL/EBREAK 两条指令,让软件可以自己主动产生异常,其中 ECALL 主要用于环境调用,Linux 系统调用就是通过这个指令执行内核系统调用。而 EBREAK 主要是在调试场景下用。
+
+## Linux 系统下的系统调用实现
+
+下面以 Linux 系统 sys_open 系统调用为例,我们看一下用户态程序(特权等级 0, U 模式)是怎么陷入到 Linux 内核(特权等级 1, S 模式)中执行系统调用的。
+
+首先用户态通过 ecall 指令触发系统调用,使用 a7 寄存器传递系统调用编号,a0-a5 寄存器来传递参数:
+
+```
+ 22482: eb8d bnez a5,224b4 <__libc_open+0x64>
+ 22484: 03800893 li a7,56
+ 22488: f9c00513 li a0,-100
+ 2248c: 8622 mv a2,s0
+ 2248e: 00000073 ecall
+```
+
+陷入到内核态后,处理器从 STVEC 寄存器加载异常处理程序入口。在 Linux 内核初始化过程中(arch/riscv/kernel/head.S),就已经通过 CSR 指令设置好了 STVEC 寄存器,指向 handle_exception 函数:
+
+```
+setup_trap_vector:
+ /* Set trap vector to exception handler */
+ la a0, handle_exception
+ csrw CSR_TVEC, a0
+
+ /*
+ * Set sup0 scratch register to 0, indicating to exception vector that
+ * we are presently executing in kernel.
+ */
+ csrw CSR_SCRATCH, zero
+ ret
+```
+
+handle_exception 最终会跳转到 handle_syscall,然后从 a7 寄存器中拿到系统调用编号,从 sys_call_table 中索引到最终系统调用处理函数(arch/riscv/kernel/entry.S):
+
+```
+ /* Check to make sure we don't jump to a bogus syscall number. */
+ li t0, __NR_syscalls
+ la s0, sys_ni_syscall
+ /*
+ * Syscall number held in a7.
+ * If syscall number is above allowed value, redirect to ni_syscall.
+ */
+ bgeu a7, t0, 1f
+ /* Call syscall */
+ la s0, sys_call_table
+ slli t0, a7, RISCV_LGPTR
+ add s0, s0, t0
+ REG_L s0, 0(s0)
+1:
+ jalr s0
+```