├── .gitignore
├── LICENSE
├── README.md
├── rvcore
├── Kconfig
├── configs
│ └── defconfig
├── objects.mk
├── platform.c
└── rvcore.dts
└── uart.cc
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 CmdBlock
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RVCORE Mini Linux
2 |
3 | 为手搓的模拟器/RTL核实现,构建mini版本的Linux(with mmu)!
4 |
5 | 以尽量少的额外功能实现、尽量低的(与核本身无关的)硬件复杂度、尽量少的配置为宗旨!
6 |
7 | - 核心本身方面
8 | - 只需要实现启动Linux的最低要求:RV32IMA
9 |
10 | - 系统外设方面
11 | - CLINT肯定是要实现的,行为也需要和手册相符,但是只支持一个Hart就好了
12 | - 你甚至不需要实现PLIC
13 | - 你甚至不需要完整模拟真实串口的设备模型,只需要一个极简的实现
14 | - 当然,这样做就对shell输入支持很差。不过可以预制输入字符串,自动执行命令
15 |
16 |
17 | ## 核实现要求
18 |
19 | - RV32IMA
20 | - 由于只有一个硬件线程,原子指令的实现非常简单
21 | - 支持zifencei,zicsr,zicntr
22 | - 不需要实现zihpm
23 | - 支持S模式和U模式,以及Sv32虚拟内存机制
24 | - 无需支持指令集功能选择,misa只读,写入忽略即可
25 | - 无需支持字节序切换,mstatus.MBE/SBE/UBE可均硬编码为0
26 | - wip可实现为nop,U模式执行WFI可以立即触发非法指令异常,mstatus.TW可以忽略
27 | - mconfigptr可硬编码为0,不实现相关功能
28 | - 性能监视器,xenvcfg,mseccfg,PMP都可以不实现,访问相关CSR直接非法指令异常即可
29 |
30 | ## 外设实现
31 |
32 | - CLINT需要完整实现,但只需要支持一个Hart
33 | - 超简单的串口实现
34 | - 基地址偏移为0的写入直接输出即可,其他写操作全部忽略
35 | - 基地址偏移为0的读操作,返回下一个可读取的字符。若无字符可读取,返回0xff
36 | - 基地址偏移为5字节的读操作,返回`0x60 & x`,其中x表示是否有字符可供读取,有1无0
37 | - 其他读操作返回0即可
38 | - 由于驱动打开串口设备时会读取一次偏移为0的位置,预置输入字符串需要在最前面加一个占位的空格
39 | - 仓库根目录下`uart.cc`为一个参考实现
40 |
41 | ## 编译安装32位工具链
42 |
43 | ubuntu软件包安装的工具链不带multilib,没有32位的软浮点等等实现,且libc中带有压缩指令,很难正常进行接下来的编译。所以我们需要自己编译工具链。
44 |
45 | ```
46 | $ git clone https://github.com/riscv/riscv-gnu-toolchain
47 | $ git submodule update --init --recursive
48 | ```
49 |
50 | 进入目录,配置并开始构建
51 |
52 | ```
53 | $ cd riscv-gnu-toolchain
54 | $ ./configure --prefix=/opt/riscv --with-arch=rv32ima --with-abi=ilp32
55 | $ sudo make linux -j
56 | ```
57 |
58 | 这样,工具链将只编译出RV32IMA指令。
59 |
60 | ## 构建initramfs
61 |
62 | Linux运行需要一个根文件系统。正常的根文件系统需要实现一个块设备,但是那个实现起来就麻烦了。Linux提供两种内存文件系统:initramfs和initrd。选用initramfs是因为initrd已经deprecated了,并且initramfs构建也更加简单。
63 |
64 | 一般的做法是在initramfs中放一个busybox,不过也可以自己构建根文件系统,往里面放自己编译的程序。我们的系统是带有MMU的完整Linux,可以运行elf格式的可执行文件。
65 |
66 | ### 编译BusyBox
67 |
68 | 直接去busybox.net下载源码并解压,这里的版本是1.36.1
69 |
70 | ```
71 | $ cd busybox-1.36.1
72 | $ make CROSS_COMPILE=riscv32-unknown-linux-gnu- defconfig
73 | $ make CROSS_COMPILE=riscv32-unknown-linux-gnu- menuconfig
74 | ```
75 |
76 | 进入menuconfig界面,选中 Settings -> Build static binary (no shared libs)
77 |
78 | ```
79 | $ make CROSS_COMPILE=riscv32-unknown-linux-gnu- -j
80 | $ make CROSS_COMPILE=riscv32-unknown-linux-gnu- install
81 | ```
82 |
83 | 编译产物应该就在`_install`目录下了
84 |
85 | ### 编译自己的程序
86 |
87 | 直接用我们编译的工具链编译一般的C程序就好了。不过记得要静态编译,因为我们的系统中不包含libc
88 |
89 | ### 镜像文件构建
90 |
91 | initramfs是和内核打包在一起的。内核编译时可以接受两种initramfs输入:镜像cpio镜像文件,和描述文本文件。
92 |
93 | #### CPIO镜像
94 |
95 | 将所有需要打包的文件移动到当前目录下,例如:
96 |
97 | ```
98 | $ cp -r busybox-1.36.1/_install ./initramfs
99 | $ cd initramfs
100 | ```
101 |
102 | 将busybox全部挪过来,创建虚拟文件系统的挂载点,预置/dev下的node文件
103 |
104 | ```
105 | $ mkdir proc sys dev
106 | $ cd dev
107 | $ sudo mknod -m 644 console c 5 1
108 | $ sudo mknod -m 644 null c 1 3
109 | $ cd ..
110 | ```
111 |
112 | 然后我们还需要为linux创建一个init程序放在initramfs的根目录下。由于我们有busybox,可以使用shell脚本
113 |
114 | ```sh
115 | #!/bin/sh
116 |
117 | mount -t proc none /proc
118 | mount -t sysfs none /sys
119 |
120 | echo "Welcome to CmdBlock's RISC-V 32 mini Linux!"
121 |
122 | exec /bin/sh
123 | ```
124 |
125 | 脚本挂载了必要的虚拟文件系统,输出了一条Hello信息,并运行sh。别忘了加上可执行权限。
126 |
127 | 当然你也可以在initramfs中加入你自己编译的程序,并可通过shell运行。
128 |
129 | 接下来打包所有文件并稍微进行一个压缩,得到一个镜像文件
130 |
131 | ```
132 | $ find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz
133 | ```
134 |
135 | #### 描述文本文件
136 |
137 | ```
138 | dir /dev 755 0 0
139 |
140 | nod /dev/console 644 0 0 c 5 1
141 | nod /dev/null 644 0 0 c 1 3
142 |
143 | file /init /home/cmdblock/Develop/initramfs-new/hello 755 0 0
144 | ```
145 |
146 | 适用于文件比较少的情况,比如只有一个hello程序,打包工作交给Linux内核的构建程序。虽然文件列表也可以用find命令生成,但感觉不如自己打包。
147 |
148 | ## 构建Linux内核
149 |
150 | 直接去kernel.org下载源码并解压,这里的版本是6.10.9
151 |
152 | Linux的编译选项基于默认配置调整而来,而非最小配置。这样做会让编译/运行跑的慢一些,但是配置起来比较简单,对于核的实现测试也会更加充分(这点是笔者猜的,没什么依据)。
153 |
154 | ```
155 | $ make ARCH=riscv CROSS_COMPILE=riscv32-unknown-linux-gnu- defconfig
156 | $ make ARCH=riscv CROSS_COMPILE=riscv32-unknown-linux-gnu- menuconfig
157 | ```
158 |
159 | 进入menuconfig界面,调整以下设置
160 |
161 | - 进入 Platform type
162 | - 启用 Allow configurations that result in non-portable kernels
163 | - Base ISA 改为 RV32I
164 | - 关闭 VECTOR extension support
165 | - 关闭 Zbb extension support for bit manipulation instructions
166 | - 关闭 Zicbom extension support for non-coherent DMA operation
167 | - 关闭 Zicboz extension support for faster zeroing of memory
168 | - 关闭 FPU support
169 | - Unaligned Accesses Support 根据实际情况选择
170 | - OpenSBI能够探测硬件对于非对齐访存的支持情况,不支持时会使用软件进行模拟
171 | - Linux内核也能够探测。但是OpenSBI对非对齐访存的软件支持,对于Linux内核来说是透明的
172 | - 也就是说Linux会认为机器支持非对齐的访存,但实际上这是OpenSBI模拟的
173 | - 默认选项会对非对齐访存进行速度测试,机器不支持非对齐访存的情况下这个过程非常缓慢
174 | - 所以建议选择 Assume the system supports slow unaligned memory access
175 | - 如果你的硬件支持快速的非对齐访问,可以选择自动探测,或者假设快速非对齐访存
176 | - 进入 Boot options
177 | - 关闭 UEFI runtime support
178 | - 这需要对压缩指令的支持
179 | - 重新进入 Platform type
180 | - 关闭 Emit compressed instructions when building Linux
181 | - 内核编译不产生压缩指令
182 | - 进入 Kernel features
183 | - 打开 SBI v0.1 support
184 | - 进入 Device Drivers - Character devices
185 | - 打开 RISC-V SBI console support
186 | - 进入 Serial drivers
187 | - 打开 Early console using RISC-V SBI
188 | - 进入 General setup
189 | - 将 Initramfs source file(s) 改为镜像文件/描述文件的绝对路径
190 |
191 | ```
192 | $ make ARCH=riscv CROSS_COMPILE=riscv32-unknown-linux-gnu- -j
193 | ```
194 |
195 | 编译得到`arch/riscv/boot/Image`内核镜像文件
196 |
197 | ## 构建OpenSBI
198 |
199 | ```
200 | $ git clone https://github.com/riscv-software-src/opensbi.git
201 | $ cd opensbi
202 | $ git checkout release-1.5.x
203 | ```
204 |
205 | 笔者实验时的commit hash为`43cace6c3671e5172d0df0a8963e552bb04b7b20`
206 |
207 | ### 创建 Platform
208 |
209 | 进入`platform`目录,将`template`目录复制一份,重命名为你喜欢的名字,并进入该目录。
210 |
211 | - 创建设备树文件`xxx.dts`
212 |
213 | - 可参考仓库根目录下`rvcore/rvcore.dts`
214 | - 需要注意的是内核启动参数,`console=hvc0 earlycon=sbi`,这样做允许我们使用简陋的串口仿真模型运行真实的linux
215 | - 因为所有的输入输出都通过OpenSBI,whose串口驱动十分简单
216 | - 然后使用`dtc xxx.dts -o xxx.dtb`编译得到设备树blob(`.dtb`)文件
217 |
218 | - 编辑`platform.c`
219 |
220 | - 编辑开头的宏定义
221 | - `PLATFORM_HART_COUNT`改为1,表示一个硬件线程
222 | - `PLATFORM_CLINT_ADDR`改为CLINT的地址
223 | - `PLATFORM_ACLINT_MTIMER_FREQ`可根据你的实现确定
224 | - `PLATFORM_UART_ADDR`改为串口设备地址
225 | - 将`plic`结构体变量的定义注释掉
226 | - 编辑`mtimer`结构体成员`has_64bit_mmio`为`false`
227 | - 将`platform_irqchip_init`函数注释掉
228 | - 编辑`platform_ops`结构体成员`irqchip_init`为`NULL`
229 | - 编辑`platform`结构体成员`name`为你喜欢的名字
230 |
231 | - 编辑`objects.mk`
232 |
233 | - 编辑 Platform RISC-V XLEN, ABI, ISA and Code Model configuration
234 |
235 | - ```
236 | PLATFORM_RISCV_XLEN = 32
237 | PLATFORM_RISCV_ABI = ilp32
238 | PLATFORM_RISCV_ISA = rv32ima_zicsr_zifencei_zicntr
239 | PLATFORM_RISCV_CODE_MODEL = medany
240 | ```
241 |
242 | - 取消`# platform-objs-y +=
.o`注释,根据设备树文件名修改值,如`rvcore.o`
243 |
244 | - 取消`FW_FDT_PATH`注释,值改为设备树blob(`.dtb`)文件**绝对路径**
245 |
246 | - `FW_DYNAMIC`,`FW_JUMP`值改为`n`
247 |
248 | - `FW_PAYLOAD`值改为`y`
249 |
250 | - 将对`FW_PAYLOAD_OFFSET`赋值的ifeq语句直接全部注释掉
251 |
252 | - 取消`FW_PAYLOAD_ALIGN`注释,值改为`0x400000`
253 |
254 | - 取消`FW_PAYLOAD_PATH`注释,值改为Linux内核镜像文件**绝对路径**
255 |
256 | 仓库根目录下`rvcore`目录为一个platform示例。
257 |
258 | ### 编译生成镜像
259 |
260 | ```
261 | $ make CROSS_COMPILE=riscv32-unknown-linux-gnu- FW_TEXT_START=0x80000000 PLATFORM=rvcore
262 | ```
263 |
264 | 其中`FW_TEXT_START`为OpenSBI执行开始时的PC值。OpenSBI执行前,需要将整个镜像复制到该值开始的内存区域,并跳转到该地址。
265 |
266 | `PLATFORM`即平台名,是你喜欢的名字。
267 |
268 | 镜像位于`platform/<你喜欢的名字>/firmware/fw_payload.bin`,打包了包括OpenSBI,Linux内核,initramfs在内的全部内容,可以直接在模拟器/RTL仿真中运行!(当然你或许需要一个bootloader)
269 |
270 | 修改makefile内容后可能需要清理构建产物后重新编译:`make clean`,或者更加彻底地,`make distclean`
271 |
--------------------------------------------------------------------------------
/rvcore/Kconfig:
--------------------------------------------------------------------------------
1 | # SPDX-License-Identifier: BSD-2-Clause
2 |
3 | #
4 | # All mandatory drivers or libraries for this platform should
5 | # be directly selected by the PLATFORM_xyz kconfig symbol.
6 | #
7 | # All optional drivers or libraries for this platform should
8 | # be enabled via configs/defconfig of this platform.
9 | #
10 | config PLATFORM_TEMPLATE
11 | bool
12 | select IPI_MSWI
13 | select IRQCHIP_PLIC
14 | select SERIAL_UART8250
15 | select TIMER_MTIMER
16 | default y
17 |
--------------------------------------------------------------------------------
/rvcore/configs/defconfig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CmdBlockZQG/rvcore-mini-linux/3ca07d4744a45cd065ae002cf6bb4d56a998b607/rvcore/configs/defconfig
--------------------------------------------------------------------------------
/rvcore/objects.mk:
--------------------------------------------------------------------------------
1 | #
2 | # SPDX-License-Identifier: BSD-2-Clause
3 | #
4 | # Copyright (c) 2019 Western Digital Corporation or its affiliates.
5 | #
6 |
7 | # Compiler pre-processor flags
8 | platform-cppflags-y =
9 |
10 | # C Compiler and assembler flags.
11 | platform-cflags-y =
12 | platform-asflags-y =
13 |
14 | # Linker flags: additional libraries and object files that the platform
15 | # code needs can be added here
16 | platform-ldflags-y =
17 |
18 | #
19 | # Command for platform specific "make run"
20 | # Useful for development and debugging on plaftform simulator (such as QEMU)
21 | #
22 | # platform-runcmd = your_platform_run.sh
23 |
24 | #
25 | # Platform RISC-V XLEN, ABI, ISA and Code Model configuration.
26 | # These are optional parameters but platforms can optionaly provide it.
27 | # Some of these are guessed based on GCC compiler capabilities
28 | #
29 | PLATFORM_RISCV_XLEN = 32
30 | PLATFORM_RISCV_ABI = ilp32
31 | PLATFORM_RISCV_ISA = rv32ima_zicsr_zifencei_zicntr
32 | PLATFORM_RISCV_CODE_MODEL = medany
33 |
34 | # Space separated list of object file names to be compiled for the platform
35 | platform-objs-y += platform.o
36 |
37 | #
38 | # If the platform support requires a builtin device tree file, the name of
39 | # the device tree compiled file should be specified here. The device tree
40 | # source file be in the form .dts
41 | #
42 | platform-objs-y += rvcore.o
43 |
44 | # Optional parameter for path to external FDT
45 | FW_FDT_PATH=/home/cmdblock/Develop/rvcore-mini-linux/build/opensbi/platform/rvcore/rvcore.dtb
46 |
47 | #
48 | # Dynamic firmware configuration.
49 | # Optional parameters are commented out. Uncomment and define these parameters
50 | # as needed.
51 | #
52 | FW_DYNAMIC=n
53 |
54 | #
55 | # Jump firmware configuration.
56 | # Optional parameters are commented out. Uncomment and define these parameters
57 | # as needed.
58 | #
59 | FW_JUMP=n
60 | # This needs to be 4MB aligned for 32-bit support
61 | # This needs to be 2MB aligned for 64-bit support
62 | # ifeq ($(PLATFORM_RISCV_XLEN), 32)
63 | # FW_JUMP_OFFSET=0x400000
64 | # else
65 | # FW_JUMP_OFFSET=0x200000
66 | # endif
67 | # FW_JUMP_FDT_OFFSET=0x2200000
68 | #
69 | # You can use fixed address for jump firmware as an alternative option.
70 | # SBI will prefer "_ADDR" if both "_ADDR" and "_OFFSET" are
71 | # defined
72 | # ifeq ($(PLATFORM_RISCV_XLEN), 32)
73 | # FW_JUMP_ADDR=0x80400000
74 | # else
75 | # FW_JUMP_ADDR=0x80200000
76 | # endif
77 | # FW_JUMP_FDT_ADDR=0x82200000
78 |
79 | #
80 | # Firmware with payload configuration.
81 | # Optional parameters are commented out. Uncomment and define these parameters
82 | # as needed.
83 | #
84 | FW_PAYLOAD=y
85 | # This needs to be 4MB aligned for 32-bit support
86 | # This needs to be 2MB aligned for 64-bit support
87 | # ifeq ($(PLATFORM_RISCV_XLEN), 32)
88 | # FW_PAYLOAD_OFFSET=0x400000
89 | # else
90 | # FW_PAYLOAD_OFFSET=0x200000
91 | # endif
92 | FW_PAYLOAD_ALIGN=0x400000
93 | FW_PAYLOAD_PATH=/home/cmdblock/Develop/rvcore-mini-linux/build/linux-6.10.9/arch/riscv/boot/Image
94 | # FW_PAYLOAD_FDT_OFFSET=0x2200000
95 | #
96 | # You can use fixed address for payload firmware as an alternative option.
97 | # SBI will prefer "FW_PAYLOAD_FDT_ADDR" if both "FW_PAYLOAD_FDT_OFFSET"
98 | # and "FW_PAYLOAD_FDT_ADDR" are defined.
99 | # FW_PAYLOAD_FDT_ADDR=0x82200000
100 |
--------------------------------------------------------------------------------
/rvcore/platform.c:
--------------------------------------------------------------------------------
1 | /*
2 | * SPDX-License-Identifier: BSD-2-Clause
3 | *
4 | * Copyright (c) 2019 Western Digital Corporation or its affiliates.
5 | */
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 |
12 | /*
13 | * Include these files as needed.
14 | * See objects.mk PLATFORM_xxx configuration parameters.
15 | */
16 | #include
17 | #include
18 | #include
19 | #include
20 |
21 | #define PLATFORM_PLIC_ADDR 0xc000000
22 | #define PLATFORM_PLIC_SIZE (0x200000 + \
23 | (PLATFORM_HART_COUNT * 0x1000))
24 | #define PLATFORM_PLIC_NUM_SOURCES 1
25 | #define PLATFORM_HART_COUNT 1
26 | #define PLATFORM_CLINT_ADDR 0x20000000
27 | #define PLATFORM_ACLINT_MTIMER_FREQ 10000000
28 | #define PLATFORM_ACLINT_MSWI_ADDR (PLATFORM_CLINT_ADDR + \
29 | CLINT_MSWI_OFFSET)
30 | #define PLATFORM_ACLINT_MTIMER_ADDR (PLATFORM_CLINT_ADDR + \
31 | CLINT_MTIMER_OFFSET)
32 | #define PLATFORM_UART_ADDR 0x10000000
33 | #define PLATFORM_UART_INPUT_FREQ 10000000
34 | #define PLATFORM_UART_BAUDRATE 115200
35 |
36 | // static struct plic_data plic = {
37 | // .addr = PLATFORM_PLIC_ADDR,
38 | // .size = PLATFORM_PLIC_SIZE,
39 | // .num_src = PLATFORM_PLIC_NUM_SOURCES,
40 | // };
41 |
42 | static struct aclint_mswi_data mswi = {
43 | .addr = PLATFORM_ACLINT_MSWI_ADDR,
44 | .size = ACLINT_MSWI_SIZE,
45 | .first_hartid = 0,
46 | .hart_count = PLATFORM_HART_COUNT,
47 | };
48 |
49 | static struct aclint_mtimer_data mtimer = {
50 | .mtime_freq = PLATFORM_ACLINT_MTIMER_FREQ,
51 | .mtime_addr = PLATFORM_ACLINT_MTIMER_ADDR +
52 | ACLINT_DEFAULT_MTIME_OFFSET,
53 | .mtime_size = ACLINT_DEFAULT_MTIME_SIZE,
54 | .mtimecmp_addr = PLATFORM_ACLINT_MTIMER_ADDR +
55 | ACLINT_DEFAULT_MTIMECMP_OFFSET,
56 | .mtimecmp_size = ACLINT_DEFAULT_MTIMECMP_SIZE,
57 | .first_hartid = 0,
58 | .hart_count = PLATFORM_HART_COUNT,
59 | .has_64bit_mmio = false,
60 | };
61 |
62 | /*
63 | * Platform early initialization.
64 | */
65 | static int platform_early_init(bool cold_boot)
66 | {
67 | return 0;
68 | }
69 |
70 | /*
71 | * Platform final initialization.
72 | */
73 | static int platform_final_init(bool cold_boot)
74 | {
75 | return 0;
76 | }
77 |
78 | /*
79 | * Initialize the platform console.
80 | */
81 | static int platform_console_init(void)
82 | {
83 | /* Example if the generic UART8250 driver is used */
84 | return uart8250_init(PLATFORM_UART_ADDR, PLATFORM_UART_INPUT_FREQ,
85 | PLATFORM_UART_BAUDRATE, 0, 1, 0);
86 | }
87 |
88 | /*
89 | * Initialize the platform interrupt controller for current HART.
90 | */
91 | // static int platform_irqchip_init(bool cold_boot)
92 | // {
93 | // u32 hartid = current_hartid();
94 | // int ret;
95 | //
96 | // /* Example if the generic PLIC driver is used */
97 | // if (cold_boot) {
98 | // ret = plic_cold_irqchip_init(&plic);
99 | // if (ret)
100 | // return ret;
101 | // }
102 | //
103 | // return plic_warm_irqchip_init(&plic, 2 * hartid, 2 * hartid + 1);
104 | // }
105 |
106 | /*
107 | * Initialize IPI for current HART.
108 | */
109 | static int platform_ipi_init(bool cold_boot)
110 | {
111 | int ret;
112 |
113 | /* Example if the generic ACLINT driver is used */
114 | if (cold_boot) {
115 | ret = aclint_mswi_cold_init(&mswi);
116 | if (ret)
117 | return ret;
118 | }
119 |
120 | return aclint_mswi_warm_init();
121 | }
122 |
123 | /*
124 | * Initialize platform timer for current HART.
125 | */
126 | static int platform_timer_init(bool cold_boot)
127 | {
128 | int ret;
129 |
130 | /* Example if the generic ACLINT driver is used */
131 | if (cold_boot) {
132 | ret = aclint_mtimer_cold_init(&mtimer, NULL);
133 | if (ret)
134 | return ret;
135 | }
136 |
137 | return aclint_mtimer_warm_init();
138 | }
139 |
140 | /*
141 | * Platform descriptor.
142 | */
143 | const struct sbi_platform_operations platform_ops = {
144 | .early_init = platform_early_init,
145 | .final_init = platform_final_init,
146 | .console_init = platform_console_init,
147 | .irqchip_init = NULL, //platform_irqchip_init,
148 | .ipi_init = platform_ipi_init,
149 | .timer_init = platform_timer_init
150 | };
151 | const struct sbi_platform platform = {
152 | .opensbi_version = OPENSBI_VERSION,
153 | .platform_version = SBI_PLATFORM_VERSION(0x0, 0x00),
154 | .name = "rvcore",
155 | .features = SBI_PLATFORM_DEFAULT_FEATURES,
156 | .hart_count = 1,
157 | .hart_stack_size = SBI_PLATFORM_DEFAULT_HART_STACK_SIZE,
158 | .heap_size = SBI_PLATFORM_DEFAULT_HEAP_SIZE(1),
159 | .platform_ops_addr = (unsigned long)&platform_ops
160 | };
161 |
--------------------------------------------------------------------------------
/rvcore/rvcore.dts:
--------------------------------------------------------------------------------
1 | /dts-v1/;
2 | / {
3 | #address-cells = <0x2>;
4 | #size-cells = <0x2>;
5 |
6 | chosen {
7 | bootargs = "console=hvc0 earlycon=sbi";
8 | };
9 |
10 | memory@80000000 {
11 | device_type = "memory";
12 | reg = <0x0 0x80000000 0x0 0x10000000>;
13 | };
14 |
15 | cpus {
16 | #address-cells = <0x1>;
17 | #size-cells = <0x0>;
18 | timebase-frequency = <100000000>;
19 | cpu0: cpu@0 {
20 | phandle = <0x1>;
21 | device_type = "cpu";
22 | reg = <0x0>;
23 | status = "okay";
24 | compatible = "riscv";
25 | riscv,isa = "rv32ima";
26 | mmu-type = "riscv,sv32";
27 | cpuintr0: interrupt-controller {
28 | #interrupt-cells = <0x1>;
29 | interrupt-controller;
30 | compatible = "riscv,cpu-intc";
31 | phandle = <0x2>;
32 | };
33 | };
34 | cpu-map {
35 | cluster0 {
36 | core0 {
37 | cpu = <0x1>;
38 | };
39 | };
40 | };
41 | };
42 |
43 | serial0: serial@10000000 {
44 | clock-frequency = <1000000>;
45 | reg = <0x00 0x10000000 0x00 0x100>;
46 | compatible = "ns16850";
47 | };
48 |
49 | clint@20000000 {
50 | interrupts-extended = <&cpuintr0 0x3 &cpuintr0 0x7>;
51 | reg = <0x0 0x20000000 0x0 0x10000>;
52 | compatible = "riscv,clint0";
53 | };
54 |
55 | };
56 |
--------------------------------------------------------------------------------
/uart.cc:
--------------------------------------------------------------------------------
1 | #include "local-include/uart.h"
2 |
3 | #include
4 |
5 | // 预置的输入字符串
6 | static const char *uart_input_ptr = " " // 占位空格字符
7 | "busybox | head -n4\n"
8 | "ls\n"
9 | "./hello\n";
10 |
11 | static inline bool input_available() {
12 | return *uart_input_ptr != '\0';
13 | }
14 |
15 | // 0x1000表示mmio区域大小
16 | UART::UART(paddr_t addr): Device(addr, 0x1000) { }
17 |
18 | UART::~UART() { }
19 |
20 | void UART::write(paddr_t addr, int len, word_t data) {
21 | if (addr == 0) {
22 | putchar(data & 0xff);
23 | fflush(stdout);
24 | }
25 | }
26 |
27 | word_t UART::read(paddr_t addr, int len) {
28 | if (addr == 0) {
29 | if (input_available()) {
30 | return *uart_input_ptr++;
31 | } else {
32 | return 0xff;
33 | }
34 | } else if (addr == 5) {
35 | return 0x60 | input_available();
36 | }
37 | return 0;
38 | }
39 |
40 | word_t UART::get_ip() {
41 | return input_available();
42 | }
43 |
--------------------------------------------------------------------------------