みつきんのメモ

組み込みエンジニアです。Interface誌で「Yocto Projectではじめる 組み込みLinux開発入門」連載中

RISC-Vのベアメタル入門(自分用) 割り込み(CLINT)編 その2

はじめに

追記しました(3/28)

題名がもうなんだかわからなくなってきたが、とりあえずCLINT編の続き。

前回、michaeljclark/riscv-probeの割り込みベクタとハンドラの部分を調査した。

実際にこの部分を動かしているサンプルであるprobe.cを実行してみたところ、割り込みの発生要因がIllegal instructionしかなかった。

自分で任意にソフトウェア割り込みを発生させて、それをトラップしてみたいと思ったので実験することにした。

割り込みベクタの処理を読み直す

前回は、trap_vectorの処理をさらっと読みすぎて重要なことを素通りしてしまった。

sxsp/lxspって何?

trap_vector:
    # Save registers.
    addi    sp, sp, -CONTEXT_SIZE
    sxsp    ra, 0
    sxsp    a0, 1
    sxsp    a1, 2
    sxsp    a2, 3
    sxsp    a3, 4
    sxsp    a4, 5
    sxsp    a5, 6
    sxsp    a6, 7
    sxsp    a7, 8
    sxsp    t0, 9
    sxsp    t1, 10
    sxsp    t2, 11
    sxsp    t3, 12
    sxsp    t4, 13
    sxsp    t5, 14
    sxsp    t6, 15

    # Invoke the handler.
    mv      a0, sp
    csrr    a1, mcause
    csrr    a2, mepc
    jal     trap_handler

    # Restore registers.
    lxsp    ra, 0
    lxsp    a0, 1
    lxsp    a1, 2
    lxsp    a2, 3
    lxsp    a3, 4
    lxsp    a4, 5
    lxsp    a5, 6
    lxsp    a6, 7
    lxsp    a7, 8
    lxsp    t0, 9
    lxsp    t1, 10
    lxsp    t2, 11
    lxsp    t3, 12
    lxsp    t4, 13
    lxsp    t5, 14
    lxsp    t6, 15
    addi sp, sp, CONTEXT_SIZE

    # Return
    mret

スタックポインタに書き込む命令なんだと思ったが、Google先生に聞いても何一つ情報が見つからない。

それもそのはず、自前のマクロだった。

riscv-probe/env/common/rv32/macros.sriscv-probe/env/common/rv64/macros.sにそれぞれ定義があった。 v32のmacros.sを示す。

.equ REGBYTES, 4

.macro lx a, b
lw \a, \b
.endm

.macro sx a, b
sw \a, \b
.endm

.macro lxsp a, b
lw \a, ((\b)*REGBYTES)(sp)
.endm

.macro sxsp a, b
sw \a, ((\b)*REGBYTES)(sp)
.endm

.macro .ptr a
.4byte \a
.endm

次にrv64のmacro.s

.equ REGBYTES, 8

.macro lx a, b
ld \a, \b
.endm

.macro sx a, b
sd \a, \b
.endm

.macro lxsp a, b
ld \a, ((\b)*REGBYTES)(sp)
.endm

.macro sxsp a, b
sd \a, ((\b)*REGBYTES)(sp)
.endm

.macro .ptr a
.8byte \a
.endm

要するに32/64のビット幅を吸収している。

やっている事自体は、スタックにレジスタを退避/復帰しているってことで間違いはない。

自分でソフトウェア割り込みを発生する

トラップの部分はriscv-probeをそのまま使用できるので、割り込みを自分で発生させる方法を調べた。

処理手順としては次のような感じ。

f:id:mickey_happygolucky:20190328115103p:plain

msipのLSBを1に設定すると割り込みが発生する。

この処理を追加するのはどこでも良かったのだが、とりあえずriscv-probe/env/qemu-sifive_e/crt.sに こんな感じで処理を追加した。

    .equ   RISCV_MSTATUS_MIE, 0x08
    .equ   RISCV_MIE_MSIE,    0x08
    
    .text
    .globl soft_intr
    .type soft_intr,@function
soft_intr:
    li    t0, RISCV_MSTATUS_MIE
    csrrs zero, mstatus, t0 /* mstatus.mie = 1 */
    
    li    t0, RISCV_MIE_MSIE
    csrrs zero, mie, t0     /* mie.msie = 1 */
    
    lui t0, 0x02000         /* t0 = 0x02000000(msip for hart0)*/
    li  t1, 0x1             /* t1 = 1 */
    sw  t1, (t0)            /* *(0x02000000) = 1 */
    ret

超ベタ書き。

割り込みハンドラの方で、要因をクリアしないと割り込みが入り続けるので次のようにした。

static void trap_save_cause(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
{
    unsigned int mie;

    printf(".");

    mie = read_csr(mie); /* mie読み込み */
    mie &= ~0x8;         /* mie.msieクリア */
    write_csr(mie, mie); /* mie書き込み */
    write_csr(mepc, mepc + 4);
}

csrrcを使えば1発でクリアできるので、少し冗長だが今回は実験なのでこれでよし。

これは間違い。これでは次にmie.msieに1をセットした時点で割り込みが発生してしまう。 ただ割り込みを許可しただけで次の割り込みが発生するのはおかしい。

あと、復帰時点のPCを設定するためのwrite_csr(mepc, mepc + 4);の行も不要。これがあるとおかしくなる。 何もしなくてもちゃんと割り込み時点のpcの次の命令から実行してくれるっぽい。

最終的にはこんな感じ。

static void trap_save_cause(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
{
    unsigned int mie;
    volatile unsigned int *msip = (volatile unsigned int *)0x2000000;

    printf(":(0x%08x):\n", *msip);

    *msip = 0;
    write_csr(mcause, 0x0);
    clear_csr(mie, 0x8);
    /*write_csr(mepc, mepc + 4);*/
}

感触としてはmcauseのクリアは不要な気がする。 次の割り込みが入った時点で上書きされるだろうし。

CLINT.msipをクリアしてやる必要があるというの正しい答えだと思われる。

実験に使ったメイン関数。

int main(int argc, char **argv)
{
    char buf[32];
    unsigned int mcause;
    printf("isa: %s\n", isa_string(buf, sizeof(buf)));
    set_trap_fn(trap_save_cause);

    mcause = read_csr(mcause);
    printf("01 mcause(%08x)\n", mcause);

    soft_intr();

    mcause = read_csr(mcause);
    printf("02 mcause(%08x)\n", mcause);
    write_csr(mie, 0x8);
    printf("done\n");

    return 0;
}

実行結果はこうなる。

isa: rv32imacu
01 mcause(00000000)
:(0x00000001):
02 mcause(00000000)
done

write_csr(mie, 0x8)の行で割り込みが発生していない。

まとめ

CLINT完全に理解してなかった。 写経だけでは不十分なこともある。

NuttX ビルトインアプリケーションを作成する

はじめに

NuttXにビルトインアプリケーションを追加する方法を調査した。

ここが日本語でよくまとまっていた。

プログラムを作成する場所

プログラムをどこに追加するかと言うのが一つ考えどころだが、apps/README.txtによると、

Use of the name ''apps/external'' is suggested because that name
is included in the .gitignore file and will save you some nuisance
when working with GIT.

ということなので、apps/external以下にプログラムを追加する。

$ mkdir -p external/helloworld
$ cd external/helloworld

apps/external以下はリポジトリの管理からは外れているので自分で好き勝手に管理することができる。

$ git init

ただし、externalより下のディレクトリはビルド対象に含まれていないので、シンボリックリンクを貼る。

$ ln -s external/helloworld helloworld

プログラムの作成

次のファイル作成する。

  • Kconfig
  • Make.defs
  • Makefile
  • helloworld_main.c

これらは、apps/examples/helloを参考に、というかコピーしてきて自分のプログラムに合わせて変更する感じで良い。

Kconfig

config EXTERNAL_HELLO
    tristate "\"Hello, World!\" example"
    default n
    ---help---
        Enable the \"Hello, World!\" example

if EXTERNAL_HELLO

config EXTERNAL_HELLO_PROGNAME
    string "Program name"
    default "helloworld"
    depends on BUILD_LOADABLE
    ---help---
        This is the name of the program that will be use when the NSH ELF
        program is installed.

config EXTERNAL_HELLO_PRIORITY
    int "Hello task priority"
    default 100

config EXTERNAL_HELLO_STACKSIZE
    int "Hello stack size"
    default 2048

endif

この例では、次のコンフィグレーションがmake menuconfigで設定出来るようになっている。

  • プログラム名
  • タスクの優先度
  • スタックサイズ

今回は無いが、他のライブラリを使用する場合などは依存関係も定義できる。

プログラム名については後で詳しく説明する。

Make.defs

make menuconfigでプログラムが有効化された場合に、CONFIGURED_APPSにプログラムのディレクトリをappsからの相対パスで追加する。

ifneq ($(CONFIG_EXTERNAL_HELLO),)
CONFIGURED_APPS += helloworld
endif

CONFIGURE_APPSに追加されたディレクトリがビルド対象となる。

Makefile

-include $(TOPDIR)/Make.defs

# Hello, World! built-in application info

CONFIG_EXTERNAL_HELLO_PRIORITY ?= SCHED_PRIORITY_DEFAULT
CONFIG_EXTERNAL_HELLO_STACKSIZE ?= 2048

APPNAME = helloworld

PRIORITY  = $(CONFIG_EXTERNAL_HELLO_PRIORITY)
STACKSIZE = $(CONFIG_EXTERNAL_HELLO_STACKSIZE)

# Hello, World! Example

ASRCS =
CSRCS =
MAINSRC = helloworld_main.c

CONFIG_EXTERNAL_HELLO_PROGNAME ?= hello$(EXEEXT)
PROGNAME = $(CONFIG_EXTERNAL_HELLO_PROGNAME)

MODULE = CONFIG_EXTERNAL_HELLO

include $(APPDIR)/Application.mk

ここでは主に次の変数を設定している。

変数 概要
APPNAME アプリケーション名
PRIORITY タスクの優先度
STACKSIZE スタックサイズ
ASRCS アセンブラソース
CSRCS Cソース
MAINSRC メインのソース
PROGNAME プログラム名
MODULE ローダブルバイナリ用

APPNAME、PRIORITY、STACKSIZEの項目はKconfigで設定されてない場合のデフォルト値を考慮した記述になっている。

MODULEはCONFIG_EXTERNAL_HELLOにmを設定した場合に正しくローダブルバイナリを出力(ローダブルビルド)できるようにするために必要な変数。

ローダブルバイナリ

NuttXはelfをロードする機能を持っているので、アプリケーション単体をelfファイルとして作成し、 NuttX上のファイルシステムにおいてある動的にロード、実行することができる。

フラットビルドの場合はローダブルバイナリだったとしても実行時のアドレス空間は単一となる。

ローダブルビルドを有効にするにはCONFIG_BUILD_LOADABLEをyにする必要がある。その上でアプリのコンフィグをmに設定する。

APPNAMEとPROGNAME

APPNAMEとPROGNAMEで何が違うのか。

詳細はAPPNAME vs. PROGNAMEを参照。

ただ、リンク元の情報は古くなっているので今のBUILD_LOADABLEBUILD_KERNELの説明がごちゃまぜになっている。 以前はカーネルビルド(BUILD_KERNEL)でしかローダブルバイナリのロードができなかったようなので、その名残だと思われる。

フラットビルドかつローダブルビルドしない、つまり単一のバイナリに結合されるケースについて特に用語見つからなかったので、 ここでは「単一ビルド」と呼ぶことにする。

単一ビルドの場合、アプリケーションはlibapps.aというライブラリとして生成されnuttxにリンクされる。 多くの場合プログラムのエントリポイントはmain()だが、単一ビルドの場合は、同じシンボルを使用できないのでそれぞれにエントリポイントの名前つける必要がある。

nshでは$(APPNAME)_mainをそれぞれのアプリケーションのエントリポイントとして探すというルールがあるため、単一ビルドの場合はAPPNAMEが必要となる。

PROGNAMEはローダブルビルドした場合に生成される実行ファイルの名前を設定する。

そのため、これらはそれぞれ別の役割を持っていると言える。

APPNAMEとエントリポイントとMAINSRC

先述の通りAPPNAMEは単一ビルド時にアプリケーションのエントリポイントを探すために使用される。 そのためAPPNAMEとエントリポイントとなる関数の名前は次のルールに則っている必要がある。

$(APPNAME)_main

また、MAINSRCに設定するファイル名もエントリポイントと一致している必要がある。

APPNAMEが「helloworld」だった場合の設定例を次に示す。

項目 概要
APPNAME helloworld
エントリポイント helloworld_main
MAINSRC helloworld_main.c

単一ビルド時の

helloworld_main.c

/****************************************************************************
 * Included Files
 ****************************************************************************/

#include <nuttx/config.h>
#include <stdio.h>

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * hello_main
 ****************************************************************************/

#if defined(BUILD_MODULE)
int main(int argc, FAR char *argv[])
#else
int helloworld_main(int argc, char *argv[])
#endif
{
  printf("!!Hello, World!!\n");
  return 0;
}

BUILD_MODULEが定義されている時は、エントリポイントをmain、それ意外ではhelloworld_mainとしている。

BUILD_MODULEはプログラムをローダブルビルドした場合に自動的に定義される。

ビルド

nuttxディレクトリで次のコマンドを実行する。

$ make clean
$ make menuconfig

メニューで追加したプログラムを有効化してビルドする。

単一ビルドの場合は、次のようにBuiltin Apps:にhelloworldが追加される。

nsh> help
help usage:  help [-v] [<cmd>]

  [         cp        exec      kill      mv        set       uname
  ?         cmp       exit      ls        mw        sh        umount
  basename  dirname   false     mb        ps        sleep     unset
  break     dd        free      mkdir     pwd       test      usleep
  cat       df        help      mh        rm        time      xd
  cd        echo      hexdump   mount     rmdir     true

Builtin Apps:
  hello       helloworld

参考

RISC-Vのベアメタル入門(自分用) 割り込み(CLINT)編

はじめに

RISC-VというかFE310の割り込み周りについて勉強する。

取っ掛かりとしてはここが分かりやすかった。

割り込みの種類

RISC-V ISAではグローバル割り込み(global interrupt)ローカル割り込み(local interrupt)が定義されている。

HART(HARdware Thread)とか割り込みソースとか色々言葉が出てきてややこしい。

HARTは複数いる可能性がある。HART毎にローカル割り込みソースがあって、それらはグローバル割り込みを介さない。

グローバル割り込みソースはPLIC(Platform-Level Interrupt Controller)が処理して、これは主にペリフェラルの割り込みを処理する。

HARTはローカル割り込みソースを複数持てるが、FE310ではローカル割り込みソースはCLINT(Coreplex Local INTerrupts)しかいない。

FE310ではCLINTは次の3つしか処理しない。

  1. ソフトウェア割り込み(SI)
  2. タイマー割り込み(TI)
  3. 外部割り込み(EI)

HARTもHART0しか見当たらない。

グローバル割り込みソースであるところのPLICで処理された割り込みはCLINTの外部割り込み(EI)に投げられてくる。

こんな理解。

f:id:mickey_happygolucky:20190315164120p:plain

でも、SiFive FE310-G000 Manual v2p3では「Figure 9.1: E31 Interrupt Architecture Block Diagram.」となっていた。

f:id:mickey_happygolucky:20190315165457p:plain

まぁ、きっとそういうことなんだろう。

割り込みベクタ

割り込みが発生するとMTVEC(Machine Trap VECtor)に登録されたアドレスに処理が飛ぶ。

割り込みベクタにはシングルモードとマルチモードがある。 マルチモードの場合はMTVECに登録されたアドレスはベクターテーブルの先頭アドレスとなり、要因によってオフセットされたアドレスに処理が飛ぶことになる。

参考になる実装を見てみる

@LDScellさんに教えてもらったmichaeljclark/riscv-probeを見てみることにする。

割り込みベクタの初期化

env/qemu-sifive_e/crt.s

.include "crtm.s"

本体はenv/common/crtm.s

MTVECを初期化している部分はここ

#
# start of trap handler
#

.section .text.init,"ax",@progbits
.globl _start

_start:
    # setup default trap vector
    la      t0, trap_vector
    csrw    mtvec, t0

    # set up stack pointer based on hartid
    csrr    t0, mhartid
    slli    t0, t0, STACK_SHIFT
    la      sp, stacks + STACK_SIZE
    add     sp, sp, t0

    # park all harts excpet hart 0
    csrr    a0, mhartid
    bnez    a0, park

    # jump to libfemto_start_main
    j       libfemto_start_main

    # sleeping harts mtvec calls trap_fn upon receiving IPI
park:
    wfi
    j       park

HARTごとにスタック割り当ててるが、FE310ではHART0しか無い。

割り込みベクタ

MTVECにtrap_vectorを設定している。

trap_vector:
    # Save registers.
    addi    sp, sp, -CONTEXT_SIZE
    sxsp    ra, 0
    sxsp    a0, 1
    sxsp    a1, 2
    sxsp    a2, 3
    sxsp    a3, 4
    sxsp    a4, 5
    sxsp    a5, 6
    sxsp    a6, 7
    sxsp    a7, 8
    sxsp    t0, 9
    sxsp    t1, 10
    sxsp    t2, 11
    sxsp    t3, 12
    sxsp    t4, 13
    sxsp    t5, 14
    sxsp    t6, 15

    # Invoke the handler.
    mv      a0, sp
    csrr    a1, mcause
    csrr    a2, mepc
    jal     trap_handler

    # Restore registers.
    lxsp    ra, 0
    lxsp    a0, 1
    lxsp    a1, 2
    lxsp    a2, 3
    lxsp    a3, 4
    lxsp    a4, 5
    lxsp    a5, 6
    lxsp    a6, 7
    lxsp    a7, 8
    lxsp    t0, 9
    lxsp    t1, 10
    lxsp    t2, 11
    lxsp    t3, 12
    lxsp    t4, 13
    lxsp    t5, 14
    lxsp    t6, 15
    addi sp, sp, CONTEXT_SIZE

    # Return
    mret

次のことを行っている。

  1. レジスタの退避
  2. trap_handlerの呼び出し
  3. レジスタの復帰
  4. プログラムへの復帰

trap_handlerの呼び出し

trap_handlerの呼び出しはここ。

    # Invoke the handler.
    mv      a0, sp
    csrr    a1, mcause
    csrr    a2, mepc
    jal     trap_handler

C言語だと次のようなイメージ。

trap_handler(sp, mcause, mepc);

spはスタックポインタ、mepcとmcauseはCSRレジスタ

レジスタ Description
mepc Machine exception program counter.
mcause Machine trap cause.

mcauseは割り込み要因。 mepcは例外時のプログラムカウンタ。

exception? は割り込み発生時のPCという理解で良いのだろうか。

The RISC-V Instruction Set Manualによると、

When a trap is taken into M-mode, mepc is written with the virtual address of the instruction that
encountered the exception.

なるほど。わからん。

SiFive FE310-G000 Manual v2p3の「9.2 Interrupt Entry and Exit」にわかりやすい説明があった。

The current pc is copied into the mepc register, and then pc is set to the value of mtvec. In the
case where vectored interrupts are enabled, pc is set to mtvec.BASE + 4×exception code.

やはりその理解で良さそうだ。

プログラムへの復帰

割り込み発生前のプログラムに復帰するのはmretらしい。

    # Return
    mret

mretを実行すると次のことが行われる。

  • The privilege mode is set to the value encoded in mstatus.MPP.
  • The value of mstatus.MPIE is copied into mstatus.MIE.
  • The pc is set to the value of mepc.

この時点でmepcに設定されているアドレスに復帰するということか。

割り込みハンドラ

trap_handlerの定義を探す。./libfemto/arch/riscv/trap.cにあった。

...(snip)...

static trap_fn tfn = 0;

...(snip)...

trap_fn get_trap_fn()
{
    return tfn;
}

void set_trap_fn(trap_fn fn)
{
    tfn = fn;
}

void trap_handler(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
{
    if (tfn) {
        tfn(regs, mcause, mepc);
    } else {
        die("machine mode: unhandlable trap %d @ %p", mcause, mepc);
    }
}

static変数としてユーザーが定義するハンドラのポインタとなるtfnを定義。

set_trap_fn()で設定。

trap_handlerが割り込みベクタから直接呼び出される。trap_handlerではtfnに設定されている関数を呼び出す。

f:id:mickey_happygolucky:20190315164046p:plain

このようになるイメージ。

使用例

割り込みの使用例はexamples/probe/probe.c

...(snip)...

static void probe_all_csrs()
{
    int *csrenum = csr_enum_array();
    const char **csrnames = csr_name_array();
    const char* ws = "               ";
    set_trap_fn(trap_save_cause); //ここでハンドラを設定
    while (*csrenum != csr_none) {
        save_mcause = MCAUSE_UNSET;
        long value = read_csr_enum(*csrenum);
        const char* csrname = csrnames[*csrenum];
        if (save_mcause != MCAUSE_UNSET) {
            int async = save_mcause < 0;
            int cause = save_mcause & (((uintptr_t)-1) >> async);
            printf("csr: %s%s %s cause=%ld mtval=0x%lx\n",
                csrname, ws + strlen(csrname), cause < 16
                ? (async ? riscv_intr_names : riscv_excp_names)[cause]
                : "(unknown)", save_mcause, read_csr_enum(csr_mtval));
        } else {
            printf("csr: %s%s 0x%lx\n",
                csrname, ws + strlen(csrname), value);
        }
        csrenum++;
    }
}

...(snip)...

設定されたハンドラはtrap_save_cause()

static void trap_save_cause(uintptr_t* regs, uintptr_t mcause, uintptr_t mepc)
{
    save_mcause = mcause;
    write_csr(mepc, mepc + 4);
}
#define write_csr(reg, val) ({ \
  asm volatile ("csrw " #reg ", %0" :: "rK"(val)); })

write_csr()の第1引数は#regによって文字列化するのでレジスタ名と解釈される。 write_csr(mepc, mepc + 4);はmepcにmepc+4を書き込むということになる。

割り込み前の位置の1つ次の命令のアドレス(mepc+4)に復帰するようにmepcを書き換えている。

まとめ

CLINT完全に理解した

参考資料

meta-raspberrypyの環境でlinux5.0のdevice-tree-overlaysがビルドできない問題の調査(3/15追記)

はじめに

ただしこっちのカーネルでもoverlaysがビルドエラーを起こすので、それは調査する必要がある。

ので調査した。

ログを観察する

タスクごとのログファイルはtmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/temp/log.do_compileで参照できる。

log.do_compile自体は最新のログへのシンボリックリンクになっている。実際のファイルは実行時のプロセスIDが末尾についてlog.do_compile.12345みたいになっている。

成功ログ

ビルドがうまく行くデバイスツリーのログ。

| NOTE: make -j 16 HOSTCC=gcc  -isystem/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2 HOSTCPP=gcc  -E bcm2710-rpi-cm3.dtb
|   Using /home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work-shared/raspberrypi3/kernel-source as source for kernel
|   DTC     arch/arm/boot/dts/bcm2710-rpi-cm3.dtb

失敗ログ

ビルドが失敗するoverlaysのデバイスツリーのログ

| NOTE: make -j 16 HOSTCC=gcc  -isystem/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2 HOSTCPP=gcc  -E overlays/at86rf233.dtbo

違いがほとんど無い。

実行コマンドを観察する

実際に実行されるコマンドはtmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/temp/run.do_compileで参照できる。

これも最新のファイルへのシンボリックリンク

...(snip)...

do_compile() {
    kernel_do_compile
    for dtbf in           bcm2708-rpi-0-w.dtb     bcm2708-rpi-b.dtb     bcm2708-rpi-b-plus.dtb     bcm2709-rpi-2-b.dtb     bcm2710-rpi-3-b.dtb     bcm2710-rpi-3-b-plus.dtb     bcm2708-rpi-cm.dtb     bcm2710-rpi-cm3.dtb               overlays/at86rf233.dtbo     overlays/dwc2.dtbo     overlays/gpio-key.dtbo     overlays/hifiberry-amp.dtbo     overlays/hifiberry-dac.dtbo     overlays/hifiberry-dacplus.dtbo     overlays/hifiberry-digi.dtbo     overlays/i2c-rtc.dtbo     overlays/iqaudio-dac.dtbo     overlays/iqaudio-dacplus.dtbo     overlays/lirc-rpi.dtbo     overlays/pi3-disable-bt.dtbo     overlays/pi3-miniuart-bt.dtbo     overlays/pitft22.dtbo     overlays/pitft28-resistive.dtbo     overlays/pitft35-resistive.dtbo     overlays/pps-gpio.dtbo     overlays/rpi-ft5406.dtbo     overlays/rpi-poe.dtbo     overlays/vc4-kms-v3d.dtbo     overlays/w1-gpio-pullup.dtbo     overlays/w1-gpio.dtbo          ; do
        dtb=`normalize_dtb "$dtbf"`
        oe_runmake $dtb
    done
}

oe_runmake() {
    oe_runmake_call "$@" || die "oe_runmake failed"
}

...(snip)...

oe_runmake_call() {
    bbnote make -j 16  HOSTCC="gcc  -isystem/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2" HOSTCPP="gcc  -E" "$@"
    make -j 16  HOSTCC="gcc  -isystem/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2" HOSTCPP="gcc  -E" "$@"
}

ログと付き合わせると、エラーが発生しているのはoe_runmake_callっぽいことが分かる。

実際に実行してみる

ログから得た情報を元に、手動でコマンドを実行してみる。

devshellを使用する。

bitbake -c devshellを使用すると、クロスコンパイル用の環境変数が解決された状態のシェル(ターミナル)を開くことができる。

これを使用して、エラーが発生するコマンドを実際に実行してみる。

$ bitbake virtual/kernel -c devshell

devshellでは起動時に${S}(ソースコード展開用)に相当するディレクトリがカレントディレクトリとなっている。

しかし${B}(ビルド用)に相当するディレクトリで実行したいので移動する。

ここではtmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/linux-raspberrypi3-standard-build/${B}に相当する。

$ cd ../../../work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/linux-raspberrypi3-standard-build/

(3/15 追記) devshellでカーネルをビルドする場合、${S}でmakeを実行しても${B}に成果物が生成されるようにきちんと環境設定がされているようだ。

ログのコマンドを実行する。

まずは、ログの文字列をコピペしてdevshell上で実行してみる。

$ make -j 16 HOSTCC=gcc  -isystem/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2 HOSTCPP=gcc  -E bcm2710-rpi-cm3.dtb

bcm2710-rpi-cm3.dtbのmake

成功例であるbcm2710-rpi-cm3.dtbの方から試してみる。

# No files, no impossibilities in 0 directories.

# Implicit Rules

# No implicit rules.

# Files

# Not a target:
bcm2710-rpi-cm3.dtb:
#  Command line target.
#  Implicit rule search has not been done.
#  Modification time never checked.
#  File has not been updated.

# files hash-table stats:
# Load=1/1024=0%, Rehash=0, Collisions=0/1=0%
# VPATH Search Paths

# No 'vpath' search paths.

# No general ('VPATH' variable) search path.

# strcache buffers: 1 (0) / strings = 11 / storage = 1391 B / avg = 126 B
# current buf: size = 8162 B / used = 1391 B / count = 11 / avg = 126 B

# strcache performance: lookups = 12 / hit rate = 8%
# hash-table stats:
# Load=11/8192=0%, Rehash=0, Collisions=0/12=0%
# Finished Make data base on Wed Mar 13 08:12:22 2019

成功例のはずが何やら失敗しているらしい。

run.do_compileoe_runmake_callと比較すると、ダブルクォーテーションの有無が異なっている。

make -j 16  HOSTCC="gcc  -isystem/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2" HOSTCPP="gcc  -E" "$@"

$@の部分がターゲットのルール名に展開されるようなのでbcm2710-rpi-cm3.dtbに置き換えて実行してみる。

$ make -j 16  HOSTCC="gcc  -isystem/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2" HOSTCPP="gcc  -E" "bcm2710-rpi-cm3.dtb"
  HOSTCC  scripts/basic/fixdep
  Using /home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work-shared/raspberrypi3/kernel-source as source for kernel
  HOSTLD  scripts/dtc/dtc
  DTC     arch/arm/boot/dts/bcm2710-rpi-cm3.dtb

今度は成功した。

overlays/at86rf233.dtboのmake

こんどはoverlays/at86rf233.dtboを実行してみる。

$ make -j 16  HOSTCC="gcc  -isystem/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2" HOSTCPP="gcc  -E" "overlays/at86rf233.dtbo" 
make[2]: *** ターゲット 'overlays/at86rf233.dtbo' を make するルールがありません.  中止.
Makefile:152: recipe for target 'sub-make' failed
make[1]: *** [sub-make] Error 2
Makefile:15: recipe for target '__sub-make' failed
make: *** [__sub-make] Error 2

失敗した。

基の実装を確認する

oe_runmake_callの元の実装、つまり変数展開前の実装を確認してみる。

元の実装はpoky/meta/classes/base.bbclassにある。

oe_runmake_call() {
    bbnote ${MAKE} ${EXTRA_OEMAKE} "$@"
    ${MAKE} ${EXTRA_OEMAKE} "$@"
}

キモとなるのは${EXTRA_OEMAKE}っぽい事が分かる。

次のコマンドでEXTRA_OEMAKEの内容を表示できるので、どのようになっているか見てみる。

$ bitbake virtual/kernel -e | grep '^EXTRA_OEMAKE='

5.0 EXTRA_OEMAKE

まずは、overlaysのビルドが通らない5.0のEXTRA_OEMAKE。

EXTRA_OEMAKE=" HOSTCC=\"gcc  -isystem/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2\" HOSTCPP=\"gcc  -E\""

4.19 EXTRA_OEMAKE

meta-raspberrypiのthudブランチでは4.14が最新カーネルだが、masterブランチでは4.19が使用できるようなので、こちらのEXTRA_OEMAKEと比較してみた。

ちなみに、こちらは問題なくビルドが通っている。

EXTRA_OEMAKE=" HOSTCC=\"gcc  -isystem/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2\" HOSTCPP=\"gcc  -E\""

バージョン意外に差がほとんど見当たらない。

4.19 oe_runmake_call

run.do_compileの内容も比較してみた。

 make -j 16  HOSTCC="gcc  -isystem/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_4.19.27+gitAUTOINC+c0e09b3420-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2" HOSTCPP="gcc  -E" "overlays/at86rf233.dtbo"

こちらもほとんど差分が無い。

5.0と4.19のソースツリーをそれぞれ眺めてみる

基本的には5.0は4.19をベースとしているはずなので大きな差は無いと思っていたが、いろいろ差分を取ってみるとMakefileのデバイスツリーのルールを定義している部分が変更されていることに気づいた。

4.19

arm/arch/Makefileに次のように定義されている(抜粋)。

%.dtb: | scripts
    $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@
%.dtbo: | scripts
    $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@

PHONY += dtbs dtbs_install

dtbs: prepare scripts
    $(Q)$(MAKE) $(build)=$(boot)/dts

dtbs_install:
    $(Q)$(MAKE) $(dtbinst)=$(boot)/dts

5.0

ルートのMakefileに次のように定義されている(抜粋)。

# ---------------------------------------------------------------------------
# Devicetree files

ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/boot/dts/),)
dtstree := arch/$(SRCARCH)/boot/dts
endif

ifneq ($(dtstree),)

%.dtb: prepare3 scripts_dtc
    $(Q)$(MAKE) $(build)=$(dtstree) $(dtstree)/$@

PHONY += dtbs dtbs_install dt_binding_check
dtbs dtbs_check: prepare3 scripts_dtc
    $(Q)$(MAKE) $(build)=$(dtstree)

dtbs_check: export CHECK_DTBS=1
dtbs_check: dt_binding_check

dtbs_install:
    $(Q)$(MAKE) $(dtbinst)=$(dtstree)

ifdef CONFIG_OF_EARLY_FLATTREE
all: dtbs
endif

endif

PHONY += scripts_dtc
scripts_dtc: scripts_basic
    $(Q)$(MAKE) $(build)=scripts/dtc

dt_binding_check: scripts_dtc
    $(Q)$(MAKE) $(build)=Documentation/devicetree/bindings

いろいろチェックは増えているようだが、だいたい似たようなルールが定義されているように見える。

しかしよく見てみると、こちらには%.dtboのルールが無いことに気づいた。

dtboのルールを追加してみる

とりあえず、tmp/work-shared/raspberrypi3/kernel-sourceデバッグ用のブランチを作成してMakefileを修正し、devshellで実行してみる。

$ bitbake virtual/kernel -c cleansstate
$ bitbake virtual/kernel  #ここで失敗する
$ cd tmp/work-shared/raspberrypi3/kernel-source
$ git checkout -b debug

そしてエディタなどで次の内容で修正する。

diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 9db3c584b2cb..9e43f8119be7 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -343,6 +343,9 @@ $(BOOT_TARGETS): vmlinux
 $(INSTALL_TARGETS):
        $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $@
 
+%.dtbo: | scripts
+       $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@
+
 PHONY += vdso_install
 vdso_install:
 ifeq ($(CONFIG_VDSO),y)

arch/arm/Makefile%.dtbと同じ内容で%.dtboを追加している。

次にdevshellを実行する。

$ bitbake virtual/kernel -c devshell

開いたdevshell上で次のコマンドを実行する。

$ cd ../../../work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/linux-raspberrypi3-standard-build/
$ make -j 16  HOSTCC="gcc  -isystem/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/include -O2 -pipe -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -L/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath-link,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/usr/lib -Wl,-rpath,/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/recipe-sysroot-native/lib -Wl,-O1 -Wl,--allow-shlib-undefined -Wl,--dynamic-linker=/home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/sysroots-uninative/x86_64-linux/lib/ld-linux-x86-64.so.2" HOSTCPP="gcc  -E" "overlays/at86rf233.dtbo" 
  Using /home/mickey/work/yocto/rpi-thud-lk5/build_lk5_rpi/tmp/work-shared/raspberrypi3/kernel-source as source for kernel
  HOSTCC  scripts/basic/fixdep
  HOSTLD  scripts/dtc/dtc
  DTCO    arch/arm/boot/dts/overlays/at86rf233.dtbo

できた。

arch/arm/Makefileへの修正について

本来であれば%.dtbの定義と揃えてルートのMakefile%.dtboを定義したいところだが、このファイルはカーネルバージョンも記述されるため、 メインラインの修正も頻繁に行われることが予想される。

devicetree-overlaysはサポートしているアーキテクチャも限られるため、あえてarch/arm/Makefileに追加することにした。

レシピに組み込む

せっかく原因が解ったので、レシピにパッチを組み込む。

パッチの作成

devshellを閉じて次のコマンドを実行し、パッチを作成する。

$ git add -u
$ git commit -m "Fix missing rules for devicetree overlays"
$ git format-patch HEAD^
$ mv 0001-Fix-missing-rules-for-devicetree-overlays.patch ~/

これでパッチは作成できた。

パッチをレシピへ追加

前回のレシピに組み込んで見る。

ビルドディレクトリで次のコマンドを実行し、レシピから参照できるところにパッチを追加する。

$ mkdir ../layers/meta-rpi-lk5_r/recipes-kernel/linux/files
$ mv ~/0001-Fix-missing-rules-for-devicetree-overlays.patch ../layers/meta-rpi-lk5_r/recipes-kernel/linux/files

layers/meta-rpi-lk5_r/recipes-kernel/linux/linux-raspberrypi_5.0.bbを次の内容に修正する。

LINUX_VERSION ?= "5.0.0"

SRCREV = "00794df915cda0a4603f0a22c802dbad9045c181"
SRC_URI = " \
    git://github.com/raspberrypi/linux.git;branch=rpi-5.0.y \
    file://0001-Fix-missing-rules-for-devicetree-overlays.patch \
    "
require recipes-kernel/linux/linux-raspberrypi.inc

LIC_FILES_CHKSUM = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814"

まとめ

紆余曲折ありながらも、無事に原因を見つけて修正する方法がわかった。 恐らく遠くないうちに修正されるとは思うが、こういう調査は意外に骨が折れる。

おまけ(3/15 追記)

今回作成したパッチをラズパイカーネルの方にプルリクエストしてみた。

すると、こんな返信が帰ってきた。

The required .dtbo rule is in scripts/Makefile.lib. This PR is unnecessary.

しかしエラーがyocto環境でエラーが発生しているのは事実なので、次のように返した。

Thanks for reply.

However, when this kernel was actually built with yocto, the following error occurred.

| make[2]: *** No rule to make target 'overlays/at86rf233.dtbo'.  Stop.
Same results were obtained for other dts under overlays.

I know there are dtbo rules in scripts/Makefile.lib,
But it seems to works not fine.

If you have another approach, please let us know what you think.

すると更にこのような返信が。

If you follow the standard Raspberry Pi building instructions (https://www.raspberrypi.org/documentation/linux/kernel/building.md) with the current rpi-5.0.y tree and it doesn't build then report back here, but it works for me.

ラズパイカーネル側はこの手順が正しく実行できれば基本的にはOKとしているのだろう。

なのでパッチを外した状態で、devshellで次を実行してみて、エラーが発生しなければラズパイカーネル側には責任が無いことになると考えた。

$ KERNEL=kernel7 make -j 16 ARCH=arm CROSS_COMPILE=arm-poky-linux-gnueabi- zImage modules dtbs

この手順に従うと、overlays以下のdtboは正しく生成されるが、常にすべてビルドされる。

そこで、yocto(というかmeta-raspberrypi)でのビルド方法は、もともとラズパイカーネルのメンテナが推奨した方法から外れていて、dtboを個別にビルドしたいのはyocto(meta-raspberrypi)側の都合でしかないのかもしれないと気づいた。

ということで、プルリクエストを撤回した。

今回作成したパッチはいわばバックポート的なワークアラウンドということになる。 meta-raspberrypi側は現時点では、まだLK5には手を付けていないはずなので、きっとうまい方法これから考えてくれるのだろう。

あと、私の英語が貧弱なのはお察しください。

raspberrypi3 yoctoでLinuxカーネル5.0を試す(ラズパイカーネル編)

はじめに

RPiのカーネルこっちで管理されているが、まだ5.0-rc8なのでバニラカーネルを試すことにした。

もう-rc8が取れてたので、こっちも動かしてみる。

基本的な方針は前回と変わらずに下記のようにする。

  • meta-raspberrypiのカーネルとはバージョンが変わったのでbbappendではなくレシピを新設する。
  • meta-raspberrypi自体は汚したくないのでmeta-rpi-lk5を新規に作成する。

大体途中まではほぼ同じ手順になる。

環境構築

いつものやつ。

ソース取得

$ mkdir -p rpi-thud/layers
$ cd rpi-thud/layers
$ git clone git://git.yoctoproject.org/poky.git -b thud
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b thud
$ git clone git://git.openembedded.org/meta-openembedded -b thud

環境変数設定

$ cd ../
$ source layers/poky/oe-init-build-env build_lk5_rpi

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

レイヤ追加

$ bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-python
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-multimedia
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
$ bitbake-layers add-layer ../layers/meta-raspberrypi

local.confの修正

MACHINEの行を修正する。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

meta-rpi-lk5_rの作成

meta-rpi-lk5_rを作成し、meta-raspberrypiからカーネルのレシピをlinux-raspberrypi_5.0.bbとしてコピーする。

レイヤ名はバニラカーネルと分けるために末尾に_rをつけたけどかなり適当なので、適宜好きな名前をつけて構わない。

$ bitbake-layers create-layer meta-rpi-lk5_r
$ mv ./meta-rpi-lk5_r ../layers/
$ mkdir -p ../layers/meta-rpi-lk5_r/recipes-kernel/linux
$ bitbake-layers add-layer ../layers/meta-rpi-lk5_r
$ cp ../layers/meta-raspberrypi/recipes-kernel/linux/linux-raspberrypi_4.14.bb ../layers/meta-rpi-lk5_r/recipes-kernel/linux/linux-raspberrypi_5.0.bb

その後、SRC_URI をバニラカーネルに設定し、バージョンなども5.0向けに設定する。

LINUX_VERSION ?= "5.0.0"

SRCREV = "00794df915cda0a4603f0a22c802dbad9045c181"
SRC_URI = " \
    git://github.com/raspberrypi/linux.git;branch=rpi-5.0.y \
    "
require recipes-kernel/linux/linux-raspberrypi.inc

LIC_FILES_CHKSUM = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814"

レシピのカーネルバージョン

local.confで参照するカーネルのバージョンを明示的に設定する。

PREFERRED_VERSION_linux-raspberrypi = "5.0%"

これで5.0を参照しようとする。

incのパス

別のレイヤにあるincファイルを参照する場合は、レイヤのディレクトリをトップとした相対パスで指定する必要がある。 この場合ではmeta-rpi-lk5_rのレシピからmeta-raspberrypiにあるincファイルを参照したい。

bitbake実行時はレイヤのトップまでは解決されているので、recipes-kernel/linux/linux-raspberrypi.incのように指定する。

filesの参照パス

SRC_URI = "files://"で参照するパスは、実行時のFILESPATHによって解決される。

これはディレクトリ構成に影響されるところなので、レシピではなくlocal.confで対応する。

FILESPATH_append =  ":${TOPDIR}/../layers/meta-raspberrypi/recipes-kernel/linux/files"

カーネルのビルド

カーネルのリビルドは次のコマンドで行なう。

$ bitbake virtual/kernel 

DeviceTree overlaysがビルドできない問題

バニラカーネルだからだと思っていたarch/arm/boot/dts/overlays以下がコンパイルエラーで失敗する。

とりあえずlocal.confでビルド対象から除外して逃げる。

KERNEL_DEVICETREE_remove = " \
     overlays/at86rf233.dtbo \
     overlays/dwc2.dtbo \
     overlays/gpio-key.dtbo \
     overlays/hifiberry-amp.dtbo \
     overlays/hifiberry-dac.dtbo \
     overlays/hifiberry-dacplus.dtbo \
     overlays/hifiberry-digi.dtbo \
     overlays/i2c-rtc.dtbo \
     overlays/iqaudio-dac.dtbo \
     overlays/iqaudio-dacplus.dtbo \
     overlays/lirc-rpi.dtbo \
     overlays/pi3-disable-bt.dtbo \
     overlays/pi3-miniuart-bt.dtbo \
     overlays/pitft22.dtbo \
     overlays/pitft28-resistive.dtbo \
     overlays/pitft35-resistive.dtbo \
     overlays/pps-gpio.dtbo \
     overlays/rpi-ft5406.dtbo \
     overlays/rpi-poe.dtbo \
     overlays/vc4-kms-v3d.dtbo \
     overlays/w1-gpio-pullup.dtbo \
     overlays/w1-gpio.dtbo \
"

イメージの作成

今回はも小構成としてcore-image-minimalを作成する。

$ bitbake core-image-minimal

動作確認

無事に動いた。

Poky (Yocto Project Reference Distro) 2.6.1 raspberrypi3 ttyS0

raspberrypi3 login: root
root@raspberrypi3:~# uname -a
Linux raspberrypi3 5.0.0 #1 SMP Wed Mar 13 06:27:00 UTC 2019 armv7l GNU/Linux
root@raspberrypi3:~#

最終的なlocal.conf

自動生成されたlocal.confに次の内容を追加する。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

PREFERRED_VERSION_linux-raspberrypi = "5.0%"

FILESPATH_append =  ":${TOPDIR}/../layers/meta-raspberrypi/recipes-kernel/linux/files"

KERNEL_DEVICETREE_remove = " \
     overlays/at86rf233.dtbo \
     overlays/dwc2.dtbo \
     overlays/gpio-key.dtbo \
     overlays/hifiberry-amp.dtbo \
     overlays/hifiberry-dac.dtbo \
     overlays/hifiberry-dacplus.dtbo \
     overlays/hifiberry-digi.dtbo \
     overlays/i2c-rtc.dtbo \
     overlays/iqaudio-dac.dtbo \
     overlays/iqaudio-dacplus.dtbo \
     overlays/lirc-rpi.dtbo \
     overlays/pi3-disable-bt.dtbo \
     overlays/pi3-miniuart-bt.dtbo \
     overlays/pitft22.dtbo \
     overlays/pitft28-resistive.dtbo \
     overlays/pitft35-resistive.dtbo \
     overlays/pps-gpio.dtbo \
     overlays/rpi-ft5406.dtbo \
     overlays/rpi-poe.dtbo \
     overlays/vc4-kms-v3d.dtbo \
     overlays/w1-gpio-pullup.dtbo \
     overlays/w1-gpio.dtbo \
"

まとめ

ラズパイカーネルは、手動でパッチなどを当てなくてもビルドできるので、やはりこちらの方が簡単。

ただしこっちのカーネルでもoverlaysがビルドエラーを起こすので、それは調査する必要がある。

yocto SSH越しでbitbake virtual/kernel -c menuconfigが失敗する場合の対処

はじめに

ubuntuを使っていて、SSH越しにbitbake virtual/kernel -c menuconfig-c devshellをすると、次のエラーが出て失敗する事がある。

ERROR: linux-raspberrypi-1_5.0.0+gitAUTOINC+00794df915-r0 do_menuconfig: Unable to spawn terminal auto: Execution of '/home/mickey/work/yocto/rpi-thud/layers/poky/scripts/oe-gnome-terminal-phonehome /tmp/tmpja7518rr /home/mickey/work/yocto/rpi-thud/build_lk5_rpi/tmp/work/raspberrypi3-poky-linux-gnueabi/linux-raspberrypi/1_5.0.0+gitAUTOINC+00794df915-r0/temp/run.do_terminal.4874' failed with exit code 1:
# オプション“-x”は非推奨であり、将来のgnome-terminal バージョンでは削除される可能性があります。
# “-- ”を使用するとオプション指定を終了します。この後ろに実行するコマンドラインを指定します。
# Error constructing proxy for org.gnome.Terminal:/org/gnome/Terminal/Factory0: org.gnome.Terminal を StartServiceByName で呼び出そうとしてエラー: タイムアウトしました

表示されるエラーメッセージはmenuconfigdevshellでどちらもだいたい同じ。

原因

この2つのタスクでは、自動的にターミナルが起動されるようになっているが、その際にgnome-terminalが選択される場合に発生する。

# オプション“-x”は非推奨であり、将来のgnome-terminal バージョンでは削除される可能性があります。

こちらの行は直接の原因ではない。

試しにpoky/meta/lib/oe/terminal.pyの次の部分を修正してみたが、結果は変わらなかった。

diff --git a/meta/lib/oe/terminal.py b/meta/lib/oe/terminal.py
index caeb5e31ec..17038a0727 100644
--- a/meta/lib/oe/terminal.py
+++ b/meta/lib/oe/terminal.py
@@ -52,7 +52,7 @@ class XTerminal(Terminal):
             raise UnsupportedTerminal(self.name)
 
 class Gnome(XTerminal):
-    command = 'gnome-terminal -t "{title}" -x {command}'
+    command = 'gnome-terminal -t "{title}" -- {command}'
     priority = 2
 
     def __init__(self, sh_cmd, title=None, env=None, d=None):

どうやら、こちらが起動しない原因となっているようだ。

# Error constructing proxy for org.gnome.Terminal:/org/gnome/Terminal/Factory0: org.gnome.Terminal を StartServiceByName で呼び出そうとしてエラー: タイムアウトしました

解決法その1 - terminal.pyを修正する

ここによれば、dbus-launch経由でgnome-terminalを起動すると問題が回避できるそうだ。

次のように修正して試したところ、gnome-terminalが起動した。

diff --git a/meta/lib/oe/terminal.py b/meta/lib/oe/terminal.py
index caeb5e31ec..92ebf01f50 100644
--- a/meta/lib/oe/terminal.py
+++ b/meta/lib/oe/terminal.py
@@ -52,7 +52,7 @@ class XTerminal(Terminal):
             raise UnsupportedTerminal(self.name)
 
 class Gnome(XTerminal):
-    command = 'gnome-terminal -t "{title}" -x {command}'
+    command = 'dbus-launch gnome-terminal -t "{title}" -x {command}'
     priority = 2
 
     def __init__(self, sh_cmd, title=None, env=None, d=None):

解決法その2 - 別のターミナルを使用する

local.confOE_TERMINAL変数を設定すると、別のターミナルを使用することができる。

OE_TERMINAL = "xterm"

これでmenuconfigdevshellの時にxtermが起動されることになる。

ターミナルは若干プアになるがこれでも作業に支障はない。

マニュアルによれば、次の値が使用できる。

OE_TERMINAL¶
Controls how the OpenEmbedded build system spawns interactive terminals on the host development system (e.g. using the BitBake command with the -c devshell command-line option). For more information, see the "Using a Development Shell" section in the Yocto Project Development Tasks Manual.

You can use the following values for the OE_TERMINAL variable:

     auto
     gnome
     xfce
     rxvt
     screen
     konsole
     none

予めホストPCには使用したいターミナルのパッケージをインストールしておく必要がある。

なぜかこの一覧にはxtermがないが、手元の環境では問題なく使用できる。

まとめ

SSH経由でターミナルを起動するコマンドを使用する時にgnome-terminalが選択されると失敗する。

どうしてもgnome-terminalを使用したい場合は、terminal.pyを修正する方法でも構わないが、 pokyの持ち物をローカルで修正すると、git pullなどでツリーを更新したい時に管理が複雑になる。

gnome-terminalにこだわりがないのであれば、OE_TERMINALで他のターミナルを使用する方法をおすすめする。

raspberrypi3 yoctoでLinuxカーネル5.0を試す(バニラカーネル編)

はじめに

Linuxカーネル5.0がリリースされたのでmeta-raspberrypiの環境で試そうと思ったら、いろいろと罠があって4日も経ってしまった。

RPiのカーネルこっちで管理されているが、まだ5.0-rc8なのでバニラカーネルを試すことにした。

meta-raspberrypiのカーネルとはバージョンが変わったのでbbappendではなくレシピを新設する。ただし、meta-raspberrypi自体は汚したくないのでmeta-rpi-lk5を新規に作成する。

bbappendはレシピの拡張なのでレイヤをまたいでいても元のレシピの環境変数は引き継がれるため、incファイルなどが元のレイヤにある場合でも正常に処理できるが、 新規に作成するレシピではそれらは引き継がれないため別のレイヤのincを読み込む場合、実行時の環境変数などを考慮する必要がある。

また、今回は無理矢理でも動かす事が目的なので、本格的に使用したい場合はRPi用のカーネルカーネルがリリースされるまで待つことをおすすめする。

環境構築

いつものやつ。

ソース取得

$ mkdir -p rpi-thud/layers
$ cd rpi-thud/layers
$ git clone git://git.yoctoproject.org/poky.git -b thud
$ git clone git://git.yoctoproject.org/meta-raspberrypi -b thud
$ git clone git://git.openembedded.org/meta-openembedded -b thud

環境変数設定

$ source layers/poky/oe-init-build-env build_lk5

自動的にビルドディレクトリに移動される。 これで、bitbake関連のツールが使用可能になる。

レイヤ追加

$ bitbake-layers add-layer ../layers/meta-openembedded/meta-oe
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-python
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-multimedia
$ bitbake-layers add-layer ../layers/meta-openembedded/meta-networking
$ bitbake-layers add-layer ../layers/meta-raspberrypi

local.confの修正

MACHINEの行を修正する。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

meta-rpi-lk5の作成

まずはmeta-rpi-lk5を作成し、meta-raspberrypiからカーネルのレシピをlinux-raspberrypi_5.0.bbとしてコピーする。

$ bitbake-layers create-layer meta-rpi-lk5
$ mv ./meta-rpi-lk5 ../layers/
$ mkdir -p ../layers/meta-rpi-lk5/recipes-kernel/linux
$ bitbake-layers add-layer ../layers/meta-rpi-lk5
$ cp ../layers/meta-raspberrypi/recipes-kernel/linux/linux-raspberrypi_4.14.bb ../layers/meta-rpi-lk5/recipes-kernel/linux/linux-raspberrypi_5.0.bb

その後、SRC_URI をバニラカーネルに設定し、バージョンなども5.0向けに設定する。

LINUX_VERSION ?= "5.0.0"

SRCREV = "1c163f4c7b3f621efff9b28a47abb36f7378d783"
SRC_URI = " \
    git://github.com/torvalds/linux.git;protocol=https \
"

require recipes-kernel/linux/linux-raspberrypi.inc

LIC_FILES_CHKSUM = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814"

レシピのカーネルバージョン

local.confで参照するカーネルのバージョンを明示的に設定する。

PREFERRED_VERSION_linux-raspberrypi = "5.0%"

これで5.0を参照しようとする。

incのパス

別のレイヤにあるincファイルを参照する場合は、レイヤのディレクトリをトップとした相対パスで指定する必要がある。 この場合ではmeta-rpi-lk5のレシピからmeta-raspberrypiにあるincファイルを参照したい。

bitbake実行時はレイヤのトップまでは解決されているので、recipes-kernel/linux/linux-raspberrypi.incのように指定する。

filesの参照パス

SRC_URI = "files://"で参照するパスは、実行時のFILESPATHによって解決される。

これはディレクトリ構成に影響されるところなので、レシピではなくlocal.confで対応する。

FILESPATH_append =  ":${TOPDIR}/../layers/meta-raspberrypi/recipes-kernel/linux/files"

defconfigのダウンロード

バニラカーネルではraspberrypi3向けのdefconfigが無い1ので、raspberrypiのカーネルから持ってくる。

SRC_URI = " \
    git://github.com/torvalds/linux.git;protocol=https \
    https://raw.githubusercontent.com/raspberrypi/linux/rpi-5.0.y/arch/arm/configs/bcm2709_defconfig \
"

do_kernel_checkout_append() {
    cp ${WORKDIR}/bcm2709_defconfig ${S}/arch/arm/configs/
}

カスタマイズコードの反映

raspberrypi向けのカスタマイズコードをバニラカーネルに反映する。

とりあえず、ここにLK5に向けて作業中のリポジトリがある(がまだベースが最新ではない)ので、これを引っ張ってきてパッチを作成する。

yocto側のカーネルソースの展開

$ bitbake virtual/kernel -c clean
$ bitbake virtual/kernel -c kernel_checkout

RPiのカーネルの取得

$ mkdir TEMP
$ cd TEMP
$ git clone https://github.com/raspberrypi/linux.git -b rpi-5.0.y
$ cd linux

rsyncで追加分のみ処理したい(更新は除外したい)ので、転送先のファイルのmtimeを更新する。

$ find ../../tmp/work-shared/raspberrypi3/kernel-source/ | xargs touch

rsyncでコピー

$ rsync -rvu ./ ../../tmp/work-shared/raspberrypi3/kernel-source/ --exclude='.git*'

.git関連は除外しておく。

ラズパイ関連のキーワードとしてBCM2835が記述されているファイルをgrepで引っ掛けて上書きコピーをする。

xargsは-Iを使うと後続のコマンドに引数を渡すことができる。ここでは-IXXXとしているので、xargsに渡る引数をXXXとして参照できるようになる。

$ grep -r 'BCM2835' --exclude-dir='.git' . | cut -d':' -f1 | uniq | xargs -IXXX cp -f XXX ../../tmp/work-shared/raspberrypi3/kernel-source/XXX

grepから漏れてて、ビルドに必要なファイルを個別に上書きコピーする。

$ cp ./include/soc/bcm2835/raspberrypi-firmware.h ../../tmp/work-shared/raspberrypi3/kernel-source/include/soc/bcm2835
$ cp ./drivers/staging/vc04_services/bcm2835-camera/bcm2835-camera.h ../../tmp/work-shared/raspberrypi3/kernel-source/drivers/staging/vc04_services/bcm2835-camera

ここから一度コミットしてgit format-patchでパッチを作成

$ cd ../..
$ pushd .
$ cd tmp/work-shared/raspberrypi3/kernel-source/
$ git checkout -b test
$ git add .
$ git reset HEAD arch/arm/configs/bcm2709_defconfig
$ git commit -m "Add files for RPi"
$ git format-patch HEAD^
$ mv 0001-Add-files-for-RPi.patch ~/
$ popd

bcm2709_defconfigはSRC_URIで取得するのでパッチからは外す。

レシピにパッチを組み込む

$ mkdir ../layers/meta-rpi-lk5/recipes-kernel/linux/files
$ mv ~/0001-Add-files-for-RPi.patch ../layers/meta-rpi-lk5/recipes-kernel/linux/files/

linux-raspberrypi_5.0.bbのSRC_URIを次のようにした。

SRC_URI = " \
    git://github.com/torvalds/linux.git;protocol=https \
    https://raw.githubusercontent.com/raspberrypi/linux/rpi-5.0.y/arch/arm/configs/bcm2709_defconfig \
    file://0001-Add-files-for-RPi.patch \
"

カーネルのリビルド

カーネルのリビルドは次のコマンドで行なう。

$ bitbake virtual/kernel -c cleansstate
$ bitbake virtual/kernel 

mmcの認識

SDをなぜかmmcblk1と認識するようなので、local.confで対応する。

CMDLINE = "dwc_otg.lpm_enable=0 console=serial0,115200 root=/dev/mmcblk1p2 rootfstype=ext4 rootwait"

イメージの作成

今回はあえて最小構成としてcore-image-minimalを作成する。

$ bitbake core-image-minimal

動作確認

無事に動いた。

Poky (Yocto Project Reference Distro) 2.6.1 raspberrypi3 ttyS0

raspberrypi3 login: root
root@raspberrypi3:~# uname -a
Linux raspberrypi3 5.0.0 #1 SMP Fri Mar 8 05:46:52 UTC 2019 armv7l GNU/Linux

最終的なカーネルのレシピ

linux-raspberrypi_5.0.bb

LINUX_VERSION ?= "5.0.0"

SRCREV = "1c163f4c7b3f621efff9b28a47abb36f7378d783"
SRC_URI = " \
    git://github.com/torvalds/linux.git;protocol=https \
    https://raw.githubusercontent.com/raspberrypi/linux/rpi-5.0.y/arch/arm/configs/bcm2709_defconfig \
    file://0001-Add-files-for-RPi.patch \
"

SRC_URI[md5sum] = "706ffdea63fa9d8525e27d89aa167a31"
SRC_URI[sha256sum] = "42b90e26bb976f29d1fecc669517d16d11d0a8ba58c9040c9e70565a2216f26b"

require recipes-kernel/linux/linux-raspberrypi.inc

LIC_FILES_CHKSUM = "file://COPYING;md5=bbea815ee2795b2f4230826c0c6b8814"

do_kernel_checkout_append() {
    cp ${WORKDIR}/bcm2709_defconfig ${S}/arch/arm/configs/
}

最終的なlocal.conf

自動生成されたlocal.confに次の内容を追加する。

MACHINE = "raspberrypi3"
DL_DIR ?= "${TOPDIR}/../downloads"

# enable uart
ENABLE_UART = "1"

# systemd
DISTRO_FEATURES_append = " systemd pam"
VIRTUAL-RUNTIME_init_manager = "systemd"
DISTRO_FEATURES_BACKFILL_CONSIDERED = "sysvinit"
VIRTUAL-RUNTIME_initscripts = ""

IMAGE_INSTALL_append = " connman connman-client"

PREFERRED_VERSION_linux-raspberrypi = "5.0%"
FILESPATH_append =  ":${TOPDIR}/../layers/meta-raspberrypi/recipes-kernel/linux/files"

#IMAGE_FSTYPES="tar.bz2 ext4 rpi-sdimg"

CMDLINE = "dwc_otg.lpm_enable=0 console=serial0,115200 root=/dev/mmcblk1p2 rootfstype=ext4 rootwait"

KERNEL_DEVICETREE_remove = " \
     overlays/at86rf233.dtbo \
     overlays/dwc2.dtbo \
     overlays/gpio-key.dtbo \
     overlays/hifiberry-amp.dtbo \
     overlays/hifiberry-dac.dtbo \
     overlays/hifiberry-dacplus.dtbo \
     overlays/hifiberry-digi.dtbo \
     overlays/i2c-rtc.dtbo \
     overlays/iqaudio-dac.dtbo \
     overlays/iqaudio-dacplus.dtbo \
     overlays/lirc-rpi.dtbo \
     overlays/pi3-disable-bt.dtbo \
     overlays/pi3-miniuart-bt.dtbo \
     overlays/pitft22.dtbo \
     overlays/pitft28-resistive.dtbo \
     overlays/pitft35-resistive.dtbo \
     overlays/pps-gpio.dtbo \
     overlays/rpi-ft5406.dtbo \
     overlays/rpi-poe.dtbo \
     overlays/vc4-kms-v3d.dtbo \
     overlays/w1-gpio-pullup.dtbo \
     overlays/w1-gpio.dtbo \
"

まとめ

バニラカーネルとRPiカーネルはかなり違う。

キャッシュ周りやデバイスツリー周りなど、注意が必要。

今回は無理矢理でも動かすことがテーマだったので、ほぼRPiカーネル側の修正をバニラに持っていくという力技で対応した。 もうここまで来るとバニラカーネルではない。

[おまけ] overlays以下のデバイスツリーがビルドエラー

次のようにoverlaysのデバイスツリーのビルドで失敗する。

| make[2]: *** No rule to make target 'overlays/at86rf233.dtbo'.  Stop.

Makefileなども見直したが結局うまく行かず。最終的にはoverlaysを諦めた。

そのためにlocal.confに次の行を追加した。

KERNEL_DEVICETREE_remove = " \
     overlays/at86rf233.dtbo \
     overlays/dwc2.dtbo \
     overlays/gpio-key.dtbo \
     overlays/hifiberry-amp.dtbo \
     overlays/hifiberry-dac.dtbo \
     overlays/hifiberry-dacplus.dtbo \
     overlays/hifiberry-digi.dtbo \
     overlays/i2c-rtc.dtbo \
     overlays/iqaudio-dac.dtbo \
     overlays/iqaudio-dacplus.dtbo \
     overlays/lirc-rpi.dtbo \
     overlays/pi3-disable-bt.dtbo \
     overlays/pi3-miniuart-bt.dtbo \
     overlays/pitft22.dtbo \
     overlays/pitft28-resistive.dtbo \
     overlays/pitft35-resistive.dtbo \
     overlays/pps-gpio.dtbo \
     overlays/rpi-ft5406.dtbo \
     overlays/rpi-poe.dtbo \
     overlays/vc4-kms-v3d.dtbo \
     overlays/w1-gpio-pullup.dtbo \
     overlays/w1-gpio.dtbo \
"

OF_OVERLAYの調査

OV_OVERLAYが有効になっていないことに気づいたので掘り下げた。

RPiのカーネルではOF_CONFIGFSによりselectされる。これはbcm2709_defconfigでも設定されている。しかし、バニラカーネルではOF_CONFIGSの機能が実装されていないため結果的にOF_OVERLAYも有効にならない。

OF_CONFIGFSが実装されないのは次のような理由らしい。

I think most of us have given up on making it work in the kernel, just
too many race conditions.

結局、OF_OVERLAYを有効化しても問題は解決しなかった。

Makefileでルールの解決に失敗するのは

./scripts/Makefile.lib./scripts/Makefile.dtbinstが関係していると思ったのだが、これらをyocto側に持っていっても解決できなかった。


  1. 無いわけではないが、使えないっぽいというか試行錯誤の上断念した。