私の開発した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(); }