NetBSD/aarch64 on ROCK 5B その4 (SPI FlashにUEFI firmware)
ここまでUEFI firmawareをmicroSDに書き込んでそこから起動していたが、ROCK 5BのmicroSDスロットは割と変なところにあってかなり飛び出す。

飛び出すだけなら許容できなくもないが、表からみると電源スイッチ(写真の赤丸)と同じところにあってかなり邪魔。コネクタを挿したら起動するので電源スイッチの出番はあまりないけど強制シャットダウンとかで出番があるので。

そういうわけで本体のSPI NOR FlashにUEFI firmwareを書き込んでmicroSDを不要にする。
手順は公式wikiに書いてあるのでだいたいその通りにする。ただしwikiには「simple method」と「advanced method」の2通りがあるが、simpleのほうはLinuxイメージが必要なためadvancedのほうが簡単。
- pkgsrc で sysutils/rkdeveloptool をインストール
- wikiのリンク先から「RK3588 loader」と「alternative bootloader based on EDK2」をダウンロード
- ファンコネクタ脇にあるボタンを押しながら電源コネクタ挿入
- NetBSDからはugen0で認識される
ugen0 at uhub1 port 1 ugen0: vendor 2207 (0x2207) product 350b (0x350b), rev 2.00/1.00, addr 1
- rkdeveloptool ld (ListDevice) を実行して次のように表示されることを確認
# rkdeveloptool ld DevNo=1 Vid=0x2207,Pid=0x350b,LocationID=200 Maskrom
- rkdeveloptool db (DownloadBoot) で loaderを読み込む
# rkdeveloptool db rk3588_spl_loader_v1.15.113.bin Downloading bootloader succeeded.
- rkdeveloptool wl (WriteLBA) でNOR Flashの先頭からUEFI firmwareを書き込む
# rkdeveloptool wl 0 rock-5b_UEFI_Release_v1.1.img Write LBA from file (100%)
firmwareを書き込んだら設定でネットワークboot系はdisableにしておく。どうやらそっちの起動がUSBより優先されてしまうらしく、そのままだとLANケーブルを繋いで電源投入すると永遠にそっち待ちになってUSBから起動してくれない。
NetBSD/aarch64 on ROCK 5B その3 (swapパーティション)
arm64.imgをddで書き込んだ場合、初回起動時になんやかんやでディスクの後ろの未使用領域を使えるようにリサイズしてくれる。くれるのはいいけどswapパーティションを確保したいので全部持っていかれてしまうのは困る。なら起動前に確保してみたという記録。
まずはddでイメージを書き込む。
# dd if=arm64.img of=/dev/rsd0 bs=512
この時のパーティションはこんな感じ。本来はディスクの末尾にあるべきセカンダリGPTテーブルが途中にあり、後ろに大量の未使用領域がある。
# gpt show sd0
start size index contents
0 1 MBR
1 1 Pri GPT header
2 4 Pri GPT table
6 32762 Unused
32768 163840 1 GPT part - EFI System
196608 2842624 2 GPT part - NetBSD FFSv1/FFSv2
3039232 2043 Unused
3041275 4 Sec GPT table
3041279 1 Sec GPT header
3041280 497076912 Unused
まずはセカンダリGPTをディスク末尾に移動する。
# gpt -H resizedisk sd0 /dev/rsd0: Moving secondary GPT header gpt: /dev/rsd0: No valid PMBR partition found
ディスクの後ろに32GBのswapパーティション領域を確保。セクタはおまじないとして2048の倍数になるようにしている。
# gpt add -b 433008640 -l netbsd-swap -s 67108864s -t swap sd0 /dev/rsd0: Partition 3 added: 49f48d32-b10e-11dc-b99b-0019d1879648 433008640 67108864
FFSの後ろの未使用領域を連結して1つのパーティションに拡張する。
# gpt resize -i 2 sd0 /dev/rsd0: Partition 2 resized: 196608 432812032
結果としてこうなった。
# gpt show sd0
start size index contents
0 1 MBR
1 1 Pri GPT header
2 4 Pri GPT table
6 32762 Unused
32768 163840 1 GPT part - EFI System
196608 432812032 2 GPT part - NetBSD FFSv1/FFSv2
433008640 67108864 3 GPT part - NetBSD swap
500117504 683 Unused
500118187 4 Sec GPT table
500118191 1 Sec GPT header
パーティションサイズが増えたので、resize_ffs(8)をかけてFFSが使えるようにする。
# resize_ffs /dev/dk10 It's required to manually run fsck on file system before you can resize it Did you run fsck on your disk (Yes/No) ? yes
これでswapパーティションを用意でき、ついでにリサイズも終わらせたので設定を合わせる。
まずはマウント。
# mount /dev/dk10 /mnt
/etc/fstab にswapと、ついでにtmpfsを追加。
--- fstab.orig 2025-04-10 23:25:23.708904937 +0900 +++ fstab 2025-05-05 17:34:50.442379134 +0900 @@ -2,6 +2,8 @@ # See /usr/share/examples/fstab/ for more examples. NAME=netbsd-root / ffs rw,noatime 1 1 NAME=EFI /boot msdos rw 1 1 +NAME=netbsd-swap none swap sw,dp 0 0 ptyfs /dev/pts ptyfs rw procfs /proc procfs rw tmpfs /var/shm tmpfs rw,-m1777,-sram%25 +tmpfs /tmp tmpfs rw,-m1777,-sram%50
/etc/rc.conf でswapを有効にするのとリサイズ系を無効化。
--- rc.conf.orig 2025-05-05 17:27:02.441061549 +0900 +++ rc.conf 2025-05-05 17:28:17.466548399 +0900 @@ -47,7 +47,6 @@ rc_configured=YES hostname=arm64 -no_swap=YES savecore=NO sshd=YES dhcpcd=YES @@ -56,10 +55,6 @@ creds_msdos=YES creds_msdos_partition=/boot certctl_init=YES -resize_gpt=YES -resize_root=YES -resize_root_flags="-p" -resize_root_postcmd="/sbin/reboot -n" mdnsd=YES devpubd=YES wscons=$(dev_exists wsdisplay0)
以上。
ところで『普通に起動して最大までリサイズしてから減らしたほうが楽では?』という気がするが、それに対する resize_ffs(8) の回答がこちら。
Does not currently support shrinking FFSv2 file systems.
というわけで「増やせるけど減らせない」から増やす前に確保せざるを得ないという次第。
NetBSD/aarch64 on ROCK 5B その2 (NVMe)
PCIe、というかNVMeについては手元になかったのでまた後日ということで。
というわけで調達して色々試してみたものの、結局 NVMeを挿すと起動中に落ちる 模様。
デバッガでは splx() とか cpu_irq() とか見えるので、割り込みの何かが変なのかも知れんけどわからん。NVMeの認識がおかしいのかと思いきやdmesgのスクロールが固まる瞬間に撮影したものをよく見たら認識自体はできているっぽい。
nvme0 at pci0 dev 0 function 0:vendor 1eeb product 1202 (rev. 0x01) nvme0: NVMe: 1.4 nvme0: interrupting at irq 292 nvme0: Apacer AS2280P4U 256GB, firmware SM20980, serial 2025022501005004 ld4 at nvme0 nsid 1 ld4: 238GB, 31130 cyl, 255 head, 63 sec.
ちなみにUSB-NVMe変換を使用すればUSBメモリ扱いになるので問題なく起動する。なら当面はこれで回避することに。
NetBSD/aarch64 on ROCK 5B その1
ROCK 5Bというなかなか面白そうなボードを買ってみた。この手のSBC (Single Board Computer) で有名なRaspberry Piと比較すると違いはざっくりこんな感じ。
- Raspberry 5より一回り大きい
- 無線デバイスなし(M.2 E keyで増設可能)
- メモリ最大32GiBのモデルがある
- 標準HDMI端子が使える
- M.2 M key端子があるのでNVMe SSDが使える
- 電源USD PD 2.0、または5〜20V
最近のRaspberry Piは電源に5.1V 3〜5Aという謎電圧大電流を要求してくるのがなんだかなーという気持ちだったので、「電源が素直」「Pi5にもないメモリ32GiB」「増設せずともNVMe SSDが使える」あたりがいい感じ。無線デバイスは今のところ予定はないし、必要ならUSBになんか挿しておけばいいので気にしてない。
で、こいつはNetBSDで動かせるのかということで軽く調べてみると:
- ソース的にはsys/arch/arm/rockchipにRK3588っぽいコードはある
- 2023年5月に "NetBSD on Rock 5B: current status and WIP bootable image" で10.99.4ベースとu-bootで起動した報告がある、ただしHDMIやPCIeなどはまだ
- edk2-rk3588というリポジトリでUEFIもあり、こちらはDisplayやPCIeも対応済み
うーむ、つまりこれ UEFIからNetBSD起動したらHDMIも使えるのでは?
というわけで実験。
- microSDカードに v1.1の
rock-5b_UEFI_Release_v1.1.imgをddで書き込む - USBメモリに10.1-RELEASE(なんとなく自前ビルドしたやつ)の
arm64.img.gzを gzcat | dd で書き込む - misroSD、USBメモリ、HDMIモニタ、秋月のUSB PDアダプタを祈りながらROCK 5Bに挿す
結果。
というわけで無事さっくりと起動。モニタもちゃんと表示されてた。PCIe、というかNVMeについては手元になかったのでまた後日ということで。
NetBSDでUSBシリアル変換IC CH340Kを動かす
秋月電子通商で¥100で買えるUSBシリアルICのCH340KがNetBSD 10.99.3でもまだこんな感じで認識してくれなかったけど数行いじったら動くようになったよ、というお話。
ugen0: QinHeng Electronics (0x1a86) USB Serial (0x7522), rev 1.10/2.64, addr 1
動機
- 認識しないのは数ヶ月前から認識してたけど最近になってふと気になったので追ってみることにした
- CH341/CH340はuchcom(4)で実装されている……あれ?
- データシートないかなとメーカーのページに行ったらLinux向けのソースコードがあったけど、ざっと見た感じでは特にCH340K(というよりProductIDが0x7522)固有の処理はなさそう
- じゃあuchcom(4)でなんで動かんの、と思ったら0x7522が対象に入ってなかった
- もしかしてuchcom(4)に0x7522を追加すれば動くのでは……?
やったこと
まずsys/dev/usb/usbdevsにProductIDが0x7522になるデバイスを追加する。なお手元のはCH340Kだけどどうも他のCH340シリーズも中のチップは同じらしく、そうすると名称はCH340Kで良いものか一瞬悩む。が、そういえばメーカー純正のLinuxドライバコード中でも /* ch340k chip */ だったので気にしないことに。
--- a/sys/dev/usb/usbdevs +++ b/sys/dev/usb/usbdevs @@ -2836,6 +2836,7 @@ product QINHENG CH341_EPP 0x5512 CH341 USB-EPP/SSP Bridge product QINHENG CH341_ASP 0x5523 CH341 USB-Serial Bridge product QINHENG CH341_UPC 0x5584 CH341 USB-Printer Bridge product QINHENG CH340 0x7523 CH340 USB-Serial Bridge +product QINHENG CH340K 0x7522 CH340K USB-Serial Bridge product QINHENG2 CH341SER 0x5523 CH341/CH340 USB-Serial Bridge /* Qtronix products */
次にusbdevs.h と usbdevs_data.h を再構築する。TOOLDIRはbuild.sh時に-Tで指定しているところ。
$ TOOLDIR=../../../../tools/amd64 make -f Makefile.usbdevs
終了するとusbdevs.hにUSB_PRODUCT_QINHENG_CH340Kという定義が生えてくる。
#define USB_PRODUCT_QINHENG_CH340K 0x7522 /* CH340K USB-Serial Bridge */
最後にuchcom(4)のデバイステーブルに追加しておしまい。
--- a/sys/dev/usb/uchcom.c +++ b/sys/dev/usb/uchcom.c @@ -164,6 +164,7 @@ static const uint32_t rates4x[8] = { static const struct usb_devno uchcom_devs[] = { { USB_VENDOR_QINHENG2, USB_PRODUCT_QINHENG2_CH341SER }, { USB_VENDOR_QINHENG, USB_PRODUCT_QINHENG_CH340 }, + { USB_VENDOR_QINHENG, USB_PRODUCT_QINHENG_CH340K }, { USB_VENDOR_QINHENG, USB_PRODUCT_QINHENG_CH341_ASP }, }; #define uchcom_lookup(v, p) usb_lookup(uchcom_devs, v, p)
結果
[ 8134.473206] uchcom0 at uhub1 port 1 [ 8134.473206] uchcom0: QinHeng Electronics (0x1a86) USB Serial (0x7522), rev 1.10/2.64, addr 1 [ 8134.483205] ucom0 at uchcom0
うむ。
このCH340KはESP32のライター(自作)に使用しているので、とりあえずESP32と繋いでみたところ起動メッセージは表示されるのでちゃんとUSBシリアルとして動作していそう。なおminicomだとCtrl-A Z からの H(Hang up) をしないと何もでてこないので不具合かと思ったがFT232RLを使用したESP32ライターでも同じ動作だったのでminicomの設定か何かの問題らしい。ちなみにcuだと問題なさそう。
おまけ
FreeBSDは2022年の1月と6月ので対応したのかな。
pharo-vm on NetBSD/amd64
Pharo 9のVMがNetBSD/amd64でも動かせるようになったのでメモ。

source
リポジトリは https://github.com/pharo-project/opensmalltalk-vm にあるが、こちらはVMMakerで自動生成されたコードは入っていない。自動生成させるにはpharo-vmが必要なので、「pharo-vmバイナリを作るのにpharo-vmのソースが必要で、pharo-vmのソースを作るのにpharo-vmバイナリが必要で……」という循環参照が発生する。
幸い自動生成されたコードも含めたものが http://files.pharo.org/vm/pharo-spur64-headless/Linux-x86_64/source/ にあるので、こっちから持ってくれば問題ない。ディレクトリ名に「headless」とあるけどcmakeのオプション指定で非headlessにできるので問題なし。
patch
とりあえず動くようにはなったけど、これで正解なのかは知らない。
"cmake/<OS>.cmake" を要求するので、とりあえず "cmake/OpenBSD.cmake" を "cmake/NetBSD.cmake" にコピー
backtrace(3) を使用しているので libexecinfo が必要になる
--- CMakeLists.txt.orig +++ CMakeLists.txt @@ -367,6 +367,7 @@ check_library_exists(dl dlopen "" HAVE_LIBDL) check_library_exists(dyld dlopen "" HAVE_DYLD) check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF) +check_library_exists(execinfo backtrace "" HAVE_EXECINFO) #Required by the UUID Plugin @@ -451,6 +452,10 @@ add_executable(${VM_EXECUTABLE_NAME} ${VM_FRONTEND_APPLICATION_TYPE} ${VM_FRONTEND_SOURCES}) addLibraryWithRPATH(${VM_LIBRARY_NAME} ${VM_SOURCES}) +if(${HAVE_EXECINFO}) + target_link_libraries(${VM_LIBRARY_NAME} execinfo) +endif() + # # LibFFI #
- uuidgen(2) を使用する(同時に libuuid の uuid_generate との衝突を回避)
- もともと uuidgen(2) を使用するコードがあったのでなるべくそちらを使うように頑張ったけど、やっぱり面倒だったので libuuid の uuid_generate に統一しても良いんじゃないかと思った次第。
--- CMakeLists.txt.orig +++ CMakeLists.txt @@ -374,6 +374,6 @@ check_include_files(uuid/uuid.h HAVE_UUID_UUID_H) check_include_files(uuid.h HAVE_UUID_H) -check_library_exists(uuid uuidgen "" HAVE_UUIDGEN) +check_function_exists(uuidgen HAVE_UUIDGEN) check_library_exists(uuid uuid_generate "" HAVE_UUID_GENERATE) --- plugins.cmake.orig +++ plugins.cmake @@ -77,7 +77,7 @@ addLibraryWithRPATH(UUIDPlugin ${UUIDPlugin_SOURCES}) if(WIN) target_link_libraries(UUIDPlugin PRIVATE "-lole32") - elseif(UNIX AND NOT OSX) + elseif(UNIX AND NOT OSX AND HAVE_UUID_GENENATE) #find_path(LIB_UUID_INCLUDE_DIR uuid.h PATH_SUFFIXES uuid) find_library(LIB_UUID_LIBRARY uuid) message(STATUS "Using uuid library:" ${LIB_UUID_LIBRARY})
- uuidgen(2) は native byte-order なので、 uuid_generate に合わせて big endian に強制する
- やっぱり uuid_generate に統一したほうが楽そう
--- plugins/UUIDPlugin/common/UUIDPlugin.c.orig +++ plugins/UUIDPlugin/common/UUIDPlugin.c @@ -41,6 +41,9 @@ #if defined(HAVE_UUIDGEN) uuidgen(&uuid, 1); + uuid.time_low = htobe32(uuid.time_low); + uuid.time_mid = htobe16(uuid.time_mid); + uuid.time_hi_and_version = htobe16(uuid.time_hi_and_version); #else uuid_generate(uuid); #endif
- clone(2) と名前が衝突したので、とりあえず sqclone へ変更
--- generated/64/vm/src/gcc3x-cointerp.c.orig +++ generated/64/vm/src/gcc3x-cointerp.c @@ -1068,7 +1068,7 @@ extern sqInt classUnsafeAlien(void); static void clearLeakMapAndMapAccessibleFreeSpace(void); static sqInt NoDbgRegParms cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning); -extern sqInt clone(sqInt objOop); +extern sqInt sqclone(sqInt objOop); extern sqInt compactClassIndexOf(sqInt objOop); static sqInt NoDbgRegParms copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i); extern void countMarkedAndUnmarkdObjects(sqInt printFlags); @@ -46834,7 +46834,7 @@ /* SpurMemoryManager>>#clone: */ sqInt -clone(sqInt objOop) +sqclone(sqInt objOop) { DECL_MAYBE_SQ_GLOBAL_STRUCT sqInt classIndex; sqInt classIndex1; @@ -54408,14 +54408,14 @@ clone1 = (((longAt(obj1)) & (classIndexMask())) == ClassMethodContextCompactIndex ? cloneContext(obj1) - : clone(obj1)); + : sqclone(obj1)); if (!(clone1)) { error("Not enough space to copy the objects in two-way become. This should have been detected before"); return; } clone2 = (((longAt(obj2)) & (classIndexMask())) == ClassMethodContextCompactIndex ? cloneContext(obj2) - : clone(obj2)); + : sqclone(obj2)); if (!(clone2)) { error("Not enough space to copy the objects in two-way become. This should have been detected before"); return; @@ -75835,7 +75835,7 @@ else { if ((GIV(argumentCount) == 0) || (!(((longAt(rcvr)) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))) == 0))) { - newCopy = clone(rcvr); + newCopy = sqclone(rcvr); } else { newCopy = 0;
--- src/debugUnix.c.orig +++ src/debugUnix.c @@ -16,6 +16,12 @@ #endif +#if __NetBSD__ + +#include <ucontext.h> + +#endif + #ifdef HAVE_EXECINFO_H # include <execinfo.h> @@ -250,6 +256,20 @@ regs->mc_edi, regs->mc_edi, regs->mc_ebp, regs->mc_esp, regs->mc_eip); return regs->mc_eip; +#elif __NetBSD__ && __x86_64__ + __greg_t *regs = &uap->uc_mcontext.__gregs; + fprintf(output, + "\trax 0x%08llx rbx 0x%08llx rcx 0x%08llx rdx 0x%08llx\n" + "\trdi 0x%08llx rsi 0x%08llx rbp 0x%08llx rsp 0x%08llx\n" + "\tr8 0x%08llx r9 0x%08llx r10 0x%08llx r11 0x%08llx\n" + "\tr12 0x%08llx r13 0x%08llx r14 0x%08llx r15 0x%08llx\n" + "\trip 0x%08llx\n", + regs[_REG_RAX], regs[_REG_RBX], regs[_REG_RCX], regs[_REG_RDX], + regs[_REG_RDI], regs[_REG_RSI], regs[_REG_RBP], regs[_REG_RSP], + regs[_REG_R8 ], regs[_REG_R9 ], regs[_REG_R10], regs[_REG_R11], + regs[_REG_R12], regs[_REG_R13], regs[_REG_R14], regs[_REG_R15], + regs[_REG_RIP]); + return (void *)regs[_REG_RIP]; #elif __linux__ && __x86_64__ greg_t *regs = uap->uc_mcontext.gregs; fprintf(output, @@ -409,6 +429,9 @@ # elif __FreeBSD__ && __i386__ void *fp = (void *)(uap ? uap->uc_mcontext.mc_ebp: 0); void *sp = (void *)(uap ? uap->uc_mcontext.mc_esp: 0); +#elif __NetBSD__ + void *fp = (void *)(uap ? _UC_MACHINE_FP(uap) : 0); + void *sp = (void *)(uap ? _UC_MACHINE_SP(uap) : 0); # elif __OpenBSD__ void *fp = (void *)(uap ? uap->sc_rbp: 0); void *sp = (void *)(uap ? uap->sc_rsp: 0);
- 起動スクリプトで LD_LIBRAY_PATHに @pkglibdir@ (後で /usr/pkg/lib に置換)を追加
--- packaging/linux/bin/launch.sh.in.orig +++ packaging/linux/bin/launch.sh.in @@ -18,8 +18,8 @@ *) PLUGINS="`pwd`/$BIN" esac -if [ $(uname -s) = "OpenBSD" ]; then - LD_LIBRARY_PATH="$PLUGINS:${LD_LIBRARY_PATH}" exec $GDB "$BIN/@VM_EXECUTABLE_NAME@" "$@" +if [ $(uname -s) = "OpenBSD" -o $(uname -s) = "NetBSD" ]; then + LD_LIBRARY_PATH="$PLUGINS:@pkglibdir@:${LD_LIBRARY_PATH}" exec $GDB "$BIN/@VM_EXECUTABLE_NAME@" "$@" fi # On some linuxes there multiple versions of the C library. If the image uses
cmake option
-DFLAVOUR=CoInterpreter: デフォルト値と同じだけどJenkinsfileが指定してたので。-DALWAYS_INTERACTIVE=1: 非headlessなVMが必要な場合に指定する。これもJenkinsfileで指定されていたので。-DGENERATE_SOURCES=OFF: ソースの自動生成を行わなくする。-DPHARO_BIN_LOCATION=${PREFIX}/lib/pharo起動スクリプトがここで指定されたバイナリを実行する。
after build
build/vm/* がバイナリとライブラリ、 build/packaging/linux/bin/pharo が起動スクリプトなので適当に回収する。
なお build/vm/pharo はJITで動く関係か paxctl -m で PaX MPROTECT を解除しておく必要がる。



