組込みOS自作入門【Windows11, WSL2】(2nd ステップ以降)
書籍『12ステップで作る 組込みOS自作入門』を読みながらWindows11のWSL2環境でOS自作を進めています。
先日、環境構築をしてHello Worldするまでの方法を記事にしました。
環境構築(1/2 - USBシリアル編):
組込みOS自作入門 環境構築 【Windows11, WSL2】(1/2 - USBシリアル編) - ちょっとした公開めも
環境構築(2/2 - ツールインストール、Hello World! 編):
組込みOS自作入門 環境構築 【Windows11, WSL2】(2/2 - ツールインストール、Hello World! 編) - ちょっとした公開めも
今回は、2ndステップ以降で環境による差異がありそうなところや少し詰まるかもしれないところだけをピックアップして書き残しておきたいと思います。
(2025/3/15追記)自分で見返すためにステップごとにコメントもつけました。
目次
環境
- Windows11 Pro
- WSL2
- USB to シリアルケーブル
- ELECOM UC-SGT1 [ ELECOM USBtoシリアルケーブル モデム用 USBオス-RS-232C用 UC-SGT1 ]
- binutils-2.21
- gcc-3.4.6
- シリアル通信コマンド:cu (ver. 1.07)
2nd ステップ
書籍の通りに進めて、特に問題なく完了できました。
コメント
1st ステップでは環境構築とHello Worldの出力までを行いました。
2nd ステップでは各種ライブラリ関数の追加と数値の出力を行いました。
この時点では自動変数の書き換えはできますが静的変数の書き換えができません。
3rd ステップ
書籍の通りに進めて、特に問題なく完了できました。
コメント
1st, 2nd ステップではプログラムをフラッシュROMに書き込み、そのままROM上で実行していたため静的変数の書き換えができませんでした。
3rd ステップではこれを改善するために、プログラム起動時にROM上の変数の初期値をRAM上にコピーし(②)、プログラムから変数へのアクセスはコピーしたRAM上のアドレスに対して行いました(①)。
実装では以下の対応をしています。
①リンカ・スクリプトに「VA ≠ PA」対応、つまり仮想アドレスをRAMに、物理アドレスをROMに割り当てる対応
・MEMORYコマンドで各メモリの領域(先頭アドレスとサイズ)を定義
・SECTIONSコマンドで各セクションをMEMORYコマンドで定義したどの領域に配置するか指定
「> data AT > rom」:.dataセクションをRAM上のdata領域のアドレスをベースにリンク。物理アドレスをrom領域上に設定。(つまりROM上にロードするということ)
②main.cにデータ領域とBSS領域を初期化する処理を追加(これを行わないと初期値が未設定になる)
・データ領域はmemcpyで初期値をコピー
コピー元:.rodataセクションの終端(ROM上の.dataセクションの先頭アドレス)
コピー先:RAM上の.dataセクションの先頭アドレス
・BSS領域はmemsetでRAM上の.bssセクションの先頭アドレスから0で初期化
そもそもリンクとは...
main.cなどをmakeの実行によりコンパイルして生成したオブジェクトファイル(main.oなど)は、機械語の実行コードを持つが静的変数や関数のアドレスは決定されていない。オブジェクトファイルをリンクすることでそれらのアドレスを決定して最終的な実行形式ファイルが生成される。(p39参照)
ロードとは...
プログラム実行時にセグメント情報を参照してメモリ上に展開する処理のこと。
4th ステップ
ファイル転送アプリをインストール
最初は sx も lsx も入っていませんでした。
$ sx --version コマンド 'sx' が見つかりません。次の方法でインストールできます: sudo apt install lrzsz
lrzsz をインストールします。
$ sudo apt install lrzsz
$ sx --version sx (lrzsz) 0.12.21rc
ファイル転送
lrzsz をインストールしても lsx はインストールされないため、sx を使用します。
書籍と同様、defines.h を転送します。
書籍に記載の通りに「~C」と入力しても子プロセスの起動に移ることはできません。
kzload> load ~C[Unrecognized. Use ~~ to send ~]
(こうなると何を押しても反応しなくなると思います。H8のリセットボタンを押しましょう。)
参考記事に倣って、「~+」を入力します。
私の環境では、「~」を入力して1秒ほどで[PC名]が表示されます。間髪入れずに「+」を入力すると表示されないのですが...
表示されてもされなくても挙動は変わらないので気にしなくて良いです。
「load」をプロンプトに入力 & Enter すると約8秒に1回の頻度で文字化けしたお豆腐が表示されますが、これも気にしなくて良いです。(スクショを載せておきます)

$ sudo cu -s 9600 -l /dev/ttyUSB0 Connected. kzload (kozos boot loader) started. kzload> load ~[(PC名)]+sx defines.h Sending defines.h, 1 blocks: Give your local XMODEM receive command now. Bytes Sent: 256 BPS:37 Transfer complete XMODEM receive succeeded.
dump は書籍の通りです。
kzload> dump size: 100 23 69 66 6e 64 65 66 20 5f 44 45 46 49 4e 45 53 5f 48 5f 49 4e 43 4c 55 44 45 44 5f 0a 23 64 65 66 69 6e 65 20 5f 44 45 46 49 4e 45 53 5f 48 5f 49 4e 43 4c 55 44 45 44 5f 0a 0a 23 64 65 66 69 6e 65 20 4e 55 4c 4c 20 28 28 76 6f 69 64 20 2a 29 30 29 0a 23 64 65 66 69 6e 65 20 53 45 52 49 41 4c 5f 44 45 46 41 55 4c 54 5f 44 45 56 49 43 45 20 31 0a 0a 74 79 70 65 64 65 66 20 75 6e 73 69 67 6e 65 64 20 63 68 61 72 20 20 75 69 6e 74 38 3b 0a 74 79 70 65 64 65 66 20 75 6e 73 69 67 6e 65 64 20 73 68 6f 72 74 20 75 69 6e 74 31 36 3b 0a 74 79 70 65 64 65 66 20 75 6e 73 69 67 6e 65 64 20 6c 6f 6e 67 20 20 75 69 6e 74 33 32 3b 0a 0a 23 65 6e 64 69 66 0a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a kzload> ~[(PC名)]. Disconnected.
参考記事:
「12 ステップで作る 組込み OS 自作入門」の開発環境を揃えてみる
5th ステップ以降も順次更新していく予定です。(2024/7/20)
コメント
3rd ステップまではプログラムを修正する度にフラッシュROMに書き込んで動作させていましたが、ROMに上書きできる回数は有限のため、今後はPCでコンパイルした実行ファイル(OS)をH8のRAM上にシリアル転送する方針で準備を進めていきます。
ブート・ローダー:「OSの実行形式ファイルをシリアル経由でダウンロードし、それをRAM上に展開して起動するプログラムをフラッシュROMに書き込む」ようなプログラム。
ブート・ストラップ:電源ONでブート・ローダーを起動し、ブート・ローダーでOSをダウンロードして起動するようなOSの起動手順。
4~6th ステップでブート・ローダーを作成していきますが、4th ステップではファイルをシリアル経由でダウンロードして、その内容を16進ダンプできるようにするところまで実装しました。
ロードする際はリンカ・スクリプトを変更してRAM上に確保した.bufferセクションにロードするようにしています。
5th ステップ
書籍の通り、問題なくできました。
ただ、4th ステップの kzload.elf をそのまま5th ステップにコピーすると5th ステップの kzload.elf とファイル名が一致してしまうため、kzload04.elf にファイル名を変更する変更だけしました。
$ sudo cu -s 9600 -l /dev/ttyUSB0 Connected. kzload (kozos boot loader) started. kzload> load ~+sx kzload04.elf Sending kzload04.elf, 38 blocks: Give your local XMODEM receive command now. Bytes Sent: 4992 BPS:354 Transfer complete XMODEM receive succeeded. kzload> dump size: 1380 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 00 02 00 2e 00 00 00 01 00 00 01 00 00 00 00 34 00 00 08 d0 00 81 00 00 00 34 00 20 00 03 00 28 (略) 69 61 6c 5f 73 65 6e 64 5f 62 79 74 65 00 5f 73 74 61 63 6b 00 5f 6d 61 69 6e 00 5f 65 72 6f 64 61 74 61 00 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a 1a kzload> run 000094 00000000 00000000 00100 00100 06 01 000194 00000100 00000100 006ec 006ec 05 01 000880 00fffc20 000007ec 00010 00024 06 01 kzload> ~. Disconnected.
(2024/7/22)
コメント
4th ステップに続いてブート・ローダーの作成中です。5th ステップではダウンロードしたELFファイルを解析してセグメント情報を表示する処理を追加しました。
6th ステップ
書籍の通り、問題なくできました。
kozos.elf ファイルの中身を観察
$ cd ~/12step_emb_os/osbook_03/06/os $ make $ readelf -a kozos.elf > kozos_elf.txt
bootload ファイル書き込み
$ cd ../bootload/ $ make $ make image $ sudo make write
load, run, echo
$ cd ../os $ sudo cu -s 9600 -l /dev/ttyUSB0 Connected. kzload (kozos boot loader) started. kzload> load ~+sx kozos Sending kozos, 11 blocks: Give your local XMODEM receive command now. Bytes Sent: 1536 BPS:334 Transfer complete XMODEM receive succeeded. kzload> run starting from entry point: ffc020 Hello World! > echo 1234567890 1234567890 > echo123 123 > exit ~. Disconnected.
(2024/7/24)
コメント
6th ステップでブート・ローダーを完成させました。
解析したセグメント情報を参照して、セグメントをメモリに展開(コピー)する処理とエントリ・ポイントを取得する処理を加えました。