MPFS-DISCO-KIT 向け Ubuntu 22.04 の構築(SD-Card 作成編)


## はじめに

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 編](https://qiita.com/ikwzm/items/d7405466ed7391d4dd73)
 * [Ubuntu 22.04 Root File System 編](https://qiita.com/ikwzm/items/a3d1d5a1a9640b400872)
 * __SD-Card 作成編 (この記事)__
 * [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


この記事では、SD-Card に U-Boot、Linux Kernel、Ubuntu 22.04 Root File System を書き込む方法を説明します。

## 必要な環境

私は次の環境を用意して SD-Card に書き込みました。

 * Ubuntu 22.04
   + gdisk
   + dd
   + mkfs.vfat
   + mkfs.ext4

## SD-Card に書き込むファイルの説明

SD-Card に書き込むファイルとして以下のもの用意します。

 * target/mpfs-disco-kit
   + u-boot/
     - uboot.img                                                : U-Boot Image 
   + boot/
     - uboot.env                                                : U-Boot environment 
     - uEnv.txt                                                 : U-Boot environment variables for linux boot
     - boot.scr                                                 : U-Boot Script file
     - devicetree-6.6.51-mpfs-fpga-first-mpfs-disco-kit.dtb     : Linux Device Tree Blob   
     - devicetree-6.6.51-mpfs-fpga-first-mpfs-disco-kit.dts     : Linux Device Tree Blob
 * files/
     - vmlinuz-6.6.51-mpfs-fpga-first-1                         : Linux Kernel Image
 * ubuntu22.04-rootfs.tgz.files/                                : Ubuntu22.04 Root File System
   + x00 .. x09                                                 : (splited files)
 * debian/
   - linux-image-6.6.51-mpfs-fpga-first_6.6.51-mpfs-fpga-1_riscv64.deb   : Linux Image Package
   - linux-headers-6.6.51-mpfs-fpga-first_6.6.51-mpfs-fpga-1_riscv64.deb : Linux Headers Package


上記のファイル群は以下の URL で提供しています。一から作るのが面倒なかたは、こちらからダウンロードしてください。

 * https://github.com/ikwzm/MPFS-FPGA-Ubuntu22.04/tree/v5.0.2

### target/mpfs-disco-kit/u-boot/uboot.img

MPFS-DISCO-KIT 用の u-boot のイメージファイルです。
HSS(Hart Software Services) から起動するため、HSS Payload Generator で生成され、ブート専用のパーティションに書き込まれます。
作りかたは、[『U-Boot bootmenu 改造編』](https://qiita.com/ikwzm/items/72f75ec704dd227f9cbd) を参照してください。

### target/mpfs-disco-kit/boot/uboot.env

MPFS-DISCO-KIT 用の uboot.img が起動時に設定する環境変数のファイルです。
uboot.img から読まれるため、VFAT でフォーマットされたパーティション1 に書き込まれます。
作りかたは、[『U-Boot bootmenu 改造編』](https://qiita.com/ikwzm/items/72f75ec704dd227f9cbd) を参照してください。

### target/mpfs-disco-kit/boot/boot.scr

MPFS-DISCO-KIT 用の uboot.img が boot コマンド実行の際に実行されるスクリプトファイルです。
uEnv.txt が無い場合、または uEnv.txt で uenvcmd 変数が定義されていない場合に実行されます。
uboot.img から読まれるため、VFAT でフォーマットされたパーティション1 に書き込まれます。
作りかたは、[『U-Boot bootmenu 改造編』](https://qiita.com/ikwzm/items/72f75ec704dd227f9cbd) を参照してください。

### target/mpfs-disco-kit/boot/uEnv.txt

MPFS-DISCO-KIT 用の uboot.img が起動時に設定する環境変数のファイルです。
uboot.img から読まれるため、VFAT でフォーマットされたパーティション1 に書き込まれます。
uboot.env と異り修正しやすいテキストファイルになっています。
このファイルに uenvcmd 変数を定義しておくと、boot.scr より優先して uenvcmd 変数で定義されたコマンドを実行します。

ここでは次のような uEnv.txt を用意しています。

```text:target/mpfs-disco-kit/boot/uEnv.txt
########################################################################
# Linux Kernel Files
#  * linux_kernel_image : Linux Kernel Image File Name
#  * linux_fdt_image    : Linux Device Tree Blob File Name
########################################################################
linux_kernel_image=vmlinux-6.6.51-mpfs-fpga-first
linux_fdt_image=devicetree-6.6.51-mpfs-fpga-first-mpfs-disco-kit.dtb

########################################################################
# Linux Boot Argments
#  * linux_boot_args_console : ex) console=tty1
#                                  console=ttyPS0,115200
#  * linux_boot_args_rootfs  : ex) root=/dev/mmcblk0p2 rw rootwait
#  * linux_boot_args_systemd : ex) systemd.unit=graphical.target
#                                  systemd.unit=multi-user.target
#  * linux_boot_args_cpuidle : ex) cpuidle.off=1
#  * linux_boot_args_cma     : ex) cma=256M
#  * linux_boot_args_uio     : ex) uio=uio_pdrv_genirq.of_id=generic-uio
#  * linux_boot_args_other   :
########################################################################
linux_boot_args_console=
linux_boot_args_rootfs=root=/dev/mmcblk0p3 rw rootwait
linux_boot_args_systemd=
linux_boot_args_cpuidle=
linux_boot_args_cma=
linux_boot_args_uio=uio_pdrv_genirq.of_id=generic-uio
linux_boot_args_other=

########################################################################
# uenvcmd : During sdboot, if uenvcmd is set, uenvcmd will be executed.
########################################################################
linux_img_load_cmd=load mmc 0:1 ${kernel_addr_r} ${linux_kernel_image}
linux_fdt_load_cmd=load mmc 0:1 ${fdt_addr_r}    ${linux_fdt_image}
linux_fdt_update_cmd=fdt addr ${fdt_addr_r} ; fdt set /soc/ethernet@20110000 local-mac-address ${discokit_mac_addr0}
linux_load_cmd=env run linux_img_load_cmd && env run linux_fdt_load_cmd && env run linux_fdt_update_cmd
linux_boot_cmd=booti ${kernel_addr_r} - ${fdt_addr_r}
linux_args_cmd=if env exists linux_boot_args; then; setenv bootargs ${linux_boot_args}; else; setenv bootargs ${linux_boot_args_console} ${linux_boot_args_rootfs} ${linux_boot_args_systemd} ${linux_boot_args_cpuidle} ${linux_boot_args_cma} ${linux_boot_args_uio} ${linux_boot_args_other}; fi
uenvcmd=env run linux_args_cmd && env run linux_load_cmd && env run linux_boot_cmd

########################################################################
# external_env_file : external environment file.
# external_env_boot : import external_env_file and boot.
########################################################################
external_env_existence_test=test -e mmc 0:1 ${external_env_file}
external_env_load=load mmc 0:1 ${scriptaddr} ${external_env_file}
external_env_import=env import -t ${scriptaddr} $filesize
external_env_set=env run external_env_existence_test && env run external_env_load && env run external_env_import
external_env_boot=if env run external_env_set; then; boot; else; echo "## Error Read fail " ${external_env_file}; fi

########################################################################
# Boot Menu
########################################################################
bootmenu_0=Boot Default=boot
bootmenu_1=Boot linux-6.6.51-mpfs-fpga-first=env set external_env_file uEnv-linux-6.6.51-mpfs-fpga-first.txt && env run external_env_boot
bootdelay=5
```

 * linux_kernel_image 変数には、Linux Kernel イメージのファイル名を指定します。
   ここでは後述の vmlinuz-6.6.51-mpfs-fpga-first-1 を解凍したファイル名を指定しています。
 * linux_fdt_image 変数には Linux Kernel Device Tree Blob のファイル名を指定します。
   ここでは後述の devicetree-6.6.51-mpfs-fpga-first-mpfs-disco-kit.dtb を指定しています。
 * linux_boot_args_ で始まる変数には、Linux Kernel のブート時に Kernel に渡される引数を定義しています。
   これらの変数の内容は bootargs 変数にセットされてLinux Kernel のブート時に Kernel に渡されます。
 * uenvcmd 変数には 上記で指定したファイルを mmc 0:1(SD-Card のデバイス0 パーティション 1) からロードし、bootargs 変数をセットして booti コマンドを実行するようにしています。
 * bootmenu_0 変数には、U-boot 起動後にメニューに表示する内容と実行するコマンドを定義しています。
 * bootmenu_1 変数には、U-boot 起動後にメニューに表示する内容と実行するコマンドを定義しています。
 * bootdelay 変数には、 U-boot 起動後にメニューを表示した際にキー入力の待ち時間を秒で指定します。
   ちなみに、bootdelay 変数に 0 を指定すると、メニューを表示せずに問答無用で bootmenu_0 変数で定義されたコマンドを実行します。

### target/mpfs-disco-kit/boot/devicetree-6.6.51-mpfs-fpga-first-mpfs-disco-kit.dtb

MPFS-DISCO-KIT 用の Linux Kernel をブートする際に必要な Device Tree Blob ファイルです。
uEnv.txt で説明したように、mmc 0:1(SD-Card のデバイス0 パーティション 1) に書き込まれます。
作りかたは、[『Linux Kernel mpfs-fpga-first 編』](https://qiita.com/ikwzm/items/d7405466ed7391d4dd73) を参照してください。

### files/vmlinuz-6.6.51-mpfs-fpga-first-1

MPFS-DISCO-KIT 用の Linux Kernel をブートする際に必要な Linux Kernel Image ファイルを圧縮したものです。
uEnv.txt で説明したように、mmc 0:1(SD-Card のデバイス0 パーティション 1)に、このファイルを解凍して書き込まれます。
作りかたは、[『Linux Kernel mpfs-fpga-first 編](https://qiita.com/ikwzm/items/d7405466ed7391d4dd73) を参照してください。

### ubuntu22.04-rootfs.tgz.files/

このディレクトリには MPFS-DISCO-KIT 用の Ubuntu22.04 Root File System  を tar でまとめて gz で圧縮して 40MB 毎に細切れしたファイルが入っています。
これらのファイル群は、uEnv.txt の linux_boot_args_rootfs 変数で指定したパーティション(/dev/mmcblk0p3) に 連結して解凍して EXT4 で書き込まれます。
作りかたは、[『Ubuntu 22.04 Root File System 編』](https://qiita.com/ikwzm/items/a3d1d5a1a9640b400872) を参照してください。

### debian/linux-image-6.6.51-mpfs-fpga-first_6.6.51-mpfs-fpga-1_riscv64.deb

MPFS-DISCO-KIT 用の Linux Kernel Image の Debian Package です。
作りかたは、[『Linux Kernel mpfs-fpga-first 編](https://qiita.com/ikwzm/items/d7405466ed7391d4dd73) を参照してください。
なお、この Debian Package はすでに Ubuntu22.04 Root File System にインストール済みです。詳細は、[『Ubuntu 22.04 Root File System 編』](https://qiita.com/ikwzm/items/a3d1d5a1a9640b400872) を参照してください。

### debian/linux-headers-6.6.51-mpfs-fpga-first_6.6.51-mpfs-fpga-1_riscv64.deb

MPFS-DISCO-KIT 用の Linux Kernel Header の Debian Package です。
作りかたは、[『Linux Kernel mpfs-fpga-first 編](https://qiita.com/ikwzm/items/d7405466ed7391d4dd73) を参照してください。
なお、この Debian Package はすでに Ubuntu22.04 Root File System にインストール済みです。詳細は、[『Ubuntu 22.04 Root File System 編』](https://qiita.com/ikwzm/items/a3d1d5a1a9640b400872) を参照してください。


## SD-Card のフォーマット

### パーティションの構成

SD-Card を次のようにパーティションを構成します。

| Part No | Name    | FileSystem | Size  | Type                 | Hex code or GUID   |
|:-------:|:--------|:----------:|------:|:---------------------|:-------------------|
|   1     | boot    |    VFAT    | 256MB | Microsoft basic data | 0700               |
|   2     | primary |            |   8MB | BIOS boot partition  | 21686148-6449-6E6F-744E-656564454649 |
|   3     | rootfs  |    EXT4    |   ANY | Linux filesystem     | 8300               | 


#### パーティション1

[『U-Boot 標準編』](https://qiita.com/ikwzm/items/8b6e78e1c5599d9d375d) で説明したように、u-boot は起動後に設定する環境変数を外部ファイル(uboot.env)から読むようにビルドしています。その際、uboot.env をアクセスするファイルシステムを FAT に、デバイスを mmc に、ドライブを 0 に、パーティションを 1 にしたため、このパーティションのファイルシステムは VFAT でなければなりません。

また、パーティション1 に uboot.env だけではもったいないので、uEnv.txt、boot.scr、Linux Kernel Image ファイル、Device Tree Blob ファイルもここに置きます。
このパーティションのサイズは、上記のファイルが格納できる分を確保します。なお、筆者はいろいろと実験する都合上大きめに確保しています。

#### パーティション2

パーティション2 はファイルシステムを作らずに uboot.img を dd コマンドなどで直接書き込みます。uboot.img が格納できる容量を確保します。
HSS(Hart Software Services) から起動するため、このパーティションの Type は必ず BIOS boot partition(GUID=21686148-6449-6E6F-744E-656564454649) でなければなりません。

#### パーティション3

uEnv.txt の linux_boot_args_rootfs 変数で指定したパーティションなので、このパーティションは Ubuntu22.04 Root File System を書き込みます。
このパーティションのファイルシステムは Linux Kernel をビルドした時に有効にしておかなければなりません。
[『Linux Kernel mpfs-fpga-first 編』](https://qiita.com/ikwzm/items/d7405466ed7391d4dd73) で EXT4 を有効にしていたので、このパーティションのファイルシステムは EXT4 にします。

### gdisk & mkfs

ここでは、SD-Card のフォーマットに Ubuntu22.04 の gdisk と mkfs を使った例を示します。

#### gdisk

次のようなシェルスクリプトを用意します。

````sh:scripts/format-disk-mpfs-disco-kit.sh
#!/bin/sh

if [ -z ${DISKDEV} ]; then
    echo "Error: DISKDEV is not defined."
    exit 1
fi
if [ -f ${DISKDEV} ]; then
    echo "Error: ${DISKDEV} can not write."
    exit 1
fi

sed -e 's/\s*\([\+0-9a-zA-Z\-]*\).*/\1/' << EOF | gdisk ${DISKDEV}
          #
  o       # create a new empty GUID partition table (GPT)
  y       # Proceed? (Y/N)
  n       # add a new partition
  1       #   Partition number=1
          #   First sector=default(2048)
  +256M   #   Size=256M
  0700    #   Type=Microsoft basic data
  n       # add a new partition
  2       #   Partition number=2
          #   First sector=default
  +8M     #   Size=8M
  21686148-6449-6E6F-744E-656564454649 #   Type='BIOS boot partition'
  n       # add a new partition
  3       #   Partition number=3
          #   First sector=default
          #   Last  sector=default
  8300    #   Type=Linux filesystem
  c       # change a partition's name
  1       #   Partition number=1
  boot    #   Enter name='boot'
  c       # change a partition's name
  2       #   Partition number=2
  primary #   Enter name='primary'
  c       # change a partition's name
  3       #   Partition number=3
  rootfs  #   Enter name='rootfs'
  p       # print partitions
  w       # write table to disk and exit
  y       # Do you want to proceed? (Y/N)
EOF
````

上記のシェルスクリプトを root 権限で実行します。その際、環境変数 DISKDEV を必ず設定してください。まちがったデバイスを指定したときの責任は持ちません。

```console
shell# export DISKDEV=/dev/sdc
shell# sh scripts/format-disk-mpfs-disco-kit.sh

GPT fdisk (gdisk) version 1.0.8

Partition table scan:
  MBR: protective
  BSD: not present
  APM: not present
  GPT: present

Found valid GPT with protective MBR; using GPT.

Command (? for help): 
Command (? for help): This option deletes all partitions and creates a new protective MBR.
Proceed? (Y/N): 
Command (? for help): Partition number (1-128, default 1): First sector (34-61069278, default = 2048) or {+-}size{KMGTP}: Last sector (2048-61069278, default = 61069278) or {+-}size{KMGTP}: Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to 'Microsoft basic data'

Command (? for help): Partition number (2-128, default 2): First sector (34-61069278, default = 526336) or {+-}size{KMGTP}: Last sector (526336-61069278, default = 61069278) or {+-}size{KMGTP}: Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to 'BIOS boot partition'

Command (? for help): Partition number (3-128, default 3): First sector (34-61069278, default = 542720) or {+-}size{KMGTP}: Last sector (542720-61069278, default = 61069278) or {+-}size{KMGTP}: Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): Changed type of partition to 'Linux filesystem'

Command (? for help): Partition number (1-3): Enter name: 
Command (? for help): Partition number (1-3): Enter name: 
Command (? for help): Partition number (1-3): Enter name: 
Command (? for help): Disk /dev/sdc: 61069312 sectors, 29.1 GiB
Model: SD/MMC          
Sector size (logical/physical): 512/512 bytes
Disk identifier (GUID): F62CCEAC-1417-4974-9403-305DB94D0239
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 61069278
Partitions will be aligned on 2048-sector boundaries
Total free space is 2014 sectors (1007.0 KiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048          526335   256.0 MiB   0700  boot
   2          526336          542719   8.0 MiB     EF02  primary
   3          542720        61069278   28.9 GiB    8300  rootfs

Command (? for help): 
Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): OK; writing new GUID partition table (GPT) to /dev/sdc.
The operation has completed successfully.
```

#### mkfs

パーティション1 は VFAT で、パーテイョン3 は EXT4 でファイルシステムを作ります。

````console
shell# export DISKDEV=/dev/sdc
shell# mkfs.vfat ${DISKDEV}1
shell# mkfs.ext4 ${DISKDEV}3
````

## SD-Card への書き込み

### SD-Card のマウント

ここの例では、パーティション1 を /mnt/usb1 に、パーティション3 を /mnt/usb2 にマウントしています。

```console
shell# export DISKDEV=/dev/sdc
shell# mount ${DISKDEV}1 /mnt/usb1
shell# mount ${DISKDEV}3 /mnt/usb2
```

### パーティション2 の書き込み

パーティション2 には target/mpfs-disco-kit/u-boot/uboot.img を dd コマンドで直接書き込みます。

```console
shell# dd if=target/mpfs-disco-kit/u-boot/uboot.img of=${DISKDEV}2 bs=64k
11+1 records in
11+1 records out
741216 bytes (741 kB, 724 KiB) copied, 0.0678967 s, 10.9 MB/s
```

### パーティション1 の書き込み

パーティション1 には target/mpfs-disco-kit/boot の下にある uboot.env、boot.scr、uEnv.txt、devicetree-6.6.51-mpfs-fpga-first-mpfs-disco-kit.dtb を書き込みます。
また、files/vmlinuz-6.6.51-mpfs-fpga-first-1 を解凍して名前を vmlinux-6.6.51-mpfs-fpga-first に変更して書き込みます。

```console
shell# cp target/mpfs-disco-kit/boot/*                     /mnt/usb1
shell# gzip -d -c files/vmlinuz-6.6.51-mpfs-fpga-first-1 > /mnt/usb1/vmlinux-6.6.51-mpfs-fpga-first
```

### パーティション3 の書き込み

パーティション3 には ubuntu22.04-rootfs.tgz.files/ の中身を連結して解凍して書き込みます。

```console
shell# cat ubuntu22.04-rootfs.tgz.files/* | tar xfz - -C /mnt/usb2
```

ユーザーモードで sudo を使う場合は以下のようにしてください。

```console
shell$ sudo sh -c 'cat ubuntu22.04-rootfs.tgz.files/* | tar xfz - -C /mnt/usb2'
```

### パーティション3 の /etc/fstab にパーティション1 のマウントポイントを追加

```console
shell# cat <<EOT >> /mnt/usb2/etc/fstab
/dev/mmcblk0p1	/mnt/boot	auto	defaults	0	0
EOT
shell# mkdir /mnt/usb2/mnt/boot
```

### SD-Card のマウント解除

```console
shell# umount /mnt/usb1
shell# umount /mnt/usb2
```

## 参考

 * 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