掌機 - Gaviar (小志掌機) - 修改OpenSBI



參考資訊:
https://linux-sunxi.org/Allwinner_Nezha
https://github.com/steward-fu/website/releases/tag/gaviar
https://docs.riscv.org/reference/sbi/v3.0/ext-legacy.html
https://github.com/ylyamin/RT-Thread-on-Allwinner-D1H/blob/master/documentation/D1_2_boot_process.md

由於全志針對F133的開機設計流程過於複雜,因此,為了可以更精簡以及方便維護程式碼,司徒特別針對小志掌機修改了一版OpenSBI,修改後的流程如下所示


編譯步驟:

$ cd
$ wget https://github.com/steward-fu/website/releases/download/gaviar/thead_toolchain.tar.gz
$ tar xvf thead_toolchain.tar.gz
$ sudo mv thead /opt
$ export PATH=/opt/thead/bin:$PATH

$ cd
$ wget https://github.com/steward-fu/website/releases/download/gaviar/opensbi_20260906.tar.gz
$ tar xvf opensbi_20260906.tar.gz
$ cd opensbi

$ export PLATFORM=allwinner/f133
$ export CROSS_COMPILE=riscv64-unknown-linux-gnu-

$ make distclean
$ CONFIG=BROM make
$ python3 scripts/gen_checksum.py ./build/platform/allwinner/f133/firmware/fw_jump.bin fw_jump.bin
$ dd if=fw_jump.bin of=payload/brom.bin bs=1024 count=32
$ rm -rf fw_jump.bin

$ make distclean
$ make
$ python3 scripts/gen_checksum.py ./build/platform/allwinner/f133/firmware/fw_jump.bin payload/fw_jump.bin
$ python3 scripts/gen_checksum.py ./build/platform/allwinner/f133/firmware/fw_payload.bin payload/fw_payload.bin

$ sudo dd if=payload/brom.bin    of=/dev/sdX bs=1024 seek=8
$ sudo dd if=payload/fw_jump.bin of=/dev/sdX bs=1024 seek=40

$ sudo dd if=dtb   of=/dev/sdX bs=1024 seek=300
$ sudo dd if=Image of=/dev/sdX bs=1024 seek=400

由於目前Kernel程式碼尚未完成,因此,司徒使用LED閃爍程式替代,開機後,OpenSBI會跑LED閃爍程式


Baudrate 115200bps

[UART] Gaviar Handheld
[DRAM] ZQ value = 0x2f
[DRAM] get_pmu_exist() = -1
[DRAM] ddr_efuse_type = 0xa
[DRAM] trefi = 7.8ms
[DRAM] single rank and full DQ!
[DRAM] ddr_efuse_type = 0xa
[DRAM] trefi = 7.8ms
[DRAM] rank 0 row = 13 
[DRAM] rank 0 bank = 4 
[DRAM] rank 0 page size = 2 KB 
[DRAM] DRAM BOOT DRIVE INFO: v0.33
[DRAM] DRAM CLK = 528 MHz
[DRAM] DRAM Type = 2 (2:DDR2, 3:DDR3)
[DRAM] DRAMC read ODT  off
[DRAM] DRAM ODT off
[DRAM] ddr_efuse_type = 0xa
[DRAM] DRAM SIZE = 64M
[DRAM] dram_tpr4 = 0x0
[DRAM] PLL_DDR_CTRL_REG = 0xf8002b00
[DRAM] DRAM_CLK_REG = 0xc0000000
[DRAM] MR2 = 0x0
[DRAM] DRAM simple test OK
[eMMC] MicroSD size = 59688MB
[eMMC] Copy DTB to 0x41500000 (size = 65536)...
[eMMC] Copy Kernel to 0x40008000 (size = 8388608)...
[eMMC] Copy OpenSBI to 0x41000000 (size = 262144)...


Welcome to Gaviar handheld
OpenSBI v1.3
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : Allwinner F133
Platform Features         : medeleg
Platform HART Count       : 1
Platform IPI Device       : ---
Platform Timer Device     : --- @ 0Hz
Platform Console Device   : f133_uart
Platform HSM Device       : ---
Platform PMU Device       : ---
Platform Reboot Device    : ---
Platform Shutdown Device  : ---
Platform Suspend Device   : ---
Platform CPPC Device      : ---
Firmware Base             : 0x41000000
Firmware Size             : 194 KB
Firmware RW Offset        : 0x20000
Firmware RW Size          : 66 KB
Firmware Heap Offset      : 0x28000
Firmware Heap Size        : 34 KB (total), 2 KB (reserved), 8 KB (used), 23 KB (free)
Firmware Scratch Size     : 4096 B (total), 728 B (used), 3368 B (free)
Runtime SBI Version       : 1.0

Domain0 Name              : root
Domain0 Boot HART         : 0
Domain0 HARTs             : 0*
Domain0 Region00          : 0x0000000041000000-0x000000004101ffff M: (R,X) S/U: ()
Domain0 Region01          : 0x0000000041020000-0x000000004103ffff M: (R,W) S/U: ()
Domain0 Region02          : 0x0000000000000000-0xffffffffffffffff M: () S/U: (R,W,X)
Domain0 Next Address      : 0x0000000040008000
Domain0 Next Arg1         : 0x0000000041500000
Domain0 Next Mode         : S-mode
Domain0 SysReset          : yes
Domain0 SysSuspend        : yes

Boot HART ID              : 0
Boot HART Domain          : root
Boot HART Priv Version    : v1.11
Boot HART Base ISA        : rv64imafdcvx
Boot HART ISA Extensions  : zicntr
Boot HART PMP Count       : 8
Boot HART PMP Granularity : 2048
Boot HART PMP Address Bits: 38
Boot HART MHPM Count      : 0
Boot HART MHPM Mask       : 0x0
Boot HART MIDELEG         : 0x0000000000000222
Boot HART MEDELEG         : 0x000000000000b109
Jump to kernel (0x40008000) ...

司徒列出幫小志掌機修改的部份


為何RISC-V需要有這些模式呢?可以從下圖得知,Linux/OS一般跑在S-Mode,但是當需要控制底層時,就需要切換M-Mode,這也是SBI介面的由來,當然,如果不用Linux/OS,就可以自己實做這一部份


為了讓OpenSBI具有開機執行的能力,司徒小改了一下fw_base.S,在程式進入點_gaviar加入判斷程式,如果是從0x20000的位址執行,則執行第一次初始化的動作,包含初始化UART、DRAM、MMC等,並且加載OpenSBI、Kernel、DTB到DRAM,至於為何是判斷位址0x20000呢?這是因為F133 BROM一上電後,會先從MicroSD 8KB位址處加載32KB程式到RAM 0x20000並且執行,所以司徒在0x20000處做標記(F133_TAG),接著等第二次執行時,這時候位址已經改成從0x41000000執行,所以就會直接跑_start的程式,因此我們需要兩份fw_jump.bin,一份是給0x20000用的(32KB),另一份則是完整的fw_jump.bin,司徒使用CONFIG=brom make用來編譯0x20000的fw_jump.bin,詳細的執行流程如下所示:


預計的MicroSD Layout


更新檔案的方式

$ sudo dd if=fw_jump.bin of=/dev/sdX bs=1024 seek=8
$ sudo dd if=fw_jump.bin of=/dev/sdX bs=1024 seek=40
$ sudo dd if=dtb         of=/dev/sdX bs=1024 seek=300
$ sudo dd if=Image       of=/dev/sdX bs=1024 seek=400

由於目前司徒先使用LED閃爍程式替代Kernel,那是否代表可以修改這個LED閃爍程式來呼叫SBI Interface呢?答案肯定是可以的,程式碼如下:

    .global _start
   
    .text
    .long 0x05c0006f
    .byte 'e','G','O','N','.','B','T','0'
    .long 0x5f0a6c39
    .long 0x8000
    .long 0, 0
    .long 0, 0, 0, 0, 0, 0, 0, 0
    .long 0, 0, 0, 0, 0, 0, 0, 0
  
_start:
    la a0, msg
    call sbi_puts
1:
    j 1b

sbi_puts:
    mv t0, a0
1:
    lbu a0, 0(t0)
    beqz a0, 2f
    li a7, 1
    ecall
    addi t0, t0, 1
    j 1b
2:
    ret

msg: .asciz "\n\nHello, world!\n"
    .end

編譯、燒錄

$ riscv64-unknown-linux-gnu-as -o main.o main.s
$ riscv64-unknown-linux-gnu-ld -T main.ld -o main.elf main.o
$ riscv64-unknown-linux-gnu-objcopy -O binary main.elf tmp.bin
$ python3 gen_checksum.py tmp.bin main.bin
$ sudo dd if=main.bin of=/dev/sdb bs=1024 seek=400

開機後就可以從UART看到Hello, world!字串,如果想從屏幕上印出字串,只要修改lib/sbi/sbi_ecall_legacy.c,將#if 0改成#if 1後,重新編譯燒錄即可

static int sbi_ecall_legacy_handler(unsigned long extid, unsigned long funcid,
                    const struct sbi_trap_regs *regs,
                    unsigned long *out_val,
                    struct sbi_trap_info *out_trap)
{
    int ret = 0;
    struct sbi_tlb_info tlb_info;
    u32 source_hart = current_hartid();
    ulong hmask = 0;

    switch (extid) {
    case SBI_EXT_0_1_SET_TIMER:
#if __riscv_xlen == 32
        sbi_timer_event_start((((u64)regs->a1 << 32) | (u64)regs->a0));
#else
        sbi_timer_event_start((u64)regs->a0);
#endif
        break;
    case SBI_EXT_0_1_CONSOLE_PUTCHAR:
        sbi_putc(regs->a0);

#if 0
        /* Added by Steward for Gaviar handheld */
        void f133_slcd_putc(char ch);
        f133_slcd_putc(regs->a0);
#endf
        break;



Legacy SBI Interface有如下幾個Function,上面的那個main.s範例就是使用EID 0x01的sbi_console_putchar()印出字串,藉由上面的main.s範例,讓我們可以更了解SBI Interface的運作原理


呼叫的流程如下:


Memory Layout如下:


Device Tree保留這些區域避免被覆蓋

memory@40000000 {
    device_type = "memory";
    reg = <0x0 0x40000000 0x0 0x04000000>; /* 64 MB */
};

reserved-memory {
    #address-cells = <2>;
    #size-cells = <2>;
    ranges;

    opensbi_reserved: opensbi@43e00000 {
        reg = <0x0 0x43e00000 0x0 0x00080000>; /* 512 KiB */
        no-map;
    };

    dtb_reserved: dtb@43d00000 {
        reg = <0x0 0x43d00000 0x0 0x00010000>; /* 64 KiB */
        no-map;
    };
};

開機後的Log

[    0.000000] Linux version 5.4.61 (steward@debian) (riscv64-unknown-linux-gnu-gcc (C-SKY RISCV Tools V1.8.4 B20200702) 8.1.0, GNU ld (GNU Binutils) 2.32) #1 PREEMPT Fri Sep 11 07:05:18 CST 2026
[    0.000000] earlycon: sbi0 at I/O port 0x0 (options '')
[    0.000000] printk: bootconsole [sbi0] enabled
[    0.000000] cma: Reserved 16 MiB at 0x0000000042c00000
[    0.000000] Zone ranges:
[    0.000000]   DMA32    [mem 0x0000000040000000-0x0000000043ffffff]
[    0.000000]   Normal   empty
[    0.000000] Movable zone start for each node
[    0.000000] Early memory node ranges
[    0.000000]   node   0: [mem 0x0000000040000000-0x0000000043cfffff]
[    0.000000]   node   0: [mem 0x0000000043d10000-0x0000000043dfffff]
[    0.000000]   node   0: [mem 0x0000000043e80000-0x0000000043ffffff]
[    0.000000] Initmem setup node 0 [mem 0x0000000040000000-0x0000000043ffffff]