コグノスケ


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

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

2020年2月8日

Zephyr OSで遊ぼう その7 - 独自のシリアルドライバの枠組み

目次: Zephyr

Zephyrのビルドに成功し、gdbで追跡したところHello worldのmain関数まで到達していることもわかりました。ここまでくればあとは文字出力だけです。

まずHogeボードのdefconfigにCONFIG_UART_SPIKE=yを加えます。cmakeに「そんなコンフィグはない」と怒られるようになりますが、これから作るのでOKです。

シリアルドライバのKconfig, CMake

シリアルドライバのディレクトリはzephyr/drivers/serialにあります。ディレクトリの構造を見ると、CMakeLists.txt, Kconfig.*, ソースコードuart_*.cが並んでいます。この3つの組を追加もしくは変更すれば良さそうです。

zephyr/drivers/serial/Kconfig.spike

# SPDX-License-Identifier: Apache-2.0

menuconfig UART_SPIKE
        bool "Spike Simulator serial driver"
        depends on SOC_RISCV_SPIKE
        select SERIAL_HAS_DRIVER
        help
          This option enables the Spike Simulator serial driver.

名前は何でも良いですが、Kconfig.spikeという名前にしました。Kconfig.socとは違って、ファイルを足すだけではダメで、シリアルドライバのKconfigから読んでもらう必要があります。

zephyr/drivers/serial/Kconfigに追加する行

...

source "drivers/serial/Kconfig.litex"

source "drivers/serial/Kconfig.rtt"

source "drivers/serial/Kconfig.xlnx"

source "drivers/serial/Kconfig.spike"    # ★この行を足す★

endif # SERIAL

Kconfigを追加してcmakeをするとまだ怒られます。CMakeList.txtも変更する必要があるとのことです。

Kconfig追加後のcmake
-- Configuring done
CMake Error at ../../cmake/extensions.cmake:372 (add_library):
  No SOURCES given to target: drivers__serial
Call Stack (most recent call first):
  ../../cmake/extensions.cmake:349 (zephyr_library_named)
  ../../drivers/serial/CMakeLists.txt:3 (zephyr_library)


CMake Generate step failed.  Build files cannot be regenerated correctly.
FAILED: build.ninja

CMakeLists.txtは他の行に習って追加します。CMakeLists.txtを変更しただけだとuart_spike.cが無いと言われるので、touch uart_spike.cで空のファイルを作成します。

zephyr/drivers/serial/CMakeLists.txtに追加する行

...

zephyr_library_sources_if_kconfig(uart_liteuart.c)
zephyr_library_sources_ifdef(CONFIG_UART_RTT_DRIVER uart_rtt.c)
zephyr_library_sources_if_kconfig(uart_xlnx_ps.c)
zephyr_library_sources_if_kconfig(uart_spike.c)    # ★この行を足す★

...

他の行に習って追加するだけではつまらないので、CMakeのスクリプトも眺めます。今回使用したzephyr_library_sources_if_kconfig() とzephyr_library_sources_ifdef() との違いは、ソースコードのコメントにある説明がわかりやすいです。

ファイル名を大文字にして、CONFIG_ と連結させた名前(今回のケースだとCONFIG_UART_SPIKE)を生成して、コンフィグが有効ならソースコードをビルド対象に加えるという意味です。Kconfigのコンフィグはy, n, mの3値を取りますが、Zephyrのスクリプトでは、コンフィグが有効だとyという値が入り、無効だと未定義になるようです。

zephyr_library_sources_if_kconfigの実装

# zephyr/cmake/extensions.cmake

...

# zephyr_library_sources_if_kconfig(fft.c)
# is the same as
# zephyr_library_sources_ifdef(CONFIG_FFT fft.c)

...

function(zephyr_library_sources_if_kconfig item)
  get_filename_component(item_basename ${item} NAME_WE)
  string(TOUPPER CONFIG_${item_basename} UPPER_CASE_CONFIG)
  zephyr_library_sources_ifdef(${UPPER_CASE_CONFIG} ${item})
endfunction()

...

function(zephyr_library_sources_ifdef feature_toggle source)
  if(${${feature_toggle}})
    zephyr_library_sources(${source} ${ARGN})
  endif()
endfunction()


# zephyr/cmake/extensions.cmake

...

function(zephyr_library_sources source)
  target_sources(${ZEPHYR_CURRENT_LIBRARY} PRIVATE ${source} ${ARGN})
endfunction()

...

以上の変更でビルドが通り、シリアルドライバを実装する枠が完成しました。続きは次回。

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

コメント一覧

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



2020年2月9日

Zephyr OSで遊ぼう その8 - 独自のシリアルドライバの実装

目次: Zephyr

前回はシリアルドライバの枠組み、というか、何も実装されていないドライバを追加しました。今回はシリアルドライバを実装します。

Zephyrのドライバについてはドキュメント(Device Driver Model - Zephyr Project Documentation)があります。ドライバの定義や初期化を司るAPIがDriver APIの節に書いてあります。

今回作成するUARTドライバでは初期化は要りませんが、ドライバのAPIをいくつか実装したいのでDEVICE_AND_API_INIT() を使います。

UARTのドライバで実装できるAPIの一覧はドキュメント(UART - Zephyr Project Documentation)に書いてあります。APIはたくさんありますが、poll_in, poll_outがあれば動作するようなので、この2つを作ります。

コードは下記のようにしました。

QEMU Spikeモード用のシリアルドライバ実装

/*
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @brief UART driver for the Spike Simulator
 */

#include <kernel.h>
#include <arch/cpu.h>
#include <drivers/uart.h>

volatile u64_t tohost;
volatile u64_t fromhost;

void uart_spike_poll_out(struct device *dev, unsigned char c)
{
        u64_t cmd;

        // QEMU spike mode
        u8_t dev_no = 1;
        u8_t cmd_no = 1;
        u64_t payload = c;

        cmd = ((u64_t)dev_no << 56) |
                ((u64_t)cmd_no << 48) |
                (payload & 0xffffffffffffULL);

        tohost = cmd;

        while (fromhost == 0)
                ;
        fromhost = 0;
}

static int uart_spike_poll_in(struct device *dev, unsigned char *c)
{
        return -1;
}

static int uart_spike_init(struct device *dev)
{
        return 0;
}

static const struct uart_driver_api uart_spike_driver_api = {
        .poll_in          = uart_spike_poll_in,
        .poll_out         = uart_spike_poll_out,
};

DEVICE_AND_API_INIT(uart_spike_0, DT_INST_0_SPIKE_UART_SPIKE_LABEL,
                    uart_spike_init, NULL, NULL,
                    PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
                    (void *)&uart_spike_driver_api);

このままビルドするとDEVICE_AND_API_INIT() に渡しているラベルDT_INST_0_SPIKE_UART_SPIKE_LABELが未定義だと怒られます。これについては後ほど作り方を説明します。

ラベルが未定義のエラー
zephyr/drivers/serial/uart_spike.c:73:35: error: 'DT_INST_0_SPIKE_UART_SPIKE_LABEL' undeclared here (not in a function); did you mean 'DT_COMPAT_SPIKE_UART_SPIKE'?
 DEVICE_AND_API_INIT(uart_spike_0, DT_INST_0_SPIKE_UART_SPIKE_LABEL,
                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../include/device.h:106:11: note: in definition of macro 'DEVICE_AND_API_INIT'
   .name = drv_name, .init = (init_fn),     \
           ^~~~~~~~
[89/97] Linking C static library zephyr/libzephyr.a
ninja: build stopped: subcommand failed.

初期化はDEVICE_AND_API_INIT() で指定します。引数の意味は DEVICE_AND_API_INIT() のドキュメントを見たほうが良いでしょう。

引っかかりそうなところはdev_nameにダブルクォートを「付けない」こと、一番に初期化されるようにPRE_KERNEL_1にすることくらいでしょうか。

QEMU Spikeモードの文字出力

文字出力poll_outの実装は、グローバル変数に謎の数字を書くだけの不思議実装になっていますが、これはQEMU SpikeモードのHost, Guest間インタフェースの仕様に従っているためです。

ちなみにQEMU側はこんなコードになっています。

QEMU Spikeモードの文字出力

// https://github.com/qemu/qemu/blob/master/hw/riscv/riscv_htif.c

...

static void htif_handle_tohost_write(HTIFState *htifstate, uint64_t val_written)
{
    uint8_t device = val_written >> 56;
    uint8_t cmd = val_written >> 48;
    uint64_t payload = val_written & 0xFFFFFFFFFFFFULL;
    int resp = 0;

...

    } else if (likely(device == 0x1)) {
        /* HTIF Console */
        if (cmd == 0x0) {
            /* this should be a queue, but not yet implemented as such */
            htifstate->pending_read = val_written;
            htifstate->env->mtohost = 0; /* clear to indicate we read */
            return;
        } else if (cmd == 0x1) {
            qemu_chr_fe_write(&htifstate->chr, (uint8_t *)&payload, 1);
            resp = 0x100 | (uint8_t)payload;
        } else {
            qemu_log("HTIF device %d: unknown command\n", device);
        }
    } else {

...

    htifstate->env->mfromhost = (val_written >> 48 << 48) | (resp << 16 >> 16);
    htifstate->env->mtohost = 0; /* clear to indicate we read */
}

途中のif文を見るとわかるように、device=1, cmd=1にすると文字が出力できます。またQEMUが文字出力を終えたときにfromhostが0以外の値に書き換わります(なので、Zephyrのシリアルドライバ側ではfromhostをポーリングで監視する実装にしています)。

シリアルドライバのラベル定義とbindings

DT_INST_0_SPIKE_UART_SPIKE_LABELのようなラベルはユーザが定義するのではなく、zephyr/dts/bindings以下のファイルに存在するcompatibleと、デバイスツリーを突き合わせて自動生成するみたいです。命名規則については、以前Zephyr OSで遊ぼう その3(2020年2月4日の日記参照)で紹介したとおりです。

zephyr/dts/bindings/serial/spike,uart-spike.yaml

# SPDX-License-Identifier: Apache-2.0

description: UART for Spike Simulator

compatible: "spike,uart-spike"

include: uart-controller.yaml

デバイスツリーのcompatibleの突き合わせとラベルの自動生成は、zephyr/scripts/dts/gen_defines.pyとedtlib.py辺りでやっていることは間違いなさそうなのですが、詳細な仕組みはまだ追えていません。

若干もやっとしますがYAMLファイルを作成して、compatibleをデバイスツリーで使用したものと合わせると、目的のラベルDT_INST_0_SPIKE_UART_SPIKE_LABELが定義され、ビルドが通ります。

Hello world

ビルドが通ったら実行します。

QEMU SpikeモードでHello world
$ /usr/bin/qemu-system-riscv32 -nographic -machine spike -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/build/zephyr/zephyr.elf

qemu-system-riscv32: clint: time_hi write not implemented
qemu-system-riscv32: clint: time_lo write not implemented
qemu-system-riscv32: clint: time_hi write not implemented
*** Booting Zephyr OS build zephyr-v2.1.0-1699-gbb40304f2531  ***
Hello World! hoge

やっとHello worldが拝めました。長かった。 (2/19追記: qemu-system-riscv32: clint: time_hi write not implementedのエラーメッセージはmtimeとmtimecmpのアドレスが逆だったために発生していたエラーです。アドレス修正後は表示されません)

パッチ

今まで説明してきた変更をパッチとしてまとめておきます。

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

コメント一覧

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



2020年2月10日

Zephyr OSで遊ぼう その9 - Zephyr 2.2.0に対応する

目次: Zephyr

Zephyrは開発中なので、ときおり内部構造が変わり、過去と互換性がなくなってしまうことがあります。ボードやSoCの定義も例外ではありません。

前回までの説明ではZephyr 2.1.0を使用していましたが、2.1.0から2.2.0の間で、ボードのdefconfigの書き方が変更されました。具体的に言うと、

  • ボード側: アーキテクチャCONFIG_RISCVを書かなくて良くなった
  • SoC側: CONFIG_RISCVをdepends on → selectに変える必要が生じた

下記のパッチにあるような変更をすると、2.2.0でもHogeボードとSpike SoCの対応が適用できます。

それぞれ1行ずつしか変更していませんので、すぐわかると思います。

Zephyrの不思議なコンフィグ

ZephyrはLinuxと同じKconfigを採用していますが、コンフィグの起点に対する考え方がだいぶ違います。Zephyrは最初にcmakeで「ボード名(-DBOARDオプション)」を指定します。すなわちコンフィグの起点はボードです。一方のLinuxはexport ARCH=x86のようにアーキテクチャを設定の起点にします。

Zephyr 2.1.0では、アーキテクチャ(CONFIG_RISCVなど)、SoC(CONFIG_SOC_RISCV_SIFIVE_FREEDOMなど)、ボードの依存関係は下記のようになっていました。

2.1.0の依存関係
アーキテクチャ(ボードのdefconfigがyにする)← アーキテクチャが依存の起点
↑depends on
SoC(ボードのdefconfigがyにする)
↑depends on
ボード(ボードのdefconfigがyにする)← Zephyrはなぜかボードがコンフィグの起点

依存関係とコンフィグの起点が矛盾しているためmenuconfigが異常な動きをします。おそらくninja menuconfigを弄れば矛盾に気づくと思いますが、Zephyrはなぜかボードが未対応のアーキテクチャやSoC(例えばqemu_riscv32でCONFIG_ARMを選択できる)を有効にできます。Zephyr 2.1.0だと2つ、おかしな設定ができます。

  • qemu_riscv32ボードで未対応のアーキテクチャ(例えばCONFIG_X86)を選択すると、SoCの選択肢がおかしくなる(X86だと言っているのに、RISC-V用のSoCが出てくる)
  • qemu_riscv32ボードで未対応のSoC(例えばCONFIG_SOC_LITEX_VEXRISCV)を選択すると、ボードの一覧が空になって選べなくなる(これは2.2.0でもできる)

実際にninja menuconfigで、qemu_riscv32が未対応のアーキテクチャを選ぶと下記のようになります。

Zephyr 2.1.0の変なコンフィグ
# qemu_riscv32のデフォルト

    Modules  --->
    Board Selection (QEMU RISCV32 target)  --->
    Board Options  ----
    SoC/CPU/Configuration Selection (SiFive Freedom SOC implementation)  --->
    Hardware Configuration  --->
    RISCV Options  --->
    Architecture (RISCV architecture)  --->
    General Architecture Options  --->
    General Kernel Options  --->
    ...


# ボードが対応していないアーキテクチャを選択すると、SoCが勝手に変わる、ボードは何も選べなくなる

    Modules  --->
    Board Selection  ----
    Board Options  ----
    SoC/CPU/Configuration Selection (LiteX VexRiscv system implementation)  --->    ★なぜかRISC-V用のSoCが出てくる
    Hardware Configuration  ----
    Architecture (x86 architecture)  --->    ★x86に変えた
    General Architecture Options  --->
    General Kernel Options  --->
    ...

そもそもx86が選択可能な時点でおかしいですが、x86を選択したのにx86じゃないSoCが選択肢に出てくるなど、かなりおかしな動きです。

中途半端に直ったZephyr 2.2.0

Zephyr 2.2.0では依存関係は下記のように、少しだけ整理されました。

2.2.0の依存関係
アーキテクチャ(SoCが強制的にyにする、menuconfigでは触れない)
↑select
SoC(ボードのdefconfigがyにする)
↑depends on
ボード(ボードのdefconfigがyにする)← ここが起点

アーキテクチャを変えることが出来なくなりました。前よりマシですが、やはり変なところは残っていて、未対応のSoCを選択するとボードの選択肢が消えてしまいます。

Zephyr 2.2.0の変なコンフィグ設定
# qemu_riscv32のデフォルト

    Modules  --->
    Board Selection (QEMU RISCV32 target)  --->
    Board Options  ----
    SoC/CPU/Configuration Selection (SiFive Freedom SOC implementation)  --->
    Hardware Configuration  --->
    RISCV Options  --->
    ...


# SoCを無理やり未対応のものに変えると、ボードが選べなくなる

    Modules  --->
    Board Selection  ----
    Board Options  ----
    SoC/CPU/Configuration Selection (LiteX VexRiscv system implementation)  --->
    Hardware Configuration  ----
    RISCV Options  --->
    ...

個人的にはZephyrのKconfigの使い方は変だな、と思います。依存関係があることはわかっているのだから、依存の根本の方から設定(要は、アーキテクチャ → SoC → ボード、の順)したら良いのに、Zephyrはなぜかボードの設定から固定していて、変な動きになっています。

Linuxはどうしているかというと、最初にexport ARCH=x86とします。すなわち、アーキテクチャを設定の起点にしています。自然です。特に変な動きもしません。

Zephyrの方がLinuxより後発ですが、ビルド周りは退化したように感じます。不思議。

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

コメント一覧

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



2020年2月16日

Zephyrのブート処理 - C言語と出会うまで

目次: Zephyr

以前(2020年2月1日の日記参照)Zephyrのエントリアドレスを調べましたが、もう少し先までZephyrのブート処理を調べます。復習となりますが、起動の仕方は下記のとおりです。

QEMU SpikeモードでZephyrを起動
$ qemu-system-riscv32 -nographic -machine spike -net none -chardev stdio,id=con,mux=on -serial chardev:con -mon chardev=con,mode=readline -kernel zephyr/build/zephyr/zephyr.elf -s -S

$ riscv64-zephyr-elf-gdb zephyr/build/zephyr/zephyr.elf

...

(gdb) target remote localhost:1234
Remote debugging using localhost:1234
0x00001000 in ?? ()

ブート処理を追うにはデバッガが必須です(ブート部分でシリアル出力はできません)。これも前回の復習となりますが、QEMUはGDBでデバッグするためのオプションを持っています。オプション -sはGDBの接続をlocalhost:1234で受け付けます、という意味で、オプション -SはエミュレータをHalted状態で起動するという意味です。

GDBを接続すると、PCは0x1000を指しています。QEMU Spikeモードはリセット後0x1000から実行を開始するようです。何度かステップ実行siを行うと、0x1010から0x80000000にジャンプするはずです。

QEMUブート直後

   0x1000:      auipc   t0,0x0
   0x1004:      addi    a1,t0,32
   0x1008:      csrr    a0,mhartid
   0x100c:      lw      t0,24(t0)
=> 0x1010:      jr      t0        # 0x80000000にジャンプ

0x1000: 0x00000297      0x02028593      0xf1402573      0x0182a283
0x1010: 0x00028067      0x00000000      0x80000000      0x00000000
                                        ↑この値をロードする

特に難しい話ではなく0x1000 + 24 = 0x1018からロード(0x80000000が書かれている)して、ジャンプするだけです。ジャンプ先にはZephyrのエントリポイント __startがいます。

__start(0x80000000ジャンプ後)

// zephyr/soc/riscv/riscv-privilege/common/vector.S

SECTION_FUNC(vectors, __start)
        .option norvc;

        /*
         * Set mtvec (Machine Trap-Vector Base-Address Register)
         * to __irq_wrapper.
         */
        la t0, __irq_wrapper
        csrw mtvec, t0

        /* Jump to __initialize */
        tail __initialize


// zephyr/arch/riscv/core/isr.S

/*
 * Handler called upon each exception/interrupt/fault
 * In this architecture, system call (ECALL) is used to perform context
 * switching or IRQ offloading (when enabled).
 */
SECTION_FUNC(exception.entry, __irq_wrapper)
        /* Allocate space on thread stack to save registers */
        addi sp, sp, -__z_arch_esf_t_SIZEOF

        /*
         * Save caller-saved registers on current thread stack.
         * NOTE: need to be updated to account for floating-point registers
         * floating-point registers should be accounted for when corresponding
         * config variable is set
         */
        RV_OP_STOREREG ra, __z_arch_esf_t_ra_OFFSET(sp)
        RV_OP_STOREREG gp, __z_arch_esf_t_gp_OFFSET(sp)
        RV_OP_STOREREG tp, __z_arch_esf_t_tp_OFFSET(sp)
        RV_OP_STOREREG t0, __z_arch_esf_t_t0_OFFSET(sp)
        ...

__startは __irq_wrapper関数のアドレス(C言語の関数ではないので、関数と呼ぶのは変ですが)を割り込みベクタに設定したあと、__initializeにジャンプします。

__initialize

// zephyr/arch/riscv/core/reset.S

/*
 * Remainder of asm-land initialization code before we can jump into
 * the C domain
 */
SECTION_FUNC(TEXT, __initialize)
        /*
         * This will boot master core, just halt other cores.
         * Note: need to be updated for complete SMP support
         */
        csrr a0, mhartid
        beqz a0, boot_master_core

loop_slave_core:
        wfi
        j loop_slave_core

boot_master_core:

#ifdef CONFIG_INIT_STACKS
        ...
#endif

        /*
         * Initially, setup stack pointer to
         * _interrupt_stack + CONFIG_ISR_STACK_SIZE
         */
        la sp, _interrupt_stack
        li t0, CONFIG_ISR_STACK_SIZE
        add sp, sp, t0

#ifdef CONFIG_WDOG_INIT
        call _WdogInit
#endif

        /*
         * Jump into C domain. _PrepC zeroes BSS, copies rw data into RAM,
         * and then enters kernel z_cstart
         */
        call _PrepC

最初にmhartidというCPUコアのIDを読み取ります。0番をマスターコアとして、0番以外のコアは割り込み待ち(wfi)の無限ループに入ります。マスターコアはスタックポインタの初期化を行って、_PrepC() 関数を呼び出します。

ここからやっとC言語の世界です。

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

コメント一覧

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



2020年2月17日

Zephyrのブート処理 - ドライバの初期化まで

目次: Zephyr

前回はリセット直後からC言語の関数と出会うところまでを紹介しました。今回はドライバの初期化までを紹介します。

最初のC言語の関数 _PrepC

// zephyr/arch/riscv/core/prep_c.c

void _PrepC(void)
{
        z_bss_zero();
#ifdef CONFIG_XIP
        z_data_copy();
#endif
#if defined(CONFIG_RISCV_SOC_INTERRUPT_INIT)
        soc_interrupt_init();
#endif
        z_cstart();
        CODE_UNREACHABLE;
}

ブート後に最初に出会うC言語の関数 _PrepC() ですが、実はまだグローバル変数の初期化が終わっていませんので、この関数で初期化しています。z_bss_zero() は、ゼロ初期化される変数の領域(BSS)を0で初期化します。z_data_copy() は、変数領域をROMからRAMにコピーします。

XIP(Execution In Place)が有効な場合、コード領域はFlash ROMなどに配置され、CPUはROMから直接コードを読み出して実行します。コード領域はそれで良いですが、変数の初期値もROMにおかれていて、そのままでは変数に書き込みできません。そのためROMからRAMにコピーする必要があります。

最後のz_start() まで来ると、やっと普通のC言語の世界です。

Zephyrの初期化処理

// zephyr/kernel/init.c

FUNC_NORETURN void z_cstart(void)
{

        ...

        /* perform basic hardware initialization */
        z_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_1);    //★level = 0
        z_sys_device_do_config_level(_SYS_INIT_LEVEL_PRE_KERNEL_2);


// zephyr/include/init.h

#define _SYS_INIT_LEVEL_PRE_KERNEL_1    0
#define _SYS_INIT_LEVEL_PRE_KERNEL_2    1
#define _SYS_INIT_LEVEL_POST_KERNEL     2
#define _SYS_INIT_LEVEL_APPLICATION     3

初期化関数z_cstart() は、色々ごちゃごちゃやっているのですが、真ん中あたりでドライバの初期化が行われます。レベルをPRE_KERNEL_1 (level 0), PRE_KERNEL_2 (level 1) の順に指定して、z_sys_device_do_config_level() という関数を呼びます。ちなみにPOST_KERNEL (level 2), APPLICATION (level 3) はもっと後で、カーネルの初期化が終わった後に使われます。

初期化される対象はデバイスドライバ以外(後述の、メモリ割り当てシステムドライバなど)もありますけど、仕組みは同じみたいです。

ドライバの初期化処理

// zephyr/kernel/device.c

extern struct device __device_init_start[];
extern struct device __device_PRE_KERNEL_1_start[];
extern struct device __device_PRE_KERNEL_2_start[];
extern struct device __device_POST_KERNEL_start[];
extern struct device __device_APPLICATION_start[];
extern struct device __device_init_end[];

...

void z_sys_device_do_config_level(s32_t level)
{
        struct device *info;
        static struct device *config_levels[] = {
                __device_PRE_KERNEL_1_start,
                __device_PRE_KERNEL_2_start,
                __device_POST_KERNEL_start,
                __device_APPLICATION_start,
                /* End marker */
                __device_init_end,
        };

        for (info = config_levels[level]; info < config_levels[level+1];
                                                                info++) {
                int retval;
                const struct device_config *device_conf = info->config;
                ...

実装は上記のようになっています。level 0の場合は __device_PRE_KERNEL_1_start[] という配列を先頭から処理します。他のレベルも同様ですね。

ドライバの初期化情報
======== <-- config_levels[0]
__device_PRE_KERNEL_1_start[0]
__device_PRE_KERNEL_1_start[1]
...
__device_PRE_KERNEL_1_start[n]
======== <-- config_levels[1]
__device_PRE_KERNEL_2_start[0]
__device_PRE_KERNEL_2_start[1]
...
__device_PRE_KERNEL_2_start[n]
======== <-- config_levels[2]
__device_POST_KERNEL_start[0]
__device_POST_KERNEL_start[1]
...
__device_POST_KERNEL_start[n]
======== <-- config_levels[3]
__device_APPLICATION_start[0]
__device_APPLICATION_start[1]
...
__device_APPLICATION_start[n]
======== <-- __device_init_end

このようにメモリ上にstruct deviceがレベル順に並んでいることを期待しているようです。しかし配列の実体はCのソースコード上には存在しないように見えます。これは一体何者なんでしょう?

続きはまた今度。

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

コメント一覧

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



link もっと前
2020年2月8日 >>> 2020年2月17日
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月25日
    すずき (03/26 03:20)
    「[Might and Magic Book One TASのその後] 目次: Might and Magicファミコン版以前(...」
  • link 21年10月4日
    すずき (03/26 03:14)
    「[Might and Magicファミコン版 - まとめリンク] 目次: Might and Magicファミコン版TASに挑...」
  • link 24年3月19日
    すずき (03/20 02:52)
    「[モジュラージャックの規格] 古くは電話線で、今だとEthernetで良く見かけるモジュラージャックというコネクタとレセプタク...」
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 www.katsuster.net
RDFファイル RSS 1.0

最終更新: 03/26 03:20