作成者別アーカイブ: kunih

GPD Pocket

Cloud Funding の “Indiegogo” で募集され 3,182,160 USD も集めた “GPD Pocket” ですが、やはり気になる機種ということで出資してたところ、無事出荷され入手できました。

Ubuntu 版は同価格ながら Windows ライセンスが付属しないのであまり選択する旨味はないのですが、BIOS update と OS の自力入れ替えが必要でハマると文鎮ループのリスクも考えられるので、敢えてこちらを選択したのでした。

想像以上に高級感のあるパッケージです。

同梱物は本体、説明書、USB type-C – ACアダプタ、USB type-C ケーブル、イヤフォンで全てです。初期ロットのみですが液晶保護フィルムが付いてきました。さらにオプションの Hub 付きを選択していると別途付属されます(後述)。

99% 充電された状態だったので早速電源ボタン長押しで起動。GPD のロゴの後、Linux の console が表示され、Ubuntu の初期画面になります。例によってキーボード・言語・タイムゾーン・ユーザアカウントなどを設定して処理が終わるのを待ちます。

そして起動した画面は…なんと縦画面。これは LCD デバイスが7インチタブレットに供給されたモデルを調達したらこうなりますね。確かに 7インチ横画面の需要は皆無なのはまぁ仕方ないところ。思考を 90度回転しつつ[Setting]>[Display] を開いて “Rotation” を “Clockwise” に設定してようやくまともな画面に。

起動して気になるのはファン音。静かにしてるとこの筐体からの音が気になります。CPU は Atom 第4世代の x7-Z8750 1.59GHz、Atom とはいえかなり高パフォーマンスなため強制空冷が必要なレベルということでしょう。CPU  governor は powersave なので、OS 制御にかかわらず常時回ってそうです。

RAM は LowPower DDR3-1600 8GB、Storage は eMMC 128GB とこのクラスで使うには十分です。

キーボードはなかなかの変態配列です。記号が上と下の列に飛ばされてたり、Tab・Caps・BS が狭いのは後々厳しそうな予感がします。トラックポイントは Space を分断して確保するという面白い配置です。

外部インタフェースは USB type-C、micro HDMI、イヤフォンジャック、USB 3.0(type-B) とシンプルです。USB type-C は所有端末では初搭載なので規格についてはこれから要調査ですが、「USB Power Delivery (PD)」と「DisplayPort Alternate mode」に対応しているとのことです。USB の横に DisplayPort のロゴが見えます。

USB Power Delivery とは、従来以上の電力を給電する規格で、機器間のnegotiation が行われるので、対応する AC アダプタやケーブルが必要になります。一部では仕様と異なる情報を出すアダプタがあるようなので、付属品以外の物を使う場合には注意が必要です。

DisplayPort Alternate mode とは USB type-C で DisplayPort 信号を通す規格で、対応機器に接続すれば USB type-C だけで映像出力が可能になります。

説明書は中文・英語・日本語の各部名称・仕様諸元・内容物・保証書の記載のみです。よく見ると 4K と書いてますが、LCD の解像度は 1920×1200 です(Webでの Spec Sheet は合ってる)。

Contribution は「GPD Pocket Ubuntu+Type-C Hub」だったのですが、Hub なしとの差が 10 USD なのでまともな Hub は期待してなかったのですが、まさかの type-C 対応 SD カードリーダでした。まぁこれはネタということで。

これまで薄い PC は発売されてきたものの、このサイズの PC は使いにくさやトラブルが付き物で一般ウケしないこともあってメーカーからは絶版状態だったのですが、Libretto に始まり VAIO type P 以来の久しぶりの mini PC 導入ということでいろいろ使い込んでいきたいと思います。

参考: Indiegogo: GPD Pocket: 7.0′ UMPC-Laptop ‘Ubuntu or WIN 10 OS’

[Linux] kernel version string

Linux kernel の version 表記がどのようにして生成されているのかよく分からなかったので調べてみました。

kernel version の文字列

kernel version は major version, patchlevel, sublevel (開発バージョンはさらに extraversion) から成り、それぞれ linux/Makefile に記載されています。”make kernelversion” でそれらを表示します。

$ head -5 Makefile
VERSION = 4
PATCHLEVEL = 13
SUBLEVEL = 0
EXTRAVERSION = -rc7
NAME = Fearless Coyote
$ make kernelversion
4.13.0-rc7

kernel release version の文字列はどうやって決まる?

release version は kernel version に加えてビルド時のリポジトリ情報を追加したもので、 “make releaseversion” で表示します。config 情報を使うので、先に config を実行しておく必要があります。

$ make ARCH=arm64 defconfig
$ make ARCH=arm64 kernelrelease
4.13.0-rc7-01000-gXXXXXXX-dirty

これは linux/scripts/setlocalversion の実行結果で、config の内容とリポジトリの情報を反映しています。影響する config は CONFIG_LOCALVERSION_AUTO と CONFIG_LOCALVERSION です。

CONFIG_LOCALVERSION_AUTO によって大きく動作が異なるため、それぞれについて説明します。”make menuconfig” では以下で設定します。

"General setup   --->
  [*] Automatically append version information to the version string
CONFIG_LOCALVERSION_AUTO=y の場合

次の文字列を連結したものが localversion 文字列になります。

  • linux/localversion* のファイルがあればその文字列
  • CONFIG_LOCALVERSION で定義された文字列
  • 環境変数 LOCALVERSION で定義された文字列
  • リポジトリ情報
  • ローカル変更があった場合の “-dirty”

linux/localversion* ファイルは自分で作成したものです。ファイル名は linux/localversion-hoge でも何でもよいですが、ワイルドカードの評価順に文字列が続きます。

$ cat localversion
-localv1.0
$ cat localversion-foo
-FOO
$ cat localversion-bar
-BAR
$ cat localversion-hoge
-HOGE
$ make ARCH=arm64 kernelrelease
4.13.0-rc7-localv1.0-BAR-FOO-HOGE-01000-gXXXXXXX-dirty

CONFIG_LOCALVERSION は .config で定義される文字列です。”make menuconfig” では以下で設定します。

"General setup   --->
  () Local version - append to kernel release

環境変数 LOCALVERSION は make 実行時に定義されていたものが有効になります。

$ LOCALVERSION=-TMP make ARCH=arm64 kernelrelease
4.13.0-rc7-TMP-01000-gXXXXXXX-dirty

リポジトリ情報は、”git describe –dirty” の結果が適用されます。

annotated tag の付いた commit (通常 kernel version の更新ポイント) から commit の適用数(5桁)と、現在の commit ID が文字列となります。

“-01000-gXXXXXXX” の場合、kernel version から 1000 個の commit が適用され、現在 commit ID が XXXXXXX のリポジトリを使っていることになります。

ソースは変更されているがリポジトリに反映されていない場合、最後に “-dirty” が付きます。

これらを全部適用すると以下のようになります(長い…)。

$ grep LOCALVERSION .config
CONFIG_LOCALVERSION="-TEST"
CONFIG_LOCALVERSION_AUTO=y
$ cat localversion
-localv1.0
$ cat localversion-foo
-FOO
$ cat localversion-bar
-BAR
$ cat localversion-hoge
-HOGE
$ LOCALVERSION=-TMP make ARCH=arm64 kernelrelease
4.13.0-rc7-localv1.0-BAR-FOO-HOGE-TEST-TMP-01000-gXXXXXXX-dirty

linux/scripts/setlocalversion を都度適用する代わりに、予め linux/scripts/setlocalversion  の結果を保存した文字列を適用することもできます。
以下を実行すると linux/.scmversion に保存され、この文字列が常に適用されます。

$ ./script/setlocalversion --save-scmversion .
CONFIG_LOCALVERSION_AUTO is not set の場合

次の文字列を連結したものが localversion 文字列になります。上の3つは “CONFIG_LOCALVERSION=y” と同じです。

  • linux/localversion* のファイルがあればその文字列
  • CONFIG_LOCALVERSION で定義された文字列
  • 環境変数 LOCALVERSION で定義された文字列
  • (LOCALVERSION がない場合) リポジトリ更新を示す “+”

環境変数 LOCALVERSION が定義されていなければ、リポジトリが kernel version から更新されているかを示す “+” が付きます。

環境変数 LOCALVERSION が定義されていればその文字列だけが付きます。LOCALVERSION を空定義すると何も付かないことになります。

$ make ARCH=arm64 kernelrelease
4.13.0-rc7+
$ LOCALVERSION=-TMP make ARCH=arm64 kernelrelease
4.13.0-rc7-TMP
$ LOCALVERSION= make ARCH=arm64 kernelrelease
4.13.0-rc7

release version と kernel module

kernel module は install directory に release version が付く(/lib/modules/<release-version>) ため、気づかないうちに version 文字列が変わっててトラブルの原因になることがあります。そのため、module 開発者はどの文字列が kernel の何の設定で付与されているのかを正しく理解しておく必要があります。

[RPi] Pi Zero W

Raspberry Pi 3 を入手して間もないですが、海外通販にて Pi Zero W を入手しました。PIROMONI にて注文掛けていたのですが6日で無事到着しました。Anniversary 期間に間に合ったので £10 で入手でき(通常は £12)、せっかくなので Pibow ケースや Speaker pHAT も追加しました。送料は £5.5 です。

SoC は RPi 無印と同じ BCM2835 で ARM1176 コア、RAMは 512MB の Elpida チップが SoC 上にボンディングされています。外部端子は micro USB(OTG)、mini HDMI、micro SD カード、GPIO ピン、と RPi の互換を保ちながら最小構成でできています。Pi Zero W ではさらに Wi-Fi と Bluetooth が内蔵されています。

まずは動作確認

Raspberry Pi 3 の入手ですでにイメージは micro SD カードに作成済なので、移し替えるだけです。ただ外部端子が mini HDMI と micro USB なので、ディスプレイやUSBキーボードを接続するにはそれぞれ変換ケーブルが必要になります。

ネットワークへの接続は Ethernet がないので Wi-Fi を使用します。通常 wpa-supplicant の設定が必要ですが、とりあえず動かすなら GUI で設定した方が楽です。

$ dmesg
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.9.35+ (dc4@dc4-XPS13-9333) (gcc version 4.9.3 (crosstool-NG crosstool-ng-1.22.0-88-g8460611) ) #1014 Fri Jun 30 14:34:49 BST 2017
[    0.000000] CPU: ARMv6-compatible processor [410fb767] revision 7 (ARMv7), cr=00c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT nonaliasing instruction cache
[    0.000000] OF: fdt:Machine model: Raspberry Pi Zero W Rev 1.1
...

Pibow ケースの組み立て

今回 Pibow ケースを同時に入手しました。積層型のパーツがボードを囲む形で見た目に面白いです。組み立てに取り掛かる前に PIROMONI ページで組み立て方を確認しておいた方がよいかと思います。たぶん確認しないとどの順番で積むのかよく分かりません^^;

GPIO ピン部分はくりぬかれていて pHAT に合わせてあるため、ケースを付けたまま pHAT を取付けられます。外部端子は干渉しないコネクタを選ぶ必要がありますが、ケースのまま取付けられます。残念ながら micro SD カードの入れ替えだけは一度ケースをばらす必要があります。

Speaker pHAT の組み立て

Speaker pHAT はパーツで提供されているので、assembly guide に従って組み立てます。pin ヘッダとスピーカへの配線は半田付けが必要です。

Speaker pHAT を使うためにはいろいろパッケージを導入する必要がありますが、PIROMONI で用意されているスクリプトを本体から直接実行することで全て導入することができます。

# curl -sS https://get.pimoroni.com/speakerphat | bash

alsa device としては default の hw:0,0 で見えています。aplay を使って音出しの動作確認ができれば OK です。LED メータも同期して光ります。mp3 など PCM 以外のフォーマットはデコードする必要があるため、mpg321 などのデコードできるプレーヤーを導入します。

# aplay /usr/share/sounds/alsa/Front_Center.wav

 

[RPi][Linux] boot upstream Linux from U-boot on Raspberry Pi 3

U-boot を Raspberry Pi 3 に導入して、ビルドした mainline Linux kernel を起動してみました。Raspberry Pi は 既存の bootloader が提供されているので、SDカードの FATパーティションに kernel image を置けば起動するのですが、ビルドした kernel を直接投入するには network 経由で起動したくなるものです。

clone & build

U-boot を clone してビルドします。

$ git clone git://git.denx.de/u-boot.git
$ cd u-boot
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- CONFIG_EFI=y rpi_3_defconfig
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu-
$ cd -

Linux kernel を clone してビルドします。

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/qemu.org/torvalds/linux.git
$ cd linux
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- defconfig
$ make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image dtbs
$ cd -

tftp サーバを用意します。手順は省略しますが、ここではサーバディレクトリを /tftpboot とします。kernel image と devicetree を tftp サーバへコピーします。

$ cp linux/arch/arm64/boot/Image /tftpboot/
$ cp linux/arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b.dtb /tftpboot/

ここでは Linaro が提供する rootfs image を取得して tftp サーバへコピーします。取得する image は initramfs の uboot で読める形式で、armv8 のものを選択します。

$ wget http://releases.linaro.org/openembedded/images/minimal-initramfs-armv8/latest/linaro-image-minimal-initramfs-genericarmv8-20170127-888.rootfs.cpio.gz.u-boot
$ cp linaro-image-minimal-initramfs-genericarmv8-20170127-888.rootfs.cpio.gz.u-boot /tftpboot/rootfs.cpio.gz.u-boot

SD カードを host PC に接続し、パーティションを作成して FAT でフォーマットしマウントします。フォーマットまでは Windows で用意してもよいです。ここでは SD カードは /dev/sdX で認識されたものとしますが、dmesg などで確認して間違えないように注意してください。

※ パーティション分割やフォーマットについての詳細は他の解説にお任せします。情報は Web で探すと出てくると思います…。

$ sudo fdisk /dev/sdX
...
(パーティションを作成)
...
$ sudo mkfs.vfat -v -c -F 32 /dev/sdX1
$ sudo mount -t vfat /dev/sdX1 /mnt

github で提供されている firmware を clone して SD カードにコピーします。

$ git clone git://github.com/raspberrypi/firmware
$ cp -a firmware/boot/* /mnt/

u-boot image をSDカードにコピーし、boot 設定ファイルを作成します。

  • arm_control=0x200 を指定すると arm64 モードで起動するようになります。
  • enable_uart=1 で GPIO 端子上の Serial を有効にします。Linux では ttyS0 として見えます。
  • kernel=u-boot.bin は起動する image のファイル名を指定します。default は(arm64モードの場合) kernel8.img です。

詳しくは RPiconfig にいろいろ解説があります。

$ cp u-boot.bin /mnt/
$ cat > config.txt <<EOF
arm_control=0x200
enable_uart=1
kernel=u-boot.bin
EOF

マウントを解除して SDカードを PC から外し、Raspberry Pi 3 本体に挿入します。

$ sync
$ sudo umount /mnt

console は Serial を使うため、GPIO 端子から 接続する必要があります。今回は手持ちの FTDI chip 搭載の TTL-232r-3v3 を使用しました。Raspberry Pi 初代でも使っていた USB-Serial 変換ケーブルです。直接接続できないので、GPIO 端子を 40pin リボンケーブル(余っていた IDE ケーブル!) を介しています。

※ Raspberry Pi 3 の接続端子の詳細は他の解説にお任せします。情報は Web で探すと出てくると思います…。

また、Ethernet ケーブルで host PC との接続も行っておきます。当たり前ですが、ストレートケーブルは直結できないのでローカル LAN などに接続することになりますので、ローカル LAN の IP アドレス情報は知っておく必要があります。

電源を入れると serial に U-boot のログが出ます。一旦キーを押して止めます。

U-Boot 2017.07-00001-g13e3ef2 (Aug 13 2017 - 23:46:40 +0900)

DRAM:  944 MiB
RPI 3 Model B (0xa32082)
MMC:   sdhci@7e300000: 0
reading uboot.env
In:    serial
Out:   vidconsole
Err:   vidconsole
Net:   No ethernet found.
starting USB...
USB0:   Core Release: 2.80a
scanning bus 0 for devices... 3 USB Device(s) found
     scanning usb for storage devices... 0 Storage Device(s) found
Hit any key to stop autoboot:  0
U-Boot> 

ネットワークの設定を行います。ipaddr に target(Raspberry Pi 3) の IP アドレス、serverip に tftp サーバを立ち上げた host PC の IP アドレスを指定します。以下は手元で行った例ですので、自分の環境に合わせて設定します。

U-boot> setenv ipaddr 192.168.1.200            # target(RPi3) の IP アドレス
U-boot> setenv serverip 192.168.1.135          # tftp サーバの IP アドレス
U-boot> setenv netmask 255.255.255.0
U-boot> setenv bootargs root=/dev/ram0 rw console=ttyS0,115200

tftp で kernel image、devicetree、rootfs image をメモリへ転送し、kernel を起動します。

U-Boot> tftp 0x80000 Image
U-Boot> tftp 0x08000000 bcm2837-rpi-3-b.dtb
U-Boot> tftp 0x08008000 rootfs.cpio.gz.u-boot
U-Boot> booti 0x80000 0x08008000 0x08000000

以下のように起動が確認できました。

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.13.0-rc4 (kunih@ubuntu) (gcc version 6.3.1 20170404 (Linaro GCC 6.3-2017.05)) #1 SMP PREEMPT Fri Aug 11 11:06:59 JST 2017
[    0.000000] Boot CPU: AArch64 Processor [410fd034]
[    0.000000] Machine model: Raspberry Pi 3 Model B
...
[    2.229403] Freeing unused kernel memory: 1088K
linaro-test [rc=0]# 

Raspberry Pi の Ethernet Host は USB で接続されているようですが、現時点での kernel では USB(dwc2) の DMA に問題がありエラーで認識しませんでした。contribute が少ないためか device の mainline への対応はまだまだのようです。

参考

[QEMU] Hello world on QEMU

QEMU で Hello world を動かす試みは探してみるといくつか実施されています。

いずれも versatilepb モデルで実施されているので、vexpress や virt でもできるかどうか試してみました。toolchain は Linaro toolchain を使いましたが、外部関数を呼び出さないので baremetal toolchain でなくても使えます。

プログラムソース init.c、スタートコード startup.s、リンカスクリプト link.ld を用意します。

init.c は “Hello, world!” を一文字ずつ UART I/O に書き込むだけのプログラムです。

/* init.c */
static void print(const char *str)
{
	while (*str)
		*(volatile unsigned char *)UART0_PTR = *str++;
}
int init()
{
	print("Hello, world!\n");
}

link.ld は外部定義された START からセクション定義を行います。スタックを 0x800 byte 分空けて スタックポインタの開始位置 sp_top を定義します。

/* link.ld */
ENTRY(_start)
SECTIONS {
	. = START;
	startup : { startup.o(.text)}
	.data   : {*(.data)}
	.bss    : {*(.bss)}
	. = . + 0x800;
	sp_top = .;

vexpress(aarch32) の場合

aarch32 とaarch64 で命令体系が変わるので、startup.s はそれぞれのものを用意します。aarch32 向けはスタックポインタに直接 sp_top を設定して init 関数を呼び出します。最後は無限ループです。

/* startup.s */
	.global _start
_start:
	ldr	sp,=sp_top
	bl	init
	b	.

vexpress-a9 モデルの場合のビルド・実行例です。UART0 I/O と SDRAM がモデルで異なるので、コマンドラインで設定しています。

$ arm-linux-gnueabihf-as  -mcpu=cortex-a9 -o startup.o startup.s
$ arm-linux-gnueabihf-gcc -mcpu=cortex-a9 -c init.c -DUART0_PTR=0x10009000
$ arm-linux-gnueabihf-ld -T link.ld -defsym START=0x60010000 -o output.elf startup.o init.o
$ arm-linux-gnueabihf-objcopy -O binary output.elf output.bin
$ qemu-system-aarch64 -M vexpress-a9 -cpu cortex-a9 -nographic -kernel output.bin
Hello, world!

vexpress-a15 モデルの場合は以下のようにします。

$ arm-linux-gnueabihf-as  -mcpu=cortex-a15 -o startup.o startup.s
$ arm-linux-gnueabihf-gcc -mcpu=cortex-a15 -c init.c -DUART0_PTR=0x1c090000
$ arm-linux-gnueabihf-ld -T link.ld -defsym START=0x80010000 -o output.elf startup.o init.o
$ arm-linux-gnueabihf-objcopy -O binary output.elf output.bin
$ qemu-system-aarch64 -M vexpress-a15 -cpu cortex-a15 -nographic -kernel output.bin
Hello, world!

virt(aarch64) の場合

aarch64 向けのスタートコード startup64.s を用意します。sp は直接代入できないので、x0 を間接的に使います。

/* startup64.s */
	.global _start
_start:
	ldr	x0,=sp_top
	mov	sp,x0
	bl	init
	b	.

virt モデルの場合の実行例です。toolchain は aarch64 のものを使います。

$ aarch64-linux-gnu-as -mcpu=cortex-a53 startup64.s
$ aarch64-linux-gnu-gcc -mcpu=cortex-a53 -DUART0_PTR=0x09000000 -c init.c
$ aarch64-linux-gnu-ld -T link.ld -o output.elf startup64.o init.o
$ aarch64-linux-gnu-objcopy -O binary output.elf output.bin
$ qemu-system-aarch64 -M vexpress-a53 -cpu cortex-a53 -nographic -kernel output.bin
Hello, world!

上記のソースは Makefile を使う形で github に置く予定です。

[QEMU][Linux] boot upstream Linux from U-boot on QEMU

U-boot on arm QEMU にて QEMU で U-boot を立ち上げて 外から tftp でメモリに転送しました。次にビルドした Linux kernel を転送して起動してみます。

Linux mainline の repository から clone してビルドします。

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/qemu.org/torvalds/linux.git
$ cd linux
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- multi_v7_defconfig
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage dtbs

ビルドでできたイメージファイルを tftp サーバのディレクトリ (/tftpboot) にコピーします。

$ cp arch/arm/boot/zImage /tftpboot/
$ cp arch/arm/boot/vexpress-v2p-ca9.dtb /tftpboot/

root ファイルシステムを用意します。busybox で作るのが楽ですが、ここでは Linaro が提供している最小構成イメージを使うことにします。サイトから U-boot で読める形式のものをダウンロードします(時期によりファイル名が変更されるので先に web で確認してください)。

$ wget http://releases.linaro.org/openembedded/images/minimal-initramfs-armv7a/latest/linaro-image-minimal-initramfs-genericarmv7a-20170127-460.rootfs.cpio.gz.u-boot
$ cp linaro-image-minimal-initramfs-genericarmv7a-20170127-460.rootfs.cpio.gz.u-boot /tftpboot/rootfs.cpio.gz.u-boot

U-boot on arm QEMU  の手順で U-boot を起動します。起動したら、ネットワークの設定を行います。

=> setenv ipaddr 192.168.1.200     # target(QEMU) の IP アドレス
=> setenv serverip 192.168.1.1     # tftp サーバの IP アドレス
=> setenv netmask 255.255.255.0

bootargs に Linux の command line を設定します。

=> setenv bootargs root=/dev/ram0 rw console=ttyAMA0

tftp で先ほどサーバに置いたファイルの転送を行います。vexpress-a9 は 0x60000000 から SDRAM が実装されています。しかし先頭は kernel の実際の配置先に使用されるため、少し離して配置します。devicetree と rootfs も衝突しない場所に配置します。

=> tftp 0x62000000 zImage
=> tftp 0x63000000 vexpress-v2p-ca9.dtb
=> tftp 0x63008000 rootfs.cpio.gz.u-boot

転送した場所を指定して kernel を起動します。今回は圧縮イメージを使用しているので bootz を使用します。引数は「zImage の配置先」、「rootfs.cpio.gz.u-boot の配置先」、「vexpress-v2p-ca9.dtb の配置先」の順で指定します。

=> bootz 0x62000000 0x63008000 0x63000000
Kernel image @ 0x62000000 [ 0x000000 - 0x774fd8 ]
## Loading init Ramdisk from Legacy Image at 63008000 ...
Image Name:   linaro-image-minimal-initramfs-g
Image Type:   ARM Linux RAMDisk Image (gzip compressed)
Data Size:    8170953 Bytes = 7.8 MiB
Load Address: 00000000
Entry Point:  00000000
Verifying Checksum ... OK
## Flattened Device Tree blob at 63000000
Booting using the fdt blob at 0x63000000
Loading Ramdisk to 67710000, end 67edadc9 ... OK
Loading Device Tree to 67709000, end 6770f963 ... OK

Starting kernel ...

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.13.0-rc4 (kunih@ubuntu) (gcc version 6.3.1 20170404 (Linaro GCC 6.3-2017.05)) #1 SMP Fri Aug 11 10:47:38 JST 2017
[    0.000000] CPU: ARMv7 Processor [410fc090] revision 0 (ARMv7), cr=10c5387d
...
[    3.598945] Freeing unused kernel memory: 2048K
linaro-test [rc=0]#

無事 shell まで起動できました。

[QEMU][boot] upstream U-boot on arm QEMU

QEMU では kernel が直接起動するので bootloader は必要ないのですが、手元に Das U-boot が動作する環境がないので、とりあえず動かした例がないか探していたところ、結構古い記事が多くてトレースできなかったので、最新環境で手元でやり直してみました。

arm ではマシンに versatilepb を使うことが多いのですが、こちらの記事でも言及されている通り、U-boot では versatile が削除されてしまっています。今だと vexpress でやればいい気もするので、とりあえずメモということで qemu のビルドから始めてみます。

QEMU と U-boot のビルドと起動

QEMU の repository から clone してビルド。もちろん package でインストールでもよいです。

$ git clone git://git.qemu.org/qemu.git
$ cd qemu
$ ./configure --target-list=arm-softmmu,aarch64-softmmu
$ make
$ sudo make install

U-boot も同様に最新版をビルド。マシンは vexpress_ca9x4 を指定します。

$ git clone git://git.denx.de/u-boot.git
$ cd u-boot
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_ca9x4_defconfig
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

そして QEMU を起動…あれ何やらエラーが。バイナリが正しく読めない?

$ qemu-system-arm -M vexpress-a9 -nographic -kernel u-boot/u-boot.bin

qemu-system-arm: Trying to execute code outside RAM or ROM at 0x04000000
This usually means one of the following happened:

(1) You told QEMU to execute a kernel for the wrong machine type, and it crashed on startup (eg trying to run a raspberry pi kernel on a versatilepb QEMU machine)
(2) You didn't give QEMU a kernel or BIOS filename at all, and QEMU executed a ROM full of no-op instructions until it fell off the end
(3) Your guest kernel has a bug and crashed by jumping off into nowhere

This is almost always one of the first two, so check your command line and that you are using the right type of kernel for this machine.
If you think option (3) is likely then you can try debugging your guest with the -d debug options; in particular -d guest_errors will cause the log to include a dump of the guest register state at this point.

Execution cannot continue; stopping here.

ELF バイナリを指定したら動きました。何故だろう…

$ qemu-system-arm -M vexpress-a9 -nographic -kernel u-boot/u-boot

U-Boot 2017.07-00001-g13e3ef2 (Aug 13 2017 - 00:26:23 +0900)

DRAM:  128 MiB
WARNING: Caches not enabled
Flash: 128 MiB
MMC:   MMC: 0
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
Net:   smc911x-0
Hit any key to stop autoboot:  0
=>

Network を使う

このままの U-boot だと何もできないので network を設定します。QEMU の vexpress モデルは SMC911X NIC をモデル化しているので U-boot でも使えます。当然ですが I/O を経由しない virtio-net は使えません。

QEMU のネットワークは仮想 I/F である tap と bridge を通して host PC の物理 I/F (eth0) に通します。例として以下のように設定します。

$ sudo tunctl -g `id -u`          # tap 作成
Set 'tap0' persistent and owned by gid 1000
$ sudo brctl addbr br12345        # bridge 作成
$ sudo brctl addif br12345 eth0   # host I/F を bridge に参加
$ sudo brctl addif br12345 tap0   # tap を bridge に参加
$ sudo ifconfig tap0 up
$ sudo ifconfig br12345 up

tap0 と eth0 が bridge で接続されたので、それを使用して U-boot を起動します。

$ qemu-system-arm -M vexpress-a9 -nographic -kernel u-boot/u-boot -net nic -net tap,ifname=tap0

自分の環境に合わせて hostPC と target(QEMU) の IP アドレスを設定します。

=> setenv ipaddr 192.168.1.200     # target(QEMU) の IP アドレス
=> setenv serverip 192.168.1.1     # tftp サーバの IP アドレス

ロードするバイナリを tftp サーバにコピーして、ロードするアドレスを指定してメモリに転送します。

=> tftp 0x62000000 image
smc911x: MAC 52:54:00:12:34:56
smc911x: detected LAN9118 controller
smc911x: phy initialized
smc911x: MAC 52:54:00:12:34:56
Using smc911x-0 device
TFTP from server 192.168.1.1; our IP address is 192.168.1.200
Filename 'image'.
Load address: 0x62000000
Loading: #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         ##############
         2.7 MiB/s
done
Bytes transferred = 7833916 (77893c hex)

無事転送できました。試し終わったら、先のネットワーク設定の後片付けを忘れずに。

$ sudo ifconfig br12345 down
$ sudo ifconfig tap0 down
$ sudo brctl delif br12345 tap0
$ sudo brctl delif br12345 eth0
$ sudo brctl delbr br12345
$ sudo tunctl -d tap0 

arm64 の場合

では arm64 でも同じことができるかというと、QEMU には arm64 は virt モデルが主流のため vexpress モデルはありません。同様に U-boot は virt モデルには対応していません。つまり arm64 では U-boot が適合するモデルの組み合わせは今のところなさそうです。

後者の virt モデルをちょっと試しに(見よう見まねで) U-boot に追加してみたものの、flash のところでハマったのでやはり簡単ではなさそうです。また、この時も ELF でないと起動しなかったのが気になるところです。

[RPi] Raspberry Pi 3

以前 Raspberri Pi を15か月も待って購入したのですが、なかなかいじる時間がなく、他にも放置してるガジェットがあるので Raspberri Pi 3はいいかなと思ってたのですが、手元に最新環境が扱えるプラットフォームがないのもいまいちなので、遅ればせながら共立電子で購入しました。AC アダプタは定格以上のものを用意しないと不安定になるとのことで、お店推奨の 5V/4A の変換コネクタつきを購入しています。

内蔵のeMMC に書き込みが必要なボードが多い中、micro SDカードで直接立ち上がるのはいろいろ便利です。

まずは動作確認

とりあえず Raspbian で動作確認してみます。公式サイトからのダウンロードは1時間以上かかるので mirror から取得します。

zip ファイルがダウンロードできたら、展開して得られた img ファイルを dd か Windows なら Win32DiskImager などで micro SD カードに書き込みます。遅いカードだとかなり時間が掛かります。

あとは本体に micro SD カードをセットし、HDMI・USBキーボードを挿して、ACアダプタから電源を投入して無事動作が確認できました。簡単すぎる…

中身を少し確認

RPi3 は Broadcom の SoC BCM2837 を搭載していて 1.2GHz の Cortex-A53 4 Core を内蔵していますが、dmesg と uname を見るとやはり kernel も userland も ARMv7 compat mode で動作していました。kernel は現時点の最新 LTS  4.9 を update して使っているようです。

pi@raspberrypi:~ $ dmesg | head
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 4.9.35-v7+ (dc4@dc4-XPS13-9333) (gcc version 4.9.3 (crosstool-NG crosstool-ng-1.22.0-88-g8460611) ) #1014 SMP Fri Jun 30 14:47:43 BST 2017
[    0.000000] CPU: ARMv7 Processor [410fd034] revision 4 (ARMv7), cr=10c5383d
[    0.000000] CPU: div instructions available: patching division code
...
pi@raspberrypi:~ $ uname -a
Linux raspberrypi 4.9.35-v7+ #1014 SMP Fri Jun 30 14:47:43 BST 2017 armv7l GNU/Linux

Raspbian image は 第一パーティションを FAT32 にして boot blob、kernel image、devicetree blob などを、第二パーティションに ext4 で rootfs を置いています。fdtdump が入っているので、/boot にある dtb ファイルを覗いてデバイスの構成を見ることができます。

pi@raspberrypi:~ $ df -T
Filesystem     Type     1K-blocks    Used Available Use% Mounted on
/dev/root      ext4       7530028 4018864   3144436  57% /
devtmpfs       devtmpfs    468148       0    468148   0% /dev
tmpfs          tmpfs       472756       0    472756   0% /dev/shm
tmpfs          tmpfs       472756    6440    466316   2% /run
tmpfs          tmpfs         5120       4      5116   1% /run/lock
tmpfs          tmpfs       472756       0    472756   0% /sys/fs/cgroup
/dev/mmcblk0p1 vfat         42030   21419     20611  51% /boot
tmpfs          tmpfs        94552       0     94552   0% /run/user/1000
pi@raspberrypi:~ $ fdtdump /boot/bcm2710-rpi-3-b.dtb
/dts-v1/;
...
/memreserve/ 0 0x1000;
/ {
    compatible = "brcm,bcm2837", "brcm,bcm2836";
    model = "Raspberry Pi 3 Model B";
...

電源遮断の罠

最初の起動直後は初期処理を行うのでファイル更新を行います。そのため電源をいきなり切ると rootfs の更新ができていないままになり、次の起動でファイルが壊れたことでデスクトップが起動しなくなります。

特に user の openbox の lxde-pi-rc.xml が壊れるようで、ググるといくつか同じような質問をしているのが見つかります。当たり前ですが電源切る前の sync と shutdown コマンドは忘れずに。

[Linux] User I/O

今更ながら User I/O (UIO) を使ってみました.UIO は 2.6.23-rc1 で kernel に merge された機能で,ドライバモジュールを書くことなくハードウェア資源を userland からアクセスできる手段を提供します.

I/O にアクセスするだけなら /dev/mem を mmap すれば実現できますが,アドレスが制限されないため安全性が担保できません.UIO では予め使用する領域を定義するとともに,割込み応答もできるようになります.

UIO を利用可能にするには,下記の1行目の CONFIG を有効にします.さらに 2行目の CONFIG を有効にすることで,platform driver として扱うことができ,device tree で I/O 領域や割込みが定義できるようになります(もちろん CONFIG_OF=y が必要).CONFIG_UIO_PDRV_GENIRQ を ”m” にしているのは,ユーザが I/O を使い始めるタイミングを userland から制御できるようにするためです.

CONFIG_UIO=y
CONFIG_UIO_PDRV_GENIRQ=m

device tree には次のようなノードを追加します.reg には使用したい I/O アドレスを page 単位で指定します.これは page 単位(default は 4KB)である必要があります.この領域は例では2つ定義していますが,ノードごとに 5 つまで指定できます.interrupts はノードに対して割込み番号を 1つだけ指定できます.
※ interrupts の数値の意味はアーキテクチャにより異なります.ARM GIC では 0 は SPI, 100 は物理割込み番号の32を引いた値,4はレベル割込みを表します.

/{
        uio {
                compatible = "generic-uio", "myuio";
                reg = <0x40000000 0x1000>,<0x41000000 0x1000>;
                interrupts = <0 100 4>;
        };
};

UIO のノードを1つ作成すると,対応するデバイスファイル(/dev/uioX)が1つできます.マイナー番号は昇順に振られるため,複数のノードを作成するとuio0から順にデバイスファイルが対応付けられます.それぞれのデバイスファイルを制御することでノードごとに定義した割込みを同時に扱うことができます.

ターゲット上では modprobe (insmod) でドライバを有効にしますが,その際に引数に devicetree で定義した compatible 文字列を指定します.これで定義した I/O 領域や割込みが取り込まれ,デバイスファイルが作成されます.

# modprobe uio_pdrv_genirq.ko of_id=myuio

ユーザプログラムでは通常のドライバと同様にデバイスファイルを open した後,mmap すれば device tree で定義した I/O 領域へのアクセスが可能になります.mmap で指定する offset は page 単位で連続した相対アドレスです.例では,上記 0x40000000 が base1,0x41000000 が base2 に にマップされます.

void main()
{
        int fd;
        void __iomem *base1, *base2;

        fd = open("/dev/uio0", O_RDWR);
        base1 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0);
        base2 = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 1 * PAGE_SIZE);
        ...
        munmap(base2);
        munmap(base1);
        close(fd);
}

割込みを待つ場合は,まず割込みを許可するためにデバイスファイルに 1 を書き込みます.その後,read() を呼ぶと割込みが入るまでブロックします.再度割込みを待つ場合は再びデバイスファイルに 1 を書いて割込みを許可します.タイムアウトを追加したい場合は,ppoll() で待って read() で読み出す処理を行うことで実現できます.

void main()
{
        int fd, irqsw, count;
        fd = open("/dev/uio0", O_RDWR);

        irqsw = 1;
        write(fd, &irqsw, sizeof(irqsw)); /* enable interrupt */
        ...
        read(fd, &count, sizeof(count)); /* wait for interrupt */
        ...
        irqsw = 0;
        write(fd, &irqsw, sizeof(irqsw));  /* disable interrupt */
        close(fd);
}

ドライバを作る前に I/O を直接操作して動作を確かめたい場合や,ドライバを作るまでもないHardware制御を行いたいときに,UIO を使うと手軽に実現できます.また,諸事情により公開が難しい Proprietary Driver を作成せざるを得ない場合でも,UIO を使うことで  userland 上に実現することが可能です.

Open Source Summit Japan 2017

1年ぶりの更新になります.
先日有明のコンファレンスセンターにて開催された Open Source Summit Japan に参加してきました.このイベントは初参加で Community への貢献もまだまだの中で,全セッション英語とあってまずは雰囲気を知る機会で2日間参加しました.

最近は LinuxCon と ContainerCon と CloudOpen の統合,Automotive Linux Summit との共催で講演が多岐に渡って行われる反面,深い技術トピックが少なかったのが技術者・開発者としては寂しいところです.組込み Linux の開発関連は Automotive 側に話題が多く,OSSJ は Cloud や Server,Service の話題が多かったように思います.

KeyNote を一通り聞いた後,以下のセッション(とこの他いくつか)を聴講しました.

  • Building Debian-Based Products: Experiences in Collaboration
    • Debian の OE レシピと Image 作成のシステムを統合した話
  • Understanding SCHED_DEADLINE
    • EDF アルゴリズム(最もdeadlineが近いタスクに割り当てる)に基づくスケジューリングの話
  • Taking ARM Servers Mainstream: Designing, Building, and Deploying in the Real World
    • ARMv8 サーバにおける RHEL のソフトウェア・ハードウェア要件の話
  • Building, Deploying and Testing an Industrial Linux Platform
    • 工業向け Linux の長期間継続的なメンテナンス・テストの話
  • Adding new CPU architecture to QEMU
    • QEMU に新アーキを追加する際の translation の仕組みの話
  • Bare Metal Container
    • 遠隔サーバ上の OS 非依存のコンテナを扱うシステムの話

セッションの間はドリンクやクッキー,フルーツなどが自由に楽しめました.日本開催ということでお約束の抹茶ケーキやおかきセットなどもありました.ランチは自力解決とはいえ,有明という立地上選択肢が少なく参加者は大変だったかもしれません.

参加期間中,Community の方々に交じってランチや夕食に 同席させていただいたのですが,せっかくの機会なのに英語力の無さでいろいろ申し訳ない応答になったのが無念でした…