Vengineerの戯言

人生は短いけど、長いです。人生を楽しみましょう!

Apple M1 Mac mini内のアドレスマップを確認してみた

@Vengineerの戯言 : Twitter
SystemVerilogの世界へようこそすべては、SystemC v0.9公開から始まった 

はじめに

Apple M1 で Linux 動くようになったのは、先週のブログにまとめました。

vengineer.hatenablog.com

Linux では、Kernel (Imageなど)の他に、dts ファイルをコンパイル(バイナリ化)した dtb ファイルを読み込んでブートします。
この dts ファイルには、どのモジュールがどのアドレスにあって、どのくらいの空間(サイズ)を使っているのか?割り込みやその他の情報を設定するファイルであります。dts ファイルで設定された情報から Linux がブート中に各デバイスドライバを呼び出すのに使います。

ARM64用の dts ファイルは、arch/arm64/boot/dts ディレクトリにあります。各ベンダ毎にディレクトリに分かれています。Apple M1 Mac mini 用の dts ファイルは、apple ディレクトリの apple-m1-j274.dts です。j274 が Mac mini の型番か何かになっているんだと思います。

Apple M1 Mac mini のアドレスマップ

apple-m1-j274.dtsから各モジュールのアドレスとサイズを抽出し、どのようになっているかを見てみます。

まとめたのが、こんな感じになりました。

  • memory 0x8 0x00000000 0x2 0x00000000
    • fw_area 0x8 0x00000000 0x0 0x01000000
    • fw_area 0x9 0xd0000000 0x0 0x30000000
    • smpentry 0x8 0x03400000 0x0 0x4000
  • I/O-1 0x2 0x00000000 0x2 0x00000000
    • USB
  • I/O-2 0x5 0x00000000 0x0 0x80000000
    • USB
  • I/O-3 0x6 0x80000000 0x0 0x20000000
    • PCIe Port-0 0x6 0x81000000 0x0 0x20000
    • PCIe Port-1 0x6 0x82000000 0x0 0x20000
    • PCIe Port-2 0x6 0x83000000 0x0 0x20000

メモリは、0x8_0000_0000 からの 8GB です。この dts ファイルでは、8GBモデルになっています。16GBモデルになった場合、このエントリのサイズ部分の 0x2_0000_0000 が 0x4_0000_0000 になるのか、他のアドレス空間に 8GB のメモリがマッピングされるかはこのファイルではわかりません。
メモリの中に、fw_area が 2か所と smpentry が 1か所あります。この3つの領域は、Linuxが使用するメモリから除外されます。

I/O空間は、3つ。0x2_0000_0000 から 8GBの I/O-1、0x5_0000_0000 からの 2GBの I/O-2、0x6_8000_0000 からの 512MBのI/O-3 です。
I/O-1はUSB、I/O-2はUSB、I/O-3はPCIe の空間のようです。I/O-3のPCIe の空間では、3つのポートに対して、128KBを割り当てています。

ここまでは、CPUコアから見たアドレス空間です。下図は、先週のブログにも載せた図です。DMAを行う各モジュールには図のように I/O MMU が付いています。この I/O MMU を使って、論理アドレスから物理アドレス(上記の 0x8_0000_0000 からの8GB) に変換します。

f:id:Vengineer:20210130120045p:plain

dts ファイルでは、USBに関しては usb_dart、PCIeに関しては pcie_dart が I/O MMU になります。

下記は、I/O-1のUSB用のI/O MMU 部です。アドレス空間は2つで、0x3_82f0_0000 から 512KB と 0x3_2f80_0000 からの16KB です。page-bits というものが Page size を意味していて、14とありますので、14ビット、16KBが Page size になります。一般的なLinux (x86-64やarm32, arm64)では、4KB ですが、AppleiOS/iPadOS/最新macOS では Page size が 16KB になっているので、同じになります。

        usb_dart0: usb_dart0@382f00000 {
            compatible = "apple,dart-m1";
            clocks = <&atc0_usb>;
            reg = <0x3 0x82f00000 0x0 0x80000   0x3 0x82f80000 0x0 0x4000>;
            interrupts = <0 781 4>;
            page-bits = <14>;
            sid-mask = <11>;
            sid-remap = <0 1>;
            #iommu-cells = <1>;
        };

下記は、PCIe の I/O MMU 部です。こちらのアドレス空間は、ひとつで、0x6_8100_8000 から 16KB です。page-bits が 14 なので、こちらも Page size は 16KB になります。

        pcie_dart0: pcie_dart0@681008000 {
            compatible = "apple,dart-m1";
            clocks = <&pcie_gp_clk>;
            reg = <0x6 0x81008000 0x0 0x4000>;
            interrupts = <0 696 4>;
            pcie-dart;
            page-bits = <14>;
            sid-mask = <65535>;
            #iommu-cells = <1>;
        };

PCIe の I/O MMU は、3つ、pcie_dart0、pcie_dart1、pcie_dart2、あります。この I/O MMU は、下記の pcie の ports と空いてあるところに接続しているんだと思っています。

        pcie: pcie@690000000 {
            compatible = "apple,pcie-m1";
            reg = <0x6 0x90000000 0x0 0x1000000                                                                 /* config */
                   0x6 0x80000000 0x0 0x100000  0x6 0x8c000000 0x0 0x100000                                     /* core and AXI bridge */
                   0x6 0x81000000 0x0 0x20000   0x6 0x82000000 0x0 0x20000   0x6 0x83000000 0x0 0x20000>;       /* ports */

       途中、省略

            iommu-map = <0x0000 &pcie_dart0 0x8000 0x0100>, /* fake, and should never be used as RC bridges don't DMA */
                        <0x0100 &pcie_dart0 0x0000 0x0100>,
                        <0x0200 &pcie_dart1 0x0000 0x0100>,
                        <0x0300 &pcie_dart2 0x0000 0x0100>;
            refclk-always-on-2;
            max-speed-2 = <1>; /* 2.5 GT/s */
        };

終わりに

Linuxでは、dts ファイルを見ることで、各デバイス(SoC) 内のアドレスだけでなく、割り込みや内部構成がなんとなくわかります。
また、dts ファイルからアドレスマップを作ることで、どんな感じに I/O が割り当てられているのが分かります。

クラッチからシステムのアドレスマップを作る機会はそうそうないです。
昔、アドレスマップを決められる機会がありました。その時はいろいろ悩みました。特に、PCIのメモリ空間とPCI Config空間をどのようにマップすればいいかでした。