0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

BitVisorAdvent Calendar 2020

Day 10

BitVisorでAMD-Vi(IOMMU)隠蔽(リベンジ編)

Posted at

昨年AMD-Vi(IOMMU)隠蔽をしようとして失敗していました:
https://qiita.com/hdk_2/items/f5619d9a5eeed2e5249d

もう一度チャレンジしてみます。

環境

昨年と同じです。

  • ASUS B350M-A
  • AMD Ryzen 7 2700
  • Debian GNU/Linux 10

ACPI IVRSテーブル隠蔽方法を変える

昨年はテーブルの先頭を破壊する簡単な方法を用いました。今回は別の方法を使います。

diff --git a/core/acpi.c b/core/acpi.c
--- a/core/acpi.c
+++ b/core/acpi.c
@@ -1463,6 +1463,7 @@ get_dmar_address (void *data, u64 entry)
        dmar->valid = true;
        return q;
 }
+#endif

 static u64
 alloc_acpi_pages (unsigned int npages)
@@ -1485,7 +1486,6 @@ alloc_acpi_pages (unsigned int npages)
        }
        return addr;
 }
-#endif

 static u64
 dmar_pass_through_prepare (void)
@@ -1865,6 +1865,18 @@ acpi_init_global (void)
        remove_dup_facs_addr (facs_addr, NFACS_ADDR);
        pm1a_cnt_found = true;
        save_mcfg ();
+       {
+               char *p = find_entry ("IVRS");
+               if (p) {
+                       printf ("Found IVRS %p\n", p);
+                       u64 addr = alloc_acpi_pages (1);
+                       void *q = mapmem_hphys (addr, PAGESIZE, MAPMEM_WRITE);
+                       memset (q, 0, PAGESIZE);
+                       unmapmem (q, PAGESIZE);
+                       printf ("Dummy IVRS at 0x%llx\n", addr);
+                       modify_acpi_table ("IVRS", addr);
+               }
+       }
 }

 INITFUNC ("global3", acpi_init_global);

そしてデバイス隠蔽も行い、起動してみると、成功しました!

$ dmesg|grep IVRS
$ dmesg|grep IOMMU
[    0.717641] AMD IOMMUv2 driver by Joerg Roedel <jroedel@suse.de>
[    0.717642] AMD IOMMUv2 functionality not available on this system
$ lspci -s 00:00.2
$ ls /sys/class/iommu/
$ cat /proc/cmdline |tr \  \\n|grep iommu
amd_iommu=on
iommu=pt

このように、IOMMUはデバイスもACPIテーブルも検出されず、amd_iommu=onを指定していてもIOMMUが使われなくなりました。

何をやったのか

modify_acpi_table()は昨年はなかった関数です。DMAR pass-through対応に伴い導入されました。alloc_acpi_pages()もそうで、上のdiffでは、#ifdef DMAR_PASS_THROUGHの中に入っていたのを外に出しています。

modify_acpi_table()はACPIテーブルのアドレスを変える関数です。ACPIテーブルのアドレスはRSDTおよびXSDTの中に書き込まれており、それらを書き換えるだけでなく、UEFI環境では、SystemTable->BootServices->InstallConfigurationTable関数をフックして、これが呼び出された時に書き換えを反映し直すという処理も追加します。これを使って、テーブルアドレスを書き換えて無効化するようにしました。

VMMのログを見ると以下のようにアドレスが出ています:

Dummy IVRS at 0xdaaa9000

そしてLinuxのログにもそのアドレスが出ていることが確認できます:

$ dmesg|grep -i daaa9000
[    0.000000] ACPI:      0x00000000DAAA9000 000000 (v00                 00000000      00000000)

さて、こんな仕組みが導入された経緯は、DMAR pass-throughの実装にあたり、DMARテーブルにRMRRを追加しテーブルサイズが大きくなることに対処する必要があったのがきっかけです。最初はテーブルアドレスの書き換えだけで済ませるつもりだったのですが、F社製PCにて、SystemTable->BootServices->InstallConfigurationTable関数が呼び出されるような状況(EFI Shellを起動した時など)において、書き換えた内容が失われるという事象が発生しました。その時、VT-d隠蔽(DMARテーブルの先頭4バイトを破壊)も機能しないという、昨年のIVRSテーブル隠蔽失敗と同様の問題があることを確認しました。どうやら、ACPIテーブルのコピーがメモリー上に残されていて、新たなテーブルの追加の際にそれらのコピーを元にすべてが再構築されるようでした。まぁ、テーブルの追加によってRSDTやXSDTのサイズが変わることを考えれば、再配置が必要になることは不思議ではありません。

そこで、ASUSもF社製PCと同じような動作をしているのではないか、と考えて今回の再チャレンジ成功となりました。

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?