FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

freedom-u-sdkのLinuxを立ち上げながらLinuxのブートプロセスを学ぶ(8. start_kernel(void)の処理)

私の開発したRISC-VシミュレータはLinuxを立ち上げることができる。シミュレータのデバッグ時には相当中身を読み込んだのだが、きちんと文章化していない挙句、大昔のプロジェクトなのでもう忘れかけている。

Linuxのブートの方法から各種プロセスの取り扱いまで、思い出しながらRISC-Vシミュレータを動かしていき、ちゃんと文章化しておきたいと思った。

start_kernel(void)の処理

  • freedom-u-sdk/linux/init/main.c
asmlinkage __visible void __init start_kernel(void)
{
    char *command_line;
    char *after_dashes;

    set_task_stack_end_magic(&init_task);
    smp_setup_processor_id();
    debug_objects_early_init();
   
    cgroup_init_early();

    local_irq_disable();
    early_boot_irqs_disabled = true;
...

set_task_stack_end_magicはスタックポインタの準備で、スタックの最終アドレスにMAGICコードを埋め込む。これはスタック破壊を検出するためのコードだ。

void set_task_stack_end_magic(struct task_struct *tsk)
{
    unsigned long *stackend;

    stackend = end_of_stack(tsk);
    *stackend = STACK_END_MAGIC;    /* for overflow detection */
}

次のsmp_setup_processor_id()は特に何もしない。必要ならばオーバライドして使うようだ。

void __init __weak smp_setup_processor_id(void)
{
}

local_irq_disable()割り込み禁止を有効化する。

  • freedom-u-sdk/linux/arch/riscv/include/asm/irqflags.h
/* unconditionally disable interrupts */
static inline void arch_local_irq_disable(void)
{
    csr_clear(sstatus, SR_SIE);
}

さて、次は必要なセットアップ処理を行う。

  • freedom-u-sdk/linux/init/main.c (start_kernel()の続き)
 /*
    * Interrupts are still disabled. Do necessary setups, then
    * enable them.
    */
    boot_cpu_init();
    page_address_init();
    pr_notice("%s", linux_banner);
    setup_arch(&command_line);

boot_cpu_init(void)はブート用のプロセッサ(通常はCPU0)を有効化する。これは大体最初から有効化されている。

  • freedom-u-sdk/linux/kernel/cpu.c
/*
 * Activate the first processor.
 */
void __init boot_cpu_init(void)
{
    int cpu = smp_processor_id();

    /* Mark the boot cpu "present", "online" etc for SMP and UP case */
    set_cpu_online(cpu, true);
    set_cpu_active(cpu, true);
    set_cpu_present(cpu, true);
    set_cpu_possible(cpu, true);

#ifdef CONFIG_SMP
    __boot_cpu_id = cpu;
#endif
}

ここでsmp_processor_id()によりブート対象とするCPUのIDを入手するのだが、smp_processor_id()RISC-Vアーキテクチャでは以下のように変換されている。

  • freedom-u-sdk/linux/arch/riscv/include/asm/smp.h
/*
 * This is particularly ugly: it appears we can't actually get the definition
 * of task_struct here, but we need access to the CPU this task is running on.
 * Instead of using C we're using asm-offsets.h to get the current processor
 * ID.
 */
#define raw_smp_processor_id() (*((int*)((char*)get_current() + TASK_TI_CPU)))
  • freedom-u-sdk/linux/arch/riscv/include/asm/current.h
/*
 * This only works because "struct thread_info" is at offset 0 from "struct
 * task_struct".  This constraint seems to be necessary on other architectures
 * as well, but __switch_to enforces it.  We can't check TASK_TI here because
 * <asm/asm-offsets.h> includes this, and I can't get the definition of "struct
 * task_struct" here due to some header ordering problems.
 */
static __always_inline struct task_struct *get_current(void)
{
    register struct task_struct *tp __asm__("tp");
    return tp;
}

さらにここで指定したCPUをオンラインに設定する。といってもLinux上のグローバル変数にビットパタンを書き込むだけである。

  • freedom-u-sdk/linux/include/linux/cpumask.h
static inline void
set_cpu_online(unsigned int cpu, bool online)
{
    if (online)
        cpumask_set_cpu(cpu, &__cpu_online_mask);
    else
        cpumask_clear_cpu(cpu, &__cpu_online_mask);
}

次のpage_address_init()では特に何もしない。

  • freedom-u-sdk/linux/include/linux/mm.h
#if defined(WANT_PAGE_VIRTUAL)
static inline void *page_address(const struct page *page)
{
    return page->virtual;
}
static inline void set_page_address(struct page *page, void *address)
{
    page->virtual = address;
}
#define page_address_init()  do { } while(0)
#endif

次のpr_notice("%s", linux_banner)ではLinuxのバージョン番号などの表示を行うだけである。実際にはprintkが呼ばれている。

  • freedom-u-sdk/linux/init/version.c
/* FIXED STRINGS! Don't touch! */
const char linux_banner[] =
    "Linux version " UTS_RELEASE " (" LINUX_COMPILE_BY "@"
    LINUX_COMPILE_HOST ") (" LINUX_COMPILER ") " UTS_VERSION "\n";

次のsetup_arch(char **cmdline_p)アーキテクチャ毎のセットアップを行う関数である。

  • freedom-u-sdk/linux/arch/riscv/kernel/setup.c
void __init setup_arch(char **cmdline_p)
{
#if defined(CONFIG_EARLY_PRINTK)
       if (likely(early_console == NULL)) {
               early_console = &riscv_sbi_early_console_dev;
               register_console(early_console);
       }
#endif
    *cmdline_p = boot_command_line;

    parse_early_param();

    init_mm.start_code = (unsigned long) _stext;
    init_mm.end_code   = (unsigned long) _etext;
    init_mm.end_data   = (unsigned long) _edata;
    init_mm.brk        = (unsigned long) _end;

    setup_bootmem();
    paging_init();
    unflatten_device_tree();

    swiotlb_init(1);

#ifdef CONFIG_SMP
    setup_smp();
#endif

#ifdef CONFIG_DUMMY_CONSOLE
    conswitchp = &dummy_con;
#endif

    riscv_fill_hwcap();
}