FreeBSD-10 install image for Baytrail-M NUC(DN2820FYKH)

Intel Baytrail-M NUC(DN2820FYKH) causes kernel panic due to its broken ACPI table:
http://www.freebsd.org/cgi/query-pr.cgi?pr=187966

This install image includes patched kernel for the machine, you are able to install FreeBSD-10 on the machine without any kernel rebuilding work.

http://people.freebsd.org/~syuu/dn2820fykh_freebsd10/

Baytrail-M NUC(DN2820FYKH)でFreeBSDが起動しなかった話・解決編

これの続き。

Linuxでは僕の書いたpatchと同じ感じでreservedが来たらtriggerならlevel、polarityならlowを返すようにしています。
http://lxr.linux.no/linux+v3.13.5/arch/x86/kernel/acpi/boot.c#L1094

ですので、このパッチをマージして貰うという事で良いのではないかという気がしてきたので、PRを送ってみました:
amd64/187966: Intel Baytrail-M NUC panics because of buggy ACPI table

その後、それをみたtakawataさんが同等のコードをCURRENTにコミットしてくれました:
Revision 263795

というわけで一件落着。

Baytrail-M NUC(DN2820FYKH)でFreeBSDが起動しなかった話

bhyve用にこれ買ってみたんですよ、これ。まぁ新しいハードだし地雷だろうなと思ってたんだけど、面白そうにおもっちゃってつい人柱っちゃったんですよ。はい。

したらもういきなりドハマリして。

  • FreeBSDが入っている2.5インチHDDを繋いでもレガシーブートモードでブートセクタが認識されている様子が無い
  • USBメモリやUSB CD-ROMを認識する気配も無い
  • ふと何となくHDDを抜いてUSBメモリを刺したらUbuntuがあっさり起動
  • …それ、電源容量足りてないのでは?
  • HDDをSSDに差し替えてみたらUSBメモリSSD両方認識
  • でもUEFIの設定画面でブートデバイスのリスト空になったりするし、ブートセレクタUSBメモリでてこないし
  • UEFIの設定画面のメモリ容量0GBだし
  • UEFIの設定画面からネット経由のファームアップを選んだらエラーメッセージ
  • UEFIの設定画面からUSBメモリ経由のファームアップを実行したらフリーズ
  • もうなんか全然うまくいかないので別のマシンでSSDFreeBSDをインストールしてきてから、NUCに刺してみる
  • かーねるぱにっく!
  • インストールするFreeBSDのバージョンを変えたり試行錯誤してみたら、動く組み合わせを発見
  • 無事起動!
  • でもよくみると、USBコントローラがdmesgにエラーを吐きまくって発狂してて動いてない。CPUコアは2つのはずだが1つしか見えてない。
  • これ、ACPIのテーブルがちゃんと読めてないか壊れてたりするのでは?
  • dmesgみると「ACPI BIOS Warning (bug): Incorrect checksum in table [DSDT] - 0x8E, should be 0x51 (20130823/tbprint-233」とか出てる
  • ACPIのテーブル、ちゃんと読めてないか壊れてるとしか思えない
  • USBメモリでブートしてACPIテーブルのダンプを取る→再コンパイル→エラー無し。FreeBSDでだけおかしいのだろうか?
  • BIOSリカバリモードでなら無事ファームアップできるらしいと教えて貰ったので試してみる
  • FreeBSDがまたカーネルパニックするようになった!←イマココ

で、どんなpanicを起こしているかを見てみるわけですね。

こちらはverbose modeで起動時のpanic画面で、「Bogus Interrupt Trigger Mode」で止まってます。

ソースコードでいうと、こちらですね。

static enum intr_trigger
interrupt_trigger(UINT16 IntiFlags, UINT8 Source)
{

	switch (IntiFlags & ACPI_MADT_TRIGGER_MASK) {
	case ACPI_MADT_TRIGGER_CONFORMS:
		if (Source == AcpiGbl_FADT.SciInterrupt)
			return (INTR_TRIGGER_LEVEL);
		else
			return (INTR_TRIGGER_EDGE);
	case ACPI_MADT_TRIGGER_EDGE:
		return (INTR_TRIGGER_EDGE);
	case ACPI_MADT_TRIGGER_LEVEL:
		return (INTR_TRIGGER_LEVEL);
	default:
		panic("Bogus Interrupt Trigger Mode");
	}
}

何らかのデバイスの割り込み設定をパースしている所に見えます。
で、取り敢えず先に進みたいと思ったので、panicをコメントアウトして、PCIバイスの割り込みで通常使われているINTR_TRIGGER_LEVELをreturnするようにコードを適当に書き換えてみます。

すると、今度は「Bogus Interrupt Polarity」でpanicしました。
コードではこの辺ですね。

static enum intr_polarity
interrupt_polarity(UINT16 IntiFlags, UINT8 Source)
{

	switch (IntiFlags & ACPI_MADT_POLARITY_MASK) {
	case ACPI_MADT_POLARITY_CONFORMS:
		if (Source == AcpiGbl_FADT.SciInterrupt)
			return (INTR_POLARITY_LOW);
		else
			return (INTR_POLARITY_HIGH);
	case ACPI_MADT_POLARITY_ACTIVE_HIGH:
		return (INTR_POLARITY_HIGH);
	case ACPI_MADT_POLARITY_ACTIVE_LOW:
		return (INTR_POLARITY_LOW);
	default:
		panic("Bogus Interrupt Polarity");
	}
}

で、今度もpanicをコメントアウトして、取り敢えず適当にINTR_POLARITY_LOWを返すように書き換えてみます。

…すると、問題なく動作するようになりました。

でも、このエラーは何だったのでしょう。dmesgやソースコードを眺めた感じでは、MADTというテーブルのパース中に、あるデバイスの割り込み設定を取りに行ったら範囲外の値を受け取ってカーネルの実行を止めたというように読み取れます。

ではどんな値が来てるのかdmesgに表示してみましょう。
こんなprintfを入れてみました:

diff --git a/sys/x86/acpica/madt.c b/sys/x86/acpica/madt.c
index 9dfb77f..fdc86c0 100644
--- a/sys/x86/acpica/madt.c
+++ b/sys/x86/acpica/madt.c
@@ -308,14 +308,15 @@ interrupt_polarity(UINT16 IntiFlags, UINT8 Source)
 	case ACPI_MADT_POLARITY_ACTIVE_LOW:
 		return (INTR_POLARITY_LOW);
 	default:
-		panic("Bogus Interrupt Polarity");
+		printf("Bogus Interrupt Polarity %x, set to low\n",
+			IntiFlags & ACPI_MADT_POLARITY_MASK);
+		return (INTR_POLARITY_LOW);
 	}
 }
 
 static enum intr_trigger
 interrupt_trigger(UINT16 IntiFlags, UINT8 Source)
 {
-
 	switch (IntiFlags & ACPI_MADT_TRIGGER_MASK) {
 	case ACPI_MADT_TRIGGER_CONFORMS:
 		if (Source == AcpiGbl_FADT.SciInterrupt)
@@ -327,7 +328,9 @@ interrupt_trigger(UINT16 IntiFlags, UINT8 Source)
 	case ACPI_MADT_TRIGGER_LEVEL:
 		return (INTR_TRIGGER_LEVEL);
 	default:
-		panic("Bogus Interrupt Trigger Mode");
+		printf("Bogus Interrupt Trigger Mode %x, set to level\n",
+			IntiFlags & ACPI_MADT_TRIGGER_MASK);
+		return (INTR_TRIGGER_LEVEL);
 	}
 }

すると、以下のような出力が得られます(一部抜粋)

MADT: Found IO APIC ID 1, Interrupt 0 at 0xfec00000
ioapic0: Routing external 8259A's -> intpin 0
lapic0: Routing NMI -> LINT1
Bogus Interrupt Trigger Mode 8, set to level
lapic0: LINT1 trigger: level
lapic0: LINT1 polarity: high
lapic2: Routing NMI -> LINT1
lapic2: LINT1 trigger: level
Bogus Interrupt Polarity 2, set to low
lapic2: LINT1 polarity: low
MADT: Interrupt override: source 0, irq 2
ioapic0: Routing IRQ 0 -> intpin 2
MADT: Interrupt override: source 9, irq 9
ioapic0: intpin 9 trigger: level
ioapic0 <Version 2.0> irqs 0-86 on motherboard
lapic0: Forcing LINT1 to edge trigger

つまり、Interrupt Trigger Modeに0x8が、Interrupt Polarityに0x2が届いているらしい、という事がわかります。
この値はなんだろう、ここには実装されていないけど新しいモードでもあるのかな?と思ってACPI Specificationを眺めてみます。

…すると、どちらもreservedな値らしいという事がわかりました。なんか変ですね…。

dmesgを見る限りでは2つのコアの割り込みコントローラ(lapic0, lapic2)からそれぞれ接続されている、レガシ割り込み1番(LINT1と書いてある)の設定でしょうか…。

これ以上はMADTを眺めない限り分からなそうです。
というわけでMADTをダンプしてみます:

$ sudo acpidump -t MADT 
/*
  RSD PTR: OEM=INTEL, ACPI_Rev=2.0x (2)
	XSDT=0xb9549078, length=36, cksum=199
 */
/*
  XSDT: Length=116, Revision=1, Checksum=141,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x1072009,
	Creator ID=AMI, Creator Revision=0x10013
	Entries={ 0xb9550848, 0xb9550958, 0xb95509c0, 0xb9550a08, 0xb9550a48, 0xb9550b50, 0xb9550b88, 0xb95512f0, 0xb9551580, 0xb9551700 }
 */
/*
  FACP: Length=268, Revision=5, Checksum=89,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x1072009,
	Creator ID=AMI, Creator Revision=0x10013
 	FACS=0xb96eef80, DSDT=0xb9549180
	INT_MODEL=APIC
	Preferred_PM_Profile=Desktop (1)
	SCI_INT=9
	SMI_CMD=0xb2, ACPI_ENABLE=0xa0, ACPI_DISABLE=0xa1, S4BIOS_REQ=0x0
	PSTATE_CNT=0x80
	PM1a_EVT_BLK=0x400-0x403
	PM1a_CNT_BLK=0x404-0x405
	PM2_CNT_BLK=0x450-0x450
	PM_TMR_BLK=0x408-0x40b
	GPE0_BLK=0x420-0x42f
	CST_CNT=0x85
	P_LVL2_LAT=101 us, P_LVL3_LAT=1001 us
	FLUSH_SIZE=1024, FLUSH_STRIDE=16
	DUTY_OFFSET=1, DUTY_WIDTH=3
	DAY_ALRM=13, MON_ALRM=0, CENTURY=50
	IAPC_BOOT_ARCH={NO_VGA}
	Flags={WBINVD,SLEEP_BUTTON,S4_RTC_WAKE,RESET_REGISTER,PLATFORM_CLOCK,S4_RTC_VALID,REMOTE_POWER_ON}
	RESET_REG=0xcf9:0[8] (IO), RESET_VALUE=0x6
 */
/*
  FACS:	Length=64, HwSig=0x0000004f, Firm_Wake_Vec=0x00000000
	Global_Lock=
	Flags=
	Version=2
 */
/*
  DSDT: Length=30405, Revision=2, Checksum=79,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x1072009,
	Creator ID=INTL, Creator Revision=0x20120913
 */
/*
  APIC: Length=104, Revision=3, Checksum=174,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x1072009,
	Creator ID=AMI, Creator Revision=0x10013
	Local APIC ADDR=0xfee00000
	Flags={PC-AT}

	Type=Local APIC
	ACPI CPU=1
	Flags={ENABLED}
	APIC ID=0

	Type=Local APIC NMI
	ACPI CPU=1
	LINT Pin=60
	Flags={Polarity=active-hi, Trigger=0x2}

	Type=Local APIC
	ACPI CPU=2
	Flags={ENABLED}
	APIC ID=2

	Type=Local APIC NMI
	ACPI CPU=2
	LINT Pin=61
	Flags={Polarity=0x2, Trigger=level}

	Type=IO APIC
	APIC ID=1
	INT BASE=0
	ADDR=0x00000000fec00000

	Type=INT Override
	BUS=0
	IRQ=0
	INTR=2
	Flags={Polarity=conforming, Trigger=conforming}

	Type=INT Override
	BUS=0
	IRQ=9
	INTR=9
	Flags={Polarity=active-hi, Trigger=level}
 */
/*
  FPDT: Length=68, Revision=1, Checksum=181,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x1072009,
	Creator ID=AMI, Creator Revision=0x10013
 */
/*
  MCFG: Length=60, Revision=1, Checksum=76,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x1072009,
	Creator ID=MSFT, Creator Revision=0x97

	Base Address=0x00000000e0000000
	Segment Group=0x0000
	Start Bus=0
	End Bus=255
 */
/*
  LPIT: Length=260, Revision=1, Checksum=63,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x3,
	Creator ID=VLV2, Creator Revision=0x100000d
 */
/*
  HPET: Length=56, Revision=1, Checksum=171,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x1072009,
	Creator ID=AMI., Creator Revision=0x5
	HPET Number=0
	ADDR=0xfed00000:0[64] (Memory)	HW Rev=0x1
	Comparators=2
	Counter Size=1
	Legacy IRQ routing capable={TRUE}
	PCI Vendor ID=0x8086
	Minimal Tick=128
	Flags=0x00
 */
/*
  SSDT: Length=1891, Revision=1, Checksum=193,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x3000,
	Creator ID=INTL, Creator Revision=0x20061109
 */
/*
  SSDT: Length=656, Revision=1, Checksum=10,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x3000,
	Creator ID=INTL, Creator Revision=0x20061109
 */
/*
  SSDT: Length=378, Revision=1, Checksum=186,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x3000,
	Creator ID=INTL, Creator Revision=0x20061109
 */
/*
  UEFI: Length=66, Revision=1, Checksum=147,
	OEMID=INTEL, OEM Table ID=DN2820FY, OEM Revision=0x0,
	Creator ID=, Creator Revision=0x0
 */

よくわかりませんが、多分

	Type=Local APIC NMI
	ACPI CPU=1
	LINT Pin=60
	Flags={Polarity=active-hi, Trigger=0x2}

	Type=Local APIC NMI
	ACPI CPU=2
	LINT Pin=61
	Flags={Polarity=0x2, Trigger=level}

でしょうか。
Trigger=0x2はおそらくビットシフト後の0x8だと思われます。

間違ってるっぽいのは分かったけど、それでどうしたらいいんだろうか…。
まぁ、取り敢えず上述のパッチでpanicを起こさないようにすると動くには動くんですが。

ちなみに、新しいファームウェアではこのパッチを当てないと起動出来ないですが、CPU数はちゃんと2に増えました。
USBは相変わらずおかしいようです。

後日談後日談その2

RubyでTAPを使ったトンネリングプログラムを実装してみる

こちらのサイトでシンプルなPythonの実装が配布されていたので、Rubyでも実装してみました。

require "socket"
TUNSETIFF = 0x400454ca
IFF_TAP = 0x0002
PEER = "192.168.0.100"
PORT = 9876
tap = open("/dev/net/tun", "r+")
tap.ioctl(TUNSETIFF, ["tap%d", IFF_TAP].pack("a16S"))
sock = UDPSocket.open
sock.bind("0.0.0.0", 9876)
peer = Socket.pack_sockaddr_in(PORT, PEER)
while true
    ret = IO::select([sock, tap])
    ret[0].each do |d|
        if d == tap
            sock.send(tap.read(1500), 0, peer)
        else
            tap.write(sock.recv(65535))
        end
    end
end

RubyでもTAPの読み書きが出来るしselectも使えるしioctlも発行できるよー、っと。

OSvにマージされたmrubyを試す

$ sudo apt-get install openjdk-7-jdk autotools-dev libltdl-dev libtool autoconf autopoint libboost-all-dev \
genromfs zfs-fuse autoconf ant libffi-dev ruby bison gyp git build-essential qemu-utils qemu-system-x86 \
bridge-utils libvirt0 libvirt-bin
$ git clone git@github.com/cloudius-systems/osv
$ cd osv
$ git submodule update --init
$ cd apps/mruby
$ make module
$ cd -
$ sudo make external all image=mruby
$ sudo ./scripts/run.py
OSv v0.05-72-g4eb7d9c
mruby-eshell

$ ls
[".", "..", "libuutil.so", "libzfs.so", "dev", "tools", "proc", "zfs.so", "tmp", "etc", "zpool.so", "usr"]
$ exit

ただ、これだけだとスクリプトが何も無いので何も実行できない。
スクリプトを足して再ビルドしてみる。

$ cd apps/mruby
$ cat << EOF > tcpsocket.rb
s = TCPSocket.open("www.kame.net", 80)
s.write("GET / HTTP/1.0\r\n\r\n")
puts s.read
s.close
EOF
$ echo "/tcpsocket.rb: \${MODULE_DIR}/tcpsocket.rb" >> usr.manifest
$ cd -
$ sudo make all image=mruby
$ sudo ./scripts/run.py
OSv v0.05-72-g4eb7d9c
mruby-eshell

$ run ./tcpsocket.rb
HTTP/1.1 200 OK
Date: Sat, 25 Jan 2014 00:13:58 GMT
Server: Apache/2.2.26 (FreeBSD) mod_ssl/2.2.26 OpenSSL/0.9.8y DAV/2
Accept-Ranges: bytes
Connection: close
Content-Type: text/html
(後略)
$ exit

ソケット通信のテストが実行出来た。

FreeBSD 10.0-RELEASEのBHyVeでLinuxゲストが「WRMSR・RDMSR」などの単語を含むエラーメッセージと共に異常終了する場合

一部のLinuxカーネルと一部のIntel CPUの組み合わせでは「Unknown WRMSR code 391, val 2000000f, cpu 0」「vm exit rdmsr 0xe8, cpu 0」などのエラーがでる。
これは、該当するMSRへのアクセスをBHyVeがハンドルせずに異常終了して済ませてしまっているためで、以下の手順で-CURRENTのパッチをバックポートすることで解決出来る。

# svn co svn://svn.freebsd.org/base/head
# cd head
# svn diff -r259634:r259635 > ~/msr.diff
# cd /usr/src
# patch -p0 < ~/msr.diff
# cd usr.sbin/bhyve
# make
# make install
# cd ~/bhyve-script
# vi centos1
        BHYVECMD="/usr/sbin/bhyve \
                -c "$VCPUS" \
のところを以下のように書き換える:
        BHYVECMD="/usr/sbin/bhyve \
                -w \
                -c "$VCPUS" \

FreeBSD 10.0-RELEASEのBHyVeでOpenBSD 5.4をインストールしてみよう

OpenBSDサポートはまだ実験段階で、カーネルを改変したバージョンの5.4しか動かない。
この問題を回避するために、改変版カーネルがインストールされたディスクイメージをダウンロードする。

# cd bhyve-script
# cp vm0 openbsd3
これから作るVMの名前にvm0をコピー(末尾は数値で、他のVMと重複しない値でなければならない)
# vi openbsd3
NIC=“em0” をお使いのNIC名に
VCPUS=“1”を任意のvCPU数に
VMOS=“freebsd”を”openbsd”に
VMRAM=1024”を任意のメモリサイズに
それぞれ変更。
# mkdir -p ./vm/openbsd3
# fetch http://people.freebsd.org/~grehan/flashimg.amd64-20131014.bz2
# bunzip2 flashimg.amd64-20131014.bz2
# cp flashimg.amd64-20131014 ./vm/openbsd3/openbsd3.img
# sh openbsd3 start
rootのパスワードは'test123'でログイン可能。

TODO:これだとネットワークが繋がらない気がする。