目次: Zephyr
Zephyrをqemu_riscv32ボード向けにコンフィグし、ビルドディレクトリ下でninja runを実行すると、最終的に下記のコマンドが実行され、QMEUが起動します。
$ /usr/bin/qemu-system-riscv32 -nographic -machine sifive_e -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/build/zephyr/zephyr.elf -s -S
GDBで追うとわかりますが、QEMUの実行開始アドレスは0x1000です。0x1000には0x20400000にジャンプするコードだけが置かれています。
0x1000: lui t0,0x20400
=> 0x1004: jr t0
0x1008: unimp
0x100a: unimp
HiFive1実機であれば0x20400000はQSPI Flashがマッピングされているアドレスです(SiFive FE310-G000 Manual v2p3を参照)。QEMUの場合はELFファイルbuild/zephyr/zephyr.elfのセクション情報通りに配置されます。
$ riscv64-zephyr-elf-readelf -a build/zephyr/zephyr.elf | less ... Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] vector PROGBITS 20400000 000094 000010 00 AX 0 0 4 [ 2] exceptions PROGBITS 20400010 0000a4 000258 00 AX 0 0 4 [ 3] text PROGBITS 20400268 0002fc 002ad4 00 AX 0 0 4 [ 4] sw_isr_table PROGBITS 20402d3c 002dd0 000200 00 WA 0 0 4 [ 5] devconfig PROGBITS 20402f3c 002fd0 000060 00 A 0 0 4 [ 6] rodata PROGBITS 20402f9c 003030 000215 00 A 0 0 4 [ 7] datas PROGBITS 80000000 003248 000018 00 WA 0 0 4 [ 8] initlevel PROGBITS 80000018 003260 000060 00 WA 0 0 4 [ 9] _k_mutex_area PROGBITS 80000078 0032c0 000014 00 WA 0 0 4 [10] bss NOBITS 80000090 0032e0 00013c 00 WA 0 0 8 [11] noinit NOBITS 800001d0 0032e0 000e00 00 WA 0 0 16 ...
ジャンプ先の0x20400000にはvectorというセクションが対応していることがわかります。ELFファイルを逆アセンブルしてみると、
zephyr/zephyr.elf: file format elf32-littleriscv
Disassembly of section vector:
20400000 <__start>:
/*
* Set mtvec (Machine Trap-Vector Base-Address Register)
* to __irq_wrapper.
*/
la t0, __irq_wrapper
20400000: 00000297 auipc t0,0x0
20400004: 01028293 addi t0,t0,16 # 20400010 <__irq_wrapper>
csrw mtvec, t0
20400008: 30529073 csrw mtvec,t0
/* Jump to __initialize */
tail __initialize
2040000c: 4b40106f j 204014c0 <__initialize>
以上のように __startという関数が居るようです。実装はzephyr/soc/riscv/riscv-privilege/common/vector.Sにあります。
このエントリポイントアドレスはどのように決まるのでしょう?Zephyrでは、ボード用のデバイスツリーzephyr/boards/riscv/qemu_riscv32/qemu_riscv32.dtsにあるFlashの先頭アドレスを変えるとELFのエントリポイント(0x20400000)も追従します。
chosen {
zephyr,console = &uart0;
zephyr,shell-uart = &uart0;
zephyr,sram = &dtim;
zephyr,flash = &flash0;
};
このchosen以下を検知しているようです。スクリプトscripts/dts/gen_defines.pyを見ると、SPIのときだけ特殊処理になっていて、それ以外はregの値を先頭アドレスとみなしています。
# zephyr/scripts/dts/gen_defines.py
...
def write_flash_node(edt):
# Writes output for the top-level flash node pointed at by
# zephyr,flash in /chosen
node = edt.chosen_node("zephyr,flash")
out_comment(f"/chosen/zephyr,flash ({node.path if node else 'missing'})")
if not node:
# No flash node. Write dummy values.
out("FLASH_BASE_ADDRESS", 0)
out("FLASH_SIZE", 0)
return
if len(node.regs) != 1:
err("expected zephyr,flash to have a single register, has "
f"{len(node.regs)}")
if node.on_bus == "spi" and len(node.bus_node.regs) == 2:
reg = node.bus_node.regs[1] # QSPI flash
else:
reg = node.regs[0]
out("FLASH_BASE_ADDRESS", hex(reg.addr))
if reg.size:
out("FLASH_SIZE", reg.size//1024)
...
FlashのアドレスはDT_FLASH_BASE_ADDRESSというマクロで参照できます。エントリーポイントを最終的に決めるのはリンカースクリプトです。SoCで独自のリンカースクリプトを実装することもできるようになっていますが、RISC-Vの多くのSoCは、下記のスクリプトを使っています。
// zephyr/include/arch/riscv/common/linker.ld
MEMORY
{
#ifdef CONFIG_XIP
ROM (rx) : ORIGIN = DT_FLASH_BASE_ADDRESS, LENGTH = KB(DT_FLASH_SIZE)
#endif
RAM (rwx) : ORIGIN = CONFIG_SRAM_BASE_ADDRESS, LENGTH = KB(CONFIG_SRAM_SIZE)
/* Used by and documented in include/linker/intlist.ld */
IDT_LIST (wx) : ORIGIN = 0xFFFFF7FF, LENGTH = 2K
}
Zephyrのエントリポイントがどうやって決まるのか、少しわかった気がします。
< | 2020 | > | ||||
<< | < | 02 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | - | - | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
合計:
本日:
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2023.
Powered by PHP 8.2.15.
using GD bundled (2.1.0 compatible)(png support.)