コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2024年8月10日 >>> 2024年7月28日
link もっと後

2024年8月10日

Linuxを調べる - initrdとカーネル引数

目次: Linux

今どき(?)のinitrdとカーネル引数の渡し方を知らなかったのでメモしておきます。

昔のARM LinuxですとATAGと呼ばれるブートの情報を並べたリストをメモリ領域に置いてカーネル引数やinitrdを渡して(ATAGの仕様はVincentさんのBooting ARM Linuxが詳しいです)いました。ATAGは今も使えます(Booting ARM Linuxの4a. Setup the kernel tagged list)けど主流はデバイスツリーのはずです。たぶん。ARM Linuxの場合レジスタに指定すべき情報はこんな感じだそうです。

  • r0: 0
  • r1: machine type number discovered in (3) above
  • r2: physical address of tagged list in system RAM, or physical address of device tree block (dtb) in system RAM

RISC-V Linuxの場合はカーネルのドキュメント(RISC-V Kernel Boot Requirements and Constraints)によれば、RISC-V Linuxの場合レジスタに指定すべき情報はこんな感じだそうです。

  • $a0 to contain the hartid of the current core.
  • $a1 to contain the address of the devicetree in memory.

ARM Linuxよりさらに減ってhartidとデバイスツリーのアドレスのたった2つです、シンプルですね。

デバイスツリーの例

仕様からデバイスツリーを書いても良いですが、目の前のQEMUでDebianが動作している2024年8月2日の日記参照)のですから実際に動作しているものを調べたほうが早いでしょう。

QEMUが内部で生成しているデバイスツリーのダンプ方法は以前(2024年7月23日の日記参照)紹介した通りです。initrdとappend="root=/dev/nfs"を渡してQEMUを起動し、デバイスツリーをダンプして&dtsに変換します。

QEMUの自動生成したデバイスツリーブロブ(dtb)のダンプ方法
$ qemu-system-riscv64 \
  -machine virt \
  -bios none \
  -chardev stdio,id=con,mux=on \
  -serial chardev:con \
  -smp 4 \
  -kernel fw_payload.bin \
  -initrd initramfs.cpio \
  -append "root=/dev/nfs"

(QEMUモニターにて) dumpdtb virt_initrd_cmd.dtb

$ dtc -O dts virt_initrd_cmd.dtb > virt_initrd_cmd.dts
ダンプしたデバイスツリーのコード

/* virt_initrd_cmd.dts */

/dts-v1/;

/ {

...

	chosen {
		bootargs = "root=/dev/nfs";           /* ★appendで渡したカーネル引数★ */
		linux,initrd-end = <0x84227600>;      /* ★initrd終端アドレス★ */
		linux,initrd-start = <0x84000000>;    /* ★initrd先頭アドレス★ */
		stdout-path = "/soc/serial@10000000";
		rng-seed = <0x2a0a5693 0x41742b2a 0x49faf2f1 0xaf1d27cc 0x9c06cb87 0x277e3472 0xfdf688c7 0x5029079c>;
	};

...

ルートノード以下のchosenノードにあるbootargs, linux,initrd-なんとかがappendやinitrdオプションと対応しています。

  • bootargs: appendオプションに指定したカーネル引数
  • linux,initrd-start: initrdオプションに指定したファイルをロード(QEMUがやってくれる)した領域の「先頭」アドレス
  • linux,initrd-end: initrdオプションに指定したファイルをロード(QEMUがやってくれる)した領域の「終端」アドレス

以上、今どきのLinuxに対してカーネル引数やinitrdを渡す方法がわかりました。Linuxでデバイスツリーに対応したアーキテクチャは多いですから、一度実装しておけば多数のアーキテクチャでカーネルパラメーター指定ができます。便利ですね。

欠点はブートローダーでデバイスツリーのchosenノードを変更する必要がある、すなわちブートローダーにデバイスツリーを加工する処理を実装しなければならない点です。難しくはないでしょうけど、それなりに手間が掛かるでしょう。

ちなみにlinux,initrd-startのようなlinux,で始まるプロパティの仕様はLinuxカーネルのドキュメント(The chosen node - kernel.org)に記載があります。

QEMUのappend引数なしでカーネル引数を渡す実験

先程まで見てきたbootargsやinitrd-なんとかが正しく生成できれば、QEMUのappendやinitrdオプション無しでもカーネル引数やinitrdをカーネルに渡すことができるはずです。やってみましょう。

デバイスツリーのchosenノードにbootargsを追加
/* virt_mod.dts */

/dts-v1/;

/ {

...

	chosen {
		/* ★この行を追加する★ */
		bootargs = "root=/dev/nfs nfsroot=192.168.1.1:/path/to/debian_rootfs,v3 ip=on nfsrootdebug rw";

...

以前作成したGDBスクリプト(2024年7月29日の日記参照)を使ってカーネルとデバイスツリーをロードします。

デバイスツリーとOpenSBIをロードし、レジスタを設定するGDBスクリプト(再掲)
# load_dtb_opensbi.gdb

restore virt_mod.dtb binary 0x87f00000
thread 1
set $a1=0x87f00000
thread 2
set $a1=0x87f00000
thread 3
set $a1=0x87f00000
thread 4
set $a1=0x87f00000

restore fw_payload.bin binary 0x80000000
thread 1
set $pc=0x80000000
thread 2
set $pc=0x80000000
thread 3
set $pc=0x80000000
thread 4
set $pc=0x80000000

QEMUをkernel, dtb, append引数なし、-Sオプション付き(デバッガーのアタッチ待ち)で起動します。

QEMU起動、GDBスクリプトによるカーネルとデバイスツリーのロード
$ qemu-system-riscv64 \
  -machine virt \
  -bios none \
  -nographic \
  -chardev stdio,id=con,mux=on \
  -serial chardev:con \
  -mon chardev=con,mode=readline \
  -netdev user,id=netdev0 \
  -device virtio-net-device,netdev=netdev0 \
  -m 2g \
  -smp 4 \
  -s \
  -S

$ riscv64-unknown-linux-gnu-gdb

(gdb) target remote :1234

Remote debugging using :1234
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x0000000000001000 in ?? ()

(gdb) source load_dtb_opensbi.gdb

Restoring binary file virt_mod.dtb into memory (0x87f00000 to 0x87f01cb5)
[Switching to thread 1 (Thread 1.1)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 2 (Thread 1.2)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 3 (Thread 1.3)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 4 (Thread 1.4)]
#0  0x0000000000001000 in ?? ()

Restoring binary file fw_payload.bin into memory (0x80000000 to 0x81f51608)
[Switching to thread 1 (Thread 1.1)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 2 (Thread 1.2)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 3 (Thread 1.3)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 4 (Thread 1.4)]
#0  0x0000000000001000 in ?? ()

(gdb) continue

Continuing.

最後のcontinueを実行するとQEMU側もログが出ます。例えばこんな感じです。

Debian RISC-V 64bit版の起動例
$ qemu-system-riscv64 \
  -machine virt \
  -bios none \
  -nographic \
  -chardev stdio,id=con,mux=on \
  -serial chardev:con \
  -mon chardev=con,mode=readline \
  -netdev user,id=netdev0 \
  -device virtio-net-device,netdev=netdev0 \
  -m 2g \
  -smp 4 \
  -s \
  -S

OpenSBI v1.5
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : riscv-virtio,qemo
Platform Features         : medeleg

...

Boot HART MIDELEG         : 0x0000000000001666
Boot HART MEDELEG         : 0x0000000000f0b509
[    0.000000] Linux version 6.9.0 (katsuhiro@blackbird) (riscv64-unknown-linux-gnu-gcc (GCC) 14.1.0, GNU ld (GNU Binutils) 2.42.50.20240622) #9 SMP Thu Aug  1 00:38:49 JST 2024
[    0.000000] random: crng init done
[    0.000000] Machine model: riscv-virtio,qemo

...

[    2.652369] VFS: Mounted root (nfs filesystem) on device 0:20.
[    2.661913] devtmpfs: mounted
[    2.831443] Freeing unused kernel image (initmem) memory: 2260K
[    2.833459] Run /sbin/init as init process
[    4.984017] systemd[1]: systemd 256.4-2 running in system mode (+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBCRYPTSETUP_PLUGINS +LIBFDISK +PCRE2 +PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD +BPF_FRAMEWORK -XKBCOMMON +UTMP +SYSVINIT +LIBARCHIVE)
[    4.988413] systemd[1]: Detected virtualization qemu.
[    4.989856] systemd[1]: Detected architecture riscv64.

Welcome to Debian GNU/Linux trixie/sid!

[    5.048884] systemd[1]: Hostname set to <qemu>.

...

[  OK  ] Reached target graphical.target - Graphical Interface.
         Starting systemd-update-utmp-runle…- Record Runlevel Change in UTMP...
[  OK  ] Finished systemd-update-utmp-runle…e - Record Runlevel Change in UTMP.

Debian GNU/Linux trixie/sid qemu ttyS0

qemu login: 

Debianのログインプロンプトまで表示されれば成功です。カーネル引数を変えたりinitrdのサイズが変わるたびにdtsを書き換えてdtcでコンパイルする……なんて考えると面倒くさいことこの上ないですが、QEMUや気の利いたブートローダーは自動的にデバイスツリーのchosenノードを加工してくれます。ありがたいですね。

編集者:すずき(2024/08/12 16:23)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年8月7日

Debian独自の挙動をするQEMUとbinfmt_misc

目次: Linux

前回はbinfmt_miscの使い方や動作を調べました。今回はDebian特有の挙動を見ていきたいと思います。細かいことを言えばQEMUの実装の話でありbinfmt_miscの実装ではありません。しかし二者は密接に関わっているので関連項目として取り上げます。

Debianのbinfmt_miscの設定では、インタープリターに/usr/libexec/qemu-binfmt/以下のファイルを指定します。このディレクトリには独特なファイル名(例: (arch名)-binfmt-P)のシンボリックリンクが置かれています。一例を紹介します。

(arch名)-binfmt-Pの例、RISC-V 64bit向け
$ ls -la /usr/libexec/qemu-binfmt/riscv64-binfmt-P

lrwxrwxrwx 1 root root 29 May 20 22:14 /usr/libexec/qemu-binfmt/riscv64-binfmt-P -> ../../bin/qemu-riscv64-static

この/usr/libexec/qemu-binfmt/riscv64-binfmt-Pはシンボリックリンクで、実体はQEMU(/usr/bin/qemu-riscv64-static)であることが解ると思います。引数を何も指定せずに実行すると普通のQEMUとは異なるメッセージを出力します。

引数を指定せずに実行したときのエラーメッセージ
$ /usr/libexec/qemu-binfmt/riscv64-binfmt-P

qemu: /usr/libexec/qemu-binfmt/riscv64-binfmt-P has to be run using kernel binfmt-misc subsystem

メッセージを出力しているのは見た通りQEMUです、が、オリジナルのQEMUにこのメッセージを出すコードはありません。

Debianスペシャルなエラーメッセージ

Debianのqemu-user-staticパッケージソースコードを見るとこのエラーメッセージを表示するためのパッチがありました。

DebianのQEMUパッチの一部

// qemu/debian/patches/linux-user-binfmt-P.diff

diff --git a/linux-user/main.c b/linux-user/main.c
index e44bdb17b8..587bd02db2 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -562,7 +562,7 @@ static void usage(int exitcode)
     exit(exitcode);
 }
 
-static int parse_args(int argc, char **argv)
+static int parse_args(int argc, char **argv, bool *preserve_argv0)
 {
     const char *r;
     int optind;
@@ -579,6 +579,28 @@ static int parse_args(int argc, char **argv)
         }
     }
 
+    /* HACK alert.
+     * when run as an interpreter using kernel's binfmt-misc mechanism,
+     * we have to know where are we (our own binary), where's the binary being run,
+     * and what it's argv[0] element.
+     * Only with the P interpreter flag kernel passes all 3 elements as first 3 argv[],
+     * but we can't distinguish if we were run with or without this P flag.
+     * So we register a special name with binfmt-misc system, a name which ends up
+     * in "-binfmt-P", and if our argv[0] ends up with that, we assume we were run
+     * from kernel's binfmt with P flag and our first 3 args are from kernel.
+     */
+    if (strlen(argv[0]) > sizeof("binfmt-P") &&
+        strcmp(argv[0] + strlen(argv[0]) - sizeof("binfmt-P"), "-binfmt-P") == 0) {
+        if (argc < 3) {
+            (void) fprintf(stderr, "qemu: %s has to be run using kernel binfmt-misc subsystem\n", argv[0]);    //★★エラーメッセージ★★
+            exit(EXIT_FAILURE);
+        }
+        exec_path = argv[1];
+        handle_arg_argv0(argv[2]);
+        *preserve_argv0 = true;
+        return 2;
+    }
+
     optind = 1;
     for (;;) {
         if (optind >= argc) {

コマンド名の末尾が-binfmt-Pであり、引数が3つより少ない場合にエラーを出す実装です。引数を渡すか、シンボリックリンクをリネームして実行するとエラーメッセージは出現しなくなります。

シンボリックリンクをリネームした場合の挙動
# ls -la riscv64-binfmt-P_

lrwxrwxrwx 1 root root 29 May 20 22:14 riscv64-binfmt-P_ -> ../../bin/qemu-riscv64-static

# ./riscv64-binfmt-P_

qemu: no user program specified

# ../../bin/qemu-riscv64-static

qemu: no user program specified

QEMUで実行するbinfmt_miscのバイナリタイプ設定はこんな感じで、

binfmt_miscの設定例
# less /usr/lib/binfmt.d/qemu-riscv64.conf

:qemu-riscv64:M::\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/libexec/qemu-binfmt/riscv64-binfmt-P:OCPF

# ★FlagsはO, C, P, Fの4つ★

実装のコメントを見る限り、下記の条件を満たすことを期待しています。

  • シンボリックリンクの名前が必ず-binfmt-Pで終わる
  • P flagを指定するので必ず引数が3つ以上ある

この条件から外れる場合はエラーにしたいようです。riscv64-binfmt-Pのリンク先の実体はQEMUですが、間違ってQEMUとして起動することを防いでいる……?そこまでする理由があまり思いつかないですけど、昔に何かあったのかな?

編集者:すずき(2024/08/09 22:52)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年8月6日

他アーキテクチャ向けバイナリを実行する仕組みbinfmt_misc

目次: Linux

RISC-V 64bit用の実行ファイルをx86_64 Linuxマシン上のシェルから直接実行できる不思議な仕組み、Linuxのbinfmt_miscについて調べます。

ざっくりbinfmt_miscを説明するなら、バイナリタイプ(binary type)の先頭部分があるパターンに一致したら、設定で指定したインタープリタを起動してバイナリを渡し実行してもらう仕組みです。スクリプト言語で書かれたプログラムは直接実行できず、インタープリタ経由で実行する仕組みと似ています。今回のように他アーキテクチャのバイナリを実行する(インタープリタ: QEMU)以外に、Javaのアーカイブ*.jarを実行する(インタープリタ: Java仮想マシン)用途にも使えます。

今回はbinfmt_miscそのものの詳細な仕組みは調べず(いずれ調べたいとは思います)、binfmt_miscの設定方法、使い方を紹介します。

binfmt_miscの登録方法

Linuxの疑似ファイルシステムの一種としてbinfmt_miscは実装されています。カーネル組み込み、モジュールいずれも可能です。Debian Testingではモジュールとしてビルドされているようです。

binfmt_miscを使用する準備
#### binfmt_miscがモジュールかつロードされていない場合

# modprobe binfmt_misc


#### binfmt_miscがマウントされていない場合

# mount -t binfmt_misc none /proc/sys/fs/binfmt_misc/

バイナリタイプの登録方法はとても簡単で/proc/sys/fs/binfmt_misc/registerに設定をechoやcatで書き込むだけです。正常に登録されるとbinfmt_misc/ディレクトリの下にファイルが増えます。

バイナリタイプの確認、登録方法
#### バイナリタイプの確認、登録方法

# ls /proc/sys/fs/binfmt_misc/

register  status

# cat /usr/lib/binfmt.d/qemu-riscv64.conf > /proc/sys/fs/binfmt_misc/register

# ls /proc/sys/fs/binfmt_misc/

qemu-riscv64  register  status

# cat /proc/sys/fs/binfmt_misc/qemu-riscv64

enabled
interpreter /usr/libexec/qemu-binfmt/riscv64-binfmt-P
flags: POCF
offset 0
magic 7f454c460201010000000000000000000200f300
mask ffffffffffffff00fffffffffffffffffeffffff

試しにRISC-V 64bit用の設定をregisterに書き込むと、qemu-riscv64というファイルが増えます。ディレクトリ内のファイルを読み出すと登録した設定がわかります。statusファイルは読みだすとbinfmt_miscがenableかdisableかがわかります。書き込むと下記の効果を発揮します。

  • echo -1 > status: 全てのバイナリタイプ設定を消去します
  • echo 0 > status: binfmt_miscを無効にします
  • echo 1 > status: binfmt_miscを有効にします

詳細についてはLinuxカーネルのドキュメント(Kernel Support for miscellaneous Binary Formats (binfmt_misc) - The Linux Kernel documentation)を参照ください。

binfmt_miscの設定

設定はテキスト1行です。例えばRISC-V 64bitバイナリをQEMU上で実行するための設定は下記のとおりです。

RISC-V 64bitバイナリをQEMU上で実行する設定
# less /usr/lib/binfmt.d/qemu-riscv64.conf

:qemu-riscv64:M::\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\xf3\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/libexec/qemu-binfmt/riscv64-binfmt-P:OCPF

設定の意味はLinuxカーネルのドキュメント(Kernel Support for miscellaneous Binary Formats (binfmt_misc) - The Linux Kernel documentation)に載っています。

私がハマったポイントは最後にある4つのOCPFフラグ指定の1つ、F(fix binary)フラグです。Fフラグが指定されると、新たにバイナリタイプを登録した後にインタープリターのバイナリを削除したり書き換えても動作が変化しないです。

Fフラグを指定したバイナリタイプに対して、インタープリタープログラムを書き換えて動作の変化を見るときはバイナリタイプを登録し直さなければなりません。systemd-binfmt.serviceを再起動(= バイナリタイプを全て登録し直す)すると一番ラクかと思います。

binfmt_miscの動作

インタープリタがbinfmt_miscから受け取る引数を見るため、/usr/bin/qemu-riscv64-staticを下記のプログラムで置き換えます。

引数を全部表示するプログラム

// a.c

#include <stdio.h>

int main(int argc, char *argv[])
{
	for (int i = 0; i < argc; i++) {
		printf("%d: '%s'\n", i, argv[i]);
	}

	return 0;
}

プログラムを置き換えた後はsystemd-binfmt.serviceの再起動をお忘れなく。

インタープリターに渡される引数の例
# sysctl restart systemd-binfmt.service

$ export PATH=$PATH:/usr/lib/arch-test
$ riscv64 a b c


#### Pフラグあり

$ riscv64 a b c

0: '/usr/libexec/qemu-binfmt/riscv64-binfmt-P'
1: '/usr/lib/arch-test/riscv64'
2: 'riscv64'
3: 'a'
4: 'b'
5: 'c'

#### Pフラグなし

$ riscv64 a b c

0: '/usr/libexec/qemu-binfmt/riscv64-binfmt-P'
1: '/usr/lib/arch-test/riscv64'
2: 'a'
3: 'b'
4: 'c'

インタープリタープログラムの引数に、実行するバイナリのフルパス+バイナリ実行用の引数が渡されるシンプルな仕組みです。パスが2つ渡されているように見えるのは、P(preserve-argv[0])フラグの影響です。Pフラグを外せばフルパス1つだけになります。詳しい仕様はドキュメントをご覧ください。

話は少し変わりますが、DebianのQEMUパッケージは特殊なパッチがあたっていて、本家QEMUと少し違う動きをします。次回はその辺りを調べます。

編集者:すずき(2024/08/09 22:17)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年8月5日

debootstrapと他アーキテクチャバイナリとbinfmt_misc

目次: Linux

以前、Debianのrootfsを手軽に作成できるツールdebootstrapを使いました(2024年8月2日の日記参照)が、1つ気になった点があります。アーキテクチャが異なっていても作成可能な(例: x86_64上でRISC-V向けのDebian rootfsを作成できる)点です。

他アーキテクチャ向けバイナリをdebootstrapの内部で実行しているように見えます。qemu-user-staticを使ってエミュレーションするようですが、debootstrapとqemu-user-staticはどうやって連携をとるのでしょう?

他アーキテクチャバイナリの実行可否

まずQEMUの有無、というよりdebootstrapが他アーキテクチャバイナリの実行可否を確認している部分を見ます。コードは/usr/bin/debootstrapと/usr/share/debootstrap/*に分かれて存在しており、qemu-user-staticがインストールされていないときの「Unable to execute target architecture」エラーは下記の部分で出力しています。

debootstrapが他アーキテクチャ向けバイナリを実行できるか確認するコード

# /usr/bin/debootstrap

if [ -x /usr/bin/arch-test ] && am_doing_phase second_stage; then
	if doing_variant fakechroot; then
		ret=0; arch-test "$ARCH" || ret=$?        ★arch-testを使って確認している★
	# Avoid failure with old arch-test package
	elif arch-test --version > /dev/null 2>&1; then
		ret=0; arch-test -c "$TARGET" "$ARCH" || ret=$?
	else
		ret=3
	fi

	case $ret in
	0)      info ARCHEXEC "Target architecture can be executed" ;;
	1)      error 1 ARCHNOTEXEC "Unable to execute target architecture" ;;    ★この行がエラーメッセージ★
	*)      info ARCHEXECUNKNOWN "Can't verify that target arch works" ;;
	esac
fi

判別方法に使うarch-testはシェルスクリプトで、引数にて指定されたアーキテクチャのバイナリが実行できるかどうかを見ています。arch-testが実行するバイナリは/usr/lib/arch-testの下にあり、実行バイナリファイル名はアーキテクチャ名と同じです。

例えばRISC-V 64bit向けを調べるときはarch-test riscv64と実行します。arch-testは/usr/lib/arch-test/riscv64つまりRISC-V 64bitの実行バイナリを実行し、正常に実行できれば成功ステータス、実行できなければ失敗ステータスを返す仕組みです。

シェルから/usr/lib/arch-test/riscv64を直接実行するとokとだけ表示されるはずです。不思議ですよね?なぜx86_64 Linuxマシン上でRISC-V 64bit Linux用のバイナリが実行できるのでしょう?

秘密はLinuxのbinfmt_miscにあります。次回以降はbinfmt_miscを調べます。

編集者:すずき(2024/08/11 23:27)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年8月4日

Amazonのマイリストの不思議な挙動

目次: Kindle

いつからかわかりませんがAmazonのマイリスト(ほしい物リスト)の挙動が変わっていました。以前は未購入フィルターで絞り込みすればKindleで購入した本は出現しませんでした。


未購入で絞り込みしたリスト

今は仕様が変わったのかバグなのか何だか知りませんが、購入済みの本でもお構いなしに出てきます。例えば上記「未購入」で絞り込みして表示される「宝くじで40億当たった〜 16巻」を見ます。


未購入のリストから選んだのに購入済み

購入済みです!とデカデカと表示されます。Amazonの「未購入」とは一体どういう意味なんでしょう……??

Amazonカスタマーサポート

ちなみにTwitter(現X)でこういうことを呟くとAmazonの公式アカウントがすっ飛んできて「カスタマーサポートに連絡せよ」と言ってきます。

カスタマーサポートは返品対応のような実務は迅速で助かるんですけど、不具合の原因つまり「なぜそうなったのか?」についてはほぼ全く教えてくれません(2021年8月10日の日記参照)から、今回みたいな現象だとあまり当てにならんのではないかと予想してます。一応連絡してみるつもりですけども……。

編集者:すずき(2024/08/05 21:36)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年8月2日

Debian on RISC-V

目次: Linux

OpenSBI + Linuxの環境まで動いたので、次はLinuxのディストリビューションに挑みます。ディストリビューションはDebianにします。以前はUnofficialでしたがDebian 13(Trixie)から正式にRISC-Vをサポートする(riscv64 is now an official architecture - debian-riscv)そうです。良いですね。

DebianのドキュメントにStarFive VisionFive V1用のインストール手順があるInstalling Debian On StarFive VisionFive V1 - Debian Wiki)ので、非常に参考になります。

RISC-Vサポート予定とされているDebian 13(Trixie)は現時点だとTesting版のはずですが、RISC-V向けのパッケージが存在しないものがあって(※)動かなさそうだったので、今回はUnstable版(sid)を使用しています。

  • NFS Server: Debian Testing (Trixie)
  • NFS: nfs-common 2.6.4-5
  • QEMU: 9.0.2
  • RISC-V Linux: 6.9.0

今回の実験環境はこんな感じです。

(※)例えばlibcbor0.10はRISC-V版が存在しません。openssh-client -> libfido2-1 -> libcbor0.10の依存を持つ、OpenSSHクライアントがインストールできません。

Debianルートファイルシステムの作成

Debianを動作させるにはDebianのルートファイルシステムが必要です。debootstrapというコマンドで作成できて超便利です。

debootstrapによるRISC-V Debian rootfsの構築
$ apt-get install qemu-user-static binfmt-support debootstrap systemd-container rsync wget

$ sudo debootstrap --arch=riscv64 unstable /path/to/riscv-chroot https://deb.debian.org/debian

I: Target architecture can be executed
I: Retrieving InRelease
I: Checking Release signature
I: Valid Release signature (key id 4CB50190207B4758A3F73A796ED0E7B82643E131)
I: Retrieving Packages
I: Validating Packages
I: Resolving dependencies of required packages...
I: Resolving dependencies of base packages...
I: Checking component main on https://deb.debian.org/debian...
I: Retrieving adduser 3.137
I: Validating adduser 3.137
I: Retrieving apt 2.9.7
I: Validating apt 2.9.7
...
I: Configuring tasksel-data...
I: Configuring libc-bin...
I: Configuring ca-certificates...
I: Base system installed successfully.

このとき依存パッケージ、特にQEMU(qemu-user-static)をインストールし忘れていると、E: Unable to execute target architectureとメッセージが出て止まります。

実行

作成したDebian rootfsをQEMUからNFSでマウントします。サーバー側の設定例はこんな感じです。insecureを指定しているなど、セキュリティ完全無視の設定なので実験が終わったら設定を消してください。

NFSの設定

#### /etc/exports

/path/to/debian_rootfs    *(rw,no_root_squash,no_subtree_check,insecure)

設定を反映するにはexportfsコマンドを使います。何も出力されませんが、エラーがでなければ良いです。

NFSの設定反映
# apt-get install nfs-kernel-server nfs-common

# exportfs -a

手動でマウントする場合はこんな感じです。

NFSマウントの例
# sudo mount -t nfs -o nolock 192.168.1.1:/path/to/debian_rootfs rootfs

オプションnolockを付けている理由は、portmapやnfslockのようなデーモンなしでもマウントできるようにするためです。busyboxからマウントするときは便利です。

NFS rootfsでの動作確認

QEMUの起動引数はたくさんありますがまずはネットワークデバイスの設定をします。netdevでQEMUの外とのネットワーク接続を定義し、deviceでQEMUの中のネットワークデバイスを定義します。カーネル引数はappendに指定します。rootfsをNFSでマウントするためのオプションは、下記の3つです。

  • root=...: rootfsをNFSでマウントする
  • nfsroot=...,v3: NFSでマウントするサーバーとディレクトリの指定、NFS v3を使う
  • ip=...: IPを自動設定

NFS v4でも動きそうに思うんですけど、私の環境はなぜかNFS v3じゃないと動作しませんでした。これに気づくまでかなり時間を浪費しました……。

QEMU上でRISC-V Debian GNU/Linuxが起動する様子
$ qemu-system-riscv64 \
  -machine virt \
  -bios none \
  -nographic \
  -chardev stdio,id=con,mux=on \
  -serial chardev:con \
  -mon chardev=con,mode=readline \
  -netdev user,id=netdev0 \
  -device virtio-net-device,netdev=netdev0 \
  -kernel ../../opensbi/build/platform/generic/firmware/fw_payload.bin \
  -append "root=/dev/nfs nfsroot=192.168.1.1:/path/to/debian_rootfs,v3 ip=on nfsrootdebug rw" \
  -m 2g \
  -smp 4 \
  -s \

...

[    2.312617] ALSA device list:
[    2.313060]   No soundcards found.
[    2.534757] VFS: Mounted root (nfs filesystem) readonly on device 0:20.
[    2.541726] devtmpfs: mounted
[    2.708101] Freeing unused kernel image (initmem) memory: 2260K
[    2.710000] Run /sbin/init as init process
[    4.808617] systemd[1]: systemd 256.4-2 running in system mode (+PAM +AUDIT +SELINUX +APPARMOR +IMA +SMACK +SECCOMP +GCRYPT -GNUTLS +OPENSSL +ACL +BLKID +CURL +ELFUTILS +FIDO2 +IDN2 -IDN +IPTC +KMOD +LIBCRYPTSETUP +LIBCRYPTSETUP_PLUGINS +LIBFDISK +PCRE2 +PWQUALITY +P11KIT +QRENCODE +TPM2 +BZIP2 +LZ4 +XZ +ZLIB +ZSTD +BPF_FRAMEWORK -XKBCOMMON +UTMP +SYSVINIT +LIBARCHIVE)
[    4.811613] systemd[1]: Detected virtualization qemu.
[    4.812465] systemd[1]: Detected architecture riscv64.

Welcome to Debian GNU/Linux trixie/sid!

[    4.857724] systemd[1]: Hostname set to <qemu>.

...

Debian GNU/Linux trixie/sid qemu ttyS0

qemu login: 

うまくいけばログイン画面が拝めるはずです。

ログインユーザーを作成する方法が良くわからなかったため、Debianのrootfsを別のinitramfsからマウントして無理やりuseraddしてユーザーを追加しました。本来どうするものなんでしょうね……?

トラブルシューティング

自分でQEMUをビルドしていると、netdev user,...に対してnetwork backend 'user' is not compiledエラーが出る場合があります。その時はQEMUビルド時のconfigureのログを確認してください。

network backend〜エラー、configureのログの例
$ ./qemu-system-riscv64 -netdev user,id=a

qemu-system-riscv64: -netdev user,id=a: network backend 'user' is not compiled into this binary


#### configureのログ(NGパターン)

  Network backends
    AF_XDP support                               : NO
    slirp support                                : NO    ## ★slirpが使えない★
    vde support                                  : NO
    netmap support                               : NO


#### configureのログ(OKパターン)

  Network backends
    AF_XDP support                               : NO
    slirp support                                : YES 4.8.0    ## ★slirpが使える★
    vde support                                  : NO
    netmap support                               : NO

QEMUはnetdev user,...の実装にslirpを使っているようで、slirp supportがNOになっているとnetdev user,...が機能しません。libslirp-devをインストール(apt-get install libslirp-dev)してから、QEMUのconfigureと再ビルドをしてください。

編集者:すずき(2024/08/09 02:59)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



2024年7月29日

OpenSBIを調べる - デバイスツリーの扱い(実機想定)

目次: Linux

OpenSBIのブート部分を調べます。OpenSBIはいくつか動作モードがあるのですが、payloadを持ったタイプを調べます。対象のファイル名はbuild/platform/generic/firmware/fw_payload.binもしくは.elfです。

今回はデバイスツリーについて調べます。対象はOpenSBI 1.5です。

QEMUの場合は、QEMU起動時にデバイスツリーがロードされ、ブートコードによってレジスタa1にアドレス(-m 128Mの場合0x87e00000、メモリ量オプションによって変わる)が設定されます。実機はこのような動作はしないので、メモリがまっさらな状態からOpenSBIとデバイスツリーをどう配置すれば動くか、その方法についても調べます。

QEMUの動作を復習

QEMU向けの場合はfw_platform_init()がレジスタa1を参照しますので、ブート時にa1の設定が必要です。

OpenSBIのブート部分

//opensbi/firmware/fw_base.S

_bss_zero:
	REG_S	zero, (s4)
	add	s4, s4, __SIZEOF_POINTER__
	blt	s4, s5, _bss_zero

	/* Setup temporary trap handler */
	lla	s4, _start_hang
	csrw	CSR_MTVEC, s4

	/* Setup temporary stack */
	lla	s4, _fw_end
	li	s5, (SBI_SCRATCH_SIZE * 2)
	add	sp, s4, s5

	/* Allow main firmware to save info */
	MOV_5R	s0, a0, s1, a1, s2, a2, s3, a3, s4, a4
	call	fw_save_info
	MOV_5R	a0, s0, a1, s1, a2, s2, a3, s3, a4, s4

#ifdef FW_FDT_PATH
	/* Override previous arg1 */
	lla	a1, fw_fdt_bin
#endif

	/*
	 * Initialize platform
	 * Note: The a0 to a4 registers passed to the
	 * firmware are parameters to this function.
	 */
	MOV_5R	s0, a0, s1, a1, s2, a2, s3, a3, s4, a4
	call	fw_platform_init    /* ★この関数がa1を参照する★ */
	add	t0, a0, zero
	MOV_5R	a0, s0, a1, s1, a2, s2, a3, s3, a4, s4
	add	a1, t0, zero


//opensbi/platform/generic/platform.c

/*
 * The fw_platform_init() function is called very early on the boot HART
 * OpenSBI reference firmwares so that platform specific code get chance
 * to update "platform" instance before it is used.
 *
 * The arguments passed to fw_platform_init() function are boot time state
 * of A0 to A4 register. The "arg0" will be boot HART id and "arg1" will
 * be address of FDT passed by previous booting stage.
 *
 * The return value of fw_platform_init() function is the FDT location. If
 * FDT is unchanged (or FDT is modified in-place) then fw_platform_init()
 * can always return the original FDT location (i.e. 'arg1') unmodified.
 */
unsigned long fw_platform_init(unsigned long arg0, unsigned long arg1,
				unsigned long arg2, unsigned long arg3,
				unsigned long arg4)
{
	const char *model;
	void *fdt = (void *)arg1;    //★ここでレジスタa1の値を使っている★
	u32 hartid, hart_count = 0;
	int rc, root_offset, cpus_offset, cpu_offset, len;

...

関数fw_platform_init()は_bss_zeroの後に呼ばれます。fw_next_arg1()を呼び出す前なのでFW_PAYLOAD_FDT_ADDRやFW_PAYLOAD_FDT_OFFSETを定義しても参照されません。

もしQEMUで既存のデバイスツリーを無視して、手動でロードする実験をしたい場合は、

  • OpenSBIのバイナリをロードする
  • pcをOpenSBIのロードアドレス兼エントリアドレス(0x80000000)に設定する
  • デバイスツリーをロードする
  • a1をデバイスツリーのアドレスに設定する
  • 上記をすべてのhartに対して実行する

このような設定をすると実験できます。コマンドを手動で実行するのは辛いので、スクリプトファイルを用意してsource (スクリプトファイル)コマンドで実行したほうが楽でしょう。

デバイスツリーとOpenSBIをロードし、レジスタを設定するGDBスクリプト
# load_dtb_opensbi.gdb

restore virt.dtb binary 0x87f00000
thread 1
set $a1=0x87f00000
thread 2
set $a1=0x87f00000
thread 3
set $a1=0x87f00000
thread 4
set $a1=0x87f00000

restore build/platform/generic/firmware/fw_payload.bin binary 0x80000000
thread 1
set $pc=0x80000000
thread 2
set $pc=0x80000000
thread 3
set $pc=0x80000000
thread 4
set $pc=0x80000000

デバイスツリーを0x87f00000にロードして実行する例です。restoreコマンドでロードしているデバイスツリーのバイナリをQEMUから得る方法は前回(2024年月日の日記参照)の説明をご参照ください。

QEMUが起動時に用意するデバイスツリーとは異なるデバイスツリーをロードできることを確認するため、QEMUのデバイスツリーのmodelを少し書き換えたデバイスツリーvirt_mod.dtbを用意します。

model名だけ変更したデバイスツリー

/* virt.dts */

/dts-v1/;

/ {
	#address-cells = <0x02>;
	#size-cells = <0x02>;
	compatible = "riscv-virtio";
	model = "riscv-virtio,qemo";    /* ★★qemu → qemoに変更★★ */

DTSからDTBにするとき(その逆もできます)はデバイスツリーコンパイラdtcを使用します。

デバイスツリーのコンパイル
$ dtc -O dtb virt.dts > virt.dtb

QEMUを起動した後にデバッガーでQEMUにアタッチし、先ほど作成したGDBスクリプトにてロードとレジスタの設定を行います。

デバッガーの操作ログ
$ riscv64-unknown-linux-gnu-gdb

(gdb) target remote :1234

Remote debugging using :1234
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x0000000000001000 in ?? ()

(gdb) source load_dtb_opensbi.gdb

Restoring binary file ../qemu/build/virt_mod.dtb into memory (0x87f00000 to 0x87f01c20)
[Switching to thread 1 (Thread 1.1)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 2 (Thread 1.2)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 3 (Thread 1.3)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 4 (Thread 1.4)]
#0  0x0000000000001000 in ?? ()
Restoring binary file build/platform/generic/firmware/fw_payload.bin into memory (0x80000000 to 0x81f51608)
[Switching to thread 1 (Thread 1.1)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 2 (Thread 1.2)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 3 (Thread 1.3)]
#0  0x0000000000001000 in ?? ()
[Switching to thread 4 (Thread 1.4)]
#0  0x0000000000001000 in ?? ()

(gdb) continue

Continuing.

最後のcontinueを実行するとQEMU側もログが出ます。例えばこんな感じです。

先ほどロードしたOpenSBIのログ

$ ./qemu-system-riscv64 
  -machine virt \
  -bios none \
  -nographic \
  -chardev stdio,id=con,mux=on \
  -serial chardev:con \
  -mon chardev=con,mode=readline \
  -smp 4 \
  -s \
  -S

OpenSBI v1.5
   ____                    _____ ____ _____
  / __ \                  / ____|  _ \_   _|
 | |  | |_ __   ___ _ __ | (___ | |_) || |
 | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
 | |__| | |_) |  __/ | | |____) | |_) || |_
  \____/| .__/ \___|_| |_|_____/|____/_____|
        | |
        |_|

Platform Name             : riscv-virtio,qemo    ★★改造したmodel名になっている★★
Platform Features         : medeleg
Platform HART Count       : 4

...

OpenSBIがPlatform Nameを出しているログを見れば、先程デバイスツリーを改造したときに付けたmodel名が出力されているはずです。メモリ上にデバイスツリーやOpenSBIがロードされない実機だとしても、デバッガー経由でロードして起動することができそうです。

編集者:すずき(2024/08/09 12:18)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2024年8月10日 >>> 2024年7月28日
link もっと後

管理用メニュー

link 記事を新規作成

<2024>
<<<08>>>
----123
45678910
11121314151617
18192021222324
25262728293031

最近のコメント5件

  • link 14年6月13日
    2048playerさん (09/08 17:30)
    「私も2048の最高スコアを求めたのですが...」
  • link 14年6月13日
    2048さん (09/08 17:16)
    「私も2048の最高スコアを求めたのですが...」
  • link 14年6月13日
    2048playerさん (09/08 16:10)
    「私も2048の最高スコアを求めたのですが...」
  • link 02年8月4日
    lxbfYeaaさん (07/12 10:11)
    「555」
  • link 24年6月17日
    すずきさん (06/23 00:12)
    「ありがとうございます。バルコニーではない...」

最近の記事3件

  • link 24年8月31日
    すずき (09/01 15:01)
    「[Microsoftマウスが壊れた] 3年前くらいに購入した(2021年3月6日の日記参照)Microsoft Basic O...」
  • link 23年5月15日
    すずき (09/01 15:00)
    「[車 - まとめリンク] 目次: 車三菱FTOの話。群馬県へのドライブ将来車を買い替えるとしたら?FTOのオイル交換とオイル漏...」
  • link 24年8月25日
    すずき (09/01 14:59)
    「[レガシィの7回目の車検完了] 目次: 車ディーラーまで車検の車を取りに行きました。外は非常に暑くて辛いです…&...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www.katsuster.net
RDFファイル RSS 1.0

最終更新: 09/08 17:30