Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

【学習メモ#3rd】12ステップで作る組込みOS自作入門

2,894 views

Published on

12ステップで作る組込みOS自作入門
http://www.amazon.co.jp/dp/4877832394/
坂井 弘亮(著)
カットシステム

Published in: Technology
  • Be the first to comment

【学習メモ#3rd】12ステップで作る組込みOS自作入門

  1. 1. 12ステップで作る組込みOS自作入門 3rdステップ @sandai
  2. 2. 【参考書籍】12ステップで作る組込みOS自作入門【内容】1ステップずつ、実際に動かしながらプログラムを発展させていく方式で無理なく学べる。OSやハードウェアに詳しくない方にも理解できるように十分な説明を提供坂井 弘亮(著)カットシステム(2010/5)【税込価格】4,410円【サポートページ】http://kozos.jp/books/makeos/
  3. 3. もくじ1.メモリ構成2.リンカ・スクリプト3.「VA≠PA」の対応とプログラム実行4.まとめ
  4. 4. 1.メモリ構成
  5. 5. 組込みOSにおけるメモリの扱い● 組込みOSでは仮想メモリのような複雑な機構は 実装しないだからマイコン・ボードの持つメモ リの扱いはシビア – どのように割り当てて利用するか、調整することが 必要● 具体的には以下の3つのことをする必要がある – CPUのメモリ構成を知る – リンカ・スクリプトによるメモリ配分方法 – コンパイラのメモリの配置方法
  6. 6. 静的変数の読み書き● 今のままだと変数の書き換えができない – 下記のコードは期待通りなら「a」「14」になるは ずだが、「a」「a」になってる#include "lib.h"volatile int value = 10;int main(void){ serial_init(SERIAL_DEFAULT_DEVICE); puts("Hello World!n"); putxval(value, 0); puts("n"); /* a */ value = 20; putxval(value, 0); puts("n"); /* a(valueの値がかわっていない) */
  7. 7. 書き換えができない原因● ROMにある変数の初期値がRAMにコピーされてい ないから● ROM上にあると値を書き換えることができない – よく考えると当たり前のこと – ROMをプログラム側で書き換えることができればシ ステムはわやくそにいじれてしまう ● 「ROM = ブートローダ」と考えるとしっくりく るんじゃないか。プログラム側からブードロー ダを書き換えられるって大問題
  8. 8. メモリの基礎● メモリにはROMとRAMがある● ROMは読み込み専用で書き込み不可だが、電源 OFFでも内容は保持される – ROMには数種類あるが、H8/3069Fで扱うROMはフラッ シュROMという特別な操作により内容の書き換えが 可能なROM● RAMは読み書き可能だが、電源OFFで内容は失わ れる
  9. 9. H8/3069F内蔵のROM・RAM● 組込み向けのCPUではある程度の容量を持つROM とRAMをあらかじめ内蔵しているケースが多い● H8/3069Fは512KBのフラッシュROMと16KBのRAM を内蔵している● マッピングされているアドレスは次のスクリー ンショットの通り
  10. 10. H8/3069Fのメモリ・マップ92頁 図3.1 H8/3069Fの内蔵 ROM・RAMの配置 より
  11. 11. メモリと領域● CPUはメモリにあるプログラムしか実行できな いので、プログラムをメモリにコピーしなけれ ばならない● コピーされたプログラムはそのままの機械語 コードではなくいくつかの領域から構成されて いる● また、実行形式ファイルの内部もいくつかの領 域に分かれており、その領域単位でメモリへの コピーが行われている● メモリ上にコピーされたプログラムは、次の3 つの領域を持つ
  12. 12. メモリ上の3つの領域● テキスト領域 – CPUが実行する機械語コードが置かれる● データ領域 – 初期値を持つ静的変数などが置かれる● BSS領域 – 初期値を持たない静的変数などが置かれる
  13. 13. 静的変数と自動変数● 静的変数 – メモリ上に割り当てられた変数 – 関数外やstaticで定義した変数がこれ。グローバル 変数のことかな● 自動変数 – スタックに割り当てられた変数 – 関数内で定義した変数はこれ。ローカル変数ってこ とかな
  14. 14. 静的領域● 静的変数はメモリ上のどこかに固定で割り当て られる● この固定の割当先を静的領域と呼ぶ● 静的領域は次の2つの領域から構成される – データ領域...初期値を持つ変数はここ – BSS領域...初期値を持たない変数はここ● 初期値のある変数はプログラムの実行開始時に 初期値を設定する必要がある – なので、データ領域は変数の初期値を書き込んだ状 態で実行形式ファイル上に作成される
  15. 15. 領域のヘッダ● 実行形式ファイルの内部も領域分けされている – 単純に機械語の命令がベタに書かれているわけでは ない● 分割された領域はその領域情報やファイル情報 をヘッダとして持っている● 実行形式ファイルにも何種類かフォーマットが あるが、多くのフォーマットは最低でも3つの 領域は持っている● gccが生成する実行形式ファイルのフォーマッ トはELF形式
  16. 16. 実行形式ファイルのフォーマット● 実行形式ファイルにも何種類かフォーマットが あるが、多くのフォーマットは最低でも3つの 領域は持っている● gccが生成する実行形式ファイルのフォーマッ トはELF形式● ELF形式のファイルは、readelfコマンドで解析 することができる。 – h8300-elf-readelf -a kzload.elf
  17. 17. readelfが出力したデータ/Users/sandai/12step/src/02/bootload% h8300-elf-readelf -a kzload.elfELF Header: Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00 . .Section Headers: [Nr] Name Type Addr Off Size ES Flg Lk Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .vectors PROGBITS 00000000 000074 000100 00 WA 0 0 4 [ 2] .text PROGBITS 00000100 000174 0002b8 00 AX 0 0 2 [ 3] .text.startup PROGBITS 000003b8 00042c 000042 00 AX 0 0 2 [ 4] .rodata PROGBITS 000003fc 000470 00002b 00 A 0 0 4 . .Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000074 0x00000000 0x00000000 0x003fa 0x003fa RWE 0x1 LOAD 0x000470 0x000003fc 0x000003fc 0x0002b 0x0002b R 0x1 . .Symbol table .symtab contains 58 entries: Num: Value Size Type Bind Vis Ndx Name . . 40: 000003fc 12 OBJECT LOCAL DEFAULT 4 _regs . . 43: 00000000 256 OBJECT GLOBAL DEFAULT 1 _vectors 44: 00000278 46 NOTYPE GLOBAL DEFAULT 2 _putc 45: 000002a6 36 NOTYPE GLOBAL DEFAULT 2 _puts . . 57: 000003b8 66 NOTYPE GLOBAL DEFAULT 3 _main
  18. 18. セクション● ELF形式は、ファイルの内部をセクションとい う単位で区切って管理している – .textセクション...テキスト領域 – .dataセクション...データ領域 – .bssセクション...bss領域 – その他いくつかセクションが存在する● さっきの出力内容は書籍と違いがいくつかある – .dataがない – _mainが.text.startupセクションに置かれている (書籍では.textに置かれている)● 原因はgccのバージョンが違うから?
  19. 19. プログラムはROM上で動いている● 現状のコードはh8writeによってH8内部のフ ラッシュROMに書き込まれている● CPUはROMから命令を読み込み、逐次実行してい るだけ。これを「プログラムはROM上で動いて いる」と呼ぶ● プログラムがRAMにコピーされずに動いてい るってことかな – ノイマン型であればメモリにロードするイメージし かないんだけど、メモリにロードする前に逐次処理 してるってことでいいのか。ROMの方が遅いだろう ね
  20. 20. 変数の実体と配置1● コンピュータは変数の値を実体としてメモリに 記憶する – 自動変数はレジスタに配置されることもあるが、こ こでは深く言及しない● 変数を読み書きするとはその記憶したメモリを 読み書きするということ● 変数とは特定のメモリ領域に付けられた名前で しかない
  21. 21. 変数の実体と配置2● 変数の割り当て方法は大きく分けて2通り – 自動変数はスタックに割り当てる – 静的変数は静的領域(データ、bss領域)に割り当て る● 自動変数はスタックに割り当てられ、関数を抜 けたら捨てられる● ただし、lib.cやserial.cで利用している自動 変数はstartup.sでスタック・ポインタをRAM上 に設定しているため、読み書きできている状態 にある
  22. 22. 静的変数の書き換え● ROM上に配置された変数を読み書きするコード は、ROM上の領域を読み書きする機械語コード に置き換えられる● しかし、ROMへの書き込みはできないので変数 の値を書き換えるコードでも実際は書き換えら れない● 今後進めていく上で静的変数の値を書き換えら れないというのは面倒● そこで静的変数のデータをROM上ではなくRAM上 に配置する必要がある
  23. 23. 2.リンカ・スクリプト
  24. 24. リンカ・スクリプト(ld.scr)● リンカ・スクリプトは実行形式ファイルを作成 する際に、機械語コードや静的領域をどのよう にアドレス割り当てするのか、リンカに対して 指示するためのファイル – 物理的メモリにセクションをどのようにマッピング するかを定義するってことかな – リンクの段階でプログラムをメモリにどう配置する のか決定しているわけね● 静的領域をROMからRAMへ配置を変えるなら、こ のリンク・スクリプトによって行う
  25. 25. id.scrのコード解説1● ld.scrに書かれている.textや.dataがセクショ ンの定義になる● 先頭に. = 0x0;とあるが、「.」はロケーショ ン・カウンタ – 現在のカレント・アドレスを表す● . = 0x0はロケーション・カウンタをゼロに初 期化していることになるので、以降のセクショ ンはゼロ番地から配置される
  26. 26. id.scrのコード解説2.vectors : { vector.o(.data)}● .vectorsというセクションを作成し、vector.o の.dataセクションを配置● . = 0x0の直後なので、フラッシュROMの先頭に 配置していることになるね – 割り込みベクタはこのゼロ番地から始まる – そして割り込みベクタの先頭はリセット・ベクタな ので、ここにstartup.sの_start関数が割り当てら れているってわけ
  27. 27. id.scrのコード解説3.text : { *(.text)}● .textセクションの配置● 各オブジェクト・ファイルの.textセクション がここに配置される● *は正規表現で使う*と似たようなもんかなとも 思ったけど、なんか違うような... – とりあえずこの場合全てのオブジェクト・ファイル の.textセクションをここに配置するって意味にな ると思う
  28. 28. id.scrのコード解説4.rodata : { *(.strings) *(.rodata) *(.rodata.*)}● const定義した変数や文字列リテラルなどが配 置される● .stringsは文字列リテラルか? – コンパイラは文字列リテラルが出てきたときに、メモリに 割り当てて、C言語中で文字列が書かれた箇所はその文字列 のメモリ上のアドレス値に書き換えられる● .rodataは「Read only data」の意味 – プログラムの実行中に書き換えられることの無いやつはこ こ。割込みベクタや.textセクション(機械語コード)とか
  29. 29. ロケーション・カウンタ● .vectorsの後に.textを配置しているが、この 場合.textは.vectorsの末尾から配置される● このように、セクションはロケーション・カウ ンタの指す位置に配置され、ロケーション・カ ウンタはセクションを配置するたびにそのサイ ズ分だけ増加していく● 便利だねー
  30. 30. 静的変数の配置先● .rodataの直後に静的領域が割り当てられてい る。つまりROM上に存在することになるので、 値を書き換えられないわけだ.rodata : { . .}.data : { *(.data)}.bss : { *(.bss) *(COMMON)}
  31. 31. id.scrで配置先をRAMにしてみる● 静的変数を以下のように配置するとどうなるか.rodata : { *(.strings) *(.rodata) *(.rodata.*)}. = 0xffbf20;.data : { *(.data)}.bss : { *(.bss) *(COMMON)}
  32. 32. RAMに配置する● 前のコードはつまりロケーション・カウンタを RAMに設定して、静的領域だけRAMに配置される ようにしている – 0xffbf20はRAMのアドレス● ROM上でないから書き換えができるかとおもい きや、これは正しく動作しない
  33. 33. RAMに配置しても動作しない理由● h8writeで実行形式ファイルを書き込むわけだ けど、h8writeはフラッシュROMに対して行われ る● だからRAMに設定したところで意味はない – ROMじゃないよってh8writeでエラーも表示される● 仮にRAMに書き込めたとしても、電源を落とし たら初期値が消えてしまう – 次に電源ONしたときにそのデータが無いってこと。 コードに書かれていても実体となるデータがなけ りゃどうしようもないさ
  34. 34. 書き換えができない問題点● 問題点は次の2つ – ①変数の本体をROMに置くと ● 書き込みができない – ②変数の本体をRAMに置くと ● 電源OFFで値が消えてしまう● じゃあどうすればいいのか? – まずROMに書き込んでそれからRAMにコピーして、プ ログラムからはRAM上のデータにアクセスするよう にしたらいいじゃないか
  35. 35. 静的変数を書き換え可能にする● ①変数の初期値をROMに保存するようにしてフ ラッシュROMに書き込む● ②電源ONでプログラムを起動したときに、プロ グラムの先頭付近でフラッシュROMの変数の初 期値をRAMにコピー● そうしてプログラムから変数にアクセスすると きは、RAM上のコピー先のアドレスに対してア クセスされるようにする
  36. 36. 物理アドレスと論理アドレス● ROMからRAMへコピーしてRAMでデータを操作す るということは、初期値が配置されるアドレス とプログラムが変数にアクセスするときのアド レスが違うということになる – ROMのアドレスに実際の初期値があるけど、プログ ラムから操作するときはRAMのアドレスにあるデー タだからね● ここではROMのアドレスを物理アドレス、RAMの アドレスを論理アドレスと呼ぶ – 仮想メモリの用語とかぶるけど、readelfに合わせ るために物理アドレスや論理アドレスを使っている
  37. 37. 物理アドレス● 物理アドレス(Physical Address)はロード・ア ドレス(Load Address)とも呼ばれる – PAだったりLAと略されたり、LMA(Load Memory Address)と呼ばれることもある● ここでは単純にROM上のアドレスを物理アドレ スと考えれば良い
  38. 38. 論理アドレス● 論理アドレス(Logical Address)はリンク・ア ドレス(Link Address)とも呼ばれる – LAと略されたり、仮想アドレス(Virtual Address) と呼ばれVAと略されることがある● 単純にRAM上のアドレスが論理アドレスと考え れば良い
  39. 39. 現状のPAやVAの保存先● 実行形式ファイルにPAやVAが保存されている● readelf出力結果のうち、VirtAddrが VA、PhysAddrがPA● 今のところどちらも0x3fc – つまりどっちもROM上のアドレスを指している● これからPhysAddrをRAM上に割り当てる必要が ある – これを一般にVA≠PAにする」と言うProgram Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz FlgAlign LOAD 0x000074 0x00000000 0x00000000 0x003fa 0x003fa RWE 0x1 LOAD 0x000470 0x000003fc 0x000003fc 0x0002b 0x0002b R 0x1
  40. 40. セグメント● ELF形式はセクションの他にセグメントという 管理単位を持っている● Program Headersはセグメントの一種Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align LOAD 0x000074 0x00000000 0x00000000 0x003fa 0x003fa RWE 0x1 LOAD 0x000470 0x000003fc 0x000003fc 0x0002b 0x0002b R 0x1 . .
  41. 41. ロードとローダ● プログラムの実行時にセグメント情報が参照さ れメモリに展開される。これをロードと呼ぶ● ロードを行うプログラムをローダと呼ぶ● ローダが実行形式ファイルのセグメントを参照 して、そのとおりにメモリ上に展開するってわ け
  42. 42. セクションとセグメントの違い● セクションはリンク時に同じ内容の領域をリン カがまとめるためのもの● セグメントはプログラムの実行時にローダが参 照してメモリ上に展開するためのもの – ローダが参照するのはセグメントであって、セク ションではない● 用語の出現場所が似てるから混乱しやすいな
  43. 43. 3.「VA≠PA」の対応とプログラム実 行
  44. 44. プログラムの修正● 修正したファイル – ld.scr ● 「VA≠PA」対策 – main.c ● 静的変数の書き換えサンプル追加 – startup.s ● スタックの設定の修正● これらのうち、重要な「VA≠PA」対策について 述べておく
  45. 45. VA≠PA対策● 静的領域をRAM上でプログラムから操作できる ようにするにあたって行ったことは次の通り – .dataセクションの実際のデータをROMに配置 – これをプログラム側からはRAMに配置されているよ うにみせる – みせても実際にデータは配置されていないの で、ROMの.dataセクションをRAMにコピーして操作 できるようにする
  46. 46. リンカ・スクリプトで行うこと● ROMに配置するのと、RAMに配置されたようにみ せかけるのはリンカ・スクリプトで行う● 具体的には、まずMEMORYコマンドでセクション の各領域を定義● それから、.dataセクションにそれらの領域を 割り当てる● 「> data」がRAMに配置することを意味して、 「AT> rom」がROM上の物理アドレスに配置する ことを意味する
  47. 47. リンカ・スクリプトのコード● いろいろ省略しているが次の通りMEMORY{ . . rom(rx) : o = 0x000100, l = 0x7fff00 . . data(rwx) : o = 0xfffc20, l = 0x000300 . .}.data : { _data_start = .; *(.data) _edata = .;} > data AT> rom
  48. 48. 「> data」と「AT> rom」● .dataセクションの実際のデータをROMに配置 – 「AT> rom」の部分がこれにあたる● プログラム側からはRAMに配置されているよう にみせる – 「> data」の部分がこれにあたる● こうして.dataセクションのデータはROMに配置 されることになるが、プログラム側から は.dataセクションはRAM上にあるようにみえる● つまり、今の状態ではデータがRAMにあるよう にみえるだけで、実際のデータはROMにある – 次は変数のデータをROMからRAMにコピーして、実際 に操作できるようにする
  49. 49. ROMの静的領域をRAMにコピー● コピーはリンカ・スクリプトではなく、main.c でのプログラムの先頭で行う● .bssセクションについてはコピーではなくゼロ クリアをして、初期値を持たない変数に対して 初期値を設定している● こういった処理をまとめて初期化と呼んだりす るmemcpy(&data_start, &erodata, (long)*edata – (long)&data_start)memset(&bss_start, 0, (long)&ebss - (long)&bss_start)
  50. 50. ROMの静的領域をRAMにコピー● 詳しい説明は書籍を参照してほしいが、前の コードの意味は以下の通り – ROM上にある.dataセクションにある変数の初期値を RAMにコピーしている – RAM上の.bssセクションをゼロクリアして、.bssセ クションに配置された変数のメモリ上の値をゼロに 初期化している ● .bssセクションはコピーしているわけではない● ここまでしてようやくC言語を普通に扱えるよ うになった● 当然ながら、先ほどの初期化コードの前に静的 変数を定義すると正常に動作しない
  51. 51. ビルドで失敗● main.cで静的領域の書き換えができるかどうか のプログラムを実行するだけだが、その前に● ビルドこけた... – gcc4.7使ってるけど、たぶんここが問題だな/Users/sandai/12step/tools/lib/gcc/h8300-elf/4.7.1/../../../../h8300-elf/bin/ld: section .text.startup[0000000000000000 -> 0000000000000081] overlaps section .vectors[0000000000000000 -> 00000000000000ff]collect2: error: ld returned 1 exit statusmake: *** [kzload] Error 1
  52. 52. ld.scrの修正● .text.startupが何なのかよくわからんが、ど うやら_main関数らしい● _main関数はたぶん.textセクションなのでそこ にぶっこんだ.text : { _text_start = . ; *(.text.startup) ←こいつね *(.text) _etext = . ;} > rom
  53. 53. 再度プログラム実行● うまくいったー!あせったー/Users/sandai/12step/src/03/bootload% sudo cu -l/dev/tty.usbserial-FTG6PQ4HPassword:Connected.Hello World!*global_data = 10*global_bss = 0*static_data = 20*static_bss = 0overwrite variables.*global_data = 20*global_bss = 30*static_data = 40*static_bss = 50~.Disconnected.
  54. 54. 書籍との違い● _regs変数がどうも定数の扱いになってる – .rodataセクションに配置されているみたい – ROMに入っていて問題はないかな? – まあたぶんプログラム側から操作するような変数 じゃなかったからいいけど● 大きな違いはここと、あとは.text.startupだ なあ
  55. 55. 4.まとめ
  56. 56. まとめ● とりあえずこれでH8でC言語を普通に扱えるよ うになった – 具体的には静的領域をROMからRAMにコピーすること によって静的変数の書き換えが可能となった● 組込みプログラミングというのは自前で用意し なきゃいけないことが多い

×