mirror of
https://github.com/plctlab/riscv-operating-system-mooc.git
synced 2025-11-16 12:34:47 +00:00
initial versioin
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
*.o
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
|
||||||
25
LICENSE
Normal file
25
LICENSE
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
BSD 2-Clause License
|
||||||
|
|
||||||
|
Copyright (c) 2021, plctlab
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
* Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
19
Makefile
Normal file
19
Makefile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
SECTIONS = \
|
||||||
|
code/asm \
|
||||||
|
code/os \
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
all :
|
||||||
|
@echo "begin compile ALL exercises for assembly samples ......................."
|
||||||
|
for dir in $(SECTIONS); do $(MAKE) -C $$dir || exit "$$?"; done
|
||||||
|
@echo "compile ALL exercises finished successfully! ......"
|
||||||
|
|
||||||
|
.PHONY : clean
|
||||||
|
clean:
|
||||||
|
for dir in $(SECTIONS); do $(MAKE) -C $$dir clean || exit "$$?"; done
|
||||||
|
|
||||||
|
.PHONY : slides
|
||||||
|
slides:
|
||||||
|
rm -f ./slides/*.pdf
|
||||||
|
soffice --headless --convert-to pdf:writer_pdf_Export --outdir ./slides ./docs/ppts/*.pptx
|
||||||
|
|
||||||
78
README.md
78
README.md
@@ -1 +1,77 @@
|
|||||||
# riscv-operating-system-mooc
|
**Step by step, learn to develop an operating system on RISC-V**
|
||||||
|
|
||||||
|
<!-- TOC -->
|
||||||
|
|
||||||
|
- [Introduction](#introduction)
|
||||||
|
- [Operating environment](#operating-environment)
|
||||||
|
- [Construction and usage](#construction-and-usage)
|
||||||
|
- [References](#references)
|
||||||
|
|
||||||
|
<!-- /TOC -->
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
This course is used to teach and demonstrate how to write a simple operating system kernel for the RISC-V platform from scratch. Released under the BSD 2-Clause license (For details, please read the [LICENSE file](./LICENSE) under the root directory of this repository).
|
||||||
|
|
||||||
|
# Operating environment
|
||||||
|
|
||||||
|
All demo codes have been verified under the following equipment environment:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ lsb_release -a
|
||||||
|
No LSB modules are available.
|
||||||
|
Distributor ID: Ubuntu
|
||||||
|
Description: Ubuntu 20.04.2 LTS
|
||||||
|
Release: 20.04
|
||||||
|
Codename: focal
|
||||||
|
$ uname -r
|
||||||
|
5.8.0-45-generic
|
||||||
|
```
|
||||||
|
|
||||||
|
There may be dependent libraries that need to be installed manually. If you are prompted that other libraries and dependencies are missing during the operation, please install them by yourself according to the prompts.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo apt update
|
||||||
|
$ sudo apt install build-essential git gitk vim libfdt-dev libsdl2-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
The experiment requires some running tools, pre-compiled binary files have been provided, the specific installation steps are described as follows:
|
||||||
|
|
||||||
|
First, create a working directory, and then enter the directory.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mkdir $HOME/ws
|
||||||
|
$ cd $HOME/ws
|
||||||
|
```
|
||||||
|
|
||||||
|
Download the development tool package `tools.tar.gz`, the download address is:<https://share.weiyun.com/nyTqAGKh>。
|
||||||
|
|
||||||
|
After downloading, copy the file to `$HOME/ws` and unzip it.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ tar xzf tools.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the following path to `$HOME/.bashrc`
|
||||||
|
```
|
||||||
|
export PATH="$PATH:$HOME/ws/tools/gcc/bin:$HOME/ws/tools/qemu/bin"
|
||||||
|
```
|
||||||
|
|
||||||
|
Re-import `$HOME/.bashrc` or restart the system to make the configuration effective.
|
||||||
|
|
||||||
|
# Construction and usage
|
||||||
|
|
||||||
|
- make:Compile and build
|
||||||
|
- make run:Start qemu and run
|
||||||
|
- make debug:Start debugging
|
||||||
|
- make code:Disassemble to view binary code
|
||||||
|
- make clean:cleanup
|
||||||
|
|
||||||
|
For specific use, please refer to the Makefile under the specific sub-project.
|
||||||
|
|
||||||
|
# References
|
||||||
|
|
||||||
|
The design of this course refers to the following network resources, thank you :)
|
||||||
|
|
||||||
|
- The Adventures of OS:<https://osblog.stephenmarz.com/index.html>
|
||||||
|
- mini-riscv-os: <https://github.com/cccriscv/mini-riscv-os>
|
||||||
|
- Xv6, a simple Unix-like teaching operating system:<https://pdos.csail.mit.edu/6.828/2020/xv6.html>
|
||||||
|
|||||||
77
README_zh.md
Normal file
77
README_zh.md
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
**循序渐进,学习开发一个 RISC-V 上的操作系统**
|
||||||
|
|
||||||
|
<!-- TOC -->
|
||||||
|
|
||||||
|
- [简介](#简介)
|
||||||
|
- [运行环境](#运行环境)
|
||||||
|
- [构建和使用说明](#构建和使用说明)
|
||||||
|
- [参考文献](#参考文献)
|
||||||
|
|
||||||
|
<!-- /TOC -->
|
||||||
|
# 简介
|
||||||
|
|
||||||
|
本课程用于教学演示如何从零开始为 RISC-V 平台编写一个简单的操作系统内核。采用 BSD 2-Clause 许可证发布(具体请阅读本仓库根目录下的 [LICENSE 文件](./LICENSE))。
|
||||||
|
|
||||||
|
# 运行环境
|
||||||
|
|
||||||
|
所有演示代码在以下设备环境下验证通过:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ lsb_release -a
|
||||||
|
No LSB modules are available.
|
||||||
|
Distributor ID: Ubuntu
|
||||||
|
Description: Ubuntu 20.04.2 LTS
|
||||||
|
Release: 20.04
|
||||||
|
Codename: focal
|
||||||
|
$ uname -r
|
||||||
|
5.8.0-45-generic
|
||||||
|
```
|
||||||
|
|
||||||
|
有可能需要手动安装的依赖库,如果运行过程中提示缺少其他的库和依赖,请按照提示自行安装。
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo apt update
|
||||||
|
$ sudo apt install build-essential git gitk vim libfdt-dev libsdl2-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
实验需要一些运行工具,已经提供预先编译好的二进制文件,具体安装步骤描述如下:
|
||||||
|
|
||||||
|
首先,创建一个工作目录,然后进入该目录。
|
||||||
|
|
||||||
|
```
|
||||||
|
$ mkdir $HOME/ws
|
||||||
|
$ cd $HOME/ws
|
||||||
|
```
|
||||||
|
|
||||||
|
下载开发工具软件包 `tools.tar.gz`,下载地址为:<https://share.weiyun.com/nyTqAGKh>。
|
||||||
|
|
||||||
|
下载完毕后将该文件拷贝到 `$HOME/ws` 下并解压。
|
||||||
|
|
||||||
|
```
|
||||||
|
$ tar xzf tools.tar.gz
|
||||||
|
```
|
||||||
|
|
||||||
|
将以下路径加入 `$HOME/.bashrc`
|
||||||
|
```
|
||||||
|
export PATH="$PATH:$HOME/ws/tools/gcc/bin:$HOME/ws/tools/qemu/bin"
|
||||||
|
```
|
||||||
|
|
||||||
|
重新导入 `$HOME/.bashrc` 或者重启系统使配置生效即可。
|
||||||
|
|
||||||
|
# 构建和使用说明
|
||||||
|
|
||||||
|
- make:编译构建
|
||||||
|
- make run:启动 qemu 并运行
|
||||||
|
- make debug:启动调试
|
||||||
|
- make code:反汇编查看二进制代码
|
||||||
|
- make clean:清理
|
||||||
|
|
||||||
|
具体使用请参考具体子项目下的 Makefile 文件。
|
||||||
|
|
||||||
|
# 参考文献
|
||||||
|
|
||||||
|
本课程的设计参考了如下网络资源,在此表示感谢 :)
|
||||||
|
|
||||||
|
- The Adventures of OS:<https://osblog.stephenmarz.com/index.html>
|
||||||
|
- mini-riscv-os: <https://github.com/cccriscv/mini-riscv-os>
|
||||||
|
- Xv6, a simple Unix-like teaching operating system:<https://pdos.csail.mit.edu/6.828/2020/xv6.html>
|
||||||
63
code/asm/Makefile
Normal file
63
code/asm/Makefile
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
SECTIONS_Arithmetic = \
|
||||||
|
add \
|
||||||
|
sub \
|
||||||
|
addi \
|
||||||
|
subi \
|
||||||
|
neg \
|
||||||
|
nop \
|
||||||
|
mv \
|
||||||
|
lui \
|
||||||
|
li
|
||||||
|
|
||||||
|
SECTIONS_Logical = \
|
||||||
|
and \
|
||||||
|
andi \
|
||||||
|
not
|
||||||
|
|
||||||
|
SECTIONS_Shifting = \
|
||||||
|
slli \
|
||||||
|
srli \
|
||||||
|
srai
|
||||||
|
|
||||||
|
SECTIONS_Load_Store = \
|
||||||
|
lb \
|
||||||
|
lbu \
|
||||||
|
lw \
|
||||||
|
sb \
|
||||||
|
auipc \
|
||||||
|
la
|
||||||
|
|
||||||
|
SECTIONS_Branch = \
|
||||||
|
bne
|
||||||
|
|
||||||
|
SECTIONS_Jump = \
|
||||||
|
jalr \
|
||||||
|
|
||||||
|
SECTIONS_CallingConventions = \
|
||||||
|
cc_leaf \
|
||||||
|
cc_nested \
|
||||||
|
|
||||||
|
SECTIONS_others = \
|
||||||
|
asm2c \
|
||||||
|
c2asm
|
||||||
|
|
||||||
|
SECTIONS = \
|
||||||
|
_first \
|
||||||
|
$(SECTIONS_Arithmetic) \
|
||||||
|
$(SECTIONS_Logical) \
|
||||||
|
$(SECTIONS_Shifting) \
|
||||||
|
$(SECTIONS_Load_Store) \
|
||||||
|
$(SECTIONS_Branch) \
|
||||||
|
$(SECTIONS_Jump) \
|
||||||
|
$(SECTIONS_CallingConventions) \
|
||||||
|
$(SECTIONS_others)
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
all :
|
||||||
|
@echo "begin compile ALL exercises for assembly samples ......................."
|
||||||
|
for dir in $(SECTIONS); do $(MAKE) -C $$dir || exit "$$?"; done
|
||||||
|
@echo "compile ALL exercises finished successfully! ......"
|
||||||
|
|
||||||
|
.PHONY : clean
|
||||||
|
clean:
|
||||||
|
for dir in $(SECTIONS); do $(MAKE) -C $$dir clean || exit "$$?"; done
|
||||||
1
code/asm/_first/Makefile
Symbolic link
1
code/asm/_first/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
10
code/asm/_first/test.s
Normal file
10
code/asm/_first/test.s
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# First RISC-V Assemble Sample
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 5 # Load register x6 with the value 5
|
||||||
|
li x7, 4 # Load register x7 with the value 4
|
||||||
|
add x5, x6, x7 # Add x6 and x7 and store result in x5
|
||||||
|
stop: j stop # Infinite loop to stop execution
|
||||||
|
.end # End of file
|
||||||
|
|
||||||
1
code/asm/add/Makefile
Symbolic link
1
code/asm/add/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
19
code/asm/add/test.s
Normal file
19
code/asm/add/test.s
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Add
|
||||||
|
# Format:
|
||||||
|
# ADD RD, RS1, RS2
|
||||||
|
# Description:
|
||||||
|
# The contents of RS1 is added to the contents of RS2 and the result is
|
||||||
|
# placed in RD.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 1 # x6 = 1
|
||||||
|
li x7, 2 # x7 = 2
|
||||||
|
add x5, x6, x7 # x5 = x6 + x7
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/addi/Makefile
Symbolic link
1
code/asm/addi/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
18
code/asm/addi/test.s
Normal file
18
code/asm/addi/test.s
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Add Immediate
|
||||||
|
# Format:
|
||||||
|
# ADDI RD, RS1, IMM
|
||||||
|
# Description:
|
||||||
|
# The immediate value (a sign-extended 12-bit value, i.e., -2,048 .. +2,047)
|
||||||
|
# is added to the contents of RS1 and the result is placed in RD.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 2 # x6 = 2
|
||||||
|
addi x5, x6, 1 # x5 = x6 + 1
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/and/Makefile
Symbolic link
1
code/asm/and/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
18
code/asm/and/test.s
Normal file
18
code/asm/and/test.s
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# And
|
||||||
|
# Format:
|
||||||
|
# AND RD, RS1, RS2
|
||||||
|
# The contents of RS1 is logically ANDed with the contents of RS2 and the
|
||||||
|
# result is placed in RD.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 0x10 # x6 = b1000-0000
|
||||||
|
li x7, 0x01 # x7 = b0000-0001
|
||||||
|
and x5, x6, x7 # x5 = x6 & x7
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/andi/Makefile
Symbolic link
1
code/asm/andi/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
18
code/asm/andi/test.s
Normal file
18
code/asm/andi/test.s
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# And Immediate
|
||||||
|
# Format:
|
||||||
|
# ANDI RD, RS1, IMM
|
||||||
|
# Description:
|
||||||
|
# The immediate value (a sign-extended 12-bit value, i.e., -2,048 .. +2,047)
|
||||||
|
# is logically ANDed with the contents of RD1 and the result is placed in RD.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 0x10 # x6 = b1000-0000
|
||||||
|
andi x5, x6, 0x01 # x5 = x6 & 0x01
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
7
code/asm/asm2c/Makefile
Normal file
7
code/asm/asm2c/Makefile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
EXEC = test
|
||||||
|
|
||||||
|
SRC = $(EXEC).s $(EXEC).c
|
||||||
|
|
||||||
|
GDBINIT = ./gdbinit
|
||||||
|
|
||||||
|
include ../rule.mk
|
||||||
9
code/asm/asm2c/gdbinit
Normal file
9
code/asm/asm2c/gdbinit
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
display/z $sp
|
||||||
|
display/z $ra
|
||||||
|
display/z $a0
|
||||||
|
display/z $a1
|
||||||
|
|
||||||
|
set disassemble-next-line on
|
||||||
|
b _start
|
||||||
|
target remote : 1234
|
||||||
|
c
|
||||||
5
code/asm/asm2c/test.c
Normal file
5
code/asm/asm2c/test.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
int foo(int a, int b)
|
||||||
|
{
|
||||||
|
int sum = a + b;
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
23
code/asm/asm2c/test.s
Normal file
23
code/asm/asm2c/test.s
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# ASM call C
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
.global foo #
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
la sp, stack_end # prepare stack for calling functions
|
||||||
|
|
||||||
|
li a0, 1
|
||||||
|
li a1, 2
|
||||||
|
call foo
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
stack_start:
|
||||||
|
.rept 10
|
||||||
|
.word 0
|
||||||
|
.endr
|
||||||
|
stack_end:
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/auipc/Makefile
Symbolic link
1
code/asm/auipc/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
23
code/asm/auipc/test.s
Normal file
23
code/asm/auipc/test.s
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Add Upper Immediate to PC
|
||||||
|
# Format:
|
||||||
|
# AUIPC RD, IMM
|
||||||
|
# Description:
|
||||||
|
# AUIPC is used to build pc-relative addresses and uses the U-type format.
|
||||||
|
# AUIPC forms a 32-bit offset from the 20-bit U-immediate, filling in the
|
||||||
|
# lowest 12 bits with zeros, adds this offset to the address of the AUIPC
|
||||||
|
# instruction, then places the result in register RD.
|
||||||
|
# Note:
|
||||||
|
# The current PC can be obtained by setting the U-immediate to 0.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
auipc x6, 0 # x6 = PC
|
||||||
|
auipc x5, 0x12345 # x5 = PC + (0x12345 << 12)
|
||||||
|
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/bne/Makefile
Symbolic link
1
code/asm/bne/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
26
code/asm/bne/test.s
Normal file
26
code/asm/bne/test.s
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Branch if Not Equal
|
||||||
|
# Format:
|
||||||
|
# BNE RS1, RS2, IMM
|
||||||
|
# Description:
|
||||||
|
# The contents of RS1 is compared to the contents of RS2. If not equal,
|
||||||
|
# control jumps to a PC-relative target address.
|
||||||
|
# Note:
|
||||||
|
# When programming, we just provide label instead of immediate value, and
|
||||||
|
# leave linker to provide the final immediate value.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
# i = 0
|
||||||
|
# while (i < 5) i++;
|
||||||
|
li x5, 0
|
||||||
|
li x6, 5
|
||||||
|
loop:
|
||||||
|
addi x5, x5, 1
|
||||||
|
bne x5, x6, loop
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
7
code/asm/build.mk
Normal file
7
code/asm/build.mk
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
EXEC = test
|
||||||
|
|
||||||
|
SRC = $(EXEC).s
|
||||||
|
|
||||||
|
GDBINIT = ../gdbinit
|
||||||
|
|
||||||
|
include ../rule.mk
|
||||||
7
code/asm/c2asm/Makefile
Normal file
7
code/asm/c2asm/Makefile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
EXEC = test
|
||||||
|
|
||||||
|
SRC = $(EXEC).s $(EXEC).c
|
||||||
|
|
||||||
|
GDBINIT = ./gdbinit
|
||||||
|
|
||||||
|
include ../rule.mk
|
||||||
11
code/asm/c2asm/gdbinit
Normal file
11
code/asm/c2asm/gdbinit
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
display/z $sp
|
||||||
|
display/z $ra
|
||||||
|
display/z $s0
|
||||||
|
display/z $a0
|
||||||
|
display/z $a1
|
||||||
|
display/z $a4
|
||||||
|
display/z $a5
|
||||||
|
set disassemble-next-line on
|
||||||
|
b _start
|
||||||
|
target remote : 1234
|
||||||
|
c
|
||||||
11
code/asm/c2asm/test.c
Normal file
11
code/asm/c2asm/test.c
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
int foo(int a, int b)
|
||||||
|
{
|
||||||
|
int sum;
|
||||||
|
asm volatile(
|
||||||
|
"nop\n"
|
||||||
|
"add %0, %1, %2"
|
||||||
|
:"=r"(sum)
|
||||||
|
:"r"(a), "r"(b)
|
||||||
|
);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
23
code/asm/c2asm/test.s
Normal file
23
code/asm/c2asm/test.s
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# C all ASM
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
.global foo #
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
la sp, stack_end # prepare stack for calling functions
|
||||||
|
|
||||||
|
li a0, 1
|
||||||
|
li a1, 2
|
||||||
|
call foo
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
stack_start:
|
||||||
|
.rept 10
|
||||||
|
.word 0
|
||||||
|
.endr
|
||||||
|
stack_end:
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
7
code/asm/cc_leaf/Makefile
Normal file
7
code/asm/cc_leaf/Makefile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
EXEC = test
|
||||||
|
|
||||||
|
SRC = $(EXEC).s
|
||||||
|
|
||||||
|
GDBINIT = ./gdbinit
|
||||||
|
|
||||||
|
include ../rule.mk
|
||||||
11
code/asm/cc_leaf/gdbinit
Normal file
11
code/asm/cc_leaf/gdbinit
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
display/z $sp
|
||||||
|
display/z $ra
|
||||||
|
display/z $a0
|
||||||
|
display/z $s0
|
||||||
|
display/z $s1
|
||||||
|
display/z $s2
|
||||||
|
|
||||||
|
set disassemble-next-line on
|
||||||
|
b _start
|
||||||
|
target remote : 1234
|
||||||
|
c
|
||||||
48
code/asm/cc_leaf/test.s
Normal file
48
code/asm/cc_leaf/test.s
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Calling Convention
|
||||||
|
# Demo to create a leaf routine
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
la sp, stack_end # prepare stack for calling functions
|
||||||
|
|
||||||
|
li a0, 3
|
||||||
|
call square
|
||||||
|
|
||||||
|
# the time return here, a0 should stores the result
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
# int square(int num)
|
||||||
|
square:
|
||||||
|
# prologue
|
||||||
|
addi sp, sp, -8
|
||||||
|
sw s0, 0(sp)
|
||||||
|
sw s1, 4(sp)
|
||||||
|
|
||||||
|
# `mul a0, a0, a0` should be fine,
|
||||||
|
# programing as below just to demo we can contine use the stack
|
||||||
|
mv s0, a0
|
||||||
|
mul s1, s0, s0
|
||||||
|
mv a0, s1
|
||||||
|
|
||||||
|
# epilogue
|
||||||
|
lw s0, 0(sp)
|
||||||
|
lw s1, 4(sp)
|
||||||
|
addi sp, sp, 8
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
# add nop here just for demo in gdb
|
||||||
|
nop
|
||||||
|
|
||||||
|
# allocate stack space
|
||||||
|
stack_start:
|
||||||
|
.rept 10
|
||||||
|
.word 0
|
||||||
|
.endr
|
||||||
|
stack_end:
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
|
|
||||||
7
code/asm/cc_nested/Makefile
Normal file
7
code/asm/cc_nested/Makefile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
EXEC = test
|
||||||
|
|
||||||
|
SRC = $(EXEC).s
|
||||||
|
|
||||||
|
GDBINIT = ./gdbinit
|
||||||
|
|
||||||
|
include ../rule.mk
|
||||||
13
code/asm/cc_nested/gdbinit
Normal file
13
code/asm/cc_nested/gdbinit
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
display/z $sp
|
||||||
|
display/z $ra
|
||||||
|
display/z $a0
|
||||||
|
display/z $a1
|
||||||
|
display/z $s0
|
||||||
|
display/z $s1
|
||||||
|
display/z $s2
|
||||||
|
|
||||||
|
|
||||||
|
set disassemble-next-line on
|
||||||
|
b _start
|
||||||
|
target remote : 1234
|
||||||
|
c
|
||||||
83
code/asm/cc_nested/test.s
Normal file
83
code/asm/cc_nested/test.s
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
# Calling Convention
|
||||||
|
# Demo how to write nested routunes
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
la sp, stack_end # prepare stack for calling functions
|
||||||
|
|
||||||
|
# aa_bb(3, 4);
|
||||||
|
li a0, 3
|
||||||
|
li a1, 4
|
||||||
|
call aa_bb
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
# int aa_bb(int a, int b)
|
||||||
|
# return a^2 + b^2
|
||||||
|
aa_bb:
|
||||||
|
# prologue
|
||||||
|
addi sp, sp, -16
|
||||||
|
sw s0, 0(sp)
|
||||||
|
sw s1, 4(sp)
|
||||||
|
sw s2, 8(sp)
|
||||||
|
sw ra, 12(sp)
|
||||||
|
|
||||||
|
# cp and store the input params
|
||||||
|
mv s0, a0
|
||||||
|
mv s1, a1
|
||||||
|
|
||||||
|
# sum will be stored in s2 and is initialized as zero
|
||||||
|
li s2, 0
|
||||||
|
|
||||||
|
mv a0, s0
|
||||||
|
jal square
|
||||||
|
add s2, s2, a0
|
||||||
|
|
||||||
|
mv a0, s1
|
||||||
|
jal square
|
||||||
|
add s2, s2, a0
|
||||||
|
|
||||||
|
mv a0, s2
|
||||||
|
|
||||||
|
# epilogue
|
||||||
|
lw s0, 0(sp)
|
||||||
|
lw s1, 4(sp)
|
||||||
|
lw s2, 8(sp)
|
||||||
|
lw ra, 12(sp)
|
||||||
|
addi sp, sp, 16
|
||||||
|
ret
|
||||||
|
|
||||||
|
# int square(int num)
|
||||||
|
square:
|
||||||
|
# prologue
|
||||||
|
addi sp, sp, -8
|
||||||
|
sw s0, 0(sp)
|
||||||
|
sw s1, 4(sp)
|
||||||
|
|
||||||
|
# `mul a0, a0, a0` should be fine,
|
||||||
|
# programing as below just to demo we can contine use the stack
|
||||||
|
mv s0, a0
|
||||||
|
mul s1, s0, s0
|
||||||
|
mv a0, s1
|
||||||
|
|
||||||
|
# epilogue
|
||||||
|
lw s0, 0(sp)
|
||||||
|
lw s1, 4(sp)
|
||||||
|
addi sp, sp, 8
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
# add nop here just for demo in gdb
|
||||||
|
nop
|
||||||
|
|
||||||
|
# allocate stack space
|
||||||
|
stack_start:
|
||||||
|
.rept 10
|
||||||
|
.word 0
|
||||||
|
.endr
|
||||||
|
stack_end:
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
8
code/asm/gdbinit
Normal file
8
code/asm/gdbinit
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
display/z $x5
|
||||||
|
display/z $x6
|
||||||
|
display/z $x7
|
||||||
|
|
||||||
|
set disassemble-next-line on
|
||||||
|
b _start
|
||||||
|
target remote : 1234
|
||||||
|
c
|
||||||
1
code/asm/jalr/Makefile
Symbolic link
1
code/asm/jalr/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
44
code/asm/jalr/test.s
Normal file
44
code/asm/jalr/test.s
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Jump And Link (Short-Distance CALL)
|
||||||
|
# Format:
|
||||||
|
# JAL RD, IMM
|
||||||
|
# Description:
|
||||||
|
# This instruction is used to call a subroutine (i.e., function).
|
||||||
|
# The jump and link (JAL) instruction uses the J-type format, where the
|
||||||
|
# immediate (20 bits width) encodes a signed offset in multiples of 2 bytes.
|
||||||
|
# The offset is sign-extended and added to the address of the jump
|
||||||
|
# instruction to form the jump target address. JAL can therefore target
|
||||||
|
# a ±1 MiB range.
|
||||||
|
# JAL stores the address of the instruction following the jump (pc+4) into
|
||||||
|
# register RD.
|
||||||
|
# Note:
|
||||||
|
# When programming, we just provide label instead of immediate value, and
|
||||||
|
# leave linker to provide the final immediate value.
|
||||||
|
#
|
||||||
|
# Jump And Link Register
|
||||||
|
# Format:
|
||||||
|
# JALR RD, RS1, IMM
|
||||||
|
# Description:
|
||||||
|
# This instruction is used to call a subroutine (i.e., function).
|
||||||
|
# The indirect jump instruction JALR (jump and link register) uses the
|
||||||
|
# I-type encoding. The target address is obtained by adding the
|
||||||
|
# sign-extended 12-bit I-immediate to the register RS1, then setting
|
||||||
|
# the least-significant bit of the result to zero. JALR can therefore target
|
||||||
|
# a ±1 KiB range, relative to the address in RS1.
|
||||||
|
# The address of the instruction following the jump(pc+4) is written to register RD.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
li x6, 1
|
||||||
|
li x7, 2
|
||||||
|
jal x5, sum # call sum, return address is saved in x5
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
sum:
|
||||||
|
add x6, x6, x7 # x6 = x6 + x7
|
||||||
|
jalr x0, 0(x5) # return
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/la/Makefile
Symbolic link
1
code/asm/la/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
29
code/asm/la/test.s
Normal file
29
code/asm/la/test.s
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Load Address
|
||||||
|
# Format:
|
||||||
|
# LA RD, Address
|
||||||
|
# Description:
|
||||||
|
# The address of some memory location is copied into RD.
|
||||||
|
#
|
||||||
|
# LA is a pseudoinstruction, and is assembled to a sequence of two
|
||||||
|
# instructions to achieve the same effect.
|
||||||
|
# AUIPC RD, Upper-20
|
||||||
|
# ADDI RD, RD, Lower-12
|
||||||
|
#
|
||||||
|
# The "Address" can refer to any location within the 32-bit memory space.
|
||||||
|
# The address is converted to a PC-relative address, with an offset of
|
||||||
|
# 32 bits. This offset is then broken into two pieces: a upper 20-bit
|
||||||
|
# piece and a lower 12-bit piece.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
la x5, _start # x6 = PC
|
||||||
|
jr x5
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
exit:
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/lb/Makefile
Symbolic link
1
code/asm/lb/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
28
code/asm/lb/test.s
Normal file
28
code/asm/lb/test.s
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Load Byte (Signed)
|
||||||
|
# Format:
|
||||||
|
# LB RD, IMM(RS1)
|
||||||
|
# Description:
|
||||||
|
# An 8-bit value is fetched from memory and moved into register RD. The
|
||||||
|
# memory address is formed by adding the offset(IMM) to the contents of RS1.
|
||||||
|
# The 8-bit value is sign-extended to the full length of the register.
|
||||||
|
# Note:
|
||||||
|
# Due to IMM is 12 bits width, the target location given by the
|
||||||
|
# offset(IMM) must be within the range of -2,048 .. 2,047 relative to the
|
||||||
|
# value in RS1.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
la x5, _array # char *x5 = &(array[0])
|
||||||
|
lb x6, 0(x5) # char x6 = *x5
|
||||||
|
lb x7, 1(x5) # char x7 = *(x5 + 1)
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
_array:
|
||||||
|
.byte 0x11
|
||||||
|
.byte 0xff
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
|
|
||||||
1
code/asm/lbu/Makefile
Symbolic link
1
code/asm/lbu/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
28
code/asm/lbu/test.s
Normal file
28
code/asm/lbu/test.s
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Load Byte (Unsigned)
|
||||||
|
# Format:
|
||||||
|
# LBU RD, IMM(RS1)
|
||||||
|
# Description:
|
||||||
|
# An 8-bit value is fetched from memory and moved into register RD. The
|
||||||
|
# memory address is formed by adding the offset(IMM) to the contents of RS1.
|
||||||
|
# The 8-bit value is zero-extended to the full length of the register.
|
||||||
|
# Note:
|
||||||
|
# Due to IMM is 12 bits width, the target location given by the
|
||||||
|
# offset(IMM) must be within the range of -2,048 .. 2,047 relative to the
|
||||||
|
# value in RS1.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
la x5, _array # unsigned char *x5 = &(array[0])
|
||||||
|
lbu x6, 0(x5) # unsigned x6 = *x5
|
||||||
|
lbu x7, 1(x5) # unsigned x7 = *(x5 + 1)
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
_array:
|
||||||
|
.byte 0x11
|
||||||
|
.byte 0xff
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
|
|
||||||
1
code/asm/li/Makefile
Symbolic link
1
code/asm/li/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
44
code/asm/li/test.s
Normal file
44
code/asm/li/test.s
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Load Immediate
|
||||||
|
# Format:
|
||||||
|
# LI RD, IMM
|
||||||
|
# Description:
|
||||||
|
# The immediate value (which can be any 32-bit value) is copied into RD.
|
||||||
|
# LI is a pseudoinstruction, and is assembled differently depending on
|
||||||
|
# the actual value present.
|
||||||
|
#
|
||||||
|
# If the immediate value is in the range of -2,048 .. +2,047, then it can
|
||||||
|
# be assembled identically to:
|
||||||
|
# ADDI RD, x0, IMM
|
||||||
|
#
|
||||||
|
# If the immediate value is not within the range of -2,048 .. +2,047 but
|
||||||
|
# is within the range of a 32-bit number (i.e., -2,147,483,648 .. +2,147,483,647)
|
||||||
|
# then it can be assembled using this two-instruction sequence:
|
||||||
|
# LUI RD, Upper-20
|
||||||
|
# ADDI RD, RD, Lower-12
|
||||||
|
# where "Upper-20" represents the uppermost 20 bits of the value
|
||||||
|
# and "Lower-12" represents the least significant 12-bits of the value.
|
||||||
|
# Note that, due to the immediate operand to the addi has its
|
||||||
|
# most-significant-bit set to 1 then it will have the effect of
|
||||||
|
# subtracting 1 from the operand in the lui instruction.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x5, 0x80 # imm is in the range of [-2,048, +2,047]
|
||||||
|
addi x5, x0, 0x80 # these two instructions assemble into the same thing!
|
||||||
|
|
||||||
|
li x6, 0x12345001 # imm is NOT in the range of [-2,048, +2,047]
|
||||||
|
# and the most-significant-bit of "lower-12" is 0
|
||||||
|
lui x6, 0x12345 # these two instructions assemble into the same thing!
|
||||||
|
addi x6, x6, 0x001
|
||||||
|
|
||||||
|
li x7, 0x12345800 # imm is NOT in the range of [-2,048, +2,047]
|
||||||
|
# and the most-significant-bit of "lower-12" is 1
|
||||||
|
lui x7, 0x12346 # these two instructions assemble into the same thing!
|
||||||
|
addi x7, x7, -0x800
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/lui/Makefile
Symbolic link
1
code/asm/lui/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
19
code/asm/lui/test.s
Normal file
19
code/asm/lui/test.s
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Load Upper Immediate
|
||||||
|
# Format:
|
||||||
|
# LUI RD, IMM
|
||||||
|
# Description:
|
||||||
|
# The instruction contains a 20-bit immediate value. This value is placed
|
||||||
|
# in the leftmost (i.e., upper, most significant) 20 bits of the register
|
||||||
|
# RD and the rightmost (i.e., lower, least significant) 12-bits are set
|
||||||
|
# to zero.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
lui x5, 0x12345 # int x5 = 0x12345 << 12
|
||||||
|
addi x5, x5, 0x678 # x5 = x5 + 0x678
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/lw/Makefile
Symbolic link
1
code/asm/lw/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
31
code/asm/lw/test.s
Normal file
31
code/asm/lw/test.s
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Load Byte (Signed)
|
||||||
|
# Format:
|
||||||
|
# LW RD, IMM(RS1)
|
||||||
|
# Description:
|
||||||
|
# An 32-bit value is fetched from memory and moved into register RD. The
|
||||||
|
# memory address is formed by adding the offset(IMM) to the contents of RS1.
|
||||||
|
# Note:
|
||||||
|
# Due to IMM is 12 bits width, the target location given by the
|
||||||
|
# offset(IMM) must be within the range of -2,048 .. 2,047 relative to the
|
||||||
|
# value in RS1.
|
||||||
|
# In a machine with 32-bit registers(rv32), neither sign-extension nor
|
||||||
|
# zero-extension is necessary for value that is already 32 bits wide.
|
||||||
|
# Therefore the "signed load" instruction (LW) does the same thing as the
|
||||||
|
# "unsigned load" instruction(LWU), making LWU redundant.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
la x5, _array # int *x5 = &(array[0])
|
||||||
|
lw x6, 0(x5) # int x6 = *x5
|
||||||
|
lw x7, 4(x5) # int x7 = *(x5 + 1)
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
_array:
|
||||||
|
.word 0x11111111
|
||||||
|
.word 0xffffffff
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
|
|
||||||
1
code/asm/mv/Makefile
Symbolic link
1
code/asm/mv/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
20
code/asm/mv/test.s
Normal file
20
code/asm/mv/test.s
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Move (Register to Register)
|
||||||
|
# Format:
|
||||||
|
# MV RD, RS
|
||||||
|
# Description:
|
||||||
|
# The contents of RS is copied into RD.
|
||||||
|
# MV is a pseudoinstruction, and is assembled identically to:
|
||||||
|
# ADDI RD, RS, 0
|
||||||
|
#
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 30 # x6 = 30
|
||||||
|
mv x5, x6 # x5 = x6
|
||||||
|
addi x5, x6, 0 # these two instructions assemble into the same thing!
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/neg/Makefile
Symbolic link
1
code/asm/neg/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
21
code/asm/neg/test.s
Normal file
21
code/asm/neg/test.s
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Negate
|
||||||
|
# Format:
|
||||||
|
# NEG RD, RS
|
||||||
|
# Description:
|
||||||
|
# The contents of RS is arithmetically negated and the result is placed in RD.
|
||||||
|
# NEG is a pseudoinstruction, and is assembled identically to:
|
||||||
|
# SUB RD, x0, RS
|
||||||
|
#
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 1 # x6 = 1
|
||||||
|
neg x5, x6 # x5 = -x6
|
||||||
|
sub x5, x0, x6 # these two instructions assemble into the same thing!
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/nop/Makefile
Symbolic link
1
code/asm/nop/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
20
code/asm/nop/test.s
Normal file
20
code/asm/nop/test.s
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Nop
|
||||||
|
# Format:
|
||||||
|
# NOP
|
||||||
|
# Description:
|
||||||
|
# This instruction has no effect.
|
||||||
|
# NOP is a pseudoinstruction, and is assembled identically to:
|
||||||
|
# ADDI x0, x0, 0
|
||||||
|
#
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
nop # do nothing and has no effect on system
|
||||||
|
addi x0, x0, 0 # these two instructions assemble into the same thing!
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/not/Makefile
Symbolic link
1
code/asm/not/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
19
code/asm/not/test.s
Normal file
19
code/asm/not/test.s
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Not
|
||||||
|
# Format:
|
||||||
|
# NOT RD, RS
|
||||||
|
# The contents of RS is fetched and each of the bits is flipped. The resulting
|
||||||
|
# value is copied into RD.
|
||||||
|
# NEG is a pseudoinstruction, and is assembled identically to:
|
||||||
|
# XORI RD, RS, -1 // Note that -1 is 0xFFFFFFFF
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 0xffff0000 # x6 = 0xffff0000
|
||||||
|
not x5, x6 # x5 = ~x6
|
||||||
|
xori x5, x6, -1 # these two instructions assemble into the same thing!
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
38
code/asm/rule.mk
Normal file
38
code/asm/rule.mk
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
CROSS_COMPILE = riscv64-unknown-elf-
|
||||||
|
CFLAGS = -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall
|
||||||
|
|
||||||
|
QEMU = qemu-system-riscv32
|
||||||
|
QFLAGS = -nographic -smp 1 -machine virt -bios none
|
||||||
|
|
||||||
|
GDB = ${CROSS_COMPILE}gdb
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
all:
|
||||||
|
@$(CROSS_COMPILE)gcc $(CFLAGS) ${SRC} -Ttext=0x80000000 -o $(EXEC).elf
|
||||||
|
@$(CROSS_COMPILE)objcopy -O binary $(EXEC).elf $(EXEC).bin
|
||||||
|
|
||||||
|
.PHONY : run
|
||||||
|
run: all
|
||||||
|
@echo "Press Ctrl-A and then X to exit QEMU"
|
||||||
|
@echo "------------------------------------"
|
||||||
|
@echo "No output, please run 'make debug' to see details"
|
||||||
|
@$(QEMU) $(QFLAGS) -kernel ./$(EXEC).elf
|
||||||
|
|
||||||
|
.PHONY : debug
|
||||||
|
debug: all
|
||||||
|
@echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU"
|
||||||
|
@echo "-------------------------------------------------------"
|
||||||
|
@$(QEMU) $(QFLAGS) -kernel $(EXEC).elf -s -S &
|
||||||
|
@$(GDB) $(EXEC).elf -q -x ${GDBINIT}
|
||||||
|
|
||||||
|
.PHONY : code
|
||||||
|
code: all
|
||||||
|
@$(CROSS_COMPILE)objdump -S $(EXEC).elf | less
|
||||||
|
|
||||||
|
.PHONY : hex
|
||||||
|
hex: all
|
||||||
|
@hexdump -C $(EXEC).bin
|
||||||
|
|
||||||
|
.PHONY : clean
|
||||||
|
clean:
|
||||||
|
rm -rf *.o *.bin *.elf
|
||||||
1
code/asm/sb/Makefile
Symbolic link
1
code/asm/sb/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
28
code/asm/sb/test.s
Normal file
28
code/asm/sb/test.s
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Store Byte
|
||||||
|
# Format:
|
||||||
|
# SB RS2, IMM(RS1)
|
||||||
|
# Description:
|
||||||
|
# An 8-bit value is copied from register RS2 to memory. The upper (more
|
||||||
|
# significant) bits in RS2 are ignored. The memory address is formed by
|
||||||
|
# adding the offset(IMM) to the contents of RS1.
|
||||||
|
# Note:
|
||||||
|
# Due to IMM is 12 bits width, the target location given by the
|
||||||
|
# offset(IMM) must be within the range of -2,048 .. 2,047 relative to the
|
||||||
|
# value in RS1.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 0xffffffab # int x6 = 0xffffffab
|
||||||
|
la x5, _array # array[0] = (char)x6
|
||||||
|
sb x6, 0(x5)
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
_array:
|
||||||
|
.byte 0x00
|
||||||
|
.byte 0x00
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
|
|
||||||
1
code/asm/slli/Makefile
Symbolic link
1
code/asm/slli/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
21
code/asm/slli/test.s
Normal file
21
code/asm/slli/test.s
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Shift Left Logical Immediate
|
||||||
|
# Format:
|
||||||
|
# SLLI RD, RS1, IMM
|
||||||
|
# Description:
|
||||||
|
# The immediate value determines the number of bits to shift. The contents
|
||||||
|
# of RS1 is shifted left that many bits and the result is placed in RD.
|
||||||
|
# The bits shifted in are filled with zero.
|
||||||
|
# For 32-bit machines, the shift amount must be within 0..31, 0 means no
|
||||||
|
# shifting is done.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 1 # x6 = 1
|
||||||
|
slli x5, x6, 3 # x5 = x6 << 3
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/srai/Makefile
Symbolic link
1
code/asm/srai/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
9
code/asm/srai/test.c
Normal file
9
code/asm/srai/test.c
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// for signed int, shift right is assembled to srai
|
||||||
|
|
||||||
|
// riscv64-unknown-elf-gcc -march=rv32ima -mabi=ilp32 -c -g test.c -o test.o
|
||||||
|
// riscv64-unknown-elf-objdump -S test.o
|
||||||
|
void foo()
|
||||||
|
{
|
||||||
|
int i = 0x80000000;
|
||||||
|
i = i >> 4;
|
||||||
|
}
|
||||||
23
code/asm/srai/test.s
Normal file
23
code/asm/srai/test.s
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Shift Right Arithmetic Immediate
|
||||||
|
# Format:
|
||||||
|
# SLLI RD, RS1, IMM
|
||||||
|
# Description:
|
||||||
|
# The immediate value determines the number of bits to shift. The contents of
|
||||||
|
# RS1 is shifted right that many bits and the result is placed in RD. The shift
|
||||||
|
# is “arithmetic”, i.e., the sign bit is repeatedly shifted in on the
|
||||||
|
# most-significant end.
|
||||||
|
# Comment:
|
||||||
|
# In C, for signed integer, >> is shift right with arithmetic.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
# li x6, 0x80 # x6 = 0b1000-0000
|
||||||
|
li x6, 0x80000000 # x6 = 0b1000-0000-0000-0000-0000-0000-0000-0000
|
||||||
|
srai x5, x6, 4 # x5 = x6 >> 3
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/srli/Makefile
Symbolic link
1
code/asm/srli/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
9
code/asm/srli/test.c
Normal file
9
code/asm/srli/test.c
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// for unsigned int, shift right is assembled to srli
|
||||||
|
|
||||||
|
// riscv64-unknown-elf-gcc -march=rv32ima -mabi=ilp32 -c -g test.c -o test.o
|
||||||
|
// riscv64-unknown-elf-objdump -S test.o
|
||||||
|
void foo()
|
||||||
|
{
|
||||||
|
unsigned int i = 0x80000000;
|
||||||
|
i = i >> 4;
|
||||||
|
}
|
||||||
21
code/asm/srli/test.s
Normal file
21
code/asm/srli/test.s
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Shift Right Logical Immediate
|
||||||
|
# Format:
|
||||||
|
# SRLI RD, RS1, IMM
|
||||||
|
# Description:
|
||||||
|
# The immediate value determines the number of bits to shift. The contents
|
||||||
|
# of RS1 is shifted right that many bits and the result is placed in RD.
|
||||||
|
# The bits shifted in on the most-significant end are filled with zero.
|
||||||
|
# For 32-bit machines, the shift amount must be within 0..31, 0 means no
|
||||||
|
# shifting is done.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 0x80000000 # x6 = 0x80000000
|
||||||
|
srli x5, x6, 3 # x5 = x6 >> 3
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/sub/Makefile
Symbolic link
1
code/asm/sub/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
19
code/asm/sub/test.s
Normal file
19
code/asm/sub/test.s
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Substract
|
||||||
|
# Format:
|
||||||
|
# SUB RD, RS1, RS2
|
||||||
|
# Description:
|
||||||
|
# The contents of RS2 is subtracted from the contents of RS1 and the result
|
||||||
|
# is placed in RD.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, -1 # x6 = -1
|
||||||
|
li x7, -2 # x7 = -2
|
||||||
|
sub x5, x6, x7 # x5 = x6 - x7
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
1
code/asm/subi/Makefile
Symbolic link
1
code/asm/subi/Makefile
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../build.mk
|
||||||
16
code/asm/subi/test.s
Normal file
16
code/asm/subi/test.s
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Substract Immediate
|
||||||
|
# Description:
|
||||||
|
# There is no “subtract immediate” instruction because subtraction is
|
||||||
|
# equivalent to adding a negative value of immediate.
|
||||||
|
|
||||||
|
.text # Define beginning of text section
|
||||||
|
.global _start # Define entry _start
|
||||||
|
|
||||||
|
_start: # Label, not really required
|
||||||
|
li x6, 30 # x5 = 1
|
||||||
|
addi x5, x6, -20 # x5 = x6 - 20
|
||||||
|
|
||||||
|
stop:
|
||||||
|
j stop # Infinite loop to stop execution
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
55
code/os/00-bootstrap/Makefile
Normal file
55
code/os/00-bootstrap/Makefile
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
CROSS_COMPILE = riscv64-unknown-elf-
|
||||||
|
CFLAGS = -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall
|
||||||
|
|
||||||
|
QEMU = qemu-system-riscv32
|
||||||
|
QFLAGS = -nographic -smp 1 -machine virt -bios none
|
||||||
|
|
||||||
|
GDB = ${CROSS_COMPILE}gdb
|
||||||
|
CC = ${CROSS_COMPILE}gcc
|
||||||
|
OBJCOPY = ${CROSS_COMPILE}objcopy
|
||||||
|
OBJDUMP = ${CROSS_COMPILE}objdump
|
||||||
|
|
||||||
|
SRCS_ASM = \
|
||||||
|
start.S \
|
||||||
|
|
||||||
|
SRCS_C = \
|
||||||
|
kernel.c \
|
||||||
|
|
||||||
|
OBJS = $(SRCS_ASM:.S=.o)
|
||||||
|
OBJS += $(SRCS_C:.c=.o)
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
all: os.elf
|
||||||
|
|
||||||
|
# start.o must be the first in dependency!
|
||||||
|
os.elf: ${OBJS}
|
||||||
|
${CC} $(CFLAGS) -Ttext=0x80000000 -o os.elf $^
|
||||||
|
${OBJCOPY} -O binary os.elf os.bin
|
||||||
|
|
||||||
|
%.o : %.c
|
||||||
|
${CC} ${CFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
|
%.o : %.S
|
||||||
|
${CC} ${CFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
|
run: all
|
||||||
|
@${QEMU} -M ? | grep virt >/dev/null || exit
|
||||||
|
@echo "Press Ctrl-A and then X to exit QEMU"
|
||||||
|
@echo "------------------------------------"
|
||||||
|
@${QEMU} ${QFLAGS} -kernel os.elf
|
||||||
|
|
||||||
|
.PHONY : debug
|
||||||
|
debug: all
|
||||||
|
@echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU"
|
||||||
|
@echo "-------------------------------------------------------"
|
||||||
|
@${QEMU} ${QFLAGS} -kernel os.elf -s -S &
|
||||||
|
@${GDB} os.elf -q -x ../gdbinit
|
||||||
|
|
||||||
|
.PHONY : code
|
||||||
|
code: all
|
||||||
|
@${OBJDUMP} -S os.elf | less
|
||||||
|
|
||||||
|
.PHONY : clean
|
||||||
|
clean:
|
||||||
|
rm -rf *.o *.bin *.elf
|
||||||
|
|
||||||
5
code/os/00-bootstrap/kernel.c
Normal file
5
code/os/00-bootstrap/kernel.c
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
void start_kernel(void)
|
||||||
|
{
|
||||||
|
while (1) {}; // stop here!
|
||||||
|
}
|
||||||
|
|
||||||
15
code/os/00-bootstrap/platform.h
Normal file
15
code/os/00-bootstrap/platform.h
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef __PLATFORM_H__
|
||||||
|
#define __PLATFORM_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QEMU RISC-V Virt machine with 16550a UART and VirtIO MMIO
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* maximum number of CPUs
|
||||||
|
* see https://github.com/qemu/qemu/blob/master/include/hw/riscv/virt.h
|
||||||
|
* #define VIRT_CPUS_MAX 8
|
||||||
|
*/
|
||||||
|
#define MAXNUM_CPU 8
|
||||||
|
|
||||||
|
#endif /* __PLATFORM_H__ */
|
||||||
31
code/os/00-bootstrap/start.S
Normal file
31
code/os/00-bootstrap/start.S
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
.equ STACK_SIZE, 8192
|
||||||
|
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
.text
|
||||||
|
_start:
|
||||||
|
# park harts with id != 0
|
||||||
|
csrr t0, mhartid # read current hart id
|
||||||
|
mv tp, t0 # keep CPU's hartid in its tp for later usage.
|
||||||
|
bnez t0, park # if we're not on the hart 0
|
||||||
|
# we park the hart
|
||||||
|
# Setup stacks, the stack grows from bottom to top, so we put the
|
||||||
|
# stack pointer to the very end of the stack range.
|
||||||
|
slli t0, t0, 10 # shift left the hart id by 1024
|
||||||
|
la sp, stacks + STACK_SIZE # set the initial stack pointer
|
||||||
|
# to the end of the stack space
|
||||||
|
add sp, sp, t0 # move the current hart stack pointer
|
||||||
|
# to its place in the stack space
|
||||||
|
|
||||||
|
j start_kernel # hart 0 jump to c
|
||||||
|
|
||||||
|
park:
|
||||||
|
wfi
|
||||||
|
j park
|
||||||
|
|
||||||
|
stacks:
|
||||||
|
.skip STACK_SIZE * MAXNUM_CPU # allocate space for the harts stacks
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
56
code/os/01-helloRVOS/Makefile
Normal file
56
code/os/01-helloRVOS/Makefile
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
CROSS_COMPILE = riscv64-unknown-elf-
|
||||||
|
CFLAGS = -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall
|
||||||
|
|
||||||
|
QEMU = qemu-system-riscv32
|
||||||
|
QFLAGS = -nographic -smp 1 -machine virt -bios none
|
||||||
|
|
||||||
|
GDB = ${CROSS_COMPILE}gdb
|
||||||
|
CC = ${CROSS_COMPILE}gcc
|
||||||
|
OBJCOPY = ${CROSS_COMPILE}objcopy
|
||||||
|
OBJDUMP = ${CROSS_COMPILE}objdump
|
||||||
|
|
||||||
|
SRCS_ASM = \
|
||||||
|
start.S \
|
||||||
|
|
||||||
|
SRCS_C = \
|
||||||
|
kernel.c \
|
||||||
|
uart.c \
|
||||||
|
|
||||||
|
OBJS = $(SRCS_ASM:.S=.o)
|
||||||
|
OBJS += $(SRCS_C:.c=.o)
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
all: os.elf
|
||||||
|
|
||||||
|
# start.o must be the first in dependency!
|
||||||
|
os.elf: ${OBJS}
|
||||||
|
${CC} $(CFLAGS) -Ttext=0x80000000 -o os.elf $^
|
||||||
|
${OBJCOPY} -O binary os.elf os.bin
|
||||||
|
|
||||||
|
%.o : %.c
|
||||||
|
${CC} ${CFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
|
%.o : %.S
|
||||||
|
${CC} ${CFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
|
run: all
|
||||||
|
@${QEMU} -M ? | grep virt >/dev/null || exit
|
||||||
|
@echo "Press Ctrl-A and then X to exit QEMU"
|
||||||
|
@echo "------------------------------------"
|
||||||
|
@${QEMU} ${QFLAGS} -kernel os.elf
|
||||||
|
|
||||||
|
.PHONY : debug
|
||||||
|
debug: all
|
||||||
|
@echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU"
|
||||||
|
@echo "-------------------------------------------------------"
|
||||||
|
@${QEMU} ${QFLAGS} -kernel os.elf -s -S &
|
||||||
|
@${GDB} os.elf -q -x ../gdbinit
|
||||||
|
|
||||||
|
.PHONY : code
|
||||||
|
code: all
|
||||||
|
@${OBJDUMP} -S os.elf | less
|
||||||
|
|
||||||
|
.PHONY : clean
|
||||||
|
clean:
|
||||||
|
rm -rf *.o *.bin *.elf
|
||||||
|
|
||||||
11
code/os/01-helloRVOS/kernel.c
Normal file
11
code/os/01-helloRVOS/kernel.c
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
extern void uart_init(void);
|
||||||
|
extern void uart_puts(char *s);
|
||||||
|
|
||||||
|
void start_kernel(void)
|
||||||
|
{
|
||||||
|
uart_init();
|
||||||
|
uart_puts("Hello, RVOS!\n");
|
||||||
|
|
||||||
|
while (1) {}; // stop here!
|
||||||
|
}
|
||||||
|
|
||||||
29
code/os/01-helloRVOS/platform.h
Normal file
29
code/os/01-helloRVOS/platform.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef __PLATFORM_H__
|
||||||
|
#define __PLATFORM_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QEMU RISC-V Virt machine with 16550a UART and VirtIO MMIO
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* maximum number of CPUs
|
||||||
|
* see https://github.com/qemu/qemu/blob/master/include/hw/riscv/virt.h
|
||||||
|
* #define VIRT_CPUS_MAX 8
|
||||||
|
*/
|
||||||
|
#define MAXNUM_CPU 8
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MemoryMap
|
||||||
|
* see https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c, virt_memmap[]
|
||||||
|
* 0x00001000 -- boot ROM, provided by qemu
|
||||||
|
* 0x02000000 -- CLINT
|
||||||
|
* 0x0C000000 -- PLIC
|
||||||
|
* 0x10000000 -- UART0
|
||||||
|
* 0x10001000 -- virtio disk
|
||||||
|
* 0x80000000 -- boot ROM jumps here in machine mode, where we load our kernel
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This machine puts UART registers here in physical memory. */
|
||||||
|
#define UART0 0x10000000L
|
||||||
|
|
||||||
|
#endif /* __PLATFORM_H__ */
|
||||||
31
code/os/01-helloRVOS/start.S
Normal file
31
code/os/01-helloRVOS/start.S
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
.equ STACK_SIZE, 8192
|
||||||
|
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
.text
|
||||||
|
_start:
|
||||||
|
# park harts with id != 0
|
||||||
|
csrr t0, mhartid # read current hart id
|
||||||
|
mv tp, t0 # keep CPU's hartid in its tp for later usage.
|
||||||
|
bnez t0, park # if we're not on the hart 0
|
||||||
|
# we park the hart
|
||||||
|
# Setup stacks, the stack grows from bottom to top, so we put the
|
||||||
|
# stack pointer to the very end of the stack range.
|
||||||
|
slli t0, t0, 10 # shift left the hart id by 1024
|
||||||
|
la sp, stacks + STACK_SIZE # set the initial stack pointer
|
||||||
|
# to the end of the stack space
|
||||||
|
add sp, sp, t0 # move the current hart stack pointer
|
||||||
|
# to its place in the stack space
|
||||||
|
|
||||||
|
j start_kernel # hart 0 jump to c
|
||||||
|
|
||||||
|
park:
|
||||||
|
wfi
|
||||||
|
j park
|
||||||
|
|
||||||
|
stacks:
|
||||||
|
.skip STACK_SIZE * MAXNUM_CPU # allocate space for the harts stacks
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
9
code/os/01-helloRVOS/types.h
Normal file
9
code/os/01-helloRVOS/types.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef __TYPES_H__
|
||||||
|
#define __TYPES_H__
|
||||||
|
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
typedef unsigned long long uint64_t;
|
||||||
|
|
||||||
|
#endif /* __TYPES_H__ */
|
||||||
120
code/os/01-helloRVOS/uart.c
Normal file
120
code/os/01-helloRVOS/uart.c
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
#include "types.h"
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The UART control registers are memory-mapped at address UART0.
|
||||||
|
* This macro returns the address of one of the registers.
|
||||||
|
*/
|
||||||
|
#define UART_REG(reg) ((volatile uint8_t *)(UART0 + reg))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reference
|
||||||
|
* [1]: TECHNICAL DATA ON 16550, http://byterunner.com/16550.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UART control registers map. see [1] "PROGRAMMING TABLE"
|
||||||
|
* note some are reused by multiple functions
|
||||||
|
* 0 (write mode): THR/DLL
|
||||||
|
* 1 (write mode): IER/DLM
|
||||||
|
*/
|
||||||
|
#define RHR 0 // Receive Holding Register (read mode)
|
||||||
|
#define THR 0 // Transmit Holding Register (write mode)
|
||||||
|
#define DLL 0 // LSB of Divisor Latch (write mode)
|
||||||
|
#define IER 1 // Interrupt Enable Register (write mode)
|
||||||
|
#define DLM 1 // MSB of Divisor Latch (write mode)
|
||||||
|
#define FCR 2 // FIFO Control Register (write mode)
|
||||||
|
#define ISR 2 // Interrupt Status Register (read mode)
|
||||||
|
#define LCR 3 // Line Control Register
|
||||||
|
#define MCR 4 // Modem Control Register
|
||||||
|
#define LSR 5 // Line Status Register
|
||||||
|
#define MSR 6 // Modem Status Register
|
||||||
|
#define SPR 7 // ScratchPad Register
|
||||||
|
|
||||||
|
/*
|
||||||
|
* POWER UP DEFAULTS
|
||||||
|
* IER = 0: TX/RX holding register interrupts are bith disabled
|
||||||
|
* ISR = 1: no interrupt penting
|
||||||
|
* LCR = 0
|
||||||
|
* MCR = 0
|
||||||
|
* LSR = 60 HEX
|
||||||
|
* MSR = BITS 0-3 = 0, BITS 4-7 = inputs
|
||||||
|
* FCR = 0
|
||||||
|
* TX = High
|
||||||
|
* OP1 = High
|
||||||
|
* OP2 = High
|
||||||
|
* RTS = High
|
||||||
|
* DTR = High
|
||||||
|
* RXRDY = High
|
||||||
|
* TXRDY = Low
|
||||||
|
* INT = Low
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LINE STATUS REGISTER (LSR)
|
||||||
|
* LSR BIT 0:
|
||||||
|
* 0 = no data in receive holding register or FIFO.
|
||||||
|
* 1 = data has been receive and saved in the receive holding register or FIFO.
|
||||||
|
* ......
|
||||||
|
* LSR BIT 5:
|
||||||
|
* 0 = transmit holding register is full. 16550 will not accept any data for transmission.
|
||||||
|
* 1 = transmitter hold register (or FIFO) is empty. CPU can load the next character.
|
||||||
|
* ......
|
||||||
|
*/
|
||||||
|
#define LSR_RX_READY (1 << 0)
|
||||||
|
#define LSR_TX_IDLE (1 << 5)
|
||||||
|
|
||||||
|
#define uart_read_reg(reg) (*(UART_REG(reg)))
|
||||||
|
#define uart_write_reg(reg, v) (*(UART_REG(reg)) = (v))
|
||||||
|
|
||||||
|
void uart_init()
|
||||||
|
{
|
||||||
|
/* disable interrupts. */
|
||||||
|
uart_write_reg(IER, 0x00);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting baud rate. Just a demo here if we care about the divisor,
|
||||||
|
* but for our purpose [QEMU-virt], this doesn't really do anything.
|
||||||
|
*
|
||||||
|
* Notice that the divisor register DLL (divisor latch least) and DLM (divisor
|
||||||
|
* latch most) have the same base address as the receiver/transmitter and the
|
||||||
|
* interrupt enable register. To change what the base address points to, we
|
||||||
|
* open the "divisor latch" by writing 1 into the Divisor Latch Access Bit
|
||||||
|
* (DLAB), which is bit index 7 of the Line Control Register (LCR).
|
||||||
|
*
|
||||||
|
* Regarding the baud rate value, see [1] "BAUD RATE GENERATOR PROGRAMMING TABLE".
|
||||||
|
* We use 38.4K when 1.8432 MHZ crystal, so the corresponding value is 3.
|
||||||
|
* And due to the divisor register is two bytes (16 bits), so we need to
|
||||||
|
* split the value of 3(0x0003) into two bytes, DLL stores the low byte,
|
||||||
|
* DLM stores the high byte.
|
||||||
|
*/
|
||||||
|
uint8_t lcr = uart_read_reg(LCR);
|
||||||
|
uart_write_reg(LCR, lcr | (1 << 7));
|
||||||
|
uart_write_reg(DLL, 0x03);
|
||||||
|
uart_write_reg(DLM, 0x00);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Continue setting the asynchronous data communication format.
|
||||||
|
* - number of the word length: 8 bits
|
||||||
|
* - number of stop bits:1 bit when word length is 8 bits
|
||||||
|
* - no parity
|
||||||
|
* - no break control
|
||||||
|
* - disabled baud latch
|
||||||
|
*/
|
||||||
|
lcr = 0;
|
||||||
|
uart_write_reg(LCR, lcr | (3 << 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int uart_putc(char ch)
|
||||||
|
{
|
||||||
|
while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0);
|
||||||
|
return uart_write_reg(THR, ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_puts(char *s)
|
||||||
|
{
|
||||||
|
while (*s) {
|
||||||
|
uart_putc(*s++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
59
code/os/02-memanagement/Makefile
Normal file
59
code/os/02-memanagement/Makefile
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
CROSS_COMPILE = riscv64-unknown-elf-
|
||||||
|
CFLAGS = -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall
|
||||||
|
|
||||||
|
QEMU = qemu-system-riscv32
|
||||||
|
QFLAGS = -nographic -smp 1 -machine virt -bios none
|
||||||
|
|
||||||
|
GDB = ${CROSS_COMPILE}gdb
|
||||||
|
CC = ${CROSS_COMPILE}gcc
|
||||||
|
OBJCOPY = ${CROSS_COMPILE}objcopy
|
||||||
|
OBJDUMP = ${CROSS_COMPILE}objdump
|
||||||
|
|
||||||
|
SRCS_ASM = \
|
||||||
|
start.S \
|
||||||
|
mem.S \
|
||||||
|
|
||||||
|
SRCS_C = \
|
||||||
|
kernel.c \
|
||||||
|
uart.c \
|
||||||
|
printf.c \
|
||||||
|
page.c \
|
||||||
|
|
||||||
|
OBJS = $(SRCS_ASM:.S=.o)
|
||||||
|
OBJS += $(SRCS_C:.c=.o)
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
all: os.elf
|
||||||
|
|
||||||
|
# start.o must be the first in dependency!
|
||||||
|
os.elf: ${OBJS}
|
||||||
|
${CC} $(CFLAGS) -T os.ld -o os.elf $^
|
||||||
|
${OBJCOPY} -O binary os.elf os.bin
|
||||||
|
|
||||||
|
%.o : %.c
|
||||||
|
${CC} ${CFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
|
%.o : %.S
|
||||||
|
${CC} ${CFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
|
run: all
|
||||||
|
@${QEMU} -M ? | grep virt >/dev/null || exit
|
||||||
|
@echo "Press Ctrl-A and then X to exit QEMU"
|
||||||
|
@echo "------------------------------------"
|
||||||
|
@${QEMU} ${QFLAGS} -kernel os.elf
|
||||||
|
|
||||||
|
.PHONY : debug
|
||||||
|
debug: all
|
||||||
|
@echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU"
|
||||||
|
@echo "-------------------------------------------------------"
|
||||||
|
@${QEMU} ${QFLAGS} -kernel os.elf -s -S &
|
||||||
|
@${GDB} os.elf -q -x ../gdbinit
|
||||||
|
|
||||||
|
.PHONY : code
|
||||||
|
code: all
|
||||||
|
@${OBJDUMP} -S os.elf | less
|
||||||
|
|
||||||
|
.PHONY : clean
|
||||||
|
clean:
|
||||||
|
rm -rf *.o *.bin *.elf
|
||||||
|
|
||||||
19
code/os/02-memanagement/kernel.c
Normal file
19
code/os/02-memanagement/kernel.c
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Following functions SHOULD be called ONLY ONE time here,
|
||||||
|
* so just declared here ONCE and NOT included in file os.h.
|
||||||
|
*/
|
||||||
|
extern void uart_init(void);
|
||||||
|
extern void page_init(void);
|
||||||
|
|
||||||
|
void start_kernel(void)
|
||||||
|
{
|
||||||
|
uart_init();
|
||||||
|
uart_puts("Hello, RVOS!\n");
|
||||||
|
|
||||||
|
page_init();
|
||||||
|
|
||||||
|
while (1) {}; // stop here!
|
||||||
|
}
|
||||||
|
|
||||||
30
code/os/02-memanagement/mem.S
Normal file
30
code/os/02-memanagement/mem.S
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
.section .rodata
|
||||||
|
.global HEAP_START
|
||||||
|
HEAP_START: .word _heap_start
|
||||||
|
|
||||||
|
.global HEAP_SIZE
|
||||||
|
HEAP_SIZE: .word _heap_size
|
||||||
|
|
||||||
|
.global TEXT_START
|
||||||
|
TEXT_START: .word _text_start
|
||||||
|
|
||||||
|
.global TEXT_END
|
||||||
|
TEXT_END: .word _text_end
|
||||||
|
|
||||||
|
.global DATA_START
|
||||||
|
DATA_START: .word _data_start
|
||||||
|
|
||||||
|
.global DATA_END
|
||||||
|
DATA_END: .word _data_end
|
||||||
|
|
||||||
|
.global RODATA_START
|
||||||
|
RODATA_START: .word _rodata_start
|
||||||
|
|
||||||
|
.global RODATA_END
|
||||||
|
RODATA_END: .word _rodata_end
|
||||||
|
|
||||||
|
.global BSS_START
|
||||||
|
BSS_START: .word _bss_start
|
||||||
|
|
||||||
|
.global BSS_END
|
||||||
|
BSS_END: .word _bss_end
|
||||||
22
code/os/02-memanagement/os.h
Normal file
22
code/os/02-memanagement/os.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef __OS_H__
|
||||||
|
#define __OS_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
/* uart */
|
||||||
|
extern int uart_putc(char ch);
|
||||||
|
extern void uart_puts(char *s);
|
||||||
|
|
||||||
|
/* printf */
|
||||||
|
extern int printf(const char* s, ...);
|
||||||
|
extern void panic(char *s);
|
||||||
|
|
||||||
|
/* memory management */
|
||||||
|
extern void *page_alloc(int npages);
|
||||||
|
extern void page_free(void *p);
|
||||||
|
|
||||||
|
#endif /* __OS_H__ */
|
||||||
136
code/os/02-memanagement/os.ld
Normal file
136
code/os/02-memanagement/os.ld
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* rvos.ld
|
||||||
|
* Linker script for outputting to RVOS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/Miscellaneous-Commands.html
|
||||||
|
* OUTPUT_ARCH command specifies a particular output machine architecture.
|
||||||
|
* "riscv" is the name of the architecture for both 64-bit and 32-bit
|
||||||
|
* RISC-V target. We will further refine this by using -march=rv32ima
|
||||||
|
* and -mabi=ilp32 when calling gcc.
|
||||||
|
*/
|
||||||
|
OUTPUT_ARCH( "riscv" )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/Entry-Point.html
|
||||||
|
* ENTRY command is used to set the "entry point", which is the first instruction
|
||||||
|
* to execute in a program.
|
||||||
|
* The argument of ENTRY command is a symbol name, here is "_start" which is
|
||||||
|
* defined in start.S.
|
||||||
|
*/
|
||||||
|
ENTRY( _start )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/MEMORY.html
|
||||||
|
* The MEMORY command describes the location and size of blocks of memory in
|
||||||
|
* the target.
|
||||||
|
* The syntax for MEMORY is:
|
||||||
|
* MEMORY
|
||||||
|
* {
|
||||||
|
* name [(attr)] : ORIGIN = origin, LENGTH = len
|
||||||
|
* ......
|
||||||
|
* }
|
||||||
|
* Each line defines a memory region.
|
||||||
|
* Each memory region must have a distinct name within the MEMORY command. Here
|
||||||
|
* we only define one region named as "ram".
|
||||||
|
* The "attr" string is an optional list of attributes that specify whether to
|
||||||
|
* use a particular memory region for an input section which is not explicitly
|
||||||
|
* mapped in the linker script. Here we assign 'w' (writeable), 'x' (executable),
|
||||||
|
* and 'a' (allocatable). We use '!' to invert 'r' (read-only) and
|
||||||
|
* 'i' (initialized).
|
||||||
|
* The "ORIGIN" is used to set the start address of the memory region. Here we
|
||||||
|
* place it right at the beginning of 0x8000_0000 because this is where the
|
||||||
|
* QEMU-virt machine will start executing.
|
||||||
|
* Finally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.
|
||||||
|
* The linker will double check this to make sure everything can fit.
|
||||||
|
*/
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/SECTIONS.html
|
||||||
|
* The SECTIONS command tells the linker how to map input sections into output
|
||||||
|
* sections, and how to place the output sections in memory.
|
||||||
|
* The format of the SECTIONS command is:
|
||||||
|
* SECTIONS
|
||||||
|
* {
|
||||||
|
* sections-command
|
||||||
|
* sections-command
|
||||||
|
* ......
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Each sections-command may of be one of the following:
|
||||||
|
* (1) an ENTRY command
|
||||||
|
* (2) a symbol assignment
|
||||||
|
* (3) an output section description
|
||||||
|
* (4) an overlay description
|
||||||
|
* We here only demo (2) & (3).
|
||||||
|
*
|
||||||
|
* We use PROVIDE command to define symbols.
|
||||||
|
* https://sourceware.org/binutils/docs/ld/PROVIDE.html
|
||||||
|
* The PROVIDE keyword may be used to define a symbol.
|
||||||
|
* The syntax is PROVIDE(symbol = expression).
|
||||||
|
* Such symbols as "_text_start", "_text_end" ... will be used in mem.S.
|
||||||
|
* Notice the period '.' tells the linker to set symbol(e.g. _text_start) to
|
||||||
|
* the CURRENT location ('.' = current memory location). This current memory
|
||||||
|
* location moves as we add things.
|
||||||
|
*/
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We are going to layout all text sections in .text output section,
|
||||||
|
* starting with .text. The asterisk("*") in front of the
|
||||||
|
* parentheses means to match the .text section of ANY object file.
|
||||||
|
*/
|
||||||
|
.text : {
|
||||||
|
PROVIDE(_text_start = .);
|
||||||
|
*(.text .text.*)
|
||||||
|
PROVIDE(_text_end = .);
|
||||||
|
} >ram
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
PROVIDE(_rodata_start = .);
|
||||||
|
*(.rodata .rodata.*)
|
||||||
|
PROVIDE(_rodata_end = .);
|
||||||
|
} >ram
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
/*
|
||||||
|
* . = ALIGN(4096) tells the linker to align the current memory
|
||||||
|
* location to 4096 bytes. This will insert padding bytes until
|
||||||
|
* current location becomes aligned on 4096-byte boundary.
|
||||||
|
* This is because our paging system's resolution is 4,096 bytes.
|
||||||
|
*/
|
||||||
|
. = ALIGN(4096);
|
||||||
|
PROVIDE(_data_start = .);
|
||||||
|
/*
|
||||||
|
* sdata and data are essentially the same thing. We do not need
|
||||||
|
* to distinguish sdata from data.
|
||||||
|
*/
|
||||||
|
*(.sdata .sdata.*)
|
||||||
|
*(.data .data.*)
|
||||||
|
PROVIDE(_data_end = .);
|
||||||
|
} >ram
|
||||||
|
|
||||||
|
.bss :{
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/Input-Section-Common.html
|
||||||
|
* In most cases, common symbols in input files will be placed
|
||||||
|
* in the ‘.bss’ section in the output file.
|
||||||
|
*/
|
||||||
|
PROVIDE(_bss_start = .);
|
||||||
|
*(.sbss .sbss.*)
|
||||||
|
*(.bss .bss.*)
|
||||||
|
*(COMMON)
|
||||||
|
PROVIDE(_bss_end = .);
|
||||||
|
} >ram
|
||||||
|
|
||||||
|
PROVIDE(_memory_start = ORIGIN(ram));
|
||||||
|
PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
|
||||||
|
|
||||||
|
PROVIDE(_heap_start = _bss_end);
|
||||||
|
PROVIDE(_heap_size = _memory_end - _heap_start);
|
||||||
|
}
|
||||||
189
code/os/02-memanagement/page.c
Normal file
189
code/os/02-memanagement/page.c
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Following global vars are defined in mem.S
|
||||||
|
*/
|
||||||
|
extern uint32_t TEXT_START;
|
||||||
|
extern uint32_t TEXT_END;
|
||||||
|
extern uint32_t DATA_START;
|
||||||
|
extern uint32_t DATA_END;
|
||||||
|
extern uint32_t RODATA_START;
|
||||||
|
extern uint32_t RODATA_END;
|
||||||
|
extern uint32_t BSS_START;
|
||||||
|
extern uint32_t BSS_END;
|
||||||
|
extern uint32_t HEAP_START;
|
||||||
|
extern uint32_t HEAP_SIZE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* _alloc_start points to the actual start address of heap pool
|
||||||
|
* _alloc_end points to the actual end address of heap pool
|
||||||
|
* _num_pages holds the actual max number of pages we can allocate.
|
||||||
|
*/
|
||||||
|
static uint32_t _alloc_start = 0;
|
||||||
|
static uint32_t _alloc_end = 0;
|
||||||
|
static uint32_t _num_pages = 0;
|
||||||
|
|
||||||
|
#define PAGE_SIZE 4096
|
||||||
|
#define PAGE_ORDER 12
|
||||||
|
|
||||||
|
#define PAGE_TAKEN (uint8_t)(1 << 0)
|
||||||
|
#define PAGE_LAST (uint8_t)(1 << 1)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Page Descriptor
|
||||||
|
* flags:
|
||||||
|
* - bit 0: flag if this page is taken(allocated)
|
||||||
|
* - bit 1: flag if this page is the last page of the memory block allocated
|
||||||
|
*/
|
||||||
|
struct Page {
|
||||||
|
uint8_t flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void _clear(struct Page *page)
|
||||||
|
{
|
||||||
|
page->flags = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int _is_free(struct Page *page)
|
||||||
|
{
|
||||||
|
if (page->flags & PAGE_TAKEN) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _set_flag(struct Page *page, uint8_t flags)
|
||||||
|
{
|
||||||
|
page->flags |= flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int _is_last(struct Page *page)
|
||||||
|
{
|
||||||
|
if (page->flags & PAGE_LAST) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* align the address to the border of page(4K)
|
||||||
|
*/
|
||||||
|
static inline uint32_t _align_page(uint32_t address)
|
||||||
|
{
|
||||||
|
uint32_t order = (1 << PAGE_ORDER) - 1;
|
||||||
|
return (address + order) & (~order);
|
||||||
|
}
|
||||||
|
|
||||||
|
void page_init()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We reserved 8 Page (8 x 4096) to hold the Page structures.
|
||||||
|
* It should be enough to manage at most 128 MB (8 x 4096 x 4096)
|
||||||
|
*/
|
||||||
|
_num_pages = (HEAP_SIZE / PAGE_SIZE) - 8;
|
||||||
|
printf("HEAP_START = %x, HEAP_SIZE = %x, num of pages = %d\n", HEAP_START, HEAP_SIZE, _num_pages);
|
||||||
|
|
||||||
|
struct Page *page = (struct Page *)HEAP_START;
|
||||||
|
for (int i = 0; i < _num_pages; i++) {
|
||||||
|
_clear(page);
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_alloc_start = _align_page(HEAP_START + 8 * PAGE_SIZE);
|
||||||
|
_alloc_end = _alloc_start + (PAGE_SIZE * _num_pages);
|
||||||
|
|
||||||
|
printf("TEXT: 0x%x -> 0x%x\n", TEXT_START, TEXT_END);
|
||||||
|
printf("RODATA: 0x%x -> 0x%x\n", RODATA_START, RODATA_END);
|
||||||
|
printf("DATA: 0x%x -> 0x%x\n", DATA_START, DATA_END);
|
||||||
|
printf("BSS: 0x%x -> 0x%x\n", BSS_START, BSS_END);
|
||||||
|
printf("HEAP: 0x%x -> 0x%x\n", _alloc_start, _alloc_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a memory block which is composed of contiguous physical pages
|
||||||
|
* - npages: the number of PAGE_SIZE pages to allocate
|
||||||
|
*/
|
||||||
|
void *page_alloc(int npages)
|
||||||
|
{
|
||||||
|
/* Note we are searching the page descriptor bitmaps. */
|
||||||
|
int found = 0;
|
||||||
|
struct Page *page_i = (struct Page *)HEAP_START;
|
||||||
|
for (int i = 0; i < (_num_pages - npages); i++) {
|
||||||
|
if (_is_free(page_i)) {
|
||||||
|
found = 1;
|
||||||
|
/*
|
||||||
|
* meet a free page, continue to check if following
|
||||||
|
* (npages - 1) pages are also unallocated.
|
||||||
|
*/
|
||||||
|
struct Page *page_j = page_i;
|
||||||
|
for (int j = i; j < (i + npages); j++) {
|
||||||
|
if (!_is_free(page_j)) {
|
||||||
|
found = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
page_j++;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* get a memory block which is good enough for us,
|
||||||
|
* take housekeeping, then return the actual start
|
||||||
|
* address of the first page of this memory block
|
||||||
|
*/
|
||||||
|
if (found) {
|
||||||
|
struct Page *page_k = page_i;
|
||||||
|
for (int k = i; k < (i + npages); k++) {
|
||||||
|
_set_flag(page_k, PAGE_TAKEN);
|
||||||
|
page_k++;
|
||||||
|
}
|
||||||
|
page_k--;
|
||||||
|
_set_flag(page_k, PAGE_LAST);
|
||||||
|
return (void *)(_alloc_start + i * PAGE_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
page_i++;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the memory block
|
||||||
|
* - p: start address of the memory block
|
||||||
|
*/
|
||||||
|
void page_free(void *p)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Assert (TBD) if p is invalid
|
||||||
|
*/
|
||||||
|
if (!p || (uint32_t)p >= _alloc_end) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/* get the first page descriptor of this memory block */
|
||||||
|
struct Page *page = (struct Page *)HEAP_START;
|
||||||
|
page += ((uint32_t)p - _alloc_start)/ PAGE_SIZE;
|
||||||
|
/* loop and clear all the page descriptors of the memory block */
|
||||||
|
while (!_is_free(page)) {
|
||||||
|
if (_is_last(page)) {
|
||||||
|
_clear(page);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
_clear(page);
|
||||||
|
page++;;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void page_test()
|
||||||
|
{
|
||||||
|
void *p = page_alloc(2);
|
||||||
|
printf("p = 0x%x\n", p);
|
||||||
|
//page_free(p);
|
||||||
|
|
||||||
|
void *p2 = page_alloc(7);
|
||||||
|
printf("p2 = 0x%x\n", p2);
|
||||||
|
page_free(p2);
|
||||||
|
|
||||||
|
void *p3 = page_alloc(4);
|
||||||
|
printf("p3 = 0x%x\n", p3);
|
||||||
|
}
|
||||||
|
|
||||||
29
code/os/02-memanagement/platform.h
Normal file
29
code/os/02-memanagement/platform.h
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#ifndef __PLATFORM_H__
|
||||||
|
#define __PLATFORM_H__
|
||||||
|
|
||||||
|
/*
|
||||||
|
* QEMU RISC-V Virt machine with 16550a UART and VirtIO MMIO
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* maximum number of CPUs
|
||||||
|
* see https://github.com/qemu/qemu/blob/master/include/hw/riscv/virt.h
|
||||||
|
* #define VIRT_CPUS_MAX 8
|
||||||
|
*/
|
||||||
|
#define MAXNUM_CPU 8
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MemoryMap
|
||||||
|
* see https://github.com/qemu/qemu/blob/master/hw/riscv/virt.c, virt_memmap[]
|
||||||
|
* 0x00001000 -- boot ROM, provided by qemu
|
||||||
|
* 0x02000000 -- CLINT
|
||||||
|
* 0x0C000000 -- PLIC
|
||||||
|
* 0x10000000 -- UART0
|
||||||
|
* 0x10001000 -- virtio disk
|
||||||
|
* 0x80000000 -- boot ROM jumps here in machine mode, where we load our kernel
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* This machine puts UART registers here in physical memory. */
|
||||||
|
#define UART0 0x10000000L
|
||||||
|
|
||||||
|
#endif /* __PLATFORM_H__ */
|
||||||
138
code/os/02-memanagement/printf.c
Normal file
138
code/os/02-memanagement/printf.c
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ref: https://github.com/cccriscv/mini-riscv-os/blob/master/05-Preemptive/lib.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int _vsnprintf(char * out, size_t n, const char* s, va_list vl)
|
||||||
|
{
|
||||||
|
int format = 0;
|
||||||
|
int longarg = 0;
|
||||||
|
size_t pos = 0;
|
||||||
|
for (; *s; s++) {
|
||||||
|
if (format) {
|
||||||
|
switch(*s) {
|
||||||
|
case 'l': {
|
||||||
|
longarg = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'p': {
|
||||||
|
longarg = 1;
|
||||||
|
if (out && pos < n) {
|
||||||
|
out[pos] = '0';
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
if (out && pos < n) {
|
||||||
|
out[pos] = 'x';
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
case 'x': {
|
||||||
|
long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
|
||||||
|
int hexdigits = 2*(longarg ? sizeof(long) : sizeof(int))-1;
|
||||||
|
for(int i = hexdigits; i >= 0; i--) {
|
||||||
|
int d = (num >> (4*i)) & 0xF;
|
||||||
|
if (out && pos < n) {
|
||||||
|
out[pos] = (d < 10 ? '0'+d : 'a'+d-10);
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
longarg = 0;
|
||||||
|
format = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'd': {
|
||||||
|
long num = longarg ? va_arg(vl, long) : va_arg(vl, int);
|
||||||
|
if (num < 0) {
|
||||||
|
num = -num;
|
||||||
|
if (out && pos < n) {
|
||||||
|
out[pos] = '-';
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
long digits = 1;
|
||||||
|
for (long nn = num; nn /= 10; digits++);
|
||||||
|
for (int i = digits-1; i >= 0; i--) {
|
||||||
|
if (out && pos + i < n) {
|
||||||
|
out[pos + i] = '0' + (num % 10);
|
||||||
|
}
|
||||||
|
num /= 10;
|
||||||
|
}
|
||||||
|
pos += digits;
|
||||||
|
longarg = 0;
|
||||||
|
format = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 's': {
|
||||||
|
const char* s2 = va_arg(vl, const char*);
|
||||||
|
while (*s2) {
|
||||||
|
if (out && pos < n) {
|
||||||
|
out[pos] = *s2;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
s2++;
|
||||||
|
}
|
||||||
|
longarg = 0;
|
||||||
|
format = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'c': {
|
||||||
|
if (out && pos < n) {
|
||||||
|
out[pos] = (char)va_arg(vl,int);
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
longarg = 0;
|
||||||
|
format = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (*s == '%') {
|
||||||
|
format = 1;
|
||||||
|
} else {
|
||||||
|
if (out && pos < n) {
|
||||||
|
out[pos] = *s;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (out && pos < n) {
|
||||||
|
out[pos] = 0;
|
||||||
|
} else if (out && n) {
|
||||||
|
out[n-1] = 0;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char out_buf[1000]; // buffer for _vprintf()
|
||||||
|
|
||||||
|
static int _vprintf(const char* s, va_list vl)
|
||||||
|
{
|
||||||
|
int res = _vsnprintf(NULL, -1, s, vl);
|
||||||
|
if (res+1 >= sizeof(out_buf)) {
|
||||||
|
uart_puts("error: output string size overflow\n");
|
||||||
|
while(1) {}
|
||||||
|
}
|
||||||
|
_vsnprintf(out_buf, res + 1, s, vl);
|
||||||
|
uart_puts(out_buf);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int printf(const char* s, ...)
|
||||||
|
{
|
||||||
|
int res = 0;
|
||||||
|
va_list vl;
|
||||||
|
va_start(vl, s);
|
||||||
|
res = _vprintf(s, vl);
|
||||||
|
va_end(vl);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void panic(char *s)
|
||||||
|
{
|
||||||
|
printf("panic: ");
|
||||||
|
printf(s);
|
||||||
|
printf("\n");
|
||||||
|
while(1){};
|
||||||
|
}
|
||||||
41
code/os/02-memanagement/start.S
Normal file
41
code/os/02-memanagement/start.S
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
.equ STACK_SIZE, 8192
|
||||||
|
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
.text
|
||||||
|
_start:
|
||||||
|
# park harts with id != 0
|
||||||
|
csrr t0, mhartid # read current hart id
|
||||||
|
mv tp, t0 # keep CPU's hartid in its tp for later usage.
|
||||||
|
bnez t0, park # if we're not on the hart 0
|
||||||
|
# we park the hart
|
||||||
|
|
||||||
|
# Set all bytes in the BSS section to zero.
|
||||||
|
la a0, _bss_start
|
||||||
|
la a1, _bss_end
|
||||||
|
bgeu a0, a1, 2f
|
||||||
|
1:
|
||||||
|
sw zero, (a0)
|
||||||
|
addi a0, a0, 4
|
||||||
|
bltu a0, a1, 1b
|
||||||
|
2:
|
||||||
|
# Setup stacks, the stack grows from bottom to top, so we put the
|
||||||
|
# stack pointer to the very end of the stack range.
|
||||||
|
slli t0, t0, 10 # shift left the hart id by 1024
|
||||||
|
la sp, stacks + STACK_SIZE # set the initial stack pointer
|
||||||
|
# to the end of the stack space
|
||||||
|
add sp, sp, t0 # move the current hart stack pointer
|
||||||
|
# to its place in the stack space
|
||||||
|
|
||||||
|
j start_kernel # hart 0 jump to c
|
||||||
|
|
||||||
|
park:
|
||||||
|
wfi
|
||||||
|
j park
|
||||||
|
|
||||||
|
stacks:
|
||||||
|
.skip STACK_SIZE * MAXNUM_CPU # allocate space for the harts stacks
|
||||||
|
|
||||||
|
.end # End of file
|
||||||
9
code/os/02-memanagement/types.h
Normal file
9
code/os/02-memanagement/types.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#ifndef __TYPES_H__
|
||||||
|
#define __TYPES_H__
|
||||||
|
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
typedef unsigned long long uint64_t;
|
||||||
|
|
||||||
|
#endif /* __TYPES_H__ */
|
||||||
119
code/os/02-memanagement/uart.c
Normal file
119
code/os/02-memanagement/uart.c
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The UART control registers are memory-mapped at address UART0.
|
||||||
|
* This macro returns the address of one of the registers.
|
||||||
|
*/
|
||||||
|
#define UART_REG(reg) ((volatile uint8_t *)(UART0 + reg))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reference
|
||||||
|
* [1]: TECHNICAL DATA ON 16550, http://byterunner.com/16550.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UART control registers map. see [1] "PROGRAMMING TABLE"
|
||||||
|
* note some are reused by multiple functions
|
||||||
|
* 0 (write mode): THR/DLL
|
||||||
|
* 1 (write mode): IER/DLM
|
||||||
|
*/
|
||||||
|
#define RHR 0 // Receive Holding Register (read mode)
|
||||||
|
#define THR 0 // Transmit Holding Register (write mode)
|
||||||
|
#define DLL 0 // LSB of Divisor Latch (write mode)
|
||||||
|
#define IER 1 // Interrupt Enable Register (write mode)
|
||||||
|
#define DLM 1 // MSB of Divisor Latch (write mode)
|
||||||
|
#define FCR 2 // FIFO Control Register (write mode)
|
||||||
|
#define ISR 2 // Interrupt Status Register (read mode)
|
||||||
|
#define LCR 3 // Line Control Register
|
||||||
|
#define MCR 4 // Modem Control Register
|
||||||
|
#define LSR 5 // Line Status Register
|
||||||
|
#define MSR 6 // Modem Status Register
|
||||||
|
#define SPR 7 // ScratchPad Register
|
||||||
|
|
||||||
|
/*
|
||||||
|
* POWER UP DEFAULTS
|
||||||
|
* IER = 0: TX/RX holding register interrupts are bith disabled
|
||||||
|
* ISR = 1: no interrupt penting
|
||||||
|
* LCR = 0
|
||||||
|
* MCR = 0
|
||||||
|
* LSR = 60 HEX
|
||||||
|
* MSR = BITS 0-3 = 0, BITS 4-7 = inputs
|
||||||
|
* FCR = 0
|
||||||
|
* TX = High
|
||||||
|
* OP1 = High
|
||||||
|
* OP2 = High
|
||||||
|
* RTS = High
|
||||||
|
* DTR = High
|
||||||
|
* RXRDY = High
|
||||||
|
* TXRDY = Low
|
||||||
|
* INT = Low
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LINE STATUS REGISTER (LSR)
|
||||||
|
* LSR BIT 0:
|
||||||
|
* 0 = no data in receive holding register or FIFO.
|
||||||
|
* 1 = data has been receive and saved in the receive holding register or FIFO.
|
||||||
|
* ......
|
||||||
|
* LSR BIT 5:
|
||||||
|
* 0 = transmit holding register is full. 16550 will not accept any data for transmission.
|
||||||
|
* 1 = transmitter hold register (or FIFO) is empty. CPU can load the next character.
|
||||||
|
* ......
|
||||||
|
*/
|
||||||
|
#define LSR_RX_READY (1 << 0)
|
||||||
|
#define LSR_TX_IDLE (1 << 5)
|
||||||
|
|
||||||
|
#define uart_read_reg(reg) (*(UART_REG(reg)))
|
||||||
|
#define uart_write_reg(reg, v) (*(UART_REG(reg)) = (v))
|
||||||
|
|
||||||
|
void uart_init()
|
||||||
|
{
|
||||||
|
/* disable interrupts. */
|
||||||
|
uart_write_reg(IER, 0x00);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting baud rate. Just a demo here if we care about the divisor,
|
||||||
|
* but for our purpose [QEMU-virt], this doesn't really do anything.
|
||||||
|
*
|
||||||
|
* Notice that the divisor register DLL (divisor latch least) and DLM (divisor
|
||||||
|
* latch most) have the same base address as the receiver/transmitter and the
|
||||||
|
* interrupt enable register. To change what the base address points to, we
|
||||||
|
* open the "divisor latch" by writing 1 into the Divisor Latch Access Bit
|
||||||
|
* (DLAB), which is bit index 7 of the Line Control Register (LCR).
|
||||||
|
*
|
||||||
|
* Regarding the baud rate value, see [1] "BAUD RATE GENERATOR PROGRAMMING TABLE".
|
||||||
|
* We use 38.4K when 1.8432 MHZ crystal, so the corresponding value is 3.
|
||||||
|
* And due to the divisor register is two bytes (16 bits), so we need to
|
||||||
|
* split the value of 3(0x0003) into two bytes, DLL stores the low byte,
|
||||||
|
* DLM stores the high byte.
|
||||||
|
*/
|
||||||
|
uint8_t lcr = uart_read_reg(LCR);
|
||||||
|
uart_write_reg(LCR, lcr | (1 << 7));
|
||||||
|
uart_write_reg(DLL, 0x03);
|
||||||
|
uart_write_reg(DLM, 0x00);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Continue setting the asynchronous data communication format.
|
||||||
|
* - number of the word length: 8 bits
|
||||||
|
* - number of stop bits:1 bit when word length is 8 bits
|
||||||
|
* - no parity
|
||||||
|
* - no break control
|
||||||
|
* - disabled baud latch
|
||||||
|
*/
|
||||||
|
lcr = 0;
|
||||||
|
uart_write_reg(LCR, lcr | (3 << 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
int uart_putc(char ch)
|
||||||
|
{
|
||||||
|
while ((uart_read_reg(LSR) & LSR_TX_IDLE) == 0);
|
||||||
|
return uart_write_reg(THR, ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
void uart_puts(char *s)
|
||||||
|
{
|
||||||
|
while (*s) {
|
||||||
|
uart_putc(*s++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
61
code/os/03-contextswitch/Makefile
Normal file
61
code/os/03-contextswitch/Makefile
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
CROSS_COMPILE = riscv64-unknown-elf-
|
||||||
|
CFLAGS = -nostdlib -fno-builtin -mcmodel=medany -march=rv32ima -mabi=ilp32 -g -Wall
|
||||||
|
|
||||||
|
QEMU = qemu-system-riscv32
|
||||||
|
QFLAGS = -nographic -smp 1 -machine virt -bios none
|
||||||
|
|
||||||
|
GDB = ${CROSS_COMPILE}gdb
|
||||||
|
CC = ${CROSS_COMPILE}gcc
|
||||||
|
OBJCOPY = ${CROSS_COMPILE}objcopy
|
||||||
|
OBJDUMP = ${CROSS_COMPILE}objdump
|
||||||
|
|
||||||
|
SRCS_ASM = \
|
||||||
|
start.S \
|
||||||
|
mem.S \
|
||||||
|
entry.S \
|
||||||
|
|
||||||
|
SRCS_C = \
|
||||||
|
kernel.c \
|
||||||
|
uart.c \
|
||||||
|
printf.c \
|
||||||
|
page.c \
|
||||||
|
sched.c \
|
||||||
|
|
||||||
|
OBJS = $(SRCS_ASM:.S=.o)
|
||||||
|
OBJS += $(SRCS_C:.c=.o)
|
||||||
|
|
||||||
|
.DEFAULT_GOAL := all
|
||||||
|
all: os.elf
|
||||||
|
|
||||||
|
# start.o must be the first in dependency!
|
||||||
|
os.elf: ${OBJS}
|
||||||
|
${CC} $(CFLAGS) -T os.ld -o os.elf $^
|
||||||
|
${OBJCOPY} -O binary os.elf os.bin
|
||||||
|
|
||||||
|
%.o : %.c
|
||||||
|
${CC} ${CFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
|
%.o : %.S
|
||||||
|
${CC} ${CFLAGS} -c -o $@ $<
|
||||||
|
|
||||||
|
run: all
|
||||||
|
@${QEMU} -M ? | grep virt >/dev/null || exit
|
||||||
|
@echo "Press Ctrl-A and then X to exit QEMU"
|
||||||
|
@echo "------------------------------------"
|
||||||
|
@${QEMU} ${QFLAGS} -kernel os.elf
|
||||||
|
|
||||||
|
.PHONY : debug
|
||||||
|
debug: all
|
||||||
|
@echo "Press Ctrl-C and then input 'quit' to exit GDB and QEMU"
|
||||||
|
@echo "-------------------------------------------------------"
|
||||||
|
@${QEMU} ${QFLAGS} -kernel os.elf -s -S &
|
||||||
|
@${GDB} os.elf -q -x ../gdbinit
|
||||||
|
|
||||||
|
.PHONY : code
|
||||||
|
code: all
|
||||||
|
@${OBJDUMP} -S os.elf | less
|
||||||
|
|
||||||
|
.PHONY : clean
|
||||||
|
clean:
|
||||||
|
rm -rf *.o *.bin *.elf
|
||||||
|
|
||||||
106
code/os/03-contextswitch/entry.S
Normal file
106
code/os/03-contextswitch/entry.S
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# save all General-Purpose(GP) registers to context
|
||||||
|
# struct context *base = &ctx_task;
|
||||||
|
# base->ra = ra;
|
||||||
|
# ......
|
||||||
|
.macro reg_save base
|
||||||
|
sw ra, 0(\base)
|
||||||
|
sw sp, 4(\base)
|
||||||
|
sw gp, 8(\base)
|
||||||
|
sw tp, 12(\base)
|
||||||
|
sw t0, 16(\base)
|
||||||
|
sw t1, 20(\base)
|
||||||
|
sw t2, 24(\base)
|
||||||
|
sw s0, 28(\base)
|
||||||
|
sw s1, 32(\base)
|
||||||
|
sw a0, 36(\base)
|
||||||
|
sw a1, 40(\base)
|
||||||
|
sw a2, 44(\base)
|
||||||
|
sw a3, 48(\base)
|
||||||
|
sw a4, 52(\base)
|
||||||
|
sw a5, 56(\base)
|
||||||
|
sw a6, 60(\base)
|
||||||
|
sw a7, 64(\base)
|
||||||
|
sw s2, 68(\base)
|
||||||
|
sw s3, 72(\base)
|
||||||
|
sw s4, 76(\base)
|
||||||
|
sw s5, 80(\base)
|
||||||
|
sw s6, 84(\base)
|
||||||
|
sw s7, 88(\base)
|
||||||
|
sw s8, 92(\base)
|
||||||
|
sw s9, 96(\base)
|
||||||
|
sw s10, 100(\base)
|
||||||
|
sw s11, 104(\base)
|
||||||
|
sw t3, 108(\base)
|
||||||
|
sw t4, 112(\base)
|
||||||
|
sw t5, 116(\base)
|
||||||
|
sw t6, 120(\base)
|
||||||
|
.endm
|
||||||
|
|
||||||
|
# restore all General-Purpose(GP) registers from the context
|
||||||
|
# struct context *base = &ctx_task;
|
||||||
|
# ra = base->ra;
|
||||||
|
# ......
|
||||||
|
.macro reg_restore base
|
||||||
|
lw ra, 0(\base)
|
||||||
|
lw sp, 4(\base)
|
||||||
|
lw gp, 8(\base)
|
||||||
|
lw tp, 12(\base)
|
||||||
|
lw t0, 16(\base)
|
||||||
|
lw t1, 20(\base)
|
||||||
|
lw t2, 24(\base)
|
||||||
|
lw s0, 28(\base)
|
||||||
|
lw s1, 32(\base)
|
||||||
|
lw a0, 36(\base)
|
||||||
|
lw a1, 40(\base)
|
||||||
|
lw a2, 44(\base)
|
||||||
|
lw a3, 48(\base)
|
||||||
|
lw a4, 52(\base)
|
||||||
|
lw a5, 56(\base)
|
||||||
|
lw a6, 60(\base)
|
||||||
|
lw a7, 64(\base)
|
||||||
|
lw s2, 68(\base)
|
||||||
|
lw s3, 72(\base)
|
||||||
|
lw s4, 76(\base)
|
||||||
|
lw s5, 80(\base)
|
||||||
|
lw s6, 84(\base)
|
||||||
|
lw s7, 88(\base)
|
||||||
|
lw s8, 92(\base)
|
||||||
|
lw s9, 96(\base)
|
||||||
|
lw s10, 100(\base)
|
||||||
|
lw s11, 104(\base)
|
||||||
|
lw t3, 108(\base)
|
||||||
|
lw t4, 112(\base)
|
||||||
|
lw t5, 116(\base)
|
||||||
|
lw t6, 120(\base)
|
||||||
|
.endm
|
||||||
|
|
||||||
|
# Something to note about save/restore:
|
||||||
|
# - We use mscratch to hold a pointer to context of previous task
|
||||||
|
# - We use t6 as the 'base' for reg_save/reg_restore, because it is the
|
||||||
|
# very bottom register (x31) and would not be overwritten during loading.
|
||||||
|
|
||||||
|
.text
|
||||||
|
|
||||||
|
# void switch_to(struct context *next);
|
||||||
|
# a0: pointer to the context of the next task
|
||||||
|
.globl switch_to
|
||||||
|
.align 4
|
||||||
|
switch_to:
|
||||||
|
csrrw t6, mscratch, t6 # swap t6 and mscratch
|
||||||
|
beqz t6, 1f # Notice: previous task may be NULL
|
||||||
|
reg_save t6 # save context of prev task
|
||||||
|
|
||||||
|
1:
|
||||||
|
# switch mscratch to point to the context of the next task
|
||||||
|
csrw mscratch, a0
|
||||||
|
|
||||||
|
# Restore all GP registers
|
||||||
|
# Use t6 to point to the context of the new task
|
||||||
|
mv t6, a0
|
||||||
|
reg_restore t6
|
||||||
|
|
||||||
|
# Do actual context switching.
|
||||||
|
ret
|
||||||
|
|
||||||
|
.end
|
||||||
|
|
||||||
26
code/os/03-contextswitch/kernel.c
Normal file
26
code/os/03-contextswitch/kernel.c
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#include "os.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Following functions SHOULD be called ONLY ONE time here,
|
||||||
|
* so just declared here ONCE and NOT included in file os.h.
|
||||||
|
*/
|
||||||
|
extern void uart_init(void);
|
||||||
|
extern void page_init(void);
|
||||||
|
extern void sched_init(void);
|
||||||
|
extern void schedule(void);
|
||||||
|
|
||||||
|
void start_kernel(void)
|
||||||
|
{
|
||||||
|
uart_init();
|
||||||
|
uart_puts("Hello, RVOS!\n");
|
||||||
|
|
||||||
|
page_init();
|
||||||
|
|
||||||
|
sched_init();
|
||||||
|
|
||||||
|
schedule();
|
||||||
|
|
||||||
|
uart_puts("Would not go here!\n");
|
||||||
|
while (1) {}; // stop here!
|
||||||
|
}
|
||||||
|
|
||||||
30
code/os/03-contextswitch/mem.S
Normal file
30
code/os/03-contextswitch/mem.S
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
.section .rodata
|
||||||
|
.global HEAP_START
|
||||||
|
HEAP_START: .word _heap_start
|
||||||
|
|
||||||
|
.global HEAP_SIZE
|
||||||
|
HEAP_SIZE: .word _heap_size
|
||||||
|
|
||||||
|
.global TEXT_START
|
||||||
|
TEXT_START: .word _text_start
|
||||||
|
|
||||||
|
.global TEXT_END
|
||||||
|
TEXT_END: .word _text_end
|
||||||
|
|
||||||
|
.global DATA_START
|
||||||
|
DATA_START: .word _data_start
|
||||||
|
|
||||||
|
.global DATA_END
|
||||||
|
DATA_END: .word _data_end
|
||||||
|
|
||||||
|
.global RODATA_START
|
||||||
|
RODATA_START: .word _rodata_start
|
||||||
|
|
||||||
|
.global RODATA_END
|
||||||
|
RODATA_END: .word _rodata_end
|
||||||
|
|
||||||
|
.global BSS_START
|
||||||
|
BSS_START: .word _bss_start
|
||||||
|
|
||||||
|
.global BSS_END
|
||||||
|
BSS_END: .word _bss_end
|
||||||
61
code/os/03-contextswitch/os.h
Normal file
61
code/os/03-contextswitch/os.h
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
#ifndef __OS_H__
|
||||||
|
#define __OS_H__
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
/* uart */
|
||||||
|
extern int uart_putc(char ch);
|
||||||
|
extern void uart_puts(char *s);
|
||||||
|
|
||||||
|
/* printf */
|
||||||
|
extern int printf(const char* s, ...);
|
||||||
|
extern void panic(char *s);
|
||||||
|
|
||||||
|
/* memory management */
|
||||||
|
extern void *page_alloc(int npages);
|
||||||
|
extern void page_free(void *p);
|
||||||
|
|
||||||
|
/* task management */
|
||||||
|
struct context {
|
||||||
|
/* ignore x0 */
|
||||||
|
reg_t ra;
|
||||||
|
reg_t sp;
|
||||||
|
reg_t gp;
|
||||||
|
reg_t tp;
|
||||||
|
reg_t t0;
|
||||||
|
reg_t t1;
|
||||||
|
reg_t t2;
|
||||||
|
reg_t s0;
|
||||||
|
reg_t s1;
|
||||||
|
reg_t a0;
|
||||||
|
reg_t a1;
|
||||||
|
reg_t a2;
|
||||||
|
reg_t a3;
|
||||||
|
reg_t a4;
|
||||||
|
reg_t a5;
|
||||||
|
reg_t a6;
|
||||||
|
reg_t a7;
|
||||||
|
reg_t s2;
|
||||||
|
reg_t s3;
|
||||||
|
reg_t s4;
|
||||||
|
reg_t s5;
|
||||||
|
reg_t s6;
|
||||||
|
reg_t s7;
|
||||||
|
reg_t s8;
|
||||||
|
reg_t s9;
|
||||||
|
reg_t s10;
|
||||||
|
reg_t s11;
|
||||||
|
reg_t t3;
|
||||||
|
reg_t t4;
|
||||||
|
reg_t t5;
|
||||||
|
reg_t t6;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int task_create(void (*task)(void));
|
||||||
|
extern void task_delay(volatile int count);
|
||||||
|
|
||||||
|
#endif /* __OS_H__ */
|
||||||
136
code/os/03-contextswitch/os.ld
Normal file
136
code/os/03-contextswitch/os.ld
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* rvos.ld
|
||||||
|
* Linker script for outputting to RVOS
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/Miscellaneous-Commands.html
|
||||||
|
* OUTPUT_ARCH command specifies a particular output machine architecture.
|
||||||
|
* "riscv" is the name of the architecture for both 64-bit and 32-bit
|
||||||
|
* RISC-V target. We will further refine this by using -march=rv32ima
|
||||||
|
* and -mabi=ilp32 when calling gcc.
|
||||||
|
*/
|
||||||
|
OUTPUT_ARCH( "riscv" )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/Entry-Point.html
|
||||||
|
* ENTRY command is used to set the "entry point", which is the first instruction
|
||||||
|
* to execute in a program.
|
||||||
|
* The argument of ENTRY command is a symbol name, here is "_start" which is
|
||||||
|
* defined in start.S.
|
||||||
|
*/
|
||||||
|
ENTRY( _start )
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/MEMORY.html
|
||||||
|
* The MEMORY command describes the location and size of blocks of memory in
|
||||||
|
* the target.
|
||||||
|
* The syntax for MEMORY is:
|
||||||
|
* MEMORY
|
||||||
|
* {
|
||||||
|
* name [(attr)] : ORIGIN = origin, LENGTH = len
|
||||||
|
* ......
|
||||||
|
* }
|
||||||
|
* Each line defines a memory region.
|
||||||
|
* Each memory region must have a distinct name within the MEMORY command. Here
|
||||||
|
* we only define one region named as "ram".
|
||||||
|
* The "attr" string is an optional list of attributes that specify whether to
|
||||||
|
* use a particular memory region for an input section which is not explicitly
|
||||||
|
* mapped in the linker script. Here we assign 'w' (writeable), 'x' (executable),
|
||||||
|
* and 'a' (allocatable). We use '!' to invert 'r' (read-only) and
|
||||||
|
* 'i' (initialized).
|
||||||
|
* The "ORIGIN" is used to set the start address of the memory region. Here we
|
||||||
|
* place it right at the beginning of 0x8000_0000 because this is where the
|
||||||
|
* QEMU-virt machine will start executing.
|
||||||
|
* Finally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.
|
||||||
|
* The linker will double check this to make sure everything can fit.
|
||||||
|
*/
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/SECTIONS.html
|
||||||
|
* The SECTIONS command tells the linker how to map input sections into output
|
||||||
|
* sections, and how to place the output sections in memory.
|
||||||
|
* The format of the SECTIONS command is:
|
||||||
|
* SECTIONS
|
||||||
|
* {
|
||||||
|
* sections-command
|
||||||
|
* sections-command
|
||||||
|
* ......
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* Each sections-command may of be one of the following:
|
||||||
|
* (1) an ENTRY command
|
||||||
|
* (2) a symbol assignment
|
||||||
|
* (3) an output section description
|
||||||
|
* (4) an overlay description
|
||||||
|
* We here only demo (2) & (3).
|
||||||
|
*
|
||||||
|
* We use PROVIDE command to define symbols.
|
||||||
|
* https://sourceware.org/binutils/docs/ld/PROVIDE.html
|
||||||
|
* The PROVIDE keyword may be used to define a symbol.
|
||||||
|
* The syntax is PROVIDE(symbol = expression).
|
||||||
|
* Such symbols as "_text_start", "_text_end" ... will be used in mem.S.
|
||||||
|
* Notice the period '.' tells the linker to set symbol(e.g. _text_start) to
|
||||||
|
* the CURRENT location ('.' = current memory location). This current memory
|
||||||
|
* location moves as we add things.
|
||||||
|
*/
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We are going to layout all text sections in .text output section,
|
||||||
|
* starting with .text. The asterisk("*") in front of the
|
||||||
|
* parentheses means to match the .text section of ANY object file.
|
||||||
|
*/
|
||||||
|
.text : {
|
||||||
|
PROVIDE(_text_start = .);
|
||||||
|
*(.text .text.*)
|
||||||
|
PROVIDE(_text_end = .);
|
||||||
|
} >ram
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
PROVIDE(_rodata_start = .);
|
||||||
|
*(.rodata .rodata.*)
|
||||||
|
PROVIDE(_rodata_end = .);
|
||||||
|
} >ram
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
/*
|
||||||
|
* . = ALIGN(4096) tells the linker to align the current memory
|
||||||
|
* location to 4096 bytes. This will insert padding bytes until
|
||||||
|
* current location becomes aligned on 4096-byte boundary.
|
||||||
|
* This is because our paging system's resolution is 4,096 bytes.
|
||||||
|
*/
|
||||||
|
. = ALIGN(4096);
|
||||||
|
PROVIDE(_data_start = .);
|
||||||
|
/*
|
||||||
|
* sdata and data are essentially the same thing. We do not need
|
||||||
|
* to distinguish sdata from data.
|
||||||
|
*/
|
||||||
|
*(.sdata .sdata.*)
|
||||||
|
*(.data .data.*)
|
||||||
|
PROVIDE(_data_end = .);
|
||||||
|
} >ram
|
||||||
|
|
||||||
|
.bss :{
|
||||||
|
/*
|
||||||
|
* https://sourceware.org/binutils/docs/ld/Input-Section-Common.html
|
||||||
|
* In most cases, common symbols in input files will be placed
|
||||||
|
* in the ‘.bss’ section in the output file.
|
||||||
|
*/
|
||||||
|
PROVIDE(_bss_start = .);
|
||||||
|
*(.sbss .sbss.*)
|
||||||
|
*(.bss .bss.*)
|
||||||
|
*(COMMON)
|
||||||
|
PROVIDE(_bss_end = .);
|
||||||
|
} >ram
|
||||||
|
|
||||||
|
PROVIDE(_memory_start = ORIGIN(ram));
|
||||||
|
PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));
|
||||||
|
|
||||||
|
PROVIDE(_heap_start = _bss_end);
|
||||||
|
PROVIDE(_heap_size = _memory_end - _heap_start);
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user