U-Boot の第 1 段階である board_init_f は、リロケーション(RAM へのコード複写)前にグローバルデータ gd を整え、各種周辺機器とメモリ空間を準備する。本稿では Mini2440(S3C2440)を対象に、この関数がどのように U-Boot を DRAM 上へ展開する準備を進めるかを追う。
gd 構造体と初期フラグ
gd_t は U-Boot 全体で共有されるグローバルデータで、起動中の状態・メモリ情報・デバイスハンドラ等を保持する。board_init_f ではまず gd->flags と gd->have_console に初期値をセットし、以降の関数ポインタテーブルを順に実行する。
void board_init_f(ulong boot_flags)
{
gd_t data;
gd = &data;
zero_global_data();
gd->flags = boot_flags;
gd->have_console = 0;
if (initcall_run_list(init_sequence_f))
hang();
}
init_sequence_f テーブル
init_sequence_f は init_fnc_t 型の関数ポインタ配列で、条件コンパイルにより不要なエントリが除去される。Mini2440 では実質的に以下の関数が実行される。
setup_mon_len: U-Boot イメージサイズ測定initf_malloc: 早期 malloc 領域設定initf_console_record: コンソール記録初期化arch_cpu_init/initf_dm/arch_cpu_init_dm: 架構・ドライバモデル初期化mark_bootstage: ブートステージ記録board_early_init_f: クロック・GPIO 初期化timer_init: タイマー初期化env_init: 環境変数の検証init_baud_rate/serial_init/console_init_f: シリアル/コンソールdisplay_options/display_text_info/print_cpuinfo: 情報表示announce_dram_init/dram_init: DRAM 検出- メモリ予約群 :
setup_dest_addr~setup_reloc
U-Boot サイズの測定
setup_mon_len は U-Boot 全体の長さを gd->mon_len に格納する。
static int setup_mon_len(void)
{
#if defined(__ARM__)
gd->mon_len = (ulong)&__bss_end - (ulong)&_start;
#else
gd->mon_len = (ulong)&__bss_end - CONFIG_SYS_MONITOR_BASE;
#endif
return 0;
}
クロックと GPIO の初期化
board_early_init_f では S3C2440 の MPLL/UPLL を設定し、必要な GPIO を初期化する。以下は実装の要点を整理したものである。
int board_early_init_f(void)
{
struct s3c24x0_clock_power *clk =
s3c24x0_get_base_clock_power();
struct s3c24x0_gpio *io = s3c24x0_get_base_gpio();
/* ロックタイム最小化 */
writel(0xFFFFFF, &clk->locktime);
/* MPLL: FCLK 生成 */
writel((M_MDIV << 12) | (M_PDIV << 4) | M_SDIV,
&clk->mpllcon);
pll_delay(4000);
/* UPLL: USB 用クロック */
writel((U_MDIV << 12) | (U_PDIV << 4) | U_SDIV,
&clk->upllcon);
pll_delay(8000);
/* GPIO ポート設定 */
writel(0x007FFFFF, &io->gpacon);
writel(0x00044555, &io->gpbcon);
writel(0x000007FF, &io->gpbup);
/* ... 以下ポート設定 ... */
return 0;
}
タイマー設定
timer_init は PWM Timer 4 を 10 ms 間隔で動作させ、以降の get_timer() 等で利用できるよう gd->arch 内のカウンタ情報を初期化する。
int timer_init(void)
{
struct s3c24x0_timers *t = s3c24x0_get_base_timers();
ulong ctrl;
/* prescaler = 16 */
writel(0x0f00, &t->tcfg0);
if (gd->arch.tbu == 0) {
gd->arch.tbu = get_PCLK() / (2 * 16 * 100);
gd->arch.timer_rate_hz = get_PCLK() / (2 * 16);
}
writel(gd->arch.tbu, &t->tcntb4);
ctrl = readl(&t->tcon) & ~0x0700000;
writel(ctrl | 0x0600000, &t->tcon); /* 手動ロード */
writel(ctrl | 0x0500000, &t->tcon); /* 自動ロード&開始 */
gd->arch.lastinc = 0;
gd->arch.tbl = 0;
return 0;
}
環境変数の検証
env_init は保存領域先頭の CRC を検証し、成否に応じて gd->env_valid を切り替える。Mini2440 のデフォルトでは NOR フラッシュ上の env_t を参照する。CRC が合わなければ default_environment(ビルド時に生成されるデフォルト値)が使われる。
シリアル・コンソール
serial_init は GD_FLG_SERIAL_READY フラグを立て、デフォルト UART(CONFIG_SERIAL1)を初期化する。get_current() で取得されるデバイス構造体の start メソッドが UART レジスタをセットアップする。
static int s3c_uart_setup(int idx)
{
struct s3c24x0_uart *uart = s3c24x0_get_base_uart(idx);
unsigned int div;
/* FIFO 有効、Tx/Rx クリア */
writel(0x07, &uart->ufcon);
writel(0x00, &uart->umcon);
/* 8N1 */
writel(0x03, &uart->ulcon);
writel(0x245, &uart->ucon);
div = get_PCLK() / (16 * gd->baudrate) - 1;
writel(div, &uart->ubrdiv);
return 0;
}
情報表示
コンソールが利用可能になった後、以下の関数が順に実行される。
display_options: U-Boot バージョン文字列を出力display_text_info: デバッグ時に TEXT/BSS 情報を出力print_cpuinfo: CPU ID および FCLK/HCLK/PCLK を出力announce_dram_init:DRAM:と出力
DRAM サイズの確定
dram_init は gd->ram_size に実装済みの SDRAM サイズをセットする。Mini2440 では 64 MB。
int dram_init(void)
{
gd->ram_size = PHYS_SDRAM_1_SIZE; /* 64 MB */
return 0;
}
DRAM 末尾からのメモリ予約
DRAM 点 0x30000000 + 64 MB から順に、U-Boot 実行に必要な領域を確保していく。各予約関数は gd->relocaddr(U-Boot リロケーション先)および gd->start_addr_sp(最終スタック)を更新する。
| 関数 | 内容 |
|---|---|
| setup_dest_addr | DRAM 天端を ram_top、初期 relocaddr に設定 |
| reserve_round_4k | relocaddr を 4 KB 境界に切り下げ |
| reserve_mmu | TLB テーブル(16 KB)を予約し 64 KB 整列 |
| reserve_trace | トレースバッファを予約 |
| reserve_uboot | U-Boot コード・データ・BSS 分を 4 KB 整列で予約 |
| reserve_malloc | malloc プール(4 MB)を予約 |
| reserve_board | bd_t 構造体を予約 |
| setup_machine | マシンタイプ ID を設定 |
| reserve_global_data | 新 gd_t を予約 |
| reserve_fdt | FDT 用領域を予約 |
| reserve_stacks | スタックを 16 バイト境界に調整 |
| setup_dram_config | バンクごとの DRAM 情報を gd->bd に設定 |
| show_dram_config | DRAM 合計サイズを表示 |
リロケーション準備の完了
reloc_fdt は FDT を移動先にコピーし、setup_reloc は最終的なオフセット gd->reloc_off を計算し、現在の gd_t を新しい gd_t 領域へ複製する。
static int setup_reloc(void)
{
#ifdef CONFIG_SYS_TEXT_BASE
gd->reloc_off = gd->relocaddr - CONFIG_SYS_TEXT_BASE;
#endif
memcpy(gd->new_gd, (char *)gd, sizeof(gd_t));
return 0;
}
最終的な gd フィールド(Mini2440 例)
以上の処理後、主要な gd フィールドは以下のようになる。
| フィールド | 内容 |
|---|---|
| ram_size | 64 MB |
| ram_top | 0x34000000 |
| relocaddr | 0x34000000 - U-Boot サイズ - 64 KB 程度 |
| start_addr_sp | relocaddr より malloc/bd/gd/スタック 分を引いた位置 |
| baudrate | 115200 bps |
| have_console | 1 |
| env_valid | 0(初回はデフォルト環境変数) |
| mon_len | U-Boot イメージ全体のバイト数 |