Linux
kernel
FPGA
arm64
zynq

UltraZed 向け Debian GNU/Linux (v2017.3版) の構築(Linux Kernel編)


はじめに

UltraZed-EG スターターキットに Debian GNU/Linux (v2017.3版) を構築する方法について、具体的な方法をいくつかに分けて説明します。

この記事では、ZynqMP の Linux Kernel について説明をします。


注意

Linux Kernel では 起動時に ATF(ARM Trusted Firmware) のバージョンをチェックしています。Debian GNU/Linux (v2017.3版) で構築する Linux Kernel 4.9.0 (xilinx-v2017.3) では ATF(ARM Trusted Firmware) のバージョンは v0.3 である必要があります。これ以外のバージョンでは Kernel が Panic を起こして起動出来ないので注意してください。


Linux Kernel の構築


必要な環境


  • gcc-aarch64-linux-gnu


Linux Kernel 構築環境の準備

次の URL から git clone でリポジトリをダウンロードして v2017.3.0 をチェックアウトします。

shell$ git clone git://github.com/ikwzm/ZynqMP-FPGA-Linux

shell$ cd ZynqMP-FPGA-Linux
shell$ git checkout v2017.3.0


ソースコードのダウンロード

Linux Kernel のソースコードを以下の URL からダウンロードします。今回は Linux のメインラインからではなく、Xilinx が提供してる linux-xlnx を使います。

shell$ git clone https://github.com/Xilinx/linux-xlnx.git linux-xlnx-v2017.3-zynqmp-fpga


xilinx-v2017.3 をチェックアウト

linux-xlnx の xilinx-v2017.3 というタグをチェックアウトして linux-xlnx-v2017.3-zynqmp-fpga という作業用のブランチを作ります。

shell$ cd linux-xlnx-v2017.3-zynqmp-fpga

shell$ git checkout -b linux-xlnx-v2017.3-zynqmp-fpga refs/tags/xilinx-v2017.3


ZynqMP 用の各種ファイルを追加

ZynqMP 用のファイルを追加してコミットします。追加するファイルの詳細は後述します。

shell$ patch -p0 < ../files/linux-xlnx-v2017.3-zynqmp-fpga.diff

shell$ git add --update
shell$ git add arch/arm64/boot/dts/xilinx/zynqmp-uz3eg-iocc.dts
shell$ git commit -m "[patch] for linux-xlnx-v2017.3-zynqmp-fpga."


Debian Package を作る際の問題を修正

チェックアウトした xilinx-v2017.3 には、Debain のパッケージを作る際に arm64 用ではなくクロスコンパイルを実行したプラットフォーム用のパッケージが出来てしまう問題があります。そのための修正を追加します。この件の詳細は後述します。

shell$ patch -p0 < ../files/linux-xlnx-v2017.3-builddeb.diff

shell$ git add --update
shell$ git commit -m "[fix] build wrong architecture debian package when ARCH=arm64 and cross compile."
shell$ patch -p0 < ../files/linux-xlnx-v2017.3-builddeb2.diff
shell$ git add --update
shell$ git commit -m "[update] scripts/package/builddeb to add tools/include and postinst script to header package"


タグとバージョン番号の設定

shell$ git tag -a xilinx-v2017.3-zynqmp-fpga -m "release xilinx-v2017.3-zynqmp-fpga"

shell$ echo 1 > .version


構築の準備

環境変数を設定します。アーキテクチャ(ARCH)に arm64 を指定します。ここでは、クロスコンパイルに使うツールチェーン(CROSS_COMPILE) に aarch64-linux-gnu-を指定します。この変数は構築する環境にあわせてください。その後、ZynqMP 用のコンフィギュレーション定義ファイルを使って構築の準備をします。

shell$ cd linux-xlnx-v2017.3-zynqmp-fpga

shell$ export ARCH=arm64
shell$ export CROSS_COMPILE=aarch64-linux-gnu-
shell$ make xilinx_zynqmp_defconfig


カーネルと Debian パッケージの構築

shell$ export DTC_FLAGS=--symbols

shell$ make deb-pkg


構築したカーネルのイメージとデバイスツリーを target/UltraZed-EG-IOCC/boot にコピー

無事にカーネルのイメージ(arch/arm64/boot/Image) と UltraZed-EG-IOCC 用のデバイスツリー(arch/arm64/boot/dts/xilinx/zynqmp-uz3eg-iocc.dtb) が出来たら、それを target/UltraZed-EG-IOCC/build にコピーします。その際、作ったカーネルのバージョン等をわかりやすくするため名前を変更しておきます。

また、dtc(Device Tree Compiler) を使って、デバイスツリーのバイナリファイル (devicetree-4.9.0-xlnx-v2017.3-fpga-zynqmp-uz3eg-iocc.dtb) をデバイスツリーのソースファイル (devicetree-4.9.0-xlnx-v2017.3-fpga-zynqmp-uz3eg-iocc.dts) に戻しておきます。何かデバイスツリーを変更したい時は、このデバイスツリーソースファイルを修正して dtc でデバイスツリーバイナリファイルにコンパイルしてください。

なお、「デバイスツリーにシンボル情報を埋め込む」で説明したように、古いバージョンの dtc では --symbols オプションをサポートしていない場合があります。その場合は Linux Kernel をビルドした時にホスト用に生成された dtc (scripts/dtc/dtc) を使ってコンパイルすると良いでしょう。

shell$ cp arch/arm64/boot/Image ../target/UltraZed-EG-IOCC/boot/image-4.9.0-xlnx-v2017.3-fpga

shell$ cp arch/arm64/boot/dts/xilinx/zynqmp-uz3eg-iocc.dtb ../target/UltraZed-EG-IOCC/boot/devicetree-4.9.0-xlnx-v2017.3-fpga-zynqmp-uz3eg-iocc.dtb
shell$ ./scripts/dtc/dtc -I dtb -O dts --symbols -o ../target/UltraZed-EG-IOCC/boot/devicetree-4.9.0-xlnx-v2017.3-fpga-zynqmp-uz3eg-iocc.dts ../target/UltraZed-EG-IOCC/boot/devicetree-4.9.0-xlnx-v2017.3-fpga-zynqmp-uz3eg-iocc.dtb


Linux Kernel の起動


uEnv.txt

「UltraZed 向け Debian GNU/Linux (v2017.3版) の構築(U-Boot編)」 で構築した U-Boot は、起動の際、ストレージに uEnv.txt があればそれを読み込みます。そして uenvcmd 変数が定義されていれば、その変数で定義されている内容を実行します。そこで uEnv.txt に、この記事で構築したカーネルのイメージとデバイスツリーを指定して実行するように記述します。


target/UltraZed-EG-IOCC/boot/uEnv.txt

linux_img_load_cmd=fatload mmc 1 $kernel_addr image-4.9.0-xlnx-v2017.3-fpga

linux_dtb_load_cmd=fatload mmc 1 $fdt_addr devicetree-4.9.0-xlnx-v2017.3-fpga-zynqmp-uz3eg-iocc.dtb
linux_set_boot_cmd=setenv bootargs console=ttyPS0,115200 root=/dev/mmcblk1p2 rw rootwait uio_pdrv_genirq.of_id=generic-uio
linux_boot_img_cmd=booti $kernel_addr - $fdt_addr
uenvcmd=run linux_img_load_cmd && run linux_dtb_load_cmd && run linux_set_boot_cmd && run linux_boot_img_cmd



  • linux_img_load_cmd 変数に mmc 1 (SD-Card の FAT file system)から image-4.9.0-xlnx-v2017.3-fpga をメモリ上の $kernel_addr で指定されたアドレスにロードするコマンドを記述。


  • linux_dtb_load_cmd 変数に mmc 1 (SD-Card の FAT file system)から devicetree-4.9.0-xlnx-v2017.3-fpga-zynqmp-uz3eg-iocc.dtb をメモリ上の $fdt_addr で指定されたアドレスにロードするコマンドを記述。


  • linux_set_boot_cmd 変数に bootargs 変数を設定するコマンドを記述。


  • bootargs には root file system として /dev/mmcblk1p2 (SD-Card の第2パーティション) を指定。


  • linux_boot_img_cmd 変数に booti コマンドを使ってメモリ上にロードされたカーネルをブートするコマンドを記述。


  • uenvcmd 変数に上記各種変数をコマンドとして実行するように記述。

Zynq と異なるのは、ブートするカーネルのイメージが Linux kernel ARM boot executable zImage 形式ではなく、arm64 Linux Image 形式であることです。arm64 用に構築した U-Boot には arm64 Linux Image 形式のイメージをブートするためのコマンド booti があるので、そちらを使います。


ファイルの説明

ここでは linux-xlnx を ZynqMP 用に構築する際に新たに追加したファイルおよび修正したファイルの説明をします。


arch/arm64/configs/xilinx_zynqmp_defconfig

ZynqMP 用のコンフィギュレーション定義ファイルです。基本的には linux-xlnx にすでにある xilinx_zynqmp_defconfig と同じですが、Device Tree Overlay を使うために CONFIG_OF_OVERLAY=yを、さらに Device Tree Overlay を Configuration File System から行うために CONFIG_OF_CONFIGFS=y を新たに追加しています。


arch/arm64/boot/dts/xilinx/zynqmp-uz3eg-iocc.dts

UltraZed-EG-IOCC 用のデバイスツリーです。実はこれと同じデバイスツリーを U-Boot のビルド時にも流用しています。


arch/arm64/boot/dts/xilinx/zynqmp-uz3eg-iocc.dts

/*

*/
/dts-v1/;
/include/ "zynqmp.dtsi"
/include/ "zynqmp-clk-ccf.dtsi"
/ {
model = "ZynqMP UltraZed-EG IO Carrier Card";
compatible = "xlnx,zynqmp-uz3eg-iocc", "xlnx,zynqmp";

chosen {
bootargs = "earlycon clk_ignore_unused";
stdout-path = "serial0:115200n8";
};
aliases {
ethernet0 = &gem3;
serial0 = &uart0;
serial1 = &uart1;
spi0 = &qspi;
};
memory {
device_type = "memory";
reg = <0x0 0x0 0x0 0x80000000>;
};

cpus {
};

amba_pl: amba_pl@0 {
#address-cells = <2>;
#size-cells = <2>;
compatible = "simple-bus";
ranges ;
};
};

&lpd_dma_chan1 {
status = "okay";
};
&lpd_dma_chan2 {
status = "okay";
};
&lpd_dma_chan3 {
status = "okay";
};
&lpd_dma_chan4 {
status = "okay";
};
&lpd_dma_chan5 {
status = "okay";
};
&lpd_dma_chan6 {
status = "okay";
};
&lpd_dma_chan7 {
status = "okay";
};
&lpd_dma_chan8 {
status = "okay";
};
&xilinx_ams {
status = "okay";
};
&xlnx_dp {
phy-names = "dp-phy0","dp-phy1";
phys = <&lane3 5 0 3 27000000>,
<&lane2 5 1 3 27000000>;
status = "okay";
xlnx,max-lanes = <2>;
};
&xlnx_dpdma {
status = "okay";
};
&gem3 {
status = "okay";
xlnx,ptp-enet-clock = <0x0>;
phy-mode = "rgmii-id";
phy-handle = <&phy9>;
phy9: phy@9 {
reg = <0x9>;
ti,rx-internal-delay = <0x8>;
ti,tx-internal-delay = <0xa>;
ti,fifo-depth = <0x1>;
ti,rxctrl-strap-worka;
};
};
&fpd_dma_chan1 {
status = "okay";
};
&fpd_dma_chan2 {
status = "okay";
};
&fpd_dma_chan3 {
status = "okay";
};
&fpd_dma_chan4 {
status = "okay";
};
&fpd_dma_chan5 {
status = "okay";
};
&fpd_dma_chan6 {
status = "okay";
};
&fpd_dma_chan7 {
status = "okay";
};
&fpd_dma_chan8 {
status = "okay";
};
&gpio {
emio-gpio-width = <32>;
gpio-mask-high = <0x0>;
gpio-mask-low = <0x5600>;
status = "okay";
};
&gpu {
status = "okay";
};
&i2c1 {
clock-frequency = <400000>;
status = "okay";
};
&pinctrl0 {
status = "okay";
};
&qspi {
is-dual = <1>;
num-cs = <1>;
status = "okay";
spi-rx-bus-width = <0x4>;
spi-tx-bus-width = <0x4>;
flash@0 {
compatible = "n25q512a", "micron,m25p80";
spi-tx-bus-width = <0x1>;
spi-rx-bus-width = <0x4>;
reg = <0x0>;
#address-cells = <0x1>;
#size-cells = <0x1>;
spi-max-frequency = <0x66ff300>;

partition@0x00000000 {
label = "boot";
reg = <0x0 0x100000>;
};

partition@0x00100000 {
label = "bootenv";
reg = <0x100000 0x40000>;
};

partition@0x00140000 {
label = "kernel";
reg = <0x140000 0x1600000>;
};
};
};
&rtc {
status = "okay";
};
&sata {
ceva,p0-burst-params = "/bits/ 8 <0x13 0x08 0x4A 0x06>";
ceva,p0-cominit-params = "/bits/ 8 <0x18 0x40 0x18 0x28>";
ceva,p0-comwake-params = "/bits/ 8 <0x06 0x14 0x08 0x0E>";
ceva,p0-retry-params = "/bits/ 16 <0x96A4 0x3FFC>";
status = "okay";
};
&sdhci0 {
clock-frequency = <199998000>;
status = "okay";
xlnx,mio_bank = <0x0>;
disable-wp;
};
&sdhci1 {
clock-frequency = <199998000>;
status = "okay";
xlnx,mio_bank = <0x1>;
max-frequency = <50000000>;
no-1-8-v;
disable-wp;
};
&serdes {
status = "okay";
};
&uart0 {
device_type = "serial";
port-number = <0>;
status = "okay";
u-boot,dm-pre-reloc ;
};
&uart1 {
device_type = "serial";
port-number = <1>;
status = "okay";
u-boot,dm-pre-reloc ;
};
&dwc3_0 {
dr_mode = "host";
status = "okay";
};
&watchdog0 {
status = "okay";
};
&ams_ps {
status = "okay";
};
&ams_pl {
status = "okay";
};
&xilinx_drm {
status = "okay";
};
&xlnx_dp_sub {
status = "okay";
};
&xlnx_dp_snd_pcm0 {
status = "disabled";
};
&xlnx_dp_snd_pcm1 {
status = "disabled";
};
&xlnx_dp_snd_card {
status = "disabled";
};
&xlnx_dp_snd_codec0 {
status = "disabled";
};
&usb0 {
status = "okay";
};


この Device Tree の詳細について以下の節で説明します。


sdhci0 および sdhci1 に disable-wp

これは「UltraZed で SD Card が書き込み不可になってしまう問題」 で説明したとおりです。もともとは Avnet のボード設定ファイルに問題があったのですが、もうすでにそのボード設定ファイルを使ったデザインがあり、もしそのデザインを使って FSBL を作ったら SD Card が書き込み不可になってしまってブートに失敗してしまいます。その予防策として、あえて、Device Tree にも disable-wp を追加しています。


arch/arm64/boot/dts/xilinx/zynqmp-uz3eg-iocc.dts

&sdhci0 {

clock-frequency = <199998000>;
status = "okay";
xlnx,mio_bank = <0x0>;
disable-wp;
};
&sdhci1 {
clock-frequency = <199998000>;
status = "okay";
xlnx,mio_bank = <0x1>;
max-frequency = <50000000>;
no-1-8-v;
disable-wp;
};


Ethernet の Phy の物理アドレス

当初、Phy の物理アドレスは「UltraZed™-EG SOM Hardware User Guide Version 1.1」の「2.5.1 Ethernet PHY Strapping Resistors」の記述に基づいて <0x01> を設定していましたが、認識されませんでした。どうやら物理アドレスは <0x09> が正しいようです。


arch/arm64/boot/dts/xilinx/zynqmp-uz3eg-iocc.dts

&gem3 {

status = "okay";
xlnx,ptp-enet-clock = <0x0>;
phy-mode = "rgmii-id";
phy-handle = <&phy9>;
phy9: phy@9 {
reg = <0x9>;
ti,rx-internal-delay = <0x8>;
ti,tx-internal-delay = <0xa>;
ti,fifo-depth = <0x1>;
ti,rxctrl-strap-worka;
};
};


サウンド関係をディセーブル

私が構築した環境では、何故かサウンド関係(xlnx_dp_snd_pcm0、xlnx_dp_snd_pcm1、xlnx_dp_snd_card、xlnx_dp_snd_codec0)を有効にすると、カーネルのブート時に PLL 関連で カーネルがパニックしました。問題の原因はまだ不明ですが、とりあえずサウンド関係は使わないだろうと判断してディセーブルにしています。


arch/arm64/boot/dts/xilinx/zynqmp-uz3eg-iocc.dts

&xlnx_dp_snd_pcm0 {

status = "disabled";
};
&xlnx_dp_snd_pcm1 {
status = "disabled";
};
&xlnx_dp_snd_card {
status = "disabled";
};
&xlnx_dp_snd_codec0 {
status = "disabled";
};


scripts/package/builddeb


arm64 用のパッケージが出来ない問題

これは Debian のパッケージを作るスクリプトです。チェックアウトした xilinx-v2017.3 にはこのスクリプトファイルに問題があって、Debain のパッケージを作る際に arm64 用ではなくクロスコンパイルを実行したプラットフォーム用のパッケージが出来てしまいます。

具体的には次のような箇所が問題です。


scripts/package/builddeb

set_debarch() {

# Attempt to find the correct Debian architecture
case "$UTS_MACHINE" in
:
(中略)
:
arm64)
debarch=arm64 ;;
:
(中略)
:
*)
debarch=$(dpkg --print-architecture)
echo "" >&2
echo "** ** ** WARNING ** ** **" >&2
echo "" >&2
echo "Your architecture doesn't have it's equivalent" >&2
echo "Debian userspace architecture defined!" >&2
echo "Falling back to using your current userspace instead!" >&2
echo "Please add support for $UTS_MACHINE to ${0} ..." >&2
echo "" >&2
esac

$UTS_MACHINEに定義されたアーキテクチャに基づいて Debian パッケージを作るのですが、ZynqMP の場合、arch/arm64/Makefile によると、$UTS_MACHINEは arm64 ではなく aarch64 になります。

したがって scripts/package/builddeb を次のように修正する必要があります。


scripts/package/builddeb

    aarch64*|arm64*)

debarch=arm64 ;;

このパッチを当てる差分ファイルが files/linux-xlnx-v2017.3-builddeb.diff です。

files/linux-xlnx-v2017.3-builddeb.diff


files/linux-xlnx-v2017.3-builddeb.diff

diff --git scripts/package/builddeb scripts/package/builddeb

index 8ea9fd2..11d61a2 100755
--- scripts/package/builddeb
+++ scripts/package/builddeb
@@ -51,7 +51,7 @@ set_debarch() {
debarch=hppa ;;
mips*)
debarch=mips$(grep -q CPU_LITTLE_ENDIAN=y $KCONFIG_CONFIG && echo el || true) ;;
- arm64)
+ aarch64*|arm64*)
debarch=arm64 ;;
arm*)
if grep -q CONFIG_AEABI=y $KCONFIG_CONFIG; then


ちなみに、メインラインの方の Linux Kernel にはこの修正がすでに入っています。そのうち linux-xlnx にも反映されると思います。


ヘッダーパッケージにホスト用の実行ファイルが含まれてしまう問題

詳細は以下の記事を参照してください。

このパッチを当てる差分ファイルが files/linux-xlnx-v2017.3-builddeb2.diff です。

files/linux-xlnx-v2017.3-builddeb2.diff


files/linux-xlnx-v2017.3-builddeb2.diff

diff --git scripts/package/builddeb scripts/package/builddeb

index 11d61a2..c52aa28 100755
--- scripts/package/builddeb
+++ scripts/package/builddeb
@@ -325,7 +325,7 @@ fi

# Build kernel header package
(cd $srctree; find . -name Makefile\* -o -name Kconfig\* -o -name \*.pl) > "$objtree/debian/hdrsrcfiles"
-(cd $srctree; find arch/*/include include scripts -type f) >> "$objtree/debian/hdrsrcfiles"
+(cd $srctree; find arch/*/include include tools/include scripts -type f) >> "$objtree/debian/hdrsrcfiles"
(cd $srctree; find arch/$SRCARCH -name module.lds -o -name Kbuild.platforms -o -name Platform) >> "$objtree/debian/hdrsrcfiles"
(cd $srctree; find $(find arch/$SRCARCH -name include -o -name scripts -type d) -type f) >> "$objtree/debian/hdrsrcfiles"
if grep -q '^CONFIG_STACK_VALIDATION=y' $KCONFIG_CONFIG ; then
@@ -342,6 +342,15 @@ mkdir -p "$destdir"
(cd $objtree; cp $KCONFIG_CONFIG $destdir/.config) # copy .config manually to be where it's expected to be
ln -sf "/usr/src/linux-headers-$version" "$kernel_headers_dir/lib/modules/$version/build"
rm -f "$objtree/debian/hdrsrcfiles" "$objtree/debian/hdrobjfiles"
+mkdir -m 755 -p "$kernel_headers_dir/DEBIAN"
+cat <<EOF >> $kernel_headers_dir/DEBIAN/postinst
+#!/bin/sh -e
+
+make -C /usr/src/linux-headers-$version scripts
+
+EOF
+
+chmod 755 $kernel_headers_dir/DEBIAN/postinst

cat <<EOF >> debian/control




参考