目次: 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で見つかります。
// 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で確認するとわかりやすいです。
$ 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に含まれるようになるんでしょう?
続きはまた今度。
< | 2020 | > | ||||
<< | < | 02 | > | >> | ||
日 | 月 | 火 | 水 | 木 | 金 | 土 |
- | - | - | - | - | - | 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
合計:
本日:
管理者: Katsuhiro Suzuki(katsuhiro( a t )katsuster.net)
This is Simple Diary 1.0
Copyright(C) Katsuhiro Suzuki 2006-2023.
Powered by PHP 8.2.18.
using GD 2.3.3(png support.)