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