コグノスケ


link 未来から過去へ表示(*)  link 過去から未来へ表示

link もっと前
2020年2月18日 >>> 2020年2月18日
link もっと後

2020年2月18日

Zephyrのブート処理 - ドライバの初期化情報の謎

目次: Zephyr

ドライバの初期化に重要な役割を果たしている __device_PRE_KERNEL_1_start[] の謎を追っていきます。謎について復習すると「実体はバイナリにあるもののソースコードには見当たらない」という点でした。

ソースコードでわからなければバイナリを見るのも一つの手です。シンボルの一覧を調べます。

ドライバの初期化情報(シンボル)
$ riscv64-zephyr-elf-nm -n zephyr/build/zephyr/zephyr.elf

...

80002c38 D __device_PRE_KERNEL_1_start
80002c38 D __device_init_start
80002c38 d __device_sys_init_init_static_pools3
80002c44 d __device_uart_spike
80002c50 d __device_sys_init_uart_console_init0
80002c5c D __device_PRE_KERNEL_2_start
80002c5c d __device_sys_init_z_clock_driver_init0
80002c68 D __device_APPLICATION_start
80002c68 D __device_POST_KERNEL_start
80002c68 D __device_init_end

...

メモリイメージを図示すると下記のような感じで、前回のドライバの初期化処理が期待していると予想した並びになっています。違っていたら動きませんから、当たり前ですけども。

ドライバの初期化情報(メモリイメージ)
======== 80002c38 <-- __device_PRE_KERNEL_1_start, __device_init_start
__device_sys_init_init_static_pools3

======== 80002c44
__device_uart_spike

======== 80002c50
__device_sys_init_uart_console_init0

======== 80002c5c <-- __device_PRE_KERNEL_2_start
__device_sys_init_z_clock_driver_init0

======== 80002c68 <-- __device_POST_KERNEL_start, __device_APPLICATION_start, __device_init_end

POST_KERNELとAPPLICATIONは何も要素を持っておらず、同じアドレスになってしまっているものの、確かにレベル順(PRE_KERNEL_1, PRE_KERNEL_2, POST_KERNEL, APPLICATION)に並んでいます。

ドライバの初期化情報が配置されているセクション
$ riscv64-zephyr-elf-readelf -a zephyr/build/zephyr/zephyr.elf

...

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] vector            PROGBITS        80000000 000060 000010 00  AX  0   0  4
  [ 2] exceptions        PROGBITS        80000010 000070 000258 00  AX  0   0  4
  [ 3] text              PROGBITS        80000268 0002c8 002604 00  AX  0   0  4
  [ 4] sw_isr_table      PROGBITS        8000286c 0028cc 000080 00  WA  0   0  4
  [ 5] devconfig         PROGBITS        800028ec 00294c 000030 00   A  0   0  4
  [ 6] rodata            PROGBITS        8000291c 00297c 000305 00   A  0   0  4
  [ 7] datas             PROGBITS        80002c24 002c84 000014 00  WA  0   0  4
  [ 8] initlevel         PROGBITS        80002c38 002c98 000030 00  WA  0   0  4    ★ここに配置される★
  [ 9] _k_mutex_area     PROGBITS        80002c68 002cc8 000014 00  WA  0   0  4
  [10] bss               NOBITS          80002c80 002cdc 000140 00  WA  0   0  8
  [11] noinit            NOBITS          80002dc0 002cdc 000e00 00  WA  0   0 16
  ...

実体はあるのにソースコードに見当たらず、initlevelという変わった名前のセクションに置かれています。どうやら通常のグローバル変数や、static変数ではなさそうです。

一番最初に出てくる __device_sys_init_init_static_pools3がどうやって作られるのか、追いかけたらわかるかもしれません。部分一致でも良いので、それっぽい名前をgrepするとkernel/mempool.cで見つかります。

mempoolシステムドライバの宣言

// zephyr/kernel/mempool.c

SYS_INIT(init_static_pools, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_OBJECTS);


// zephyr/include/init.h

/* A counter is used to avoid issues when two or more system devices
 * are declared in the same C file with the same init function.
 */
#define Z_SYS_NAME(init_fn) _CONCAT(_CONCAT(sys_init_, init_fn), __COUNTER__)

/**
 * @def SYS_INIT
 *
 * @brief Run an initialization function at boot at specified priority
 *
 * @details This macro lets you run a function at system boot.
 *
 * @param init_fn Pointer to the boot function to run
 *
 * @param level The initialization level, See DEVICE_AND_API_INIT for details.
 *
 * @param prio Priority within the selected initialization level. See
 * DEVICE_AND_API_INIT for details.
 */
#define SYS_INIT(init_fn, level, prio) \
        DEVICE_AND_API_INIT(Z_SYS_NAME(init_fn), "", init_fn, NULL, NULL, level,\
        prio, NULL)


// (参考1)
//  init_fn = init_static_pools
//  level   = PRE_KERNEL_1
//  prio    = CONFIG_KERNEL_INIT_PRIORITY_OBJECTS
//
//  Z_SYS_NAME(init_static_pools) = sys_init_init_static_pools3

マクロの嵐で面食らいますが、基本的に引数をそのまま渡していくだけなので1つずつ見ればさほど難しくありません。

SYS_INIT() はmempoolがシステムドライバであることを宣言するためのAPIです(公式ドキュメント Device Driver Model - Zephyr Project Documentation, System Driver節)。最終的にはDEVICE_AND_API_INIT() が呼ばれます。実はこいつもマクロです、あとで紹介します。

若干難しいのはZ_SYS_NAME() でしょうか?このマクロはsys_init_ と、引数init_fnとカウンタ(__COUNTER__)を連結したトークンを返します。

私の環境でビルドしたときはinit_fnはinit_static_poolsで、__COUNTER__ は3でしたから、sys_init_ とinit_static_poolsと3が連結され、sys_init_init_static_pools3になります。このトークンがDEVICE_AND_API_INIT() の第一引数に渡されます。

シンボル名がsys_init_init_... とinitがダブっていて、変な名前だな?と思った方もいるでしょう、このZ_SYS_NAME() マクロが原因でした。ま、それはさておいて、続きを追いかけます。

ドライバの宣言マクロ

// zephyr/include/device.h

/**
 * @def DEVICE_AND_API_INIT
 *
 * @brief Create device object and set it up for boot time initialization,
 * with the option to set driver_api.
 *
 * @copydetails DEVICE_INIT
 * @param api Provides an initial pointer to the API function struct
 * used by the driver. Can be NULL.
 * @details The driver api is also set here, eliminating the need to do that
 * during initialization.
 */
#ifndef CONFIG_DEVICE_POWER_MANAGEMENT
#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info,  \
                            level, prio, api)                             \
        static const struct device_config _CONCAT(__config_, dev_name) __used \
        __attribute__((__section__(".devconfig.init"))) = {               \
                .name = drv_name, .init = (init_fn),                      \
                .config_info = (cfg_info)                                 \
        };                                                                \
        static Z_DECL_ALIGN(struct device) _CONCAT(__device_, dev_name) __used \
        __attribute__((__section__(".init_" #level STRINGIFY(prio)))) = { \
                .config = &_CONCAT(__config_, dev_name),                  \
                .driver_api = api,                                        \
                .driver_data = data                                       \
        }
#else
/*
 * Use the default device_pm_control for devices that do not call the
 * DEVICE_DEFINE macro so that caller of hook functions
 * need not check device_pm_control != NULL.
 */
#define DEVICE_AND_API_INIT(dev_name, drv_name, init_fn, data, cfg_info, \
                            level, prio, api)                            \
        DEVICE_DEFINE(dev_name, drv_name, init_fn,                       \
                      device_pm_control_nop, data, cfg_info, level,      \
                      prio, api)
#endif


//DEVICE_AND_API_INITを展開すると最終的に下記のようになる
//
//変数名
//  _CONCAT(__config_, dev_name) = __config_sys_init_init_static_pools3
//構造体メンバー
//  drv_name = ""
//  init_fn = init_static_pools
//  cfg_info = NULL
//なので、

static const struct device_config __config_sys_init_init_static_pools3 __used
__attribute__((__section__(".devconfig.init"))) = {
        .name = "",
        .init = init_static_pools,
        .config_info = NULL
};


//変数名
//  _CONCAT(__device_, dev_name) = __device_sys_init_init_static_pools3
//セクション名
//  level = PRE_KERNEL_1
//  prio = 30
//  ".init_" #level STRINGIFY(prio) = ".init_PRE_KERNEL_130"
//構造体メンバー
//  _CONCAT(__config_, dev_name) = __config_sys_init_init_static_pools3
//  api  = NULL
//  data = NULL
//なので、

static Z_DECL_ALIGN(struct device) __device_sys_init_init_static_pools3 __used
__attribute__((__section__(".init_PRE_KERNEL_130"))) = {
        .config = &__config_sys_init_init_static_pools3,
        .driver_api = NULL,
        .driver_data = NULL
}


//(参考2: DEVICE_AND_API_INITの引数と値)
//  dev_name = Z_SYS_NAME(init_fn) = Z_SYS_NAME(init_static_pools) = sys_init_init_static_pools3
//  drv_name = ""
//  init_fn  = init_fn = init_static_pools
//  data     = NULL
//  cfg_info = NULL
//  level    = level = PRE_KERNEL_1
//  prio     = prio = CONFIG_KERNEL_INIT_PRIORITY_OBJECTS = 30
//  api      = NULL

//(参考1: SYS_INITの引数と値)
//  init_fn = init_static_pools
//  level   = PRE_KERNEL_1
//  prio    = CONFIG_KERNEL_INIT_PRIORITY_OBJECTS = 30

ちょっと長いですがDEVICE_AND_API_INIT() を見ていくと、最後は上記のように __config_sys_init_init_static_pools3と __device_sys_init_init_static_pools3の2つの構造体の宣言に展開されることがわかります。

後者の __device_sys_init_init_static_pools3は、先程nmでzephyr.elfを見たときに出てきた、アイツです。それに加えて __device_sys_init_init_static_pools3は、.init_PRE_KERNEL_130という変な名前のセクションに配置されることもわかると思います。

理解が合っているか不安なときは、答え合わせとしてバイナリをチェックです。ビルド時に生成されるオブジェクトbuild/zephyr/kernel/CMakeFiles/kernel.dir/mempool.c.objのシンボルテーブルをobjdumpで確認するとわかりやすいです。

mempool.c.objのシンボルテーブル
$ riscv64-zephyr-elf-objdump -t zephyr/build/zephyr/kernel/CMakeFiles/kernel.dir/mempool.c.obj

...

00000000 l     O .bss.lock      00000000 lock
00000000 l    d  .devconfig.init        00000000 .devconfig.init
00000000 l     O .devconfig.init        0000000c __config_sys_init_init_static_pools3
00000000 l    d  .init_PRE_KERNEL_130   00000000 .init_PRE_KERNEL_130
00000000 l     O .init_PRE_KERNEL_130   0000000c __device_sys_init_init_static_pools3

...

セクション .init_PRE_KERNEL_130に __device_sys_init_init_static_pools3が配置されていることが確認できました。じゃあ、どうやって __device_PRE_KERNEL_1_startに含まれるようになるんでしょう?

続きはまた今度。

編集者:すずき(2023/09/24 12:03)

コメント一覧

  • コメントはありません。
open/close この記事にコメントする



link もっと前
2020年2月18日 >>> 2020年2月18日
link もっと後

管理用メニュー

link 記事を新規作成

<2020>
<<<02>>>
------1
2345678
9101112131415
16171819202122
23242526272829

最近のコメント5件

  • link 21年3月13日
    すずきさん (03/05 15:13)
    「あー、このプログラムがまずいんですね。ご...」
  • link 21年3月13日
    emkさん (03/05 12:44)
    「キャストでvolatileを外してアクセ...」
  • link 24年1月24日
    すずきさん (02/19 18:37)
    「簡単にできる方法はPowerShellの...」
  • link 24年1月24日
    KKKさん (02/19 02:30)
    「追伸です。\nネットで調べたらマイクロソ...」
  • link 24年1月24日
    KKKさん (02/19 02:25)
    「私もエラーで困ってます\n手動での回復パ...」

最近の記事3件

  • link 24年3月19日
    すずき (03/20 02:52)
    「[モジュラージャックの規格] 古くは電話線で、今だとEthernetで良く見かけるモジュラージャックというコネクタとレセプタク...」
  • link 23年4月10日
    すずき (03/19 11:48)
    「[Linux - まとめリンク] 目次: Linuxカーネル、ドライバ関連。Linuxのstruct pageって何?Linu...」
  • link 24年3月18日
    すずき (03/19 11:47)
    「[画面のブランクを無効にする] 目次: LinuxROCK 3 model CのDebian bullseyeイメージは10分...」
link もっとみる

こんてんつ

open/close wiki
open/close Linux JM
open/close Java API

過去の日記

open/close 2002年
open/close 2003年
open/close 2004年
open/close 2005年
open/close 2006年
open/close 2007年
open/close 2008年
open/close 2009年
open/close 2010年
open/close 2011年
open/close 2012年
open/close 2013年
open/close 2014年
open/close 2015年
open/close 2016年
open/close 2017年
open/close 2018年
open/close 2019年
open/close 2020年
open/close 2021年
open/close 2022年
open/close 2023年
open/close 2024年
open/close 過去日記について

その他の情報

open/close アクセス統計
open/close サーバ一覧
open/close サイトの情報

合計:  counter total
本日:  counter today

link About www2.katsuster.net
RDFファイル RSS 1.0

最終更新: 03/20 02:52