MPFS-DISCO-KIT 向け Ubuntu 22.04 の構築(Linux Kernel mpfs-fpga-first 編)


## はじめに

MPFS-DISCO-KIT(Microchip PolarFire SoC FPGA Discovery Kit) で動作する Ubuntu 22.04 を構築する方法をいくつかの記事に分けて説明します。

 * [イントロ編](https://qiita.com/ikwzm/items/1866dc3bb5292fd1f6e1)
 * [HSS(Hart Software Services)編](https://qiita.com/ikwzm/items/3d6d0a645dd60aba90d2)
 * [U-Boot 標準編](https://qiita.com/ikwzm/items/8b6e78e1c5599d9d375d)
 * [U-Boot bootmenu 改造編](https://qiita.com/ikwzm/items/72f75ec704dd227f9cbd)
 * [Linux Kernel linux4microchip+fpga-2024.09 編](https://qiita.com/ikwzm/items/0ca6aea057a3696bd5f0)
 * __Linux Kernel mpfs-fpga-first 編 (この記事)__
 * [Ubuntu 22.04 Root File System 編](https://qiita.com/ikwzm/items/a3d1d5a1a9640b400872)
 * [SD-Card 作成編](https://qiita.com/ikwzm/items/19af3dd7a34ea5364683)
 * [SD-Card 起動編](https://qiita.com/ikwzm/items/42a1aa7b355734a813f8)

上記の記事で構築した MPFS-DISCO-KIT 向け Ubuntu 22.04 Root File System、Linux Kernel、Boot Loader は次の URL にて公開しています。なお、これらはオフシャルなものではなく、筆者の魔改造がはいっています。ご使用の際はこの点にご留意してください。

 * https://github.com/ikwzm/MPFS-FPGA-Ubuntu22.04
 * https://github.com/ikwzm/MPFS-FPGA-Linux-Kernel-6.6
 * https://github.com/ikwzm/MPFS-DISCO-KIT-U-Boot


筆者は Linux Kernel や Device Tree をいろいろと改造することが多いです。
その度に linux4microchip(Microchip Technology 社が提供する linux Kernel) にパッチをあててビルドするのは面倒なので、筆者専用のディストリビューションを作っています。

この記事では、そのディストリビューション(Linux Kernel mpfs-fpga-first) の解説とビルド方法について説明します。

なお、この記事で紹介する Linux Kernel mpfs-fpga-first のビルド済みのファイルは以下の URL にあります。ビルドが面倒な方はどうぞ。

 * https://github.com/ikwzm/MPFS-FPGA-Linux-Kernel-6.6/tree/6.6.51-mpfs-fpga-first-1

## Linux Kernel mpfs-fpga-first とは

Linux Kernel mpfs-fpga-first は、筆者が MPFS-DISCO-KIT 向け Ubuntu 22.04 で使う Linux Kernel のディストリビューションです。Microchip Technology 社が提供する linux Kernel linux4microchip とは以下の点で異っています。

### linux4microchip+fpga-2024.09 との違い

#### 元のソースコード

linux4microchip+fpga-2024.09 は [Linux Kernel linux4microchip+fpga-2024.09 編](https://qiita.com/ikwzm/items/0ca6aea057a3696bd5f0) で説明したように、https://github.com/linux4microchip/linux の linux4microchip+fpga-2024.09 を元にしています。

mpfs-fpga-first は linux-stable.git の v6.6.51 を元に、linux4microchip+fpga-2024.09 相当になるようにパッチをあてています。

#### コンフィギュレーション定義ファイル

linux4microchip+fpga-2024.09 は、linux4microchip についてくるコンフィギュレーション定義ファイル mpfs_defconfig を使います。
mpfs-fpga-first は専用のコンフィギュレーション定義ファイル mpfs_fpga_first_defconfig を用意しています。

#### バージョン名

linux4microchip+fpga-2024.09 の Makefile に定義してある EXTRAVERSION は-linux4microchip+fpga-2024.09 です。
また、linux4microchip+fpga-2024.09 のコンフィギュレーション定義ファイル mpfs_defconfig には CONFIG_LOCALVERSION は定義されていません。

mpfs-fpga-first の Makefile の EXTRAVERSION は -mpfs-fpga です。
また、mpfs-fpga-first は専用のコンフィギュレーション定義ファイル mpfs_fpga_first_defconfig を用意しており、CONFIG_LOCALVERSION に "-first" を定義しています。

#### u-dma-buf の削除

u-dma-buf は筆者が開発しているデバイスドライバで、 Linux のカーネル空間に連続したメモリ領域をDMAバッファとして確保し、ユーザー空間からアクセス可能にします。詳細は以下の URL を参照してください。

  * https://github.com/ikwzm/udmabuf

linux4microchip+fpga-2024.09 の drivers/dma-buf には u-dma-buf がデフォルトで組み込まれています。
しかし、あらかじめ u-dma-buf が組込まれていると筆者が u-dma-buf の実験や開発をする際に不便なので mpfs-fpga-first では組み込まないように変更しています。組み込みたい場合は Device Tree Overlay を使って Linux 起動後に動的に組み込むようにします。

また、Device Tree(arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts) からも u-dma-buf のノードを削除しています。

#### Device Tree の Fabric ノードを空に

linux4microchip+fpga-2024.09 の MPFS-DISCO-KIT 用の Device Tree には何故か Fabric にデバイスが追加されています。具体的には、arch/riscv/boot/dts/microchip/mpfs-disco-kit-fabric.dtsi が次のように fabric-bus@40000000 に core_pwm0、fpgadma、fpgalsram、i2c2 が追加されています。

<details><summary>arch/riscv/boot/dts/microchip/mpfs-disco-kit-fabric.dts (長いので折りたたみ)</summary>

```:arch/riscv/boot/dts/microchip/mpfs-disco-kit-fabric.dtsi
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Copyright (c) 2020-2021 Microchip Technology Inc */

#include "dt-bindings/mailbox/miv-ihc.h"

/ {
	compatible = "microchip,mpfs-disco-kit", "microchip,mpfs";

	fabric-bus@40000000 {
		compatible = "simple-bus";
		#address-cells = <2>;
		#size-cells = <2>;
		ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, /* FIC3-FAB */
			 <0x0 0x60000000 0x0 0x60000000 0x0 0x20000000>, /* FIC0, LO */
			 <0x0 0xe0000000 0x0 0xe0000000 0x0 0x20000000>, /* FIC1, LO */
			 <0x20 0x0 0x20 0x0 0x10 0x0>, /* FIC0,HI */
			 <0x30 0x0 0x30 0x0 0x10 0x0>; /* FIC1,HI */

		core_pwm0: pwm@40000000 {
			compatible = "microchip,corepwm-rtl-v4";
			reg = <0x0 0x40000000 0x0 0xF0>;
			microchip,sync-update-mask = /bits/ 32 <0>;
			#pwm-cells = <3>;
			clocks = <&ccc_nw CLK_CCC_PLL0_OUT3>;
			status = "disabled";
		};

		fpgadma: dma-controller@60010000 {
			compatible = "microchip,mpfs-fpga-dma";
			reg = <0x0 0x60010000 0x0 0x1000>;
			interrupt-parent = <&plic>;
			interrupts = <120>;
			#dma-cells = <1>;
			status = "disabled";
		};

		fpgalsram: uio@60000000 {
			compatible = "generic-uio";
			linux,uio-name = "fpga_lsram";
			reg = <0x0 0x60000000 0x0 0x1000>;
			status = "disabled";
		};

		i2c2: i2c@40000200 {
			compatible = "microchip,corei2c-rtl-v7";
			reg = <0x0 0x40000200 0x0 0x100>;
			#address-cells = <1>;
			#size-cells = <0>;
			clocks = <&ccc_nw CLK_CCC_PLL0_OUT3>;
			interrupt-parent = <&plic>;
			interrupts = <122>;
			clock-frequency = <100000>;
			status = "disabled";
		};
	};

	ihc: mailbox {
		compatible = "microchip,miv-ihc";
		interrupt-parent = <&plic>;
		interrupts = <IHC_HART1_INT>;
		microchip,miv-ihc-remote-context-id = <IHC_CONTEXT_B>;
		#mbox-cells = <1>;
		status = "disabled";
	};

	mpfs_dma_proxy: mpfs-dma-proxy {
		compatible = "microchip,mpfs-dma-proxy";
		dmas = <&pdma 0>, <&pdma 1>, <&pdma 2>, <&pdma 3>;
		dma-names = "dma-proxy0", "dma-proxy1", "dma-proxy2", "dma-proxy3";
	};
};
```

</details>

このままだと、別の Fabric をプログラムした時にデバイスドライバがバッティングして危険です。そこで mpfs-fpga-first では次のように Fabric ノードを空にしておきます。

<details><summary>arch/riscv/boot/dts/microchip/mpfs-disco-kit-fabric.dts (長いので折りたたみ)</summary>

```:arch/riscv/boot/dts/microchip/mpfs-disco-kit-fabric.dtsi
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Copyright (c) 2020-2021 Microchip Technology Inc */

/ {
	compatible = "microchip,mpfs-disco-kit", "microchip,mpfs";

	fabric-bus@40000000 {
		compatible = "simple-bus";
		#address-cells = <2>;
		#size-cells = <2>;
		ranges = <0x0 0x40000000 0x0 0x40000000 0x0 0x20000000>, /* FIC3-FAB */
			 <0x0 0x60000000 0x0 0x60000000 0x0 0x20000000>, /* FIC0, LO */
			 <0x0 0xe0000000 0x0 0xe0000000 0x0 0x20000000>, /* FIC1, LO */
			 <0x20 0x0 0x20 0x0 0x10 0x0>, /* FIC0,HI */
			 <0x30 0x0 0x30 0x0 0x10 0x0>; /* FIC1,HI */
	};
};
```

</details>

さらに arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts から Fabric から削除したノード(core_pwm0、fpgadma、fpgalsram、i2c2) を削除しています。

<details><summary>arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts (長いので折りたたみ)</summary>

```:arch/riscv/boot/dts/microchip/mpfs-disco-kit.dts
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/* Copyright (c) 2020-2021 Microchip Technology Inc */

/dts-v1/;

#include "mpfs.dtsi"
#include "mpfs-disco-kit-fabric.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/leds/common.h>

/* Clock frequency (in Hz) of the rtcclk */
#define RTCCLK_FREQ		   1000000

/ {
	#address-cells = <2>;
	#size-cells = <2>;
	model = "Microchip PolarFire-SoC Discovery Kit";
	compatible = "microchip,mpfs-disco-kit", "microchip,mpfs";

	soc {
		dma-ranges = <0x14 0x0 0x0 0x80000000 0x0 0x4000000>,
			     <0x14 0x4000000 0x0 0xc4000000 0x0 0x6000000>,
			     <0x14 0xa000000 0x0 0x8a000000 0x0 0x8000000>,
			     <0x14 0x12000000 0x14 0x12000000 0x0 0x10000000>,
			     <0x14 0x22000000 0x10 0x22000000 0x0 0x1e000000>;
	};

	aliases {
		ethernet0 = &mac0;
		serial4 = &mmuart4;
	};

	chosen {
		stdout-path = "serial4:115200n8";
	};

	cpus {
		timebase-frequency = <RTCCLK_FREQ>;
	};

	leds {
		compatible = "gpio-leds";

		led-1 {
			gpios = <&gpio2 17 GPIO_ACTIVE_HIGH>;
			color = <LED_COLOR_ID_AMBER>;
			label = "led1";
		};

		led-2 {
			gpios = <&gpio2 18 GPIO_ACTIVE_HIGH>;
			color = <LED_COLOR_ID_RED>;
			label = "led2";
		};

		led-3 {
			gpios = <&gpio2 19 GPIO_ACTIVE_HIGH>;
			color = <LED_COLOR_ID_AMBER>;
			label = "led3";
		};

		led-4 {
			gpios = <&gpio2 20 GPIO_ACTIVE_HIGH>;
			color = <LED_COLOR_ID_RED>;
			label = "led4";
		};

		led-5 {
			gpios = <&gpio2 21 GPIO_ACTIVE_HIGH>;
			color = <LED_COLOR_ID_AMBER>;
			label = "led5";
		};

		led-6 {
			gpios = <&gpio2 22 GPIO_ACTIVE_HIGH>;
			color = <LED_COLOR_ID_RED>;
			label = "led6";
		};

		led-7 {
			gpios = <&gpio2 23 GPIO_ACTIVE_HIGH>;
			color = <LED_COLOR_ID_AMBER>;
			label = "led7";
		};

		led-8 {
			gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
			color = <LED_COLOR_ID_RED>;
			label = "led8";
		};
	};

	kernel: memory@80000000 {
		device_type = "memory";
		reg = <0x0 0x80000000 0x0 0x4000000>;
	};

	ddr_cached_low: memory@8a000000 {
		device_type = "memory";
		reg = <0x0 0x8a000000 0x0 0x8000000>;
	};

	ddr_non_cached_low: memory@c4000000 {
		device_type = "memory";
		reg = <0x0 0xc4000000 0x0 0x6000000>;
	};

	ddr_cached_high: memory@1022000000 {
		device_type = "memory";
		reg = <0x10 0x22000000 0x0 0x1e000000>;
	};

	ddr_non_cached_high: memory@1412000000 {
		device_type = "memory";
		reg = <0x14 0x12000000 0x0 0x10000000>;
	};

	reserved-memory {
		#address-cells = <2>;
		#size-cells = <2>;
		ranges;

		hss: hss-buffer@103fc00000 {
			compatible = "shared-dma-pool";
			reg = <0x10 0x3fc00000 0x0 0x200000>;
			no-map;
		};

		dma_non_cached_low: non-cached-low-buffer {
			compatible = "shared-dma-pool";
			size = <0x0 0x4000000>;
			no-map;
			alloc-ranges = <0x0 0xc4000000 0x0 0x4000000>;
		};

		dma_non_cached_high: non-cached-high-buffer {
			compatible = "shared-dma-pool";
			size = <0x0 0x10000000>;
			no-map;
			linux,dma-default;
			alloc-ranges = <0x14 0x12000000 0x0 0x10000000>;
		};

		fabricbuf0ddrc: buffer@88000000 {
			compatible = "shared-dma-pool";
			reg = <0x0 0x88000000 0x0 0x2000000>;
			no-map;
		};

		fabricbuf1ddrnc: buffer@c8000000 {
			compatible = "shared-dma-pool";
			reg = <0x0 0xc8000000 0x0 0x2000000>;
			no-map;
		 };

		fabricbuf2ddrncwcb: buffer@d8000000 {
			compatible = "shared-dma-pool";
			reg = <0x0 0xd8000000 0x0 0x2000000>;
			no-map;
		};
	};
};

&gpio1 {
	status = "okay";
};

&gpio2 {
	status = "okay";
};

&i2c0 {
	status = "okay";
};

&mac0 {
	dma-noncoherent;
	status = "okay";
	phy-mode = "sgmii";
	phy-handle = <&phy0>;

	phy0: ethernet-phy@b {
		reg = <0xb>;
	};
};

&mbox {
	status = "okay";
};

&mmc {
	dma-noncoherent;
	bus-width = <4>;
	disable-wp;
	cap-sd-highspeed;
	cap-mmc-highspeed;
	sd-uhs-sdr12;
	sd-uhs-sdr25;
	sd-uhs-sdr50;
	sd-uhs-sdr104;
	no-1-8-v;
	status = "okay";
};

&mmuart1 {
	status = "okay";
};

&mmuart4 {
	status = "okay";
};

&refclk {
	clock-frequency = <125000000>;
};

&rtc {
	status = "okay";
};

&spi0 {
	status = "okay";
};

&spi1 {
	status = "okay";
};

&syscontroller {
	status = "okay";
};
```

</details>

こうしておいて、新に Fabric をプログラムした時は Device Tree Overlay を使って Linux 起動後に動的に組み込むようにします。

#### Linux Headers Debian Package ビルドスクリプトの修正

以下の記事にて、クロスコンパイルした Debian GNU/Linux や Ubuntu でデバイスドライバを、実機でセルフコンンパイルする際の注意点を紹介しました。

 * [『クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点(Linux Kernel 5.x編)』 @Qiita](https://qiita.com/ikwzm/items/0aa165fa21fa9cca15c0)
 * [『クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点』 @Qiita](https://qiita.com/ikwzm/items/e31ecb099d0d50cb027c)

上の記事は Linux Kernel 4.x および Linux Kernel 5.x を対象にしたものでしたが、Linux Kernel 6.6 ではまた少し変更が必要でした。そこで以下のような修正スクリプトを用意しました。

```diff:patches/linux-6.6.51-mpfs-fpga-builddeb.diff
diff --git a/Makefile b/Makefile
index f9d7b55e7..a98e4f848 100644
--- a/Makefile
+++ b/Makefile
@@ -1200,6 +1200,11 @@ prepare0: archprepare
 	$(Q)$(MAKE) $(build)=scripts/mod
 	$(Q)$(MAKE) $(build)=. prepare
 
+# linux-headers debian package postinst
+PHONY += linux-headers-postinst
+linux-headers-postinst: archscripts scripts
+	$(Q)$(MAKE) $(build)=scripts/mod
+
 # All the preparing..
 prepare: prepare0
 ifdef CONFIG_RUST
diff --git a/scripts/package/builddeb b/scripts/package/builddeb
index d7dd0d04c..418ad3a67 100755
--- a/scripts/package/builddeb
+++ b/scripts/package/builddeb
@@ -163,6 +163,27 @@ install_kernel_headers () {
 
 	"${srctree}/scripts/package/install-extmod-build" "${pdir}/usr/src/linux-headers-${version}"
 
+	# create postinst 
+	if [ "$cross_compiling" -eq 1 ]; then
+		(
+			cd "${srctree}"
+			find . -name Makefile\* -o -name Kconfig\* -o -name \*.pl
+			find include tools/include security/selinux/include -type f -o -type l
+			find scripts -type f -o -type l
+			find arch/$SRCARCH -name Kbuild.platforms -o -name Platform
+			find $(find arch/$SRCARCH -name include -o -name scripts -o -name tools -type d) -type f
+		) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${pdir}/usr/src/linux-headers-${version}"
+		find "${pdir}/usr/src/linux-headers-${version}" -type f -exec file {} + | grep 'ELF' | cut -d: -f1 | xargs rm -f
+
+		mkdir -m 755 -p "$pdir/DEBIAN"
+		cat <<EOF >> $pdir/DEBIAN/postinst
+#!/bin/sh -e
+
+make -C /usr/src/linux-headers-$version linux-headers-postinst
+
+EOF
+		chmod 755 $pdir/DEBIAN/postinst
+	fi
 	mkdir -p $pdir/lib/modules/$version/
 	ln -s /usr/src/linux-headers-$version $pdir/lib/modules/$version/build
 }
```

この修正により、実機に Linux Headers Debian Package をインストールした際に、自動的に実機のアーキテクチャでツールのビルドが行われるようになります。

## Linux Kernel の構築

### 必要な環境

* riscv64-unknown-linux-gnu-

### Linux Kernel 構築環境の準備

```console
shell$ git clone --depth 1 --branch develop-6.6.51-mpfs-fpga-first https://github.com/ikwzm/MPFS-FPGA-Linux-Kernel-6.6.git develop-6.6.51-mpfs-fpga-first
shell$ cd develop-6.6.51-mpfs-fpga-first
```

### linux-stable.git から v6.6.51 をダウンロード

```console
shell$ git clone --depth 1 -b v6.6.51 git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git linux-6.6.51-mpfs-fpga-first
```

### ビルド用ブランチの生成

```console
shell$ cd linux-6.6.51-mpfs-fpga-first
shell$ git checkout -b linux-6.6.51-mpfs-fpga-first refs/tags/v6.6.51
```

### Patch for linux-6.6.51-mpfs-fpga

ダウンロードした linux-stable を linux4microchip+fpga-2024.09 相当になるようにパッチをあてます。

```console
shell$ sh ../patches/linux-6.6.51-mpfs-fpga/first_patch.sh
```
### Patch for builddeb

実機でセルフコンパイルできるように Linux Headers Debian Package のビルドスクリプトを修正します。

```console
shell$ patch -p1 < ../patches/linux-6.6.51-mpfs-fpga-builddeb.diff
shell$ git add --all
shell$ git commit -m "[update] scripts/package/builddeb to add tools/include and postinst script to header package."
```

### Add mpfs_fpga_first_defconfig

```console
shell$ cp ../files/mpfs_fpga_first_defconfig arch/riscv/configs/
shell$ git add arch/riscv/configs/mpfs_fpga_first_defconfig
shell$ git commit -m "[add] mpfs_fpga_first_defconfig to arch/riscv/configs"
```

### Create tag and .version

ビルド前に commit にタグをつけます。

```console
shell$ git tag -a v6.6.51-mpfs-fpga -m "release v6.6.51-mpfs-fpga-first"
shell$ echo 1 > .version
```

なお、Linux Kernel 6.6 からは、タグの名前を ```v${KERNELVERSION}``` としておくと、生成された Debian Package の名前によけいなコミット番号がつかなくてきれいになります。```${KERNELVERSION}``` は次のように得ることができます。

```console
shell$ make kernelversion
6.6.51-mpfs-fpga
```

### Setup for Build 

````console
shell$ cd linux-6.6.51-mpfs-fpga-first
shell$ export ARCH=riscv
shell$ export CROSS_COMPILE=riscv64-unknown-linux-gnu-
shell$ make mpfs_fpga_first_defconfig
````

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

````console
shell$ export DTC_FLAGS=--symbols
shell$ make deb-pkg
````

### 構築したカーネルのイメージとデバイスツリーを ビルド用のディレクトリにコピー

```console
shell$ cp arch/riscv/boot/Image.gz ../vmlinuz-6.6.51-mpfs-fpga-first-1
shell$ install -d                  ../files
shell$ cp .config                  ../files/config-6.6.51-mpfs-fpga-first-1
```

```console
shell$ install -d                         ../devicetrees/6.6.51-mpfs-fpga-first-1
shell$ cp arch/riscv/boot/dts/microchip/* ../devicetrees/6.6.51-mpfs-fpga-first-1
```

### ディレクトリ構造とファイル

最終的に次のようなディレクトリ構造になります。

 * files/
   + mpfs_fpga_first_defconfig
   + __config-6.6.51-mpfs-fpga-first-1 (ビルドした成果物)__
 * patches/
   + linux-6.6.51-mpfs-fpga/
     - first_patch.sh
     - *.patch
   + linux-6.6.51-mpfs-fpga-builddeb.diff
 * __vmlinuz-6.6.51-mpfs-fpga-first-1 (ビルドした成果物)__
 * devicetrees/
   + __6.6.51-mpfs-fpga-first-1/ (ビルドした成果物)__
     - mpfs.dtsi
     - mpfs-disco-kit-fabric.dtsi
     - mpfs-disco-kit.dts
     - mpfs-disco-kit.dtb
 * __linux-image-6.6.51-mpfs-fpga-first_6.6.51-mpfs-fpga-1_riscv64.deb (ビルドした成果物)__
 * __linux-headers-6.6.51-mpfs-fpga-first_6.6.51-mpfs-fpga-1_riscv64.deb (ビルドした成果物)__


## 参考

 * https://github.com/ikwzm/MPFS-FPGA-Linux-Kernel-6.6
 * [『クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点(Linux Kernel 5.x編)』 @Qiita](https://qiita.com/ikwzm/items/0aa165fa21fa9cca15c0)
 * [『クロスコンパイルした Debian GNU/Linux でデバイスドライバをセルフコンパイルする際の注意点』 @Qiita](https://qiita.com/ikwzm/items/e31ecb099d0d50cb027c)