はじめに
ZCU104 上で Dual Linux を動作させる手順について記述します。Dual Linux の実現には自作の Hypervisor を使っています。本記事では Hypervisor が提供する機能概要と、構築手順について記述します。
Hypervisor の実装は4年ほど前ですが、公開にあたりセキュリティー面で問題のある機能の削除・改修を実施しました。
Hypervisor 機能概要
公開場所
Hypervisor は GitHub にて公開しています。
URL | https://github.com/hkato-sssl/Hypervisor-OG.git |
ソース・ツリーの概要は以下の通りです。ビルドに関係無いファイルも多く存在します。
Hypervisor-OG/
|-- CMakeLists.txt
|-- LICENSE
|-- docs - 作業時のメモ
|-- projects
| |-- drivers - aarch64 用 Bare-Metal driver 一式
| |-- guest - テスト用 Guest 環境作成
| |-- hypervisor - Hypervisor 共通機能ライブラリ
| |-- libs - aarch64 用 各種ライブラリ
| `-- zcu104-linux - Main Project
|-- scripts - cmake, ld 用スクリプト
`-- work - ソースの自動生成に利用した作業場所
ビルドに必要なのは projects
配下の drivers
, libs
, hypervisor
, zcu104-linux
の4つです。 各種ライブラリとドライバを使い zcu104-linux
で Hypervisor 本体をビルドするイメージです。ハード固有設定や Hypervisor に関するコンフィグレーションは zcu104-linux
に含まれています。
guest
はテスト環境作成用プロジェクトです。公開環境ではビルド対象から外しています。
基本機能
Hypervisor は arm の Virtualization Extension(以下、VE と記述)を使い Type-1 型で実装しています。実装にはC言語とアセンブラを利用しています。
主な提供機能は以下の通りです。
- メモリ空間の仮想化
- 割り込み番号の仮想化
- 周辺デバイスの割り当て
- ユーザーによる機能拡張
公開環境では2つの Linux が動作し、rootfs として MMC と USB を使います。以降では Guest OS として動作する LInux を Linux-MMC, Linux-USB と記述します。
メモリ空間の仮想化
Hypervisor は複数の VM(Virtual Machine) を起動する事が可能であり、VM 毎に独立したメモリ空間を提供します。この機能は Stage2 Translation と SMMU を使って実現しています。
Stage2 Translation はプロセッサに対するメモリ空間の仮想化に、SMMU はバス・マスター・デバイスに対するメモリ空間の仮想化に利用します。Zynq UltraScale+ は SMMU-500 を実装しているので、これを利用しています。
公開環境でのメモリ・マップは以下の通りです。
Petalinux はデフォルトで 0-7FEF_FFFFh を RAM 領域として利用します。この領域を2つの領域に分割して各 Linux に割り当てます。これらは各 Linux にて 0 番地から始まる RAM 領域として認識されます。
Peripheral Devices には各 Linux が利用するデバイスのレジスタ領域のみがマッピングされています。割り当てられていないデバイスのレジスタ領域はアクセス不可能に設定されています。
割り込み番号の仮想化
これは割り込み番号を変更する機能です。デバイスが割り込みを通知した際、プロセッサに対してデバイスに割り当てられている番号とは異なる任意の番号で割り込みを通知します。これは VE で提供される機能の一つです。
周辺デバイスの割り当て
これは前述のメモリと割り込み番号の仮想化を合わせて実現する機能です。組み込み用途を想定した Hypervisor の為、周辺デバイスの完全仮想化や準仮想化は行わず、デバイス単位で VM に割り当てる(利用可能にする)形を取っています。VM に割り当てられたデバイスは、他の VM からはアクセス不可能となります。
今回公開する環境では以下の様にデバイスを割り当てています。
Linux-MMC | DDR MC, UART0, MMC, Ethernet, RTC |
Linux-USB | DDR MC, UART1, USB |
Hypervisor | UART Lite |
UART1 は Linux-USB 上では UART0 として認識されます。これは割り込み番号とメモリ領域の仮想化により実現しています。Hypervisor に割り当てられている UART は PL 部で追加される UART Lite です。これはメッセージ出力専用のコンソールとして利用します。
各 Linux での未割り当てデバイスへのアクセスが発生した場合、Hypervisor がトラップしてアクセス情報をコンソールに出力します。この時、VM 上の仮想プロセッサはストール状態となります。
DDR MC(Memory Controller) だけ双方の Linux にマッピングされています。このマッピングを行わないと Linux が起動しなかった為この様な形になっています。DDR MC は Stage2 Translation で Read Only 属性でマッピングされており、レジスタ領域の参照のみ可能となっています。
ユーザーによる機能拡張
VE では VM から Hypervisor に処理を要求する用途で HVC 命令が用意されています。この命令を使う事で、Hypervisor に新しいサービスを実装する事が可能となります。使用例として公開環境では OS 間通信を行う仮想デバイスを実装しています。
ビルド手順
作業環境
本記事では下記環境でビルドと動作確認を行っています。
- Bord
- AMD ZCU104 Rev.A
- Tool
- Vivado ML Edition 2024.1.2 (Windows11)
- Petalinux 2024.1 (Ubuntu 22.04.4)
Vivado でのビルド作業
最初に Vivado を使い、システムの実行環境を作成します。実行環境は PS 部と PL 部が存在し、PS 部は ZCU104 のデフォルト設定を使い、PL 部では UART Lite を1つ実装します。UART Lite は Hypervisor がメッセージ出力に利用します。
実行環境の作成には下記の Tcl スクリプトを使います。
動作環境作成スクリプト
# set project parameters
set PROJ_NAME zcu104-ps-uart
set PROJ_ROOT c:/vivado
set PROJ_DIR $PROJ_ROOT/$PROJ_NAME
set PROJ_BD_NAME design_1
# create a project
file mkdir $PROJ_ROOT
create_project $PROJ_NAME $PROJ_DIR -part xczu7ev-ffvc1156-2-e
set_property board_part xilinx.com:zcu104:part0:1.1 [current_project]
# create a Block Design
create_bd_design $PROJ_BD_NAME
create_bd_cell -type ip -vlnv xilinx.com:ip:zynq_ultra_ps_e:3.5 zynq_ultra_ps_e_0
create_bd_cell -type ip -vlnv xilinx.com:ip:axi_uartlite:2.0 axi_uartlite_0
set_property CONFIG.C_BAUDRATE {115200} [get_bd_cells axi_uartlite_0]
apply_board_connection -board_interface "uart2_pl" -ip_intf "axi_uartlite_0/UART" -diagram $PROJ_BD_NAME
apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {Auto} Clk_xbar {Auto} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_LPD} Slave {/axi_uartlite_0/S_AXI} ddr_seg {Auto} intc_ip {New AXI Interconnect} master_apm {0}} [get_bd_intf_pins axi_uartlite_0/S_AXI]
apply_bd_automation -rule xilinx.com:bd_rule:zynq_ultra_ps_e -config {apply_board_preset "1" } [get_bd_cells zynq_ultra_ps_e_0]
apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {/zynq_ultra_ps_e_0/pl_clk0 (100 MHz)} Clk_xbar {/zynq_ultra_ps_e_0/pl_clk0 (100 MHz)} Master {/zynq_ultra_ps_e_0/M_AXI_HPM0_FPD} Slave {/axi_uartlite_0/S_AXI} ddr_seg {Auto} intc_ip {/ps8_0_axi_periph} master_apm {0}} [get_bd_intf_pins zynq_ultra_ps_e_0/M_AXI_HPM0_FPD]
apply_bd_automation -rule xilinx.com:bd_rule:axi4 -config { Clk_master {Auto} Clk_slave {/zynq_ultra_ps_e_0/pl_clk0 (100 MHz)} Clk_xbar {/zynq_ultra_ps_e_0/pl_clk0 (100 MHz)} Master {/zynq_ultra_ps_e_0/M_AXI_HPM1_FPD} Slave {/axi_uartlite_0/S_AXI} ddr_seg {Auto} intc_ip {/ps8_0_axi_periph} master_apm {0}} [get_bd_intf_pins zynq_ultra_ps_e_0/M_AXI_HPM1_FPD]
regenerate_bd_layout
make_wrapper -files [get_files $PROJ_DIR/${PROJ_NAME}.srcs/sources_1/bd/$PROJ_BD_NAME/${PROJ_BD_NAME}.bd] -top
add_files -norecurse $PROJ_DIR/${PROJ_NAME}.gen/sources_1/bd/$PROJ_BD_NAME/hdl/${PROJ_BD_NAME}_wrapper.v
update_compile_order -fileset sources_1
launch_runs impl_1 -to_step write_bitstream -jobs 8
wait_on_run impl_1
write_hw_platform -fixed -include_bit -force -file $PROJ_DIR/$PROJ_NAME.xsa
Vivado の Tcl Console か Tcl Shell からスクリプトを実行して下さい。プロジェクトは C:\vivado\
配下に作成されます。プロジェクトの作成場所を変更したい場合は、最初のパラメータ定義を修正して下さい。スクリプトの実行は Windows 11 上で確認しています。
スクリプトで作成される Block Design は以下の通りです。
スクリプトの実行が成功するとプロジェクト配下に zcu104-ps-uart.xsa
が作成されます。このファイルは Petalinux のビルドに利用するので、後ほど Petalinux のビルド環境へコピーします。
Hypervisor のビルド手順
Hypervisor は Petalinux Tools でコンパイルします。事前に Petalinux Tools のインストール・ディレクトリに用意されている setting.sh
を実行してツール群を利用可能にしておいて下さい。
Hypervisor は cmake を使ってビルドします。最初にソースを GitHub から取得します。
HYP_DIR=$HOME
cd $HYP_DIR
git clone https://github.com/hkato-sssl/Hypervisor-OG.git -b v1.0.0
cd Hypervisor-OG
mkdir build
cd build
cmake ..
make
HYP_DIR
配下でビルドを実施します。これには任意のパスを設定可能です。
ビルドに成功すると $HYP_DIR/Hypervisor-OG/build/projects/zcu104-linux/hypervisor.bin
が生成されます。
Linux のビルド手順
ビルド方針
Linux のビルドは少々面倒です。今回は Guest OS として2つの Linux を動作させる為、Linux 環境を2つビルドする必要があります。ビルドの手間を削減する為、今回は以下の方針を取ります。
- Linux Kernel Image は共通の物を使用する
- Linux 毎に固有の Devicetree を用意する
上記方針によりビルドは下記手順で行います。
- BOOT.BIN, u-boot, Kernel Image のビルド
- Linux-MMC 用 system.dtb のビルド
- Linux-USB 用 system.dtb のビルド
BOOT.BIN, u-boot, Kernel Image のビルド
最初に Vivado で作成した zcu104-ps-uart.xsa
を使いプロジェクトの作成からビルドまでを行います。
最初に作業用ディレクトリの作成を行います。
WORK_DIR=$HOME/petalinux
PROJ_NAME=zcu104-ps-uart
PROJ_DIR=$WORK_DIR/$PROJ_NAME
mkdir -p $WORK_DIR
WORK_DIR
が作業ディレクトリになります。任意のディレクトリを指定して下さい。
次にプロジェクトの作成とハードウエア情報の取り込みを行います。zcu104-ps-uart.xsa
はホーム・ディレクトリ直下にある事を前提としています。
XSA_FILE=$HOME/zcu104-ps-uart.xsa
cd $WORK_DIR
petalinux-create project -n $PROJ_NAME --template zynqMP
cd $PROJ_DIR
petalinux-config --get-hw-description $XSA_FILE --silentconfig
次にカーネル・モジュールのレシピを追加します。このレシピでは OS 間通信ドライバをビルドします。
最初に仮想デバイス・ドライバ用のレシピを追加します。
petalinux-create modules --name p128 --enable
p128 がレシピの名前です。追加が成功すると $PROJ_DIR/project-spec/meta-user/recipes-modules/p128
が作成されます。
レシピが追加されたら p128.bb
を以下の様に修正して下さい。
SUMMARY = "Recipe for build an external p128 Linux kernel module"
SECTION = "PETALINUX/modules"
LICENSE = "GPLv2"
LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e"
inherit module
INHIBIT_PACKAGE_STRIP = "1"
SRC_URI = "git://github.com/hkato-sssl/Hypervisor-OG-p128.git;protocol=https;branch=main"
SRCREV = "506ac7835ed295c6894723957d9b2b636cf697ee"
S = "${WORKDIR}/git"
# The inherit of module.bbclass will automatically name module packages with
# "kernel-module-" prefix as required by the oe-core build environment.
petalinux-create
実行時に生成される files
ディレクトリは使いません。
次に Kernel Configuration を変更します。bsp.cfg
を下記の様に変更します。
# CONFIG_CRYPTO_DEV_XILINX_RSA is not set
# CONFIG_CRYPTO_DEV_ZYNQMP_AES is not set
# CONFIG_CRYPTO_DEV_ZYNQMP_SHA3 is not set
これは双方の Linux から同時利用するのを避ける為に無効化しています。通常は Devicetree で対応するのですが、これらは Kernel Configuration の変更が必要でした。
最後にビルドを実行します。
petalinux-build
ビルドが成功すると $PROJ_DIR/images/linux/
配下に成果物が生成されますので、これらを使い BOOT.BIN を作成します。
cd images/linux/
petalinux-package --boot --fsbl zynqmp_fsbl.elf --u-boot u-boot.elf --pmufw pmufw.elf --fpga system.bit
cd -
以降に記述するビルド作業では、ここで作成したビルド環境をそのまま使います。
Linux-MMC 用 sysytem.dtb のビルド
$PROJ_DIR/project-spec/meta-user/recipes-bsp/device-tree/files
配下に system-user-mmc.dtsi
を追加します。
system-user-mmc.dtsi
/ {
chosen {
bootargs = "earlycon console=ttyPS0,115200 root=/dev/mmcblk0p2 rootwait rw clk_ignore_unused";
};
memory@0 {
reg = <0x0 0x0 0x0 0x40000000>;
};
pmu {
status = "disabled";
};
p128@0 {
compatible = "sssl,hvcs-p128";
device-id = <0x50313238>; /* "P128" */
interrupt-parent = <&gic>;
};
};
&phy0 {
/delete-property/ reset-gpios;
};
&lpd_dma_chan1 {
status = "disabled";
};
&lpd_dma_chan2 {
status = "disabled";
};
&lpd_dma_chan3 {
status = "disabled";
};
&lpd_dma_chan4 {
status = "disabled";
};
&lpd_dma_chan5 {
status = "disabled";
};
&lpd_dma_chan6 {
status = "disabled";
};
&lpd_dma_chan7 {
status = "disabled";
};
&lpd_dma_chan8 {
status = "disabled";
};
&xilinx_ams {
status = "disabled";
};
&can1 {
status = "disabled";
};
&cci {
status = "disabled";
};
&zynqmp_dpsub {
status = "disabled";
};
&zynqmp_dpdma {
status = "disabled";
};
&gem3 {
status = "okay";
};
&fpd_dma_chan1 {
status = "disabled";
};
&fpd_dma_chan2 {
status = "disabled";
};
&fpd_dma_chan3 {
status = "disabled";
};
&fpd_dma_chan4 {
status = "disabled";
};
&fpd_dma_chan5 {
status = "disabled";
};
&fpd_dma_chan6 {
status = "disabled";
};
&fpd_dma_chan7 {
status = "disabled";
};
&fpd_dma_chan8 {
status = "disabled";
};
&gpio {
status = "disabled";
};
&gpu {
status = "disabled";
};
&i2c1 {
status = "disabled";
};
&qspi {
status = "disabled";
};
&rtc {
status = "disabled";
};
&sata {
status = "disabled";
};
&sdhci1 {
status = "okay";
};
&psgtr {
status = "disabled";
};
&ttc0 {
status = "disabled";
};
&ttc1 {
status = "disabled";
};
&ttc2 {
status = "disabled";
};
&ttc3 {
status = "disabled";
};
&uart0 {
status = "okay";
};
&uart1 {
status = "disabled";
};
&usb0 {
status = "disabled";
};
&dwc3_0 {
status = "disabled";
};
&lpd_watchdog {
status = "disabled";
};
&watchdog0 {
status = "disabled";
};
&ams_ps {
status = "disabled";
};
&ams_pl {
status = "disabled";
};
&dcc {
status = "disabled";
};
&zynqmp_dp_snd_pcm0 {
status = "disabled";
};
&zynqmp_dp_snd_pcm1 {
status = "disabled";
};
&zynqmp_dp_snd_card0 {
status = "disabled";
};
&zynqmp_dp_snd_codec0 {
status = "disabled";
};
&zynqmp_ipi {
status = "disabled";
};
&perf_monitor_ocm {
status = "disabled";
};
&perf_monitor_ddr {
status = "disabled";
};
&perf_monitor_cci {
status = "disabled";
};
&perf_monitor_lpd {
status = "disabled";
};
次に同じディレクトリ配下にある system-user.dtsi
を下記内容に書き換えます。
#include "zcu104-reva.dtsi"
/include/ "system-conf.dtsi"
/include/ "system-user-mmc.dtsi"
device-tree.bbappend
を下記内容に書き換えます。
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append = " file://system-user.dtsi \
file://system-user-mmc.dtsi \
"
全ての修正を終えたら Devicetree のビルドを実行します。ビルドが成功すると $PROJ_DIR/images/linux/system.dtb
が更新されますので、これを system-mmc.dtb
に変更します。
petalinux-build -c device-tree
mv $PROJ_DIR/images/linux/system.dtb $PROJ_DIR/images/linux/system-mmc.dtb
以上で Linux-MMC 用のビルド作業は終了です。
Linux-USB 用 sysytem.dtb のビルド
作業手順は Linux-MMC と同じです。
$PROJ_DIR/project-spec/meta-user/recipes-bsp/device-tree/files
配下に system-user-usb.dtsi
を追加します。
system-user-usb.dtsi
/ {
chosen {
bootargs = "earlycon console=ttyPS0,115200 root=/dev/sda1 rootwait rw clk_ignore_unused";
};
memory@0 {
reg = <0x0 0x0 0x0 0x3f000000>;
};
pmu {
status = "disabled";
};
p128@0 {
compatible = "sssl,hvcs-p128";
device-id = <0x50313238>; /* "P128" */
interrupt-parent = <&gic>;
};
};
&lpd_dma_chan1 {
status = "disabled";
};
&lpd_dma_chan2 {
status = "disabled";
};
&lpd_dma_chan3 {
status = "disabled";
};
&lpd_dma_chan4 {
status = "disabled";
};
&lpd_dma_chan5 {
status = "disabled";
};
&lpd_dma_chan6 {
status = "disabled";
};
&lpd_dma_chan7 {
status = "disabled";
};
&lpd_dma_chan8 {
status = "disabled";
};
&xilinx_ams {
status = "disabled";
};
&can1 {
status = "disabled";
};
&cci {
status = "disabled";
};
&zynqmp_dpsub {
status = "disabled";
};
&zynqmp_dpdma {
status = "disabled";
};
&gem3 {
status = "disabled";
};
&fpd_dma_chan1 {
status = "disabled";
};
&fpd_dma_chan2 {
status = "disabled";
};
&fpd_dma_chan3 {
status = "disabled";
};
&fpd_dma_chan4 {
status = "disabled";
};
&fpd_dma_chan5 {
status = "disabled";
};
&fpd_dma_chan6 {
status = "disabled";
};
&fpd_dma_chan7 {
status = "disabled";
};
&fpd_dma_chan8 {
status = "disabled";
};
&gpio {
status = "disabled";
};
&gpu {
status = "disabled";
};
&i2c1 {
status = "disabled";
};
&qspi {
status = "disabled";
};
&rtc {
status = "disabled";
};
&sata {
status = "disabled";
};
&sdhci1 {
status = "disabled";
};
&psgtr {
status = "okay";
};
&ttc0 {
status = "disabled";
};
&ttc1 {
status = "disabled";
};
&ttc2 {
status = "disabled";
};
&ttc3 {
status = "disabled";
};
&uart0 {
status = "okay";
};
&uart1 {
status = "disabled";
};
&usb0 {
status = "okay";
};
&dwc3_0 {
status = "okay";
};
&lpd_watchdog {
status = "disabled";
};
&watchdog0 {
status = "disabled";
};
&ams_ps {
status = "disabled";
};
&ams_pl {
status = "disabled";
};
&dcc {
status = "disabled";
};
&zynqmp_dp_snd_pcm0 {
status = "disabled";
};
&zynqmp_dp_snd_pcm1 {
status = "disabled";
};
&zynqmp_dp_snd_card0 {
status = "disabled";
};
&zynqmp_dp_snd_codec0 {
status = "disabled";
};
&zynqmp_ipi {
status = "disabled";
};
&perf_monitor_ocm {
status = "disabled";
};
&perf_monitor_ddr {
status = "disabled";
};
&perf_monitor_cci {
status = "disabled";
};
&perf_monitor_lpd {
status = "disabled";
};
次に同じディレクトリ配下にある system-user.dtsi
を下記内容に書き換えます。
#include "zcu104-reva.dtsi"
/include/ "system-conf.dtsi"
/include/ "system-user-usb.dtsi"
device-tree.bbappend
を下記内容に書き換えます。
FILESEXTRAPATHS:prepend := "${THISDIR}/files:"
SRC_URI:append = " file://system-user.dtsi \
file://system-user-usb.dtsi \
"
全ての修正を終えたら Devicetree のビルドを実行します。ビルドが成功すると $PROJ_DIR/images/linux/system.dtb
が更新されますので、これを system-usb.dtb
に変更します。
petalinux-build -c device-tree
mv $PROJ_DIR/images/linux/system.dtb $PROJ_DIR/images/linux/system-usb.dtb
以上で Linux-usb 用のビルド作業は終了です。
u-boot と system-mmc.dtb や system-usb.dtb をまとめてビルドしないで下さい。
各 DTB では RAM 領域のサイズを変更しており、まとめてビルドした場合この設定が u-boot に反映されてしまいます。
u-boot は DTB で設定された RAM 領域外にプログラムをロードする事は出来ず、結果としてHypervisor を含めたシステム起動が不可能になります。
起動メディア作成
SD Memory Card と USB Memory に起動用ファイルと rootfs を書き込みます。
先ずは起動用 SD Memory Card を作成します。これはボード起動用の他、 Linux-MMC の rootfs として利用します。
SD Memory Card にパーティションを2つ作成して下さい。1つ目は FAT、2つ目は Linux で作成します。
各パーティションをフォーマットしたら、FAT パーティションに $PROJ_DIR/images/linux/
配下にある下記ファイルをコピーします。
- BOOT.BIN
- image.ub
- boot.scr
- Image
- system-mmc.dtb
- system-usb.dtb
次に Hypervisor のバイナリ・イメージをコピーします。コピーするファイルは $HYP_DIR/build/projects/zcu104-linux/hypervisor.bin
です。
次は Linux パーティションを EXT4 でフォーマットした後、 $PROJ_DIR/images/linux/rootfs.tar.gz
を展開します。これで SD Memory Card での作業は終了です。
続けて USB Memory の作成です。これは Linux-USB の rootfs として利用しますので、Linux パーティションを1つ作成して下さい。
パーティションを作成したら EXT4 でフォーマットを行い、 SD と同様に $PROJ_DIR/images/linux/rootfs.tar.gz
を Linux パーティションに展開します。
起動手順
作成した SD Memory Card と USB Memory を ZCU104 に装着して下さい。USB Memory はボード上の USB ポートに挿入しても問題ありませんが、直挿しだとメディアを認識しない事がありました。 所有しているボードの問題かも知れませんが USB 3.0 対応の USB Memory を利用した時のみ発生しています。この様な症状が出た時は USB Hub 経由での接続を試してみて下さい。尚、ボード上の Ethernet Port は Linux-MMC に割り当てられていますので、Linux-USB でネットワーク利用する場合は、USB Hub 経由で USB Memory と USB Ethernet を接続して下さい。
ボードの電源を投入すると u-boot が立ち上がります。カウント・ダウンが始まったら Enter Key を押します。今回利用する UART0, UART1, UART Lite の Baud Rate は全て 115200 に設定して下さい。
プロンプトが表示されたら下記コマンドを投入してシステムを起動します。
fatload mmc 0:1 280000 Image
fatload mmc 0:1 100000 system-mmc.dtb
fatload mmc 0:1 40280000 Image
fatload mmc 0:1 40100000 system-usb.dtb
fatload mmc 0:1 7f000000 hypervisor.bin
booti 7f000000 - 100000
最後の booti
で Hypervisor を起動します。第3引数で system-mmc.dtb
のアドレスを指定していますが、これはダミーです。booti
コマンドには必ず DTB ファイルを渡す必要があるのでダミーを指定しています。
Hypervisor は arm の EL2 で実行します。EL2 で実行するには bootX コマンドを使う必要がある為、この様な形となっています。
起動が成功すれば UART0/UART1 に Linux の起動メッセージが、UART Lite に Hypervisor の起動メッセージが出力されます。
Hypervisor の起動メッセージは以下の通りです。
Start HypervisorOG ver.1.0.0
Start a thread for Linux#1: PhysicalProcessor=1 VirtualProcessor=1
Start a thread for Linux#1: PhysicalProcessor=3 VirtualProcessor=0
Start a thread for Linux#0: PhysicalProcessor=2 VirtualProcessor=1
Start a thread for Linux#0: PhysicalProcessor=0 VirtualProcessor=0
上記は物理コアと仮想コアの割り当てを出力しています。Linux#0
と Linux#1
は VM を指しています。本記事との対応は以下の通りです。
Linux#0 | Linux-MMC |
Linux#1 | Linux-USB |
Dual Linux の動作状態確認
ここでは Linux-MMC と Linux-USB が別々に動作している事を確認します。
メモリ・マップと割り込みの利用状況の2つを見れば、各 Linux が異なる環境で動作している事が解るかと思います。
メモリ・マップ確認
各 Linux 上で /proc/iomem
を表示してみて下さい。異なるメモリ・マップが以下の様に表示されます。
Linux-USB では UART1 が UART0 と同じアドレスにマッピングされています。
zcu104-ps-uart:/home/petalinux# cat /proc/iomem
00000000-3fffffff : System RAM
00100000-0010bfff : reserved
00290000-015cffff : Kernel code
015d0000-0188ffff : reserved
01890000-01a0ffff : Kernel data
2aac0000-3fbfffff : reserved
3fc64000-3fd65fff : reserved
3fd66000-3fdb1fff : reserved
3fdb3000-3fdb6fff : reserved
3fdb7000-3fdc7fff : reserved
3fdc8000-3fffffff : reserved
ff000000-ff000fff : xuartps
ff0e0000-ff0e0fff : ff0e0000.ethernet ethernet@ff0e0000
ff170000-ff170fff : ff170000.mmc mmc@ff170000
zcu104-ps-uart:/home/petalinux# cat /proc/iomem
00000000-3effffff : System RAM
00100000-0010bfff : reserved
00290000-015cffff : Kernel code
015d0000-0188ffff : reserved
01890000-01a0ffff : Kernel data
29ac0000-3ebfffff : reserved
3ec6c000-3ed6cfff : reserved
3ed6d000-3edb8fff : reserved
3edbb000-3edbbfff : reserved
3edbc000-3edbefff : reserved
3edbf000-3edcffff : reserved
3edd0000-3effffff : reserved
fd3d0000-fd3d0fff : fd400000.phy siou
fd400000-fd43ffff : fd400000.phy serdes
fe200000-fe207fff : usb@fe200000
fe200000-fe207fff : xhci-hcd.0.auto usb@fe200000
fe20c100-fe23ffff : fe200000.usb usb@fe200000
ff000000-ff000fff : xuartps
ff9d0000-ff9d00ff : ff9d0000.usb usb@ff9d0000
割り込み番号確認
これは /proc/interrupts
を表示します。公開環境では以下の様に表示されます。
メモリ・マップと同様に Linux-USB では UART1 の割り込み番号が UART0 の割り込み番号になっています。
zcu104-ps-uart:/home/petalinux# cat /proc/interrupts
CPU0 CPU1
12: 5585 5280 GIC-0 27 Level arch_timer
14: 483 0 GIC-0 53 Level xuartps
15: 0 0 GIC-0 95 Level eth0, eth0
16: 2576 0 GIC-0 81 Level mmc0
17: 0 0 GIC-0 168 Level p128
IPI0: 89 141 Rescheduling interrupts
IPI1: 1342 2207 Function call interrupts
IPI2: 0 0 CPU stop interrupts
IPI3: 0 0 CPU stop (for crash dump) interrupts
IPI4: 0 0 Timer broadcast interrupts
IPI5: 0 0 IRQ work interrupts
IPI6: 0 0 CPU wake-up interrupts
Err: 0
zcu104-ps-uart:/home/petalinux# cat /proc/interrupts
CPU0 CPU1
12: 3674 5993 GIC-0 27 Level arch_timer
14: 463 0 GIC-0 53 Level xuartps
15: 3553 0 GIC-0 97 Level xhci-hcd:usb1
16: 0 0 GIC-0 170 Level p128
IPI0: 57 143 Rescheduling interrupts
IPI1: 2283 5016 Function call interrupts
IPI2: 0 0 CPU stop interrupts
IPI3: 0 0 CPU stop (for crash dump) interrupts
IPI4: 0 0 Timer broadcast interrupts
IPI5: 0 0 IRQ work interrupts
IPI6: 0 0 CPU wake-up interrupts
Err: 0
p128
は OS 間通信を担う仮想デバイスです。これらは割り込み番号として PL 用に割り当てられている SPI を利用しています。
OS 間通信の確認
各 Linux 上に /dev/p128-0
というデバイスが存在しますが、これが OS 間通信を実現する仮想デバイスです。このデバイスに書き込んだデータが対向する Linux で読み取り可能となります。
p128 という名称は Packet と 128-byte の組み合わせです。この仮想デバイスは 128-byte 固定長パケットによる Peer-to-Peer の双方向通信を提供します。
仮想デバイスが持つ通信用バッファはパケット1つ分しかありませんので、送信したパケットを対向が読み取るまで次のパケットは送信不可能になります。対向が先に送信したパケットを読み取る前に次のパケットを送信した場合、送信プロセスはブロックされます。
以下は簡単な確認手順です。デバイスへのアクセスは root 権限で行う必要があります。
echo Hello > /dev/p128-0
cat /dev/p128-0
上記は Linux-MMC で実行した echo
コマンドの出力を Linux-USB で表示します。実行コマンドを逆にする事も可能です。
Linux-USB で cat
コマンドを実行する前に再度 Linux-MMC で echo
コマンドを実行してみて下さい。Linux-USB が cat
を実行するまで処理がブロックされます。
p128 は双方向通信なので実行コマンドを逆にしても問題ありません。
echo
の代わりに cat
で適当なファイルを出力すれば、対向の Linux で表示されます。
cat /etc/netconfig > /dev/p128-0
おわりに
各 VM に割り当てるデバイスが必要最小現に抑えていますので大した事は出来ませんが、Dual Linux の動作環境を試すには必要最低限の環境が作れたと思います。