LoginSignup
1
0

More than 3 years have passed since last update.

NetBSD-9.0 on Qemu/KVM

Last updated at Posted at 2020-07-07

NetBSD-9.0 on Qemu/KVM

もう使ってる人なんていないのかしらん。久々にQemu/KVMの上で使ってみた。

ftp.jp.netbsd.org.にはNetBSD-9.0はミラーしていない

たぶん、メンテナンスをする人がいないんでしょうね。NetBSD-9.0も8.2も、ミラーできていません。しかたがないので、IIJのミラーから、amd64のisoイメージを取得しました。

最近のlibvirt + Qemu/KVMではvirtioを認識できない

この件。これはデグレードしているというわけではなく、NetBSDのvirtioドライバが新しい仕様に追随できていないだけですね (だけ?)。

sysinstが満足にパーティションを切れない。

/と/usr、/var、/homeに分けたいだけなんですけどね。手でパーティションを切りました。後で試してみたところ、UEFIではなくBIOSで仮想マシンを作ったら、ちゃんとsysinstも機能しました。使っていく上で困ることではないので、追求していません。

shutdownすると必ずpanicする

ddb.onpanic=0にしていると、shutdown -pして席を離れている間に、なぜか再起動していたりします。ddb.onpanic=1にしてみると、panicしているのは、ichsmbのdetach処理。currentや、9-STABLEでは直っていました

あとで試してみたところ、これもUEFIのときだけで、BIOSのときはpanicしないみたいです。dmesgで、attach時のメッセージを確認してみると、このif文 (cvswebは特定の行を指し示せないので、githubのミラーを使います) の結果が違うみたいです。BIOSだとLPCIB_SMB_HOSTC_HSTEN bitがセットされていて、この先でI/O空間のレジスタのマップなど、初期化処理が進みますが、UEFIではクリアされていて、ここで中断してしまいます。。detach処理では、初期化されているのが前提になっていたので、UEFIでは落ちる、と言うわけです。SeaBIOSとOVMFのハードウェア初期化処理の差、ということでしょうか。

ちなみに、Linuxでは問答無用でこのビットをセットしています。これに倣うと、

diff -u -r1.60 ichsmb.c
--- sys/dev/pci/ichsmb.c        10 Dec 2018 06:23:54 -0000      1.60
+++ sys/dev/pci/ichsmb.c        5 Jul 2020 10:41:21 -0000
@@ -169,8 +169,8 @@
        DPRINTF(("%s: conf 0x%08x\n", device_xname(sc->sc_dev), conf));

        if ((conf & LPCIB_SMB_HOSTC_HSTEN) == 0) {
-               aprint_error_dev(self, "SMBus disabled\n");
-               goto out;
+               conf |= LPCIB_SMB_HOSTC_HSTEN;
+               pci_conf_write(pa->pa_pc, pa->pa_tag, LPCIB_SMB_HOSTC, conf);
        }

        /* Map I/O space */

という解決もありますが、この先のI2Cバスに何かつながっている、というわけでもなさそうなので、あまり意味はありません。

QEMU USB Tabletが使えない

これは長くなったので別記事に。

UEFIのとき、boot -dでハングアップする

USB Tabletのデバッグ中に見つけたこの問題です。ハングアップというか、どうやらboot processorのCPU利用率が100%に張り付きます。似たような起動オプションに、-cというのがあり、これはuserconfのプロンプトを呼び出すものですが、こちらは正しく動作するようです。

DDBが使えませんので、Qemuのgdbserverの機能でも使ってみましょうか。ホスト側にクロス開発用のgdbを用意してもいいのですが、BIOSでの動作も知りたかったので、もう一つBIOSのVMを作り、そこにNetBSD-9.0をインストールしてみました。BIOSでは、-dオプションをつけると正しくDDBのプロンプトが呼び出されます。そうそう、sysinstでパーティションが切れることも、shutdown時にichsmbでpanicしないことも確認できました。

さて、このBIOSのVM (デバッグ側) には、あらかじめ、先ほどのマウスドライバデバッグ済みのカーネルをコンパイルしたディレクトリにあるnetbsd.gdbというファイルを転送しておきます。デバッグ対象のUEFIのVM (ターゲット側) は、再起動して「3」でブートプロンプトを出し、再現直前の状態にしておきましょう。

ここで、ホストでQemuのgdbserver機能を有効にします。libvirt配下の場合、Qemuのモニターコマンドはvirshを使って実行することになります。

$ virsh qemu-monitor-command --hmp VMNAME gdbserver
Waiting for gdb connection on device 'tcp::1234'

gdbserverがホストのTCPポート1234で待ち受け始めました。デバッグ側でgdbを起動します。ホストのファイアウォール (iptables) に注意。

$ gdb .../netbsd.gdb
GNU gdb (GDB) 8.3
...
(gdb) target remote HOSTIPADDRESS:1234

ターゲット側の実行は止まりますが、この状態ではまだターゲット側のNetBSDカーネルはロードされていませんので、gdbの真価は発揮できません。gdbのcコマンドでターゲット側の実行を再開させ、boot -dで現象を再現させます。

通常のユーザースペースのプログラムと同じように、gdbからCtrl-Cで実行を止めてbtでバックトレースを見たり、変数の内容を覗いたりできます。ここでわかるのは、このあたりから呼ばれるcngetc()が、cn_tabがNULLであるために0を返し、while()が無限ループになっている、ということです。この時点でまだコンソールが初期化されていないようです。

BIOSのときはこの時点でもう初期化されていたのでしょうか。デバッグ側とターゲット側をひっくり返して同じことをしようとして気づきました。UEFIのときはBIOSの時と比べると、画面が広いのです。つまり、UEFIのときはVGA (640x480/80x25) ではありません。dmesgを見ると、BIOSのときは

agp0 at pchb0autoconfiguration error: : can't find internal VGA config space
vga0 at pci0 dev 1 function 0: vendor 1b36 product 0100 (rev. 0x04)
wsdisplay0 at vga0 kbdmux 1: console (80x25, vt100 emulation), using wskbd0
wsmux1: connecting to wsdisplay0

wsmux1というのがキーボードです。一方UEFIのときは、

agp0 at pchb0autoconfiguration error: : can't find internal VGA config space
genfb0 at pci0 dev 1 function 0: Red Hat QXL Video (rev. 0x04)
genfb0: framebuffer at 0xc4000000, size 1024x768, depth 32, stride 4096
genfb0: shadow framebuffer enabled, size 3072 KB
wsdisplay0 at genfb0 kbdmux 1: console (default, vt100 emulation), using wskbd0
wsmux1: connecting to wsdisplay0
drm at genfb0 not configured

コンソールデバイスが違いました。この差がどの辺で生まれるのか、ちょっと辿ってみますと、こんなコードに行き当たります。おそらく、UEFIではこのif文が成立し、BIOSでは成立しないのでその下のvgaのコードに進むのでしょう。

ここでgenfbの時に呼ばれているx86_genfb_cnattach()は、ここにあります。ちょっと目を疑うようなコードですね。

int
x86_genfb_cnattach(void)
{
    static int ncalls = 0;
    struct rasops_info *ri = &x86_genfb_console_screen.scr_ri;
    long defattr;

    /* XXX jmcneill
     *  Defer console initialization until UVM is initialized
     */
    ++ncalls;
    if (ncalls < 3)
        return -1;

// 以下略

3回呼ばれるまでは-1を返していて、なぜかというとUVM (仮想メモリ) が初期化されていないからだ、というのです。確かに、x86_genfb_cnattach()を呼ぶconsinit()は、ここ、すなわちmain()が呼ばれる前のアーキテクチャ依存の初期化部ここ、すなわちmain()の中ここ、すなわちmain()から呼ばれるcpu_startup()の中の3回呼ばれています。boot -d、すなわちRB_KDBを処理しているのは、1回目の直後です。

#ifdef DDB
    if (boothowto & RB_KDB)
        Debugger();
#endif

試しに、このRB_KDBを処理しているif〜Debugger();を、3回目のconsinit()の後に移動してみると、無事DDBのプロンプトが拝めました。ここに移動するのが正しいのでしょうか。ちょっと他のアーキテクチャのソースコードを調べてみました。

結果、大体3通りあることがわかりました。

  1. amd64やi386と同じ、main()の前の初期化部 (多数派)
  2. main()から呼ばれるcpu_startup()の中
  3. consinit()の成功時 (m68k系)

DDBのプロンプトは、できるだけ早く拝みたいので、3がいいんじゃないかと思います。

Index: sys/arch/amd64/amd64/machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/amd64/amd64/machdep.c,v
retrieving revision 1.335
diff -u -r1.335 machdep.c
--- sys/arch/amd64/amd64/machdep.c  24 Jul 2019 16:36:47 -0000  1.335
+++ sys/arch/amd64/amd64/machdep.c  6 Jul 2020 22:04:06 -0000
@@ -1922,10 +1922,6 @@
    splraise(IPL_HIGH);
    x86_enable_intr();

-#ifdef DDB
-   if (boothowto & RB_KDB)
-       Debugger();
-#endif
 #ifdef KGDB
    kgdb_port_init();
    if (boothowto & RB_KDB) {
Index: sys/arch/i386/i386/machdep.c
===================================================================
RCS file: /cvsroot/src/sys/arch/i386/i386/machdep.c,v
retrieving revision 1.820
diff -u -r1.820 machdep.c
--- sys/arch/i386/i386/machdep.c    19 May 2019 08:46:15 -0000  1.820
+++ sys/arch/i386/i386/machdep.c    6 Jul 2020 22:04:11 -0000
@@ -1393,10 +1393,6 @@
    splraise(IPL_HIGH);
    x86_enable_intr();

-#ifdef DDB
-   if (boothowto & RB_KDB)
-       Debugger();
-#endif
 #ifdef KGDB
    kgdb_port_init();
    if (boothowto & RB_KDB) {
Index: sys/arch/x86/x86/consinit.c
===================================================================
RCS file: /cvsroot/src/sys/arch/x86/x86/consinit.c,v
retrieving revision 1.31
diff -u -r1.31 consinit.c
--- sys/arch/x86/x86/consinit.c 31 May 2019 03:10:31 -0000  1.31
+++ sys/arch/x86/x86/consinit.c 6 Jul 2020 22:04:13 -0000
@@ -37,6 +37,7 @@
 #include <sys/device.h>
 #include <sys/bus.h>
 #include <sys/cpu.h>
+#include <sys/reboot.h>
 #include <machine/bootinfo.h>
 #include <arch/x86/include/genfb_machdep.h>

@@ -224,7 +225,7 @@
        if (error)
            printf("WARNING: no console keyboard, error=%d\n",
                   error);
-       return;
+       goto doddb;
    }
 #if (NCOM > 0)
    if (!strcmp(consinfo->devname, "com")) {
@@ -235,7 +236,7 @@
        puc_cnprobe(NULL);
        rv = puc_cninit(NULL);
        if (rv == 0)
-           return;
+           goto doddb;
 #endif

        if (addr == 0)
@@ -247,7 +248,7 @@
                 COM_FREQ, COM_TYPE_NORMAL, comcnmode);
        if (rv != 0)
            panic("can't init serial console @%x", consinfo->addr);
-       return;
+       goto doddb;
    }
 #endif
 #if (NNULLCONS > 0)
@@ -259,6 +260,12 @@
    }
 #endif
    panic("invalid console device %s", consinfo->devname);
+
+doddb:
+#ifdef DDB
+   if (boothowto & RB_KDB)
+       Debugger();
+#endif
 }

 #ifdef KGDB

ほんとはgenfb_machdep.cのXXX jmcneillも直したいですね。UVMが初期化済みかどうか、簡単に知る方法はないんでしょうかね。

おしまい

うっかり匿名のQiitaに記事を書いてしまったので、メールアドレスが必要なsend-prはしにくくなってしまいました。どなたか目に留まった方報告しといてくださいな。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0