RISCV基础开发(十七)

gewenbin
gewenbin
gewenbin
188
文章
15
评论
2021年8月22日10:38:35 评论 947

QEMU OpenSBI 裸机开发之字符打印

1. 编译OpenSBI镜像

前面的章节我们是直接从m模式开发的裸机程序,从本章节开始我们来学习在已经有OpenSBI的基础下进行裸机开发。首先需要下载OpenSBI源码进行编译,官方仓库地址如下。

git clone https://github.com/riscv/opensbi.git

下载完成后进入目录输入如下命令进行编译:

export CROSS_COMPILE=riscv64-unknown-elf-
make PLATFORM=generic clean
make PLATFORM=generic FW_JUMP_ADDR=0x80200000

其中0x80200000是指后面我们编写的裸机程序的入口地址,这个是OpenSBI源码中默认的跳转地址,当然也可以修改成其他合法地址,在后面的裸机程序编译时,我们也需要将链接地址进行修改。

编译完成后,生成的fw_jump.elf文件在build/platform/generic/firmware/目录下,这个文件就是我们要用的OpenSBI镜像。

2. 工程改动

2.1 总览

在OpenSBI环境下开发的裸机程序工程和原来相比有些地方要做修改,先来看下整体的文件分布,如下所示。

RISCV基础开发(十七)

  • entry.S:入口文件,主要设置了栈。
  • fw_jump.elf:OpenSBI镜像。
  • kernel.ld:编译用的链接脚本。
  • Makefile:makefile文件,控制源码的编译过程。
  • sbi.h:对OpenSBI调用做了一些接口封装,方便使用。
  • start.c:是C语言实现的入口,在entry.S中会调转到C函数中。

2.2 Makefile

Makefile和之前的相比,主要是qemu启动命令修改了,因为现在要先启动OpenSBI镜像,然后再加载我们的程序启动运行,所以需要将“bios”选项修改为“-bios fw_jump.elf”,如下所示。

QEMUOPTS = -machine virt -bios fw_jump.elf -kernel $(KERNEL_IMAGE_NAME) -m 128M -smp $(CPUS) -nographic

2.3 连接脚本

链接脚本相比于原来的主要是修改了链接地址为0x80200000,如下所示。

OUTPUT_ARCH( "riscv" )
ENTRY( _entry )

SECTIONS
{
  /*
   * ensure that entry.S / _entry is at 0x80200000,
   * where qemu's -kernel jumps.
   */
  . = 0x80200000;

  .text : {
    *(.text .text.*)
    . = ALIGN(0x1000);
    PROVIDE(etext = .);
  }

2.4 入口处理

在entry.S中,原来是先读取当前cpu核id然后再设置栈地址,但是现在OpenSBI跳转到我们的裸机程序时,裸机程序已经处于s模式,就不然再运行只能在m模式下执行的指令了,所以直接将栈地址加上4KB即可,如下所示。

.section .text

.global _entry
_entry:

    la sp, __stack_start
    li a0, 4096
    add sp, sp, a0

    call start
loop:
    j loop

2.5 SBI接口

我们将SBI调用封装成一些接口方便使用,定义在sbi.h中,这个文件也就是对之前的ecall.h进行完善而成,如下所示。

#ifndef _ASM_RISCV_SBI_H
#define _ASM_RISCV_SBI_H

#define SBI_SET_TIMER 0
#define SBI_CONSOLE_PUTCHAR 1
#define SBI_CONSOLE_GETCHAR 2
#define SBI_CLEAR_IPI 3
#define SBI_SEND_IPI 4
#define SBI_REMOTE_FENCE_I 5
#define SBI_REMOTE_SFENCE_VMA 6
#define SBI_REMOTE_SFENCE_VMA_ASID 7
#define SBI_SHUTDOWN 8

#define SBI_CALL(which, arg0, arg1, arg2) ({            \
    register unsigned long a0 asm ("a0") = (unsigned long)(arg0);   \
    register unsigned long a1 asm ("a1") = (unsigned long)(arg1);   \
    register unsigned long a2 asm ("a2") = (unsigned long)(arg2);   \
    register unsigned long a7 asm ("a7") = (unsigned long)(which);  \
    asm volatile ("ecall"                   \
              : "+r" (a0)               \
              : "r" (a1), "r" (a2), "r" (a7)        \
              : "memory");              \
    a0;                         \
})

/* Lazy implementations until SBI is finalized */
#define SBI_CALL_0(which) SBI_CALL(which, 0, 0, 0)
#define SBI_CALL_1(which, arg0) SBI_CALL(which, arg0, 0, 0)
#define SBI_CALL_2(which, arg0, arg1) SBI_CALL(which, arg0, arg1, 0)

static inline void sbi_console_putchar(int ch)
{
    SBI_CALL_1(SBI_CONSOLE_PUTCHAR, ch);
}

static inline int sbi_console_getchar(void)
{
    return SBI_CALL_0(SBI_CONSOLE_GETCHAR);
}

static inline void sbi_set_timer(unsigned long stime_value)
{
    SBI_CALL_1(SBI_SET_TIMER, stime_value);
}

static inline void sbi_shutdown(void)
{
    SBI_CALL_0(SBI_SHUTDOWN);
}

static inline void sbi_clear_ipi(void)
{
    SBI_CALL_0(SBI_CLEAR_IPI);
}

static inline void sbi_send_ipi(const unsigned long *hart_mask)
{
    SBI_CALL_1(SBI_SEND_IPI, hart_mask);
}

static inline void sbi_remote_fence_i(const unsigned long *hart_mask)
{
    SBI_CALL_1(SBI_REMOTE_FENCE_I, hart_mask);
}

static inline void sbi_remote_sfence_vma(const unsigned long *hart_mask,
                     unsigned long start,
                     unsigned long size)
{
    SBI_CALL_1(SBI_REMOTE_SFENCE_VMA, hart_mask);
}

static inline void sbi_remote_sfence_vma_asid(const unsigned long *hart_mask,
                          unsigned long start,
                          unsigned long size,
                          unsigned long asid)
{
    SBI_CALL_1(SBI_REMOTE_SFENCE_VMA_ASID, hart_mask);
}

#endif

在SBI规范中,已经提供了输出字符的调用,我们直接使用即可在终端上打印出字符。

2.6 C函数处理

C函数start通过SBI接口来打印字符即可,如下所示。

#include "sbi.h"

void start(void)
{
    sbi_console_putchar('r');
    sbi_console_putchar('u');
    sbi_console_putchar('n');
}

3. 测试

在命令行输入“make qemu”,然后在最后就可以看到字符信息输出,如下所示。

ewenbin@gewenbin-virtual-machine:~/Desktop/qemu_test/lesson12$ make qemu
riscv64-unknown-elf-gcc    -c -o entry.o entry.S
riscv64-unknown-elf-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie   -c -o start.o start.c
riscv64-unknown-elf-ld -z max-page-size=4096 -T kernel.ld -o kernelimage entry.o start.o 
riscv64-unknown-elf-objdump -S kernelimage > kernel.asm
riscv64-unknown-elf-objdump -t kernelimage | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel.sym
qemu-system-riscv64 -machine virt -bios fw_jump.elf -kernel kernelimage -m 128M -smp 1 -nographic

OpenSBI v0.8-81-g7dcb1e1
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : riscv-virtio,qemu
Platform Features         : timer,mfdeleg
Platform HART Count       : 1
Firmware Base             : 0x80000000
Firmware Size             : 112 KB
Runtime SBI Version       : 0.2

Domain0 Name              : root
Domain0 Boot HART         : 0
Domain0 HARTs             : 0*
Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
Domain0 Next Address      : 0x0000000080200000
Domain0 Next Arg1         : 0x0000000082200000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes

Boot HART ID              : 0
Boot HART Domain          : root
Boot HART ISA             : rv64imafdcsu
Boot HART Features        : scounteren,mcounteren,time
Boot HART PMP Count       : 16
Boot HART PMP Granularity : 4
Boot HART PMP Address Bits: 54
Boot HART MHPM Count      : 0
Boot HART MHPM Count      : 0
Boot HART MIDELEG         : 0x0000000000000222
Boot HART MEDELEG         : 0x000000000000b109
run

4. 工程源码

链接:https://pan.baidu.com/s/1TnTYr7mywdKj5bxpdmWnyA,提取码:q772,见lesson12。

gewenbin
  • 本文由 发表于 2021年8月22日10:38:35
  • 转载请务必保留本文链接:http://www.databusworld.cn/10550.html
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: