Linuxでユーザー空間で動作するプログラムとハードウェアがメモリを共有するためのデバイスドライバ(reserved-memory編)

はじめに
-----------------------------------------------------------------------------------

Linux にはデバイスツリーで 次のように reserved-memory(予約メモリ)空間を指定することが出来ます。

```devicetree.dts
/ {
	#address-cells = <1>;
	#size-cells = <1>;
	memory {
		reg = <0x40000000 0x40000000>;
	};
	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;
		display_reserved: framebuffer@78000000 {
			reg = <0x78000000 0x800000>;
		};
	};
	fb0: video@12300000 {
		memory-region = <&display_reserved>;
		/* ... */
	};
};
```

上の例では ディスプレイのフレームバッファとして 0x78000000 ～ 0x787FFFFF を予約しています。

Linux のカーネルは、この reserved-memory で指定された物理メモリ空間を**通常**のメモリアロケーションの対象外にします。この reserved-memory 領域にアクセスするためには、/dev/mem 等の汎用メモリアクセスドライバを使うか、デバイスツリーでデバイスドライバに紐付ける必要があります。上の例では、fb0:video@12300000 というデバイスドライバに memory-region プロパティを使って reserved-memory/display_reserved に紐付けています。

この記事は、[「Linuxでユーザー空間で動作するプログラムとハードウェアがメモリを共有するためのデバイスドライバ」](http://qiita.com/ikwzm/items/cc1bb33ff43a491440ea)で紹介した[udmabuf](https://github.com/ikwzm/udmabuf)を使って、この reserved-memory 空間にアクセスする方法を示します。

udmabuf の更新
-----------------------------------------------------------------------------------

reserved-memory にアクセスするには udmabuf の v0.10.0 以降が必要です。

[https://github.com/ikwzm/udmabuf/releases](https://github.com/ikwzm/udmabuf/releases)

また、[「FPGA+SoC+Linux+Device Tree Overlay+FPGA Region(ブートイメージの提供)」](https://qiita.com/ikwzm/items/7e90f0ca2165dbb9a577)で紹介した [https://github.com/ikwzm/FPGA-SoC-Linux](https://github.com/ikwzm/FPGA-SoC-Linux) には、この udmabuf v0.10.0 をパッケージングした fpga-soc-linux-drivers-4.14.13-armv7-fpga_0.1.1-1_armhf.deb を公開しています。

デバイスツリーの設定
-----------------------------------------------------------------------------------

udmabuf で reserved-memory にアクセスするためには、reserved-memory の指定した領域を CMA からアロケート出来るようにする必要があります。

```devicetree.dts
/ {
	reserved-memory {
		#address-cells = <1>;
		#size-cells = <1>;
		ranges;

		image_buf0: image_buf@0 {
			compatible = "shared-dma-pool";
			reusable;
			reg = <0x3C000000 0x04000000>; // offset: 960MiB, size: 64MiB
			label = "image_buf0";
		};
	};

	udmabuf@0 {
		compatible = "ikwzm,udmabuf-0.10.a";
		device-name = "udmabuf0";
		minor-number = <0>;
		size = <0x04000000>; // 64MiB
		memory-region = <&image_buf0>;
	};
};
```

上の例では image_buf0 として 0x3C000000 ～ 0x3FFFFFFF の64Mbyte を reserved-memory として確保しています。この image_buf0 に compatible プロパティに "shared-dma-pool" を、そして reusable プロパティを指定します。これらのプロパティの指定によって、この reserved-memory の領域を CMA がアロケートする領域とします。また、アドレスとサイズのアライメントに注意する必要があります。


reserved_memory の各種プロパティの詳細は Linux のソースに含まれる Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt を参照してください。

また、udmabuf0 に上記の image_buf0 を memory-region プロパティで紐付けしています。この紐付けによって udmabuf0 は image_buf0 で指定した CMA 領域から物理メモリを確保します。

上記のデバイスツリーを使って Linux をブートすると、冒頭に次のようなメッセージが出ます。

```console
Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.14.13-armv7-fpga (ichiro@sphinx-vm-ubuntu) (gcc version 5.4.0 20160609 (Ubuntu/Linaro 5.4.0-6ubuntu1~16.04.4)) #1 SMP PREEMPT Wed Jan 10 23:55:41 JST 2018
[    0.000000] CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=18c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] OF: fdt: Machine model: Zynq ZYBO Z7 Development Board
[    0.000000] Memory policy: Data cache writealloc
[    0.000000] Reserved memory: created CMA memory pool at 0x3c000000, size 64 MiB
[    0.000000] OF: reserved mem: initialized node image_buf@0, compatible id shared-dma-pool
[    0.000000] cma: Reserved 16 MiB at 0x3b000000
[    0.000000] random: fast init done
```

Reserved memory: で 0x3c000000 から 64MiB の CMA 領域が割り当てられていることが判ります。

また udmabuf がカーネルにロードされた時点で、次のように reserved-memory で割り当てられた物理メモリの領域(0x3c000000 から 64MByte)が udmabuf0 として確保されていることがわかります。

```console
[    4.530252] udmabuf udmabuf0: driver installed
[    4.551955] udmabuf udmabuf0: major number   = 245
[    4.551962] udmabuf udmabuf0: minor number   = 0
[    4.551971] udmabuf udmabuf0: phys address   = 0x3c000000
[    4.551978] udmabuf udmabuf0: buffer size    = 67108864
[    4.551985] udmabuf udmabuf0: dma coherent   = 0
[    4.551994] udmabuf udmabuf@0x00: driver installed.
```

reusable プロパティの替わりに no-map プロパティを指定した場合、カーネルがパニックを起こすことが報告されています。

 * Kernel Oops when synching issue #10 (https://github.com/ikwzm/udmabuf/issues/10)

これは reusable プロパティが指定された時と no-map プロパティが指定された時とではまったく異なるメカニズムが使われるためです。
no-map プロパティの場合は、driver/base/dma-coherent.c にある **DMA memory pool** メカニズムが使われます。
reusable プロパティの場合は、driver/base/dma-contiguous.c にある **CMA memory pool** メカニズムが使われます。

**DMA** と **CMA** とでは１文字しか違わないので紛らわしいのですが、両者は別メカニズムです。

現時点の udmabuf で reserved-memory を使う時は、reusable プロパティを指定して **CMA memory pool** メカニズムを使ってください。




謝辞
-----------------------------------------------------------------------------------

udmabuf のこの機構は次の pull request で実現しました。送ってくださった方に感謝です。

 * Support reserved mem in device-tree #7 (https://github.com/ikwzm/udmabuf/pull/7)



参考
-----------------------------------------------------------------------------------

 * [https://github.com/ikwzm/udmabuf](https://github.com/ikwzm/udmabuf)
 * [「Linuxでユーザー空間で動作するプログラムとハードウェアがメモリを共有するためのデバイスドライバ」@Qiita](http://qiita.com/ikwzm/items/cc1bb33ff43a491440ea)
 * [reserved-memory.txt @ linux-source の Documentation/devicetree/bindings/reserved-memory](https://elixir.free-electrons.com/linux/v4.14.14/source/Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt)