平台
查看qemu支持的riscv64的board
$ qemu-system-riscv64 -machine help
Supported machines are:
none empty machine
sifive_e RISC-V Board compatible with SiFive E SDK
sifive_u RISC-V Board compatible with SiFive U SDK
spike RISC-V Spike Board (default)
spike_v1.10 RISC-V Spike Board (Privileged ISA v1.10)
spike_v1.9.1 RISC-V Spike Board (Privileged ISA v1.9.1)
virt RISC-V VirtIO board
手里没有riscv64的board,使用virt board
DTS文件
查看virt的dts
$ qemu-system-riscv64 -machine virt,dumpdtb=qemu-riscv64-virt.dtb
$ dtc qemu-riscv64-virt.dtb > qemu-riscv64-virt.dts
如果提示dtc命令不存在,安装
$ sudo apt install device-tree-compiler
生成文件:qemu-riscv64-virt.dts
查看内存布局
memory@80000000 {
device_type = "memory";
reg = <0x00 0x80000000 0x00 0x8000000>;
};
根据内存起始地址,设置链接脚本的开始地址为0x80000000
查看uart布局
uart@10000000 {
interrupts = <0x0a>;
interrupt-parent = <0x03>;
clock-frequency = <0x384000>;
reg = <0x00 0x10000000 0x00 0x100>;
compatible = "ns16550a";
};
可以将字符输出到0x10000000的内存地址。
代码解析
.section .text.boot #指定此文件编译出的代码在.text.boot段
.globl _start
_start:
li s1, 0x10000000 # s1寄存器赋值为uart内存的地址
la s2, message # s2寄存器赋值为message的首地址
addi s3, s2, 14 # s3寄存器赋值为message的结束地址
1:
lb s4, 0(s2) # s4赋值为s2内存中的值
sb s4, 0(s1) # s4寄存器内的值输出到s1(uart)内存中
addi s2, s2, 1 # s2 := s2 + 1
blt s2, s3, 1b # if s2 < s3, branch back to 1
.section .data
message:
.string "Hello, world!\n"
测试
make
$ make
rm -rf build *.img kernel.map
mkdir -p build
riscv64-linux-gnu-gcc -g -Wall -nostdlib -Iinclude -MMD -c src/start.c -o build/start_c.o
riscv64-linux-gnu-gcc -g -Iinclude -MMD -c -D__ASSEMBLY__ src/startup.S -o build/startup_s.o
riscv64-linux-gnu-ld -T src/linker.ld -Map kernel.map -o build/kernel.elf build/start_c.o build/startup_s.o
riscv64-linux-gnu-objcopy build/kernel.elf -O binary kernel.img
run
make run
qemu-system-riscv64 -machine virt -bios none -kernel build/kernel.elf -nographic
Hello, world!