make new kernel for gdb

spike

riscvのシュミレータとして、 spike ってのが有るそうな。これはもう試してみる鹿。

on OpenBSD

クロスコンパイラーとかnewlibに混ってspikeが用意されてた。オープンハードを支えるオープンソフト、権利に五月蝿いOpenBSDも太鼓番です。残念ながらgdbはまだです。手が回らないのでしょう。次回リリースに期待。まあ、現状で試してみるか。

ob$ riscv64-unknown-elf-gcc z.c
ob$ spike pk a.out
libc++abi: terminating with uncaught exception of type std::runtime_error: couldn't allocate 2147483648 bytes of target memory
Abort trap (core dumped)

メモリー足りない2Gも欲しいって、そんなに必要? しょうがないんで制限無しのrootさんで。

ob$ doas spike pk a.out
<stdin>:25.39-29.9: Warning (interrupt_provider): /cpus/cpu@0/interrupt-controller: Missing #address-cells in interrupt provider
<stdin>:25.39-29.9: Warning (interrupt_provider): /cpus/cpu@0/interrupt-controller: Missing #address-cells in interrupt provider
bbl loader
Hello RISC-V

ulimit で data(kbytes) 1572864 になってたので少し増やしてみる。

ob$ ulimit -d 2200000
ob$ spike pk a.out
<stdin>:25.39-29.9: Warning (interrupt_provider): /cpus/cpu@0/interrupt-controller: Missing #address-cells in interrupt provider
<stdin>:25.39-29.9: Warning (interrupt_provider): /cpus/cpu@0/interrupt-controller: Missing #address-cells in interrupt provider
bbl loader
Hello RISC-V

今度は成功。やれやれ。

ob$ riscv64-unknown-elf-objdump -d a.out
a.out:     file format elf64-littleriscv

Disassembly of section .text:

00000000000100b0 <_start>:
   100b0:       00005197                auipc   gp,0x5
   100b4:       d2818193                addi    gp,gp,-728 # 14dd8 <__global_pointer$>
   100b8:       84018513                addi    a0,gp,-1984 # 14618 <_edata>
   100bc:       8c818613                addi    a2,gp,-1848 # 146a0 <_end>
   100c0:       8e09                    sub     a2,a2,a0
   100c2:       4581                    li      a1,0
   100c4:       1c4000ef                jal     ra,10288 <memset>
   100c8:       00000517                auipc   a0,0x0
   100cc:       11c50513                addi    a0,a0,284 # 101e4 <__libc_fini_array>
   100d0:       0ea000ef                jal     ra,101ba <atexit>
   100d4:       146000ef                jal     ra,1021a <__libc_init_array>
   100d8:       4502                    lw      a0,0(sp)
   100da:       002c                    addi    a1,sp,8
   100dc:       4601                    li      a2,0
   100de:       0be000ef                jal     ra,1019c <main>
   100e2:       0e40006f                j       101c6 <exit>

OpenBSDで間に合わなかった所を、外部のリナックス部隊が支援してくれた。有り難いのう。で、コードをよく見るとコンパクトになったものが混っているな。

on Arch Linux

今度は別舞台で確認。デフォでパッケージが用意されてたので、何も考えんと入れてあげた。

[sakae@arch ~]$ pacman -Qs riscv
local/qemu-system-riscv 7.1.0-10
    QEMU system emulator for RISC-V
local/qemu-system-riscv-firmware 7.1.0-10
    Firmware for QEMU system emulator for RISC-V
local/riscv64-elf-binutils 2.39-1
    A set of programs to assemble and manipulate binary and object files for the
    RISCV64 (bare-metal) target
local/riscv64-elf-gcc 12.2.0-1
    The GNU Compiler Collection - cross compiler for RISCV64 (bare-metal) target
local/riscv64-elf-gdb 12.1-3
    The GNU Debugger for the RISCV64 (bare-metal) target
local/riscv64-elf-newlib 4.1.0-3
    A C standard library implementation intended for use on embedded systems
    (RISCV64 bare metal)

こんな環境にspikeも追加した。で、いざ実行すると pk なんて無いよと言われた。自前で用意しろってか。

riscv-simulator を参考にpkを入れる。最初コンパイルしたら、gccが使われてエラー。しょうがないのでMakefileをいじって、

CC            := riscv64-elf-gcc
READELF       := riscv64-elf-readelf
OBJCOPY       := riscv64-elf-objcopy

こんな風に変更。無事にインストール出来た。

[sakae@arch ~]$ ls /usr/riscv64-unknown-elf/bin/
bbl*  dummy_payload*  pk*

上記をPATHに加えておいて実行。

[sakae@arch t]$ spike pk a.out
bbl loader
Hello RISC-V

RISC-V SpikeシミュレータでC/C++のprintfを実現する仕組み

これは参考になる。pkのソースも簡略版のカーネルだから、隅々まで読むべし。

try make new kernel

今迄カーネル観光をやる場合、糞石であるi386をターゲットにしてた。riscv64を知った今、最新の石にスイッチしよう。gdbで動かした場合、アドレスとかが64Bitになって、目が回るけど、それを我慢すれば、十分に乗り換える意義が有るな。

by cross compiler

spikeをやるのにクロスコンパイラーを用意した。ひょっとして、このコンパイラーを使ったらriscv64用のカーネルをコンパイル出来る鴨。

Makefileの冒頭に追加

ob# head Makefile
CC=riscv64-unknown-elf-gcc
IDENT=-DDDB -DDIAGNOSTIC ...

コンパイルしてみると多少のオプション違いでSTOP。それを無視するように設定したけど、最後にどうしようもないエラーが残った。やはり無理であったか。

  -c /usr/src/sys/dev/videomode/edid.c
In file included from /usr/src/sys/dev/videomode/edid.c:35:                     /usr/src/sys/sys/systm.h:159:5: error: 'kprintf' is an unrecognized format function type [-Werror=format=]
     __attribute__((__noreturn__,__format__(__kprintf__,1,2)));
     ^~~~~~~~~~~~~                                               

実機で動くriscvなマシンが欲しい脳。

by real compiler

諦めないで頑張るぞ。よそ者のgccだから駄目なら、OpenBSD自前のコンパイラーで挑戦。ゆくゆくは、クロスのgdbを使いたいので、舞台をArchLinuxに移します。 とっても時間がかかる事は分かりきっているので、手抜きを考える。そうジェネリックを止めて 必要最低源のものにしたい。無駄を省いて高速化作戦です。プチ下調べ。

/etc/rcの最後に、

# Re-link the kernel, placing the objects in a random order.
# Replace current with relinked kernel and inform root about it.
/usr/libexec/reorder_kernel &

こんなのが有って、起動の為にカーネル内のオブジェクトの配置をランダム化してる。とっても負荷が高いので普段はここをコメントにしてる(libcも同様な処置がされている)。

この手続で分解された部品が、/usr/share/relink/kernel/GENERIC.MP に残っている。数を数えてみると、872個あった。これがi386とかだと倍ある。

riscv# ls -l *.o | sort -k 5 -r | head -5
-rw-r-----  1 root  wheel  2772752 Sep 28 18:52 cik.o
-rw-r-----  1 root  wheel  2451864 Sep 28 18:47 if_iwm.o
-rw-r-----  1 root  wheel  2324912 Sep 28 18:27 pf.o
-rw-r-----  1 root  wheel  2164600 Sep 28 19:00 si.o
-rw-r-----  1 root  wheel  1844224 Sep 28 18:53 evergreen.o
riscv# ls -l *.o | sort -k 5 -r | tail -5
-rw-r-----  1 root  wheel      704 Sep 28 18:40 ext2fs_bswap.o
-rw-r-----  1 root  wheel      648 Sep 28 18:24 locore0.o
-rw-r-----  1 root  wheel      584 Sep 28 18:43 support.o
-rw-r-----  1 root  wheel      488 Sep 28 18:43 pagezero.o
-rw-r-----  1 root  wheel      456 Sep 28 18:43 cpufunc_asm.o

大小様々な部品から組立られている事が分る。このうち不要な物はコンパイルから外せば、無駄が省けるってものだ。例えば、 if_iwm.o は、無線LANのドライバーだし、pf.o は、パケットフィルターだ。まあ、不要と言っていいだろう。

自分用のconfig仕様書を作成。GENERICとGENERI.SPが有るけど、シングルな石用のGENERICをSEEINGて名前でコピー。大事なdebug機能をONさせるため、下記を追加。

makeoptions DEBUG="-g"

それから、不要と思われるUSB系とDRM系を削除。これだけでは話が済まないので、conf/GENERICでの機能も削除。

riscv$ grep '##' /sys/conf/GENERIC
##option                CRYPTO          # Cryptographic framework
##option                QUOTA           # UFS quotas
##option                EXT2FS          # Second Extended Filesystem
##option                NFSCLIENT       # Network File System client
##option                NFSSERVER       # Network File System server
##option                CD9660          # ISO 9660 + Rock Ridge file system
##option                INET6           # IPv6
##option                IPSEC           # IPsec

あっ、pf を消すの忘れていたわ。で、待つ事2時間。見事にエラー

cc ...  /usr/src/sys/conf/swapgeneric.c
ld -T ld.script -X --warn-common -nopie -o bsd ${SYSTEM_HEAD} vers.o ${OBJS}
ld: error: undefined symbol: usb_insert_transfer
>>> referenced by xhci.c:1203 (/usr/src/sys/dev/usb/xhci.c:1203)
>>>               xhci.o:(xhci_root_ctrl_transfer)

ああ、削りすぎたな。やり直し。

ld -T ld.script -X --warn-common -nopie -o bsd ${SYSTEM_HEAD} vers.o ${OBJS}
text    data    bss     dec     hex
2861024 87360   662528  3610912 371920
mv bsd bsd.gdb
ctfstrip -S -o bsd bsd.gdb
  101m30.36s real    62m29.39s user    32m37.31s system
riscv# ls *.o | wc
     495     495    5391

今度は上手くコンパイル出来た。

debug環境の用意

次は、ゲストOS内にあるbsd.gdbってか、その元を辿って /usr/obj/sys/... 以下のチェーンをホストOS側であるArchLinuxへ転送だな。で、はたと困った事に気付いてしまった。

ホストもゲストもIPアドレスが、10.0.2.15/24 になってる事。おまけにNAT構成。ゲスト側でtar玉にまとめたものを外に送る事になるんだけど。普通にscp -Pxxxxx obj.tgz 10.0.2.15: は、自分の所に反射してしまうな。

素敵な解決方法が見付からないので、全くよそのホストに転送。そしてArchLinux側から、それを取りに行く事にした。全く美しくない方法とは思っていますよ。

次は、ホスト側にてtar玉を展開。root:locateって持主になってたので、sakaeの持物に変更したよ。

次はQEMUとgdbが簡単に連携出来るように設定。

[sakae@arch ~]$ cat .gdbinit
add-auto-load-safe-path /usr/obj/sys/arch/riscv64/compile/SEEING/.gdbinit
[sakae@arch ~]$ cat /usr/obj/sys/arch/riscv64/compile/SEEING/.gdbinit
target remote :1234

クロス用のgdb名前が異様に長いので、短縮名をリンクで作った。

[sakae@arch ~]$ cd /usr/obj/sys/arch/riscv64/compile/SEEING/
[sakae@arch SEEING]$ ls -l rgdb
lrwxrwxrwx 1 sakae sakae 24 Nov 19 07:46 rgdb -> /usr/bin/riscv64-elf-gdb

ゲスト内では、出来上がっているbsdを / にコピー。もしもの為に先住のbsdはバックアップしとく。こんな感じ。

riscv# ls -l /bsd*
-rwx------  2 root  wobj    3508388 Nov 18 16:31 /bsd
-rwx------  2 root  wobj    3508388 Nov 18 16:31 /bsd.booted
-rwx------  1 root  wheel   7967328 Nov 18 14:48 /bsd.org
-rw-------  1 root  wheel  11768602 Nov  7 05:58 /bsd.rd
-rw-------  1 root  wheel   7909309 Nov  7 05:58 /bsd.sp

サイズ的に半分以下になってますな。

取り敢えずrun

Booting /efi\boot\bootriscv64.efi
disks: sd0*
>> OpenBSD/riscv64 BOOTRISCV64 1.4
boot>
booting sd0a:/bsd: 2273376+596872+78136+662528 [160003+122+238776+150892]=0x73b4a8
bootargs:
all mapped
type 0x4 pa 0x80000000 va 0x80000000 pages 0x20 attr 0x8
type 0x7 pa 0x80020000 va 0x80020000 pages 0x1e0 attr 0x8
type 0x2 pa 0x80200000 va 0x80200000 pages 0x4000 attr 0x8
 :
type 0x2 pa 0x9ff61000 va 0x9ff61000 pages 0x9f attr 0x8
[ using 550768 bytes of bsd ELF symbol table ]
Copyright (c) 1982, 1986, 1989, 1991, 1993
        The Regents of the University of California.  All rights reserved.
Copyright (c) 1995-2022 OpenBSD. All rights reserved.  https://www.OpenBSD.org
  :
Automatic boot in progress: starting file system checks.
/dev/sd0a (781d4a4515da21ce.a): file system is clean; not checking
/dev/sd0e (781d4a4515da21ce.e): file system is clean; not checking
/dev/sd0d (781d4a4515da21ce.d): file system is clean; not checking
pfctl: /dev/pf: Device not configured
pfctl: /dev/pf: Device not configured
kern.global_ptrace: 0 -> 1
starting network   
sysctl: net.inet6.ip6.soiikey: Protocol nsot available
pfctl: /dev/pf: Device not configured
  :
riscv# uname -a
OpenBSD riscv.my.domain 7.2 SEEING#3 riscv64

configで切ったpfとかが、not configured ってなってる。それから、今のカーネルは、SEEING#3 って事で、3回configしたななんて事が分る。

gdbと連動

既にqemuのスタートスクリプトに、-s を仕込んであるので、

Run gdb (like this): ./rgdb -i=mi bsd.gdb

Current directory is /usr/obj/sys/arch/riscv64/compile/SEEING/
GNU gdb (GDB) 12.1
 :
(gdb) bt
#0  cpu_idle_cycle () at /usr/src/sys/arch/riscv64/riscv64/machdep.c:209
#1  0xffffffc0002d1d48 in sched_idle (v=0xffffffc00083baf8 <cpu_info_primary>) \
at /usr/src/sys/kern/kern_sched.c:187
#2  0xffffffc00025fcbc in proc_trampoline ()
Backtrace stopped: frame did not save the PC
Breakpoint 2, gettime () at /usr/src/sys/kern/kern_tc.c:336
336             return time_second;     /* atomic */
(gdb) bt
#0  gettime () at /usr/src/sys/kern/kern_tc.c:336
#1  0xffffffc000284bf4 in uvm_meter () at /usr/src/sys/uvm/uvm_meter.c:90
#2  0xffffffc000361884 in schedcpu (arg=0xffffffc0008509d8 <scheduler_start.sch\
edcpu_to>) at /usr/src/sys/kern/sched_bsd.c:247
#3  0xffffffc000207374 in timeout_run (to=<optimized out>) at /usr/src/sys/kern\
/kern_timeout.c:676
#4  softclock_process_tick_timeout (to=<optimized out>, new=<optimized out>) at\
 /usr/src/sys/kern/kern_timeout.c:725
#5  0xffffffc000206198 in softclock (arg=<optimized out>) at /usr/src/sys/kern/\
kern_timeout.c:756
#6  0xffffffc0002a072c in softintr_dispatch (which=<optimized out>) at /usr/src\
/sys/arch/riscv64/riscv64/softintr.c:102
#7  0xffffffc0003a1614 in riscv_do_pending_intr (pcpl=0) at /usr/src/sys/arch/r\
iscv64/riscv64/intr.c:615
#8  0xffffffc0002ed8e0 in plic_splx (new=0) at /usr/src/sys/arch/riscv64/dev/pl\
ic.c:512
 :

sys_gettimeofday にはdateが反応しなかったので、gettimeで確認。riscv64の割込みの一端がうかがえるな。

初めから

-S を付けるとカーネルの立上りから追跡出来る。

ゲストOSを起動しておいてから、ホストOS側からrgdbを起動すればよい。

(gdb) bt
#0  0x0000000000001000 in ?? ()
(gdb) b main
Breakpoint 1 at 0xffffffc00030aac4: file /usr/src/sys/kern/init_main.c, line 170.
(gdb) c
Continuing.

Breakpoint 1, curcpu () at machine/cpu.h:140
140             __asm volatile("mv %0, tp" : "=&r"(__ci));
(gdb) bt
#0  curcpu () at machine/cpu.h:140
#1  main (framep=0xf5f4a7f6d08d5c4) at /usr/src/sys/kern/init_main.c:170
(gdb) n
main (framep=<optimized out>) at /usr/src/sys/kern/init_main.c:170
170             curproc = p = &proc0;
(gdb)
171             p->p_cpu = curcpu();

locore.S にこんなコードが有るなあ。

mv      a0, sp
call    _C_LABEL(initriscv)     /* Off we go */
call    _C_LABEL(main)

んでもって、initriscv にBP を置いてトレースしてくと、下記のようなマップの情報表示が出て来た。この locore.S と machdep.c/initriscv() が、本当の肝になるんだなあ。

(gdb)
771                     for (i = 0; i < mmap_size / mmap_desc_size; i++) {
(gdb)
773                                 desc->Type, desc->PhysicalStart,
(gdb)
774                                 desc->VirtualStart, desc->NumberOfPages,
(gdb)
775                                 desc->Attribute);
(gdb)
772                             printf("type 0x%x pa 0x%llx va 0x%llx pages 0x%llx attr 0x%llx\n",
(gdb) p/x *desc
$4 = {
  Type = 0x4,
  Pad = 0x0,
  PhysicalStart = 0x80000000,
  VirtualStart = 0x80000000,
  NumberOfPages = 0x20,
  Attribute = 0x8
}

riscv64 と 糞石の代表である amd64のアセンブラー行数の比較。

[sakae@arch riscv64]$ wc *.[sS]
  177   592  3840 copy.S
  142   446  3101 copystr.S
   43   299  1951 cpufunc_asm.S
  107   515  2930 cpuswitch.S
  245  1058  6059 exception.S
   47   282  1801 locore0.S
  338  1230  7634 locore.S
   42   195  1214 pagezero.S
   89   493  2826 support.S
 1230  5110 31356 total
[sakae@arch amd64]$ wc *.[sS]
   812   2503  19437 acpi_wakecode.S
  1187   3177  25534 aes_intel.S
   334    892   6727 copy.S
   710   2830  19385 locore0.S
  1202   3812  30345 locore.S
   200    454   4310 mds.S
   262   1012   7637 mptramp.S
   172    857   5833 spl.S
  1447   4082  51588 vector.S
   740   1942  13542 vmm_support.S
  7066  21561 184338 total

もう、圧倒的にriscvの方が学習し易いです。 今迄は解説書と言うと、糞石版しかなくて、挫折ばかりしてたけど、これからはいいぞ。

なお、同じ要領でarm64を調べると、総行数は、3266行だった。

bear riscv

裸の石を勉強するなら、下記がよい。何と言っても実例付きだからね。

RISC-V OSを作ろう (1) ~ブート処理

RISC-V OSを作ろう (2) ~ タスク切り替え

RISC-V OSを作ろう (3) ~ 割り込み

まだまだ続いているけど、ここら辺までがハードとの接点が解説されてる貴重な資料。


This year's Index

Home