Linux ではデバイスツリーで次のように reserved-memory(予約メモリ)空間を指定出来ます。
/ {
#address-cells = <2>;
#size-cells = <2>;
image_buf0: image_buf@0 {
compatible = "shared-dma-pool";
reusable;
reg = <0x0 0x6fc00000 0x0 0x00400000>;
alignment = <0x0 0x1000>;
label = "image_buf0";
};
};
この時、resuable プロパティを付けることによって、予約したメモリ領域を CMA 領域として使うことが出来ます。例えば ZynqMP-FPGA-Linux で上記のデバイスツリーを追加して Linux Kernel を起動すると、次のようなログが出ます。
Starting kernel ...
[ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034]
[ 0.000000] Linux version 4.19.0-xlnx-v2019.1-zynqmp-fpga (ichiro@Jabberwock) (gcc version 7.4.0 (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04)) #2 SMP Wed Jul 17 15:19:07 DST 2019
[ 0.000000] Machine model: Avnet Ultra96 Rev1
[ 0.000000] efi: Getting EFI parameters from FDT:
[ 0.000000] efi: UEFI not found.
[ 0.000000] Reserved memory: created CMA memory pool at 0x000000006fc00000, size 4 MiB
[ 0.000000] OF: reserved mem: initialized node image_buf@0, compatible id shared-dma-pool
[ 0.000000] cma: Reserved 256 MiB at 0x0000000070000000
:
:
:
このログを見てわかるように、0x000000006fc00000 から 4MiB が CMA 領域として確保されています。
実はこの件に関して udmabuf に issue があげられました。
- 「Can't set physical address in device tree for Zynq MPSoC」
https://github.com/ikwzm/udmabuf/issues/29
この issue によれば、次のようなデバイスツリーの設定だと、
reserved-memory {
#address-cells = <2>;
#size-cells = <2>;
ranges;
rproc_0_reserved: rproc@3ed00000 {
no-map;
reg = <0x0 0x3ed00000 0x0 0x1000000>;
};
image_buf0: image_buf@0 {
compatible = "shared-dma-pool";
reusable;
reg = <0x0 0x6fc00000 0x0 0x0100000>;
alignment = <0x0 0x1000>;
label = "image_buf0";
};
};
udmabuf@0 {
compatible = "ikwzm,udmabuf-0.10.a";
device-name = "udmabuf0";
size = <0x0 0x0100000>;
memory-region = <&image_buf0>;
};
"incorrect alignment of CMA region" というエラーが出て CMA 領域の確保に失敗します。
[ 0.000000] Reserved memory: incorrect alignment of CMA region
[ 0.000000] cma: Reserved 256 MiB at 0x000000005fc00000
:
:
:
[ 127.914295] udmabuf udmabuf@0: of_reserved_mem_device_init failed. return=-22
[ 127.921580] udmabuf udmabuf@0: driver installed.
[ 127.926204] udmabuf: probe of udmabuf@0 failed with error -22
[ 127.932553] udmabuf udmabuf.0: DMA mask not set
[ 127.937692] udmabuf udmabuf0: driver version = 1.4.5
[ 127.942653] udmabuf udmabuf0: major number = 240
[ 127.947439] udmabuf udmabuf0: minor number = 0
[ 127.952052] udmabuf udmabuf0: phys address = 0x000000005fd00000
[ 127.958140] udmabuf udmabuf0: buffer size = 1048576
[ 127.963274] udmabuf udmabuf0: dma device = udmabuf.0
[ 127.968582] udmabuf udmabuf0: dma coherent = 0
[ 127.973195] udmabuf udmabuf.0: driver installed.
そこで Linux Kernel のソースコードを見てみると、kernel/dma/contiguous.c に次のような記述がありました。
static int __init rmem_cma_setup(struct reserved_mem *rmem)
{
phys_addr_t align = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
phys_addr_t mask = align - 1;
unsigned long node = rmem->fdt_node;
struct cma *cma;
int err;
if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
of_get_flat_dt_prop(node, "no-map", NULL))
return -EINVAL;
if ((rmem->base & mask) || (rmem->size & mask)) {
pr_err("Reserved memory: incorrect alignment of CMA region\n");
return -EINVAL;
}
err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma);
if (err) {
pr_err("Reserved memory: unable to setup CMA region\n");
return err;
}
/* Architecture specific contiguous memory fixup. */
dma_contiguous_early_fixup(rmem->base, rmem->size);
if (of_get_flat_dt_prop(node, "linux,cma-default", NULL))
dma_contiguous_set_default(cma);
rmem->ops = &rmem_cma_ops;
rmem->priv = cma;
pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n",
&rmem->base, (unsigned long)rmem->size / SZ_1M);
return 0;
}
RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup);
align 変数でアライメント単位を設定してから、開始アドレス(rmem->base
) と容量(rmem->size
) がアライメント単位であるかどうかチェックしています。アライメント単位は MAX_ORDER 定数や pageblock_order 変数(or定数) によって算出されますが、ZynqMP-FPGA-Linux では 0x00400000 になります。
したがって、ZynqMP で Linux の CMA 領域を devicetree の reserved-memory で指定した時のアライメントは 0x00400000(4MiB) 単位であることがわかります。