bhyveのソースコードリーディングメモ(iommu_init():その4:vtd_iommu_enable前半メイン)

## 参考ドキュメント
####[前回ドキュメント](http://qiita.com/akachochin/items/7b43e57aa9d13761e291)
####[ASADA氏の公開記事](http://syuu1228.github.io/howto_implement_hypervisor/)
この記事から引用したときは「ChapterXX」とする。例えば第15回の記事なら(Chapter15)となる。

####[Intel ® Virtualization Technology for Directed I/O Architecture Specification](http://www.intel.co.jp/content/www/jp/ja/intelligent-systems/intel-technology/vt-directed-io-spec.html) 
このドキュメントから引用したときは(仕様書:XXX)とする。

前回の続きからです。前回はvtd_create_mapping()を抜けたところまで書いてみました。
今回はvtd_iommu_enable()の前半までを書きたいと思います。

## ホストドメインとPCIデバイスの紐付け
#### PCIデバイスの総当り調査
まず、システム上にすでに存在するPCIデバイスをすべて総当りで調べてみる。

```c
	for (bus = 0; bus <= PCI_BUSMAX; bus++) {
		for (slot = 0; slot <= PCI_SLOTMAX; slot++) {
			for (func = 0; func <= PCI_FUNCMAX; func++) {
```

ループ内でbus/slot/funcに対応するデバイスがいない場合は次を試す。

```c
				dev = pci_find_dbsf(0, bus, slot, func);
				if (dev == NULL)
					continue;
```

次にパススルーデバイスは別途仮想マシンが立ち上がった時に初期化する(本当か？要確認)ため、ここでは何もしない。

```c
				/* skip passthrough devices */
				name = device_get_name(dev);
				if (name != NULL && strcmp(name, "ppt") == 0)
					continue;
```

そして、それ以外のデバイスはホストが管理するため、ホストドメインの持つページテーブルとデバイスのContext Tableとの関係づけを行う。
それ以外のデバイスはパススルーでないため、VM Exitさせてホストが制御を実施するのだろう。(本当か、要確認？)
よって、IOMMUに対してホストの物理アドレス空間(ホストドメイン)と該当デバイスを紐付けする必要があると思われる。

```c
				/* everything else belongs to the host domain */
				iommu_add_device(host_domain,
				    pci_get_rid(dev));
			}
```

#### Context Tableのエントリ
iommu_add_device()経由で、vtd_add_device()が呼ばれる。
vtd_add_device()では、以下のようにしてContext Tableのエントリを初期化する。
当然ながら、エントリを有効にする前に必要な設定はすべて済ませることに注意する。
Context Tableのエントリは仕様書9.3「Context Entry」を参照のこと。

```c
	pt_paddr = vtophys(dom->ptp);

(省略)

	/*
	 * Order is important. The 'present' bit is set only after all fields
	 * of the context pointer are initialized.
	 */
	ctxp[idx + 1] = dom->addrwidth | (dom->id << 8);

	if (VTD_ECAP_DI(vtdmap->ext_cap))
		ctxp[idx] = VTD_CTX_TT_ALL;
	else
		ctxp[idx] = 0;

	ctxp[idx] |= pt_paddr | VTD_CTX_PRESENT;
```

## IOMMU_ENABLE
最後にIOMMU_ENABLE()経由でvtd_enable()が呼ばれる。
ここで、以前書き忘れていたことがある。

#### vtdmaps
かなり前に初期化したvtdmapsには、基本的にはACPI経由で得られたRemapping H/Wのレジスタ空間の物理アドレスが格納されている。

以前見たvtd_initをもう一度見て思い出してみよう。

```c
static int
vtd_init(void)
{
(略)
	while (remaining > sizeof(ACPI_DMAR_HEADER)) {
		hdr = (ACPI_DMAR_HEADER *)(end - remaining);
		if (hdr->Length > remaining)
			break;
		/*
		 * From Intel VT-d arch spec, version 1.3:
		 * BIOS implementations must report mapping structures
		 * in numerical order, i.e. All remapping structures of
		 * type 0 (DRHD) enumerated before remapping structures of
		 * type 1 (RMRR) and so forth.
		 */
		if (hdr->Type != ACPI_DMAR_TYPE_HARDWARE_UNIT)
			break;

		drhd = (ACPI_DMAR_HARDWARE_UNIT *)hdr;
		vtdmaps[units++] = (struct vtdmap *)PHYS_TO_DMAP(drhd->Address);
		if (units >= DRHD_MAX_UNITS)
			break;
		remaining -= hdr->Length;
	}
```

ACPI経由でとってきたDRHD構造体の中にあるレジスタベースアドレス(Register Base Address,詳細は仕様書8.3「DMA Remapping Hardware Unit Definition Structure」を参照のこと)をvtdmapsに設定していることを思い出そう。

実は、仕様書8.2「Remapping Structure Types」によると、APCI経由で取得できる「vtdmaps」には以下のようなものがある。

|vtdmapsのindex|詳細|
|-------------:|---:|
|0|DMA Remapping Hardware Unit Definition (DRHD) Structure|
|1|Reserved Memory Region Reporting (RMRR) Structure|
|2|Root Port ATS Capability Reporting (ATSR) Structure|
|3|Remapping Hardware Static Affinity (RHSA) Structure|
|4|ACPI Name-space Device Declaration (ANDD) Structure|
|>4|Reserved for future use. |

今回の制御で対象となっているのは、「DRHD」だけである。
ただ、せっかくの機会なので、他のデバイスについても軽く仕様書を眺める。

#### RMRR
仕様書3.14「Handling Requests to Reserved System Memory」によると「普通BIOSによってブート時に予約領域が確保され、そしてOSに対してその領域がシステムメモリマップ内の予約領域である旨通知される」とある。このメモリ領域に関する通知をするための領域であると思われる。

#### ATSR
仕様書「8.5 Root Port ATS Capability Reporting Structure」によると、Device-TLBをサポートするプラットフォームのみで使われ、ATSトランザクション(Address Transaction Service)をサポートするPCIeのRootポートを識別するために使われるらしい。

#### RHSA
8.6 Remapping Hardware Status Affinity (RHSA) structure
NUMAアーキテクチャで有効なもので、Remapping Hardware Unitで扱う範疇がnodeをまたぐ場合に使うものらしい。

#### ANDD
詳細は不明だが、このエントリは「プラットフォーム内でDMAを発行することのできるデバイスに関する情報(ACPI name-space enumerated device capable of issuing DMA requests in the platform.)が取得できる。
筆者はACPIに明るくないが、[このあたりの情報](https://www.kernel.org/doc/Documentation/acpi/enumeration.txt)は関係するのだろうか。

## DRHDのレジスタマップ
DRHDのレジスタマップは仕様書10.4「Register Descriptions」に書かれている。

```c
	for (i = 0; i < drhd_num; i++) {
		vtdmap = vtdmaps[i];
		vtd_wbflush(vtdmap);
```
...と今回はここまで。次回は実際のレジスタ制御を見て、IOMMUをenableにするところを見たいと思います。
(今回IOMMUをenableするところまで書きたかったのだが、結構文章量が膨れてしまうことがわかったので、ここで一旦切ります。)