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
裸の石を勉強するなら、下記がよい。何と言っても実例付きだからね。
まだまだ続いているけど、ここら辺までがハードとの接点が解説されてる貴重な資料。