とあるエンジニアの備忘log
2016年11月18日金曜日
ルーティングテーブル設定 (with DHCP)
ネットワークカード 3枚刺ししてて、そのうち 2つは DHCP。 この状況で所望のルーティングを行わせるにはどうやったらいいものか、 とゴチャゴチャいじくってるうちに、できたっぽい方法をとりあえずメモ。(Ubuntu 16.04) - enp1s0: (DHCP) (192.168.240.x) - enp3s0: (static route) (192.168.11.x) - enp4s0: (DHCP) (10.x.x.x) 指定されたもの以外は全部 enp1s0 にルーティングして欲しい場合。`x` 部分は適宜自分の環境に合わせる。 `/etc/network/interfaces` に以下を追加。 auto enp4s0 iface enp4s0 inet dhcp post-up route del default dev enp4s0 post-up route add -net 10.0.0.0 netmask 255.0.0.0 gw 10.x.x.x metric 1 auto enp3s0 iface enp3s0 inet static address 192.168.11.x netmask 255.255.255.0 gateway 192.168.11.x post-up route del default dev enp3s0 全部 static の時は `/etc/network/if-up.d/static-routes` でうまくいったっぽいが、 DHCP が絡むとうまく行かず。結局、`/etc/network/interfaces` の中で `post-up` の後ろで `route add/del` で足し引きするとなんかうまくいった。 もっとクールなやり方ありそうですが。。
自分的 Ubuntu 16.04 LTS を入れて最初にやること
Ubuntu 16.04 インストールした時の覚え書き。自分用。 #### パッケージのインストール #### `sudo apt-get install` でパッケージインストール。 - synaptic - apt-file - gnome-session-flashback # Unity の代わりに Metacity - gnome-tweak-tool # swap Ctrl and Caps Lock - emacs - emacs-goodies-el # for color-theme - git - gitk - git-email # for git send-email - cvs subversion git-svn # 最近使わないのでいらないか - ccache - fcitx-mozc - emacs-mozc - global - chromium-browser - indent - manpages-ja # 日本語 man 見たいとき LANGUAGE=ja man
- virtualbox - virtualbox-guest-additions-iso - ckermit - coccinelle - flex bison - nkf - libc6-i386 # for 32bit programs - ssh # ssh server - device-tree-compiler # dtc for building U-Boot - gimp - u-boot-tools # for mkimage - texlive-full #### Unityを やめる #### $ sudo apt-get install gnome-session-flashback でインストールした後、いったんログアウトし、 GNOME Flashback (Metacity) でログインし直す。 #### Ctrlキー入れ替えなど #### $ sudo apt-get install gnome-tweak-tool として `System Tools` -> `Preferences` -> `Tweak Tool` を開き、 `Typing` -> `Ctrl key position` で `Swap Ctrl and Caps Lock` を選択。 いったんログアウトする。 #### 日本語入力設定 #### 最初のインストール時に、英語、英語キーボードを選択したとする。 $ sudo apt-get install fcitx-mozc としたあと、`System Tools` -> `System Settings` -> `Language Support` を開き、 `Keyboard input method system` を fcitx に。 `System Tools` -> `Preferences` -> `Fcitx Configuration` を開く。 `Input Method` タブは - Keyboard-English (US) - Mozc にする。 Mozc は `+`ボタンを押して `Only Show Current Language` のチェックを外すと出てくる。 `Global Config` のタブで `Show Advance Option` をチェック。 `Activate input method` と `Inactivate Input Method` に Mozc を ON/OFF するキーを設定。 `Trigger Input Method` に `Ctrl+Space`というのが入っているが、これは消す。(Escapeを押す)。 消しておかないと emacs で `Ctrl+Space` (region選択開始)が使えない。 ### Emacs で Solarized 使う ### $ sudo apt-get install emacs-goodies-el で color theme を使えるようにしておき $ git clone git://github.com/altercation/solarized.git ~/ref/solarized で solarized を取得。 `~/.emacs` に以下を追加。 (load-file "~/ref/solarized/emacs-colors-solarized/color-theme-solarized.el") (when (require 'color-theme-solarized) (color-theme-solarized-light)) #### スペルチェッカー #### aspell はデフォルトで入ってる?っぽい。 `~/.aspell.conf` に以下の一行追加。 lang en_US nano から起動するために、 `~/.nanorc` に以下を追加。Ctrl-t で起動できる。 set speller "aspell -x -c" emacs は `M-x ispell-buffer` で起動できる。 #### ssh X forwading #### X で ssh を乗り越えさせる。 ssh クライアント (Xサーバー)側の設定: `/etc/ssh/ssh_config` を開き ForwardX11 no を ForwardX11 yes に変更。 ssh サーバー (Xクライアント)側の設定 (`sudo apt-get install ssh` でインストール必要): `/etc/ssh/sshd_config` を開き ForwardX11 yes になっていることを確認。(たぶんデフォルトでそうなっている。) #### dot ファイル復元 #### ホームディレクトリの dot ファイルを別 PC から復元する。 - .profile - .bashrc - .bash_aliases - .aspell.en.pws # aspell 辞書 - .aspell.en.prepl # aspell 辞書 - .gitconfig # git - .emacs # emacs - .nanorc # nano - .kermrc # kermit - .buildman # U-Boot buildman - .patman # U-Boot patman - .gnupg # GPG - .ssh # if necessary - .pwclientrc # pwclient - .quiltrc # quilt
2016年3月6日日曜日
GPG key sign 備忘録
この度、わけあって GnuPG というものを使い始めたので、鍵の生成から、Key Sign までやったことを、将来の自分のために書き留めておきます。多分すぐに忘れてしまうので。 ### 背景を理解する ### 「GPG とはなんぞや」とか、「公開鍵暗号とは?」みたいな初歩的な話から、「なんで Key Sign みたいな面倒なことをやるの?」とか 「どうして署名用と暗号化用とで複数のキーペアに分かれているの?」みたいな疑問を解決するには本家のドキュメントがおすすめ。 英語ですが、私は [The GNU Privacy Handbook](https://www.gnupg.org/gph/en/manual.html) はなかなかわかりやすかったと思います。 ### Key の生成 ### $ gpg --gen-key で鍵を生成する。いくつかの質問を聞かれる。 鍵の種類はデフォルトの (1) RSA and RSA (default) を選択。Master (署名用) と Sub (暗号化用) で鍵が2ペアできる。 キーサイズと、有効期限については、その人の Paranoid 具合によってデフォルトのままでよいという人もいるし、変更するという人もいる。とりあえず自分は 4096ビットで 5年 を選択した。あとは、本名とメールアドレスを入力する。パスフレーズは(空にもできるが)必ず設定しておく。乱数を生成するためにいろいろ動かしてと言われた後、鍵を生成してくれる。 忘れないうちに $ gpg --output revcert.asc --gen-revoke
で、失効証明書を作成しておく。(秘密鍵が盗まれたり、パスフレーズを忘れたりしたりした時に、鍵を失効させるのに使う。) `
` の部分は、鍵の生成時に入れた本名やメールアドレスの一部を入れればよい。 `revcert.asc` と `~/.gnupg` 以下を CD-R に焼いて大切に保管しておく。 ### 公開鍵を Key Server に上げる ### キーサーバーに上げる前に、マニュアルとか見ながら触ってみて、gpg の操作に慣れておく。キーサーバーに上げる前ならば、最悪失敗してもキーの生成からやり直せばいいので、いろいろ試してみる。 満足したらキーサーバー(公開鍵の配布を容易にしてくれるサーバー)に上げる。いったんアップロードするとやり直しが効かない(鍵を失効させるしかない)ので、本名やメールアドレスが間違ってないか今一度確認しておく。 $ gpg --keyserver pgp.nic.ad.jp --send-keys
でアップロードする。自分は `pgp.nic.ad.jp` を使ったが、キーサーバーはお互いに同期しているので、どのキーサーバーに上げてもよい。 例えば、 [http://pgp.nic.ad.jp/pgp/pks-commands-e.html](http://pgp.nic.ad.jp/pgp/pks-commands-e.html) みたいなページに行って `Search String` のところに自分のメールアドレスを入れてみれば、自分の公開鍵が出てくる。 ### Signing Party ### PGP(GnuPG) の中で一番アナログな部分が Signing Party という工程です。 その公開鍵が間違いなくその本人のものであることを確認しなくてはなりません (悪意を持った人が、その人の名を語ってなりすましている可能性があるため)。 本人確認は、身分証(パスポートなど)を見せて対面で行い、その際に、fingerprint を渡します。 自分の鍵の fingerprint を $ gpg --fingerprint で表示し、あらかじめ印刷しておくのがよいです。 紙に印刷していない場合は、端末に表示させたものを相手にカメラなどで撮ってもらう。 ### 相手の鍵に署名 ### Signing Party が終わった後は、相手の鍵に対して、自分の鍵で署名します。 相手の鍵をキーサーバーから取ってきて、fingerprint が一致するのを確認し、署名します。 署名後に、相手の鍵を勝手にアップロードするのはマナー違反とのことなので、メールで相手に送ってあげます。 その際、相手にしか復号できないように暗号化し、署名して送信、とこれを毎回手動でやるのは大変だ、というわけで それを手助けする caff というツールがあるようです。 ### postfix のセットアップ ### caff はメールの送信までやってくれますが、それにはローカルに SMTP を立てておく必要があるので、 まずそれをやります。普段は Gmail を使っているので、Gmail にリレーさせる方法をとった。 $ sudo apt-get install postfix sasl2-bin 途中で出てくる質問には `Internet Site` を選ぶ。 `/etc/postfix/main.cf` に以下を記載。 relayhost = [smtp.gmail.com]:587 smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/sasl_password smtp_sasl_security_options = noanonymous smtp_sasl_tls_security_options = noanonymous smtp_sasl_mechanism_filter = plain smtp_use_tls = yes `/etc/postfix/sasl_password` に [smtp.gmail.com]:587
@gmail.com:
を記載する。 2段階認証を利用していない場合は `
` の部分は、Gmail に使っているパスワードでよい。 2段階認証を利用している場合は、Google の 「アカウント」、「ログインとセキュリティ」、「アプリ パスワード」とたどり、アプリパスワードというのを生成し、それを `
` の部分に書く。 $ sudo chmod 600 /etc/postfix/sasl_password $ sudo postmap /etc/postfix/sasl_password $ sudo service postfix restart としたあと、以下で動作確認。 $ mail -s
### caff ### $ sudo apt-get install signing-party でインストール。 $ caff を実行すると `~/.caffrc` という設定ファイルができる。 大抵は、うまく設定してくれているが、複数の鍵を持っている場合などは修正が必要かもしれない。 最低でも、以下の3項目は正しくなっていることを確認。 $CONFIG{'owner'} = 'Masahiro Yamada'; $CONFIG{'email'} = 'yamada.masahiro@socionext.com'; $CONFIG{'keyid'} = [ qw{3D8B01079DE0DB06} ]; `owner` と `email` はメールの送信元になる。`keyid` は fingerprint の下16桁。 あとは $ caff
で全部やってくれる。 `
` は相手の fingerPrint の下8桁を入力。 相手の鍵に、複数の User ID が紐付いているときは、 Really sign all user IDs? (y/N) と聞いてくる。全部の User ID に対して署名する場合はそのまま `y` と答える。 一部の User ID にのみ署名したい場合は、いったん `n` と答えて、 `uid 2` みたいに番号を指定してから `sign` と打つ。 Really sign? (y/N) に `y` と答えたあと、`save` と打つ。 最後にメールで送るかどうか聞いてくるので `y` と答える。 ### サインしてもらった key の取り込み ### 相手も caff を使っていると添付ファイルが 2個ついてメールが送られてくる。 $ gpg -o key --decrypt msg.asc $ gpg --import key $ gpg --keyserver pgp.nic.ad.jp --send-key
で OK。
2015年10月19日月曜日
ELCE 2015 in Dublin
今年も [Embedded Linux Conference Europe](http://events.linuxfoundation.org/events/embedded-linux-conference-europe) に行ってきました。 今年の開催地はアイルランドの首都ダブリンでした。 会場の外の景色は、近年の ELCE で一番美しかったと思います。斜めの円筒形に見えるのか会場。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGCmTfG0ddMSfzl1akUuYRIKmmdO25nQC8OB5g6DBtR3FCPVrOdzbkVZ4rYKkZtwQuHHS7lbAaX5sWdQsQ8YO3YAWHTZCrsf30azIc5Nak26oSK3ElhvdPvVXpvX3fxXmiW6CgZIRufyU/s1600/dublin.jpg) 会場の Convention Centre Dublin。ユニークな形です。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh0uc4RGw4fRls0n4v_XZjAjDatW6aOq7VCKmPbpBHYTpEICvwqS8sHJv3IT8kmC4cHa2r25EdWg_cJQ4-8q-W-lkwoqkTPMaJYJLHs-UOLv8-iS_G4zcUBOTZ6P7DE2-W0YrU3eBsxgg/s1600/ccd.jpg) [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAf7fjtnzu3vbMFTBVr7k_IdU-jKBOzULNXjFFG5nlJfnjcenEzNI1swrLIrjSde0Nk4cpzRJwd-RBEaBVykbWhAiJnUvnqmassVcWUFUieTVEAcZ3INPKGSQRZ7tg6n6p8RS3vf6HXAw/s1600/welcome.jpg) アイルランドのシンボルカラーの緑に合わせてか、今年のテーマカラーは緑のようです。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXxT7X3OHjl3A9bfH4bLjYod0Eqvft9mnoeOg_i-32S6ACtzfPXylqjbsr34PMV5752tep1RmikAyCDWsK9riW_nQavuECSuVZ1KPaG0jkeSRfn0FOl5aBZddm2XWO3pmpuujtjLbAqDs/s1600/opening.jpg) 今年も [U-boot Mini Summit](http://www.denx.de/wiki/U-Boot/SummitELCE2015) やってました。内容がマニアック過ぎて、 BoF (Birds of a Feather)の様相を呈していましたが。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0PmMhazteaZSsJUav1IwWVMDkgJdQLMWTUzIXg5paB7XcJYss-XO1UTRpxo7QQTxLLqjxhLRroJGpu27imk_i59Y9O0_Ymh5mYGWeHeXDZXd8GJ5yXKzPuNtLvOU4M9hK8wTJPkgZ-LE/s1600/uboot_summit2015.jpg) 夜はパブで U-boot メンバーの飲み会。 [](http://3.bp.blogspot.com/-XGCzolTLiZQ/VhLwg7NHcfI/AAAAAAAASsE/FwzmwnoJccs/s1600/uboot_guinness_summit2015.jpg) アイルランドと言えば、このビール。個人的にはあまり好きな味ではないんですが(黒ビール自体も)。「カァァー」というのどごしがなくて、「へな〜」としてます。アサヒスーパードライの方がはるかに美味しいぞ。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEs8pAB6vuaFMyLP5qGN8CN_E0TXjbMuEcBc72yRYrj5GT7S7ODc4PW_XsB-4E9D1TMcoRLzgV3je-OnhxZz_WeB1wBYDrP4_MzZ3g_GG5bTKdX35AP65hGTEc6Gzzh9R1gw8wAqTGmdo/s1600/guinness.jpg) I2C サブシステムのメンテナーによるのプレゼン。これ見て、自分のドライバにも Slave モード実装してみるか。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ1tLMEdSdB87_OGLkS92pmPqbQGfbrwrwwP_WNegfoEURv_pIYio28sMeT1BeqKDCshZ3i2aeECDPnMjjDQdzg9IGQOXEQxoQ_udaAGlo8YSaqTqHNBRafhhvYSAS5pgQJZgsIhZRwhs/s1600/i2c_slave.jpg) 2日目は Linus も登場。"いつもの" 2人 (スキューバダイビング仲間)によるチャット。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg49MP5OVP-gK-yBb41sa9BZ_hIyRuaeuIpV3ffcWWLngRk5VVfdKxklIrvd9zuIr9_clNy0il9YcVgUKO68aUniEjq7OW0PjUNS8U2DGmMIF4OTn5t6FXz7I4Elnfz1mlWBbQ3BldNAm8/s1600/linus_chat.jpg) Closing Game。Rock, Paper, Scissors, Lizard, Spock。勝ったのかどうかわからん。。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0kgfOjt0_wGi3f3cK79ZIIC08R8Di8YzQWFowQiMp5N9yW8Xw49s3RpXan3hSPvmrfgZiY9D92HDV04l7irccGFRs8ughK0M0Z4WGExMe_re3nEfTFPyUgaIPlPozqoKxWehLKYqRb38/s1600/closing_game.jpg) Guinness Storehouse というところで、Reception Event。Guinness のできるまでを見学しながら、どんどん上に上がっていきます。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj41dSeg6fclgVtC9aKCXIzNcQk7-rfD3gnygmf8OvgGpBoIeX7TvCsoGBep_X9b2_xuGmd-kJY7RVAkS3stBJSwnUZ6KXjrh9rKBSfD5rAf9QlPSPgS2YS4pR4E8zzNGQ2vL2w3CDfzUg/s1600/guinness_house.jpg) 最上階がバーになっています。 [](http://4.bp.blogspot.com/-D0nhpTGSzYo/VhWa2lyijNI/AAAAAAAASts/K0EZROK_fOc/s1600/closing_reception.jpg) 自分で Guinness を注ぎます。 [](http://1.bp.blogspot.com/-Ghvop4_WhI0/VhWavxO0enI/AAAAAAAAStI/4QOwG2Ykr40/s1600/serve_beer.jpg) 今年の T シャツ。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbH7HSHFnoR4slSgeqVc3URfcqX6hdqNv4cBfp3xkyxe8nJszsqpaLwerdwtkV-HMhu_cSvxvS6C679q4dD7tOyaxyW8OANf6IpPSOa5ythRiYqpBjakndvF84xLMSsuBcDSgQKHllLj0/s1600/elce_shirt2015.jpg)
2015年6月6日土曜日
LinuxCon Japan 2015
6月3-5日に LinuxCon Japan 2015 に行ってきました。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLDizobXv6tYTeN_6QNNfiQL89jEmBkxy-i1IUaPWt6QeebLlpioEdG9aIq9200YRpIYh4Q0JVyw5LlxWTjezWHmk-fSVgkUAK-JzMRtxfJqxDCqeI7PJ2jqtAry58w2jMP4vl2ZQLZ4Q/s1600/lcj2015_keynote.jpg) [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk3Hat53x6klA-UCj1EVDM8bdWFPjKOVSkaI_vCW1TuoTVhI3pAnHBRnonvPOjqHoyLFkavsR3KXzYZb0YbfPKAeJ5zz3en-KfBzczyWS2dq3VMgDRwUJj0-_MwBz24ghEcrNDyiwZ29I/s1600/lcj2015.jpg) 興味深いプレゼンもいろいろと聞けました。 以下は SH2 互換チップをオープンソースな RTL で再実装し、 Xilinx の安い FPGA で動かして、 Linux まで走らせました、というもの。 セガサターンにも使われた SH2 ですが、パテント切れで面白いことができるようになってて、 FPGA にも入る規模です。 個人的には 10年前に出た 1チップ MSX も思い出して、ノスタルジックな気持ちになりました。SuperH を設計したアーキテクトが Hitachi 退職後にやっていて、なかなか楽しそうな内容です。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFUoo8K2Z7AhqSs_yJWTye0l0wwNLyO-RZUmqWEHUCy1xezHg1RWD7VmzzYdgIKh5qzgge3h6Wm0sBH3V0CmBJvL-9CuueXZj-qq3xrHTB84M6i4R65G2L9MW2PV87vDHRL4jklB-4nxM/s1600/sh2.jpg) Linux 以外の話題では git の内部設計についての解説とか。 Junio C Hamano さんの入門本にも書いてたので、だいたい知ってる内容ですが。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_-BQgzNX90WQXbaeUPlQIOM76BGn7nL9B8BFPpHtxNzCCRskGxx2YMS2uaresMQ7aff9G2i9Jiac6cXW6NJYJ8t9wMktLfkH7r4H_O5VwjD2vPNOnCTM6XZeUbTCY6SkAMvDx-J5mB_A/s1600/git.jpg) 夜は当然のごとく飲み。 [](http://4.bp.blogspot.com/-sdapUJsg2CU/VXBvMAS5wFI/AAAAAAAAQlQ/e9lVBhBJI8g/s1600/beercon2015.jpg) 今年の Tシャツ。 [](http://2.bp.blogspot.com/-xlLQ4AvQLkU/VW5I80WoU_I/AAAAAAAAQig/71RwMgwxZi0/s1600/lcj2015_shirt.jpg) さらに Booth Crawl でも Tシャツ貰ったり、プレゼンでいろいろ質問したら Raspberry Pi の Tシャツも貰えた。 Xen (右下) のTシャツは普段使いできそうなデザインです。さすがに Fujitsu のは部屋着にしか使えん。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjo3qp8u-mGMJedl6lyAejBwws7gKQzpTqGoYsynpLG-antDhZPNm7fCfdTn7PeWjpt83zNA9MxP8_gASLbUlKuMOMzzUMkSEUydrj-7cicf1Dp9IN7nb7UvVzy0FmuQvBpkqLBinx3reo/s1600/lcj2015_shirts3.jpg) 自分にとっては、プレゼンを聞きに行くだけではなくて、 普段メーリングリストでやり取りしている人とのオフ会としての意味もあるカンファレンスです。 友人と再会したり、新たに友達もできたり、非常に有意義な時間でした。
2015年5月31日日曜日
自分的 Ubuntu 14.04 LTS を入れて最初にやること
先日、PC を新調したので、Ubuntu 14.04 LTS 入れました。 最新のインストールイメージは 14.04.2 になっていました。LTS だと安心感があります。 とりあえず、自分好みの設定を、何かあった時のために、未来の自分のためにメモっておきます。(半年前の自分は、もはや他人ですので。。) #### マシン名を決める #### 設定とは直接関係ないのですが、最初にやらなくてはいけないこと、それは PC に名前をつけることです。 インストーラーが、masahiro-All-Series みたいにデフォルトで名前をつけてくれるが、これは味気ない。 自分の場合は、 Linux デスクトップ、Linux ノート、Windows デスクトップ, 実験用の板コン、VirtualBox 上の仮想PC など、いくつもの PC が稼働しており、PC も数年おきに新調するので、名前を毎回考えなくてもいいように、機械的なネーミングスキームが必要です。 しかも人間(自分)にとって覚えやすいものでなくてはならない。 そこで、 自宅のPC には昔から Sesame Street のキャラクター名をつけることにしている。 歴代愛機には elmo, cookie, bigbird, oscar, ernie, zoe, bert みたいな感じで名前をつけていた。 今回は "grover" と名付けることにした。 まだまだキャラクターがいるので、当面名前に困ることはないだろう。 自宅の LAN で勝手にやっているだけなので、権利関係で怒られることもないでしょう。 ちなみに、会社で自分が管理している PC は beagle, poodle, spitz, pug のように犬種名を付けて行っています。 ちなみにちなみに、Debian の コードネームは Toy Story のキャラクター名を付けて行っていますね。 #### パッケージのインストール #### `sudo apt-get install` でパッケージをインストールしていく - synaptic - apt-file - gnome-session-flashback # 以下 Unity やめる参照 - gnome-tweak-tool # swap Ctrl and Caps Lock - emacs - emacs-goodies-el # for color-theme - git - gitk - git-email # for git send-email - cvs subversion git-svn # 最近使わないのでいらないか - ccache - fcitx-mozc # 以下 「日本語変換設定」参照 - emacs-mozc - global - chromium-browser - indent - manpages-ja # for 日本語 man - libncurses5-dev # for Linux make menuconfig - virtualbox - virtualbox-guest-additions - ckermit - nkf - libc6:i386 libcurses5:i386 libstdc++6:i386 # for 32bit programs - ssh # ssh server - device-tree-compiler # dtc for building U-Boot - gimp - u-boot-tools # for mkimage - texlive-full #### Unityを やめる #### $ sudo apt-get install gnome-session-flashback でインストールした後、いったんログアウトし、 GNOME Flashback (Metacity) でログインし直す。 #### Ctrlキー入れ替えなど #### $ sudo apt-get install gnome-tweak-tool として 「システムツール」 -> 「設定」 -> 「Tweak Tool」 を開き、 「タイピング」 -> 「Ctrl キーの位置」 で 「Ctrl と Caps Lock を入れ替える」 を選択。 いったんログアウトする。 #### 日本語変換設定 #### 13.10 から ibus がクソになっているので fcitx を使います。 自分は昔から IME の ON は Henkan キー、 OFF は Muhenkan キーでやりたい人なのです。 トグル方式だと、今 ON なのか OFFなのかを認識していないといけないので、無意識にできない。 それと初めて買ってもらった PC が Mac だった (まだ 日本語 OS が漢字トークと呼ばれていた時代ですが。。) ので、指がそれに慣れてしまっています。 $ sudo apt-get install fcitx-mozc としたあと、「システムツール」-> 「システム設定」 -> 「言語サポート」 を開き、 「キーボード入力に使うIMシステム」が fcitx になっているのを確認。 「システムツール」-> 「設定」 -> 「Fcitx 設定」を開く。 「入力メソッド」タブは - キーボード-日本語 - Mozc になっているのを確認。(たぶん最初からそうなっている。) 「全体の設定]タブで「拡張オプションの表示」にチェックを入れる。 「入力メソッドをオンに」と「入力メソッドをオフに」という設定が出てくるので、それぞれ 「Henkanmode」, 「Muhenkan」 を設定する。 「入力メソッドのオンオフ」というところにデフォルトに 「Ctrl+Space」というのが入っているが、これは消す。(Escapeを押す)。 消しておかないと emacs で「Ctrl+Space」 (region選択開始)が使えない。 さらに、 iBus のインジケータを消すために、 「システムツール」-> 「システム設定」 -> 「テキスト入力」 を開き、 「メニューバーに現在の入力ソースを表示」のチェックを外す。 anthy は消してしまっていいので $ sudo apt-get remove anthy とする。 ibus を消そうとすると $ sudo apt-get remove ibus 以下のパッケージは「削除」されます: ibus ibus-pinyin ibus-table ubuntu-desktop unity-control-center unity-control-center-signon webaccounts-extension-common xul-ext-webaccounts アップグレード: 0 個、新規インストール: 0 個、削除: 8 個、保留: 13 個。 この操作後に 8,334 kB のディスク容量が解放されます。 続行しますか? [Y/n] と出てきて、依存関係でいろいろ消えてしまいそうだ。例えば、 unity-control-center が消されると、 「システムツール」-> 「システム設定」 のメニューがなくなって、影響が出そうなので、 ibus はとりあえず残しておくこととする。 #### 日本語ディレクトリを英語に #### 「ダウンロード」、「テンプレート」、「デスクトップ」、「ドキュメント」、「ビデオ」、「ピクチャ」、「ミュージック」、「公開」 というディレクトリを英語にする。 $ LANG=C xdg-user-dirs-gtk-update #### リポジトリを clone #### `~/ref` 以下に自分の興味のあるプロジェクトのリポジトリを reference として clone してくる。 git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git git clone --reference linux --branch stable git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git git clone git://git.denx.de/u-boot.git git clone --reference u-boot git://git.pengutronix.de/git/barebox.git git clone git://git.buildroot.net/buildroot git clone git://ozlabs.org/home/jk/git/patchwork #### emacs を目にやさしくする #### エディタはおそらく最も長時間向かうプログラムかもしれません。 しかしながら、 emacs のデフォルトの白のバックグラウンドは眩しすぎる。 そこで solarized にします。 $ sudo apt-get install emacs-goodies-el で color theme を使えるようにしておき $ git clone git://github.com/altercation/solarized.git ~/ref/solarized で solarized を取得。 `~/.emacs` に以下を追加。 (load-file "~/ref/solarized/emacs-colors-solarized/color-theme-solarized.el") (when (require 'color-theme-solarized) (color-theme-solarized-light)) #### スペルチェッカー #### aspell が入っていなければ、以下でインストール。 $ sudo apt-get install aspell `~/.aspell.conf` に以下の一行追加。 lang en_US nano から起動するために、 `~/.nanorc` に以下を追加。Ctrl-t で起動できる。 set speller "aspell -x -c" emacs は `M-x ispell-buffer` で起動できる。 #### ssh X forwading #### X で ssh を乗り越えさせる。 ssh クライアント (Xサーバー)側の設定: `/etc/ssh/ssh_config` を開き ForwardX11 no を ForwardX11 yes に変更。 ssh サーバー (Xクライアント)側の設定 (`sudo apt-get install ssh` でインストール必要): `/etc/ssh/sshd_config` を開き ForwardX11 yes になっていることを確認。(Ubuntuの場合、たぶんデフォルトでそうなっている。) #### postfix #### 自分は2段階認証を使っているので、Gmail 経由で postfix から送信するのはこちらがわかりやすかった。 [http://blog.bgbgbg.net/archives/2834](http://blog.bgbgbg.net/archives/2834) #### dot ファイル復元 #### ホームディレクトリの dot ファイルを別 PC から復元する。 - .profile - .bashrc - .bash_aliases - .aspell.conf # aspell - .aspell.en.pws # aspell 辞書 - .gitconfig # git - .emacs # emacs - .nanorc # nano - .kermrc # kermit - .buildman # U-Boot buildman - .patman # U-Boot patman
2014年12月26日金曜日
Linux の NAND の Bad Block 管理まとめ
Linux の MTD/NAND framework で、Bad Block の管理がどうなっているか、まとめてみた。 ### Bad Block と Bad Block Mark について ### NAND チップは、出荷時にある一定割合まで、不良ブロックを含んでいいことになっている。 ベンダーは、不良ブロックには、そのことがわかるように Bad Block Mark (以下 BBMと略す)というマークを付けて出荷する。 ベンダーによって若干仕様にブレがあるが、だいたい各ブロックの先頭ページの冗長領域 (以下 OOB = out of band と略す)の先頭に `0xFF` ではない値が書かれる。 また、消去と書き込みを繰り返すうちに、正常ブロックが不良化することもある。 その時にも BBM をつける。 当然ながら、BBM がついているブロックは使ってはいけない。 また、消去すると BBM も消えてしまうので、消去もしてはいけない(これ非常に重要)。 そのブロックが不良かどうかを調べるために、OOB の BBM を読みにいくと確実なのだが、かなり効率が悪いので、実際には Bad Block Table (以下 BBT と略す)を作って管理している。 各ブロックが不良かどうかを調べるには、理屈的には 3 つの方法があることになる。 - 各ブロックの OOB にある BBM を読む。もっとも効率が悪い。 - NAND Flash 上に構築される BBT (以下 flash based BBT と記す) を読む。 - Linux のメモリ上に構築される BBT (memory BBT と記す) を読む。 memory BBT が1次キャッシュで、 flash based BBT が2次キャッシュみたいなイメージで捉えてもいいかもしれない。 ### flash based BBT について ### flash based BBT は最初の一回だけ構築すればよく、その後は Bad Block が増えるごとに更新されていく。 BBT がどこに置かれるかについては、何パターンかあって、ドライバから指定できる。 チップの先頭に置くか、最後尾に置くかで 2通りある。 (`NAND_BBT_LASTBLOCK` フラグで決まる) NAND パッケージ上では 1つに見えても、内部的には複数のシリコンダイで構成されている場合があり、その場合には、全ダイ分の BBT を一箇所にまとめて置くか、ダイごとにテーブルを分けるかでも 2 通りある。 (`NAND_BBT_PERCHIP` フラグで決まる) トータル 2 x 2 = 4 通りの配置方法があり(もっと言うと任意のブロック上に BBT を置く `NAND_BBT_ABSPAGE` というフラグもある)、しかも BBT にも、メインとミラーがあるので結構ややこしい。 BBT はダイごとにテーブルを分けて、最後尾に置くのが普通なので、2 ダイ構成の NAND Flash の場合、通常以下の様な感じで BBT が置かれる。 |---------------------| | | | | | | | chip0 Data Area | | | | | |---------------------| | chip0 BBT | |---------------------| | | | | | | | chip1 Data Area | | | | | |---------------------| | chip1 BBT | |---------------------| ### memory BBT について ### memory BBT は Linux の起動時(正確に言うと NAND ドライバが `module_init` されたとき)に毎回作る。 (ただし `NAND_SKIP_BBTSCAN` フラグがセットされていると memory BBT は作成しない。) ややこしいので、フローチャートで説明すると以下のようになる。 |------------------------| | NAND_SKIP_BBTSCAN flag |---(ON)---> 何もしない |------------------------| | (OFF) | |----------------------| | NAND_BBT_NO_OOB flag |---(OFF)---> 全ブロック巡回して BBM を読み出し、 memory BBT を構築 |----------------------| | (ON) | |--------------------------| | flash based BBT exists ? |---(YES)---> flash based BBT を読みだして memory BBT を構築 |--------------------------| | (NO) | 全ブロック巡回して BBM を読み、memory BBT を構築して flash based BBT に書き込む。 実際に、とあるブロックがBad Block かどうかを問い合わせる場合には |---------------------| | memory BBT exists ? |---(YES)---> memory BBT を読み出す |---------------------| | (NO) | そのブロックの OOB の BBM を読む memory BBT は、各ブロックにつき 2bit の情報を持っている。 - `BBT_BLOCK_GOOD` (`0x00`): 正常ブロック - `BBT_BLOCK_WORN` (`0x01`): 使用しているうちに、正常ブロックから不良ブロックに変化したもの - `BBT_BLOCK_RESERVED` (`0x2`): flash based BBT が置かれているブロック - `BBT_BLOCK_FACTORY_BAD` (`0x3`): 最初から不良ブロックだったもの ### 実際の関数実装 (BBT 構築編) ### `nand_scan_tail()` 関数は (`NAND_SKIP_BBTSCAN` フラグがセットされていなければ) `chip->scan_bbt` を呼び出して、 memory BBT を作成する。 以下は関数呼び出しの概略 chip>scan_bbt (= nand_default_bbt: overridable) |-- nand_create_badblock_pattern `-- nand_scan_bbt |-- nand_memory_bbt | `-- create_bbt | |-- scan_block_fast | `-- bbt_mark_entry |-- verify_bbt_descr |-- read_abs_bbts | `-- scan_read |-- search_read_bbts | `-- search_bbt | `-- scan_read |-- check_create | |-- create_bbt | |-- read_abs_bbt | | `-- read_bbt | `-- write_bbt | |-- nand_erase_nand | `-- scan_write_bbt |-- mark_bbt_region `-- nand_update_bbt 各関数の詳細 int nand_default_bbt(struct mtd_info *mtd) `chip->scan_bbt` のデフォルト。 `NAND_BBT_USE_FLASH` フラグが立っていれば、flash based BBT を記述する BBT Descriptor (BBT をどこに置くとか、ID文字列とか、BBT の仕様を記述したもの)を メイン (`chip->bbt_td`) とミラー(`chip->bbt_md`)と 2つ分セットする。 `nand_scan_bbt` を呼び出して、BBT を scan する。 static int nand_create_badblock_pattern(struct nand_chip *this) 各ブロックの OOB の BBM を scan するときの、仕様を記述した BBT Descriptor を `chip->badblock_pattern` にセットする。 int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) `chip->bbt_td` がセットされていなければ(`NAND_BBT_USE_FLASH` がオフの時)、 `nand_memory_bbt` を呼び出して、各ブロックの OOB から BBM を読みだす。 セットされていれば、 flash based BBT が存在するか確認し、あればそれを使う。 static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) `create_bbt` の Wrapper。 static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) `chip->badblock_pattern` に基づいて 各ブロックの OOB にアクセスして BBM を読みだし、 memory BBT を作る。 通常は、各ブロックの先頭ページの OOB だけを見るが、 `NAND_BBT_SCAN2NDPAGE` フラグが立っていると、2ページ目も見る。 `NAND_BBT_SCANLASTPAGE` フラグが立っていると、ブロックの最終ページを見る。 OOB に BBM が付いているブロックには、 memory BBT 上では `BBT_BLOCK_FACTORY_BAD` とマークされる。 static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd, loff_t offs, uint8_t *buf, int numpages) `offs` 位置(ページ先頭を指している)から `numpages` ページ分の OOB を調べ、1 つでも BBM が付いていたら `1` を、 正常ブロックなら `0` を、その他のエラーならば、負の値を返す。 `buf` は temporary buffer で、 OOB を格納するのに十分な長さがないといけない。 static inline void bbt_mark_entry(struct nand_chip *chip, int block, uint8_t mark) memory BBT の先頭から `block` ブロック目のエントリーに対して、 `mark` をつける。 static void verify_bbt_descr(struct mtd_info *mtd, struct nand_bbt_descr *bd) BBT descriptor table `bd` の sanity check static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) BBT Descriptor に `NAND_BBT_ABSPAGE` フラグが立っているときのみ呼び出される。 `NAND_BBT_VERSION` フラグが立っているときは、 `td->pages[0]` に BBT 位置が指定されているので、 そのページから BBT のバージョンを読みだして、 `td->version[0]` にセットする。 `md` が存在すれば、ミラーに対しても同じことをする。 static int scan_read(struct mtd_info *mtd, uint8_t *buf, loff_t offs, size_t len, struct nand_bbt_descr *td) BBT マーカーを含む領域を `buf` に読み出す。 `td` に `NAND_BBT_NO_OOB` フラグが立っているときは、 `offs` から マーカーの長さ (4 byte) さらにバージョン情報がある場合は、もう 1 byte 読み出す。 `NAND_BBT_NO_OOB` フラグが立っていない場合は、`offs` 位置の1ページ分(MAIN + OOB)を読み出す。 static void search_read_bbts(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md) BBT Descriptor に `NAND_BBT_ABSPAGE` フラグが立っていないときのみ呼び出される。 `search_bbt` の wrapper。 `td` (メイン)、および `md` (ミラー)が存在すれば `md` についても `search_bbt` を呼び出す。 static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td) flash based BBT を探し、見つかると `td->pages[i]` に BBT の先頭のページ番号を格納する。 `NAND_BBT_VERSION` フラグが立っていれば `td->version[i]` にバージョン番号をつめる。 `NAND_BBT_PERCHIP` フラグが立っていれば、チップ単位で BBT を探す。 `NAND_BBT_LASTBLOCK` フラグが立っていれば、最終ブロックから前方に向かって、立っていなければ、先頭ブロックから後方に向かって探す。 int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) `buf` に BBT の マーカー (通常は `'B'`, `'b'`, `'t'`, `'0'` の4文字, ミラーは `'1'`, `'t'`, `'b'`, `'B'` の 4文字) が含まれているかを調べる。 含まれていれば `0` 含まれていなければ `-1` を返す。 static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) flash based BBT の読み出し、更新、新規作成をする。 `td`, `md` ともにまだ存在しない場合は、`create_bbt` を呼び出して、各ブロックの OOB から BBM を読みだして memory BBT を作る。 ただし、 `NAND_BBT_CREATE` フラグが立っていない もしくは `NAND_BBT_CREATE_EMPTY` フラグが立っていない場合は、 memory BBT は全ブロック `BBT_BLOCK_GOOD` になる。 少なくともどちらか存在する場合は、より新しいバージョンを持つ方の flash based BBT を読みだして memory BBT を作る。 int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip) flash based BBT を読み出し、memory BBT へ書き込む。 static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num, struct nand_bbt_descr *td, int offs) NAND の `page` ページ位置の flash based BBT を読み出し、memory BBT の `offs` 位置から `num` エントリー分を書き込む。 `NAND_BBT_NO_OOB` フラグが立っている場合は、 先頭ページの最初は BBTマーカーとバージョン情報なので、その部分はスキップする。 `NAND_BBT_NO_OOB` フラグが立っていない場合は、先頭バイトから読む。 flash based BBT の各エントリーのビット数は td->options で指定される。 All 1 が `BBT_BLOCK_GOOD` に対応。 `td->reserved_block_code` に `0` 以外が指定されているときは、 `BBT_BLOCK_RESERVED` に対応。 All 0 が `BBT_BLOCK_FACTORY_BAD`、 それ以外は `BBT_BLOCK_WORN` に対応する。 static int write_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) memory BBT を flash based BBT へ書き込む。 `NAND_BBT_LASTBLOCK` フラグが立っていない場合は、先頭ブロックから後方に向かって、 立っている場合は、最終ブロックから前方に向かって、bad block ではなく、ミラーでも使われていないブロックを探す。 見つかると、そのブロックを一旦消去し、 `scan_write_bbt` を呼び出して memory BBT の内容を書き込む。 void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td) BBT が置かれているブロックに `BBT_BLOCK_RESERVED` マークをつける。 `NAND_BBT_ABSPAGE` フラグが立っていない場合は、 `td->maxblocks` ブロック分、マークをつける。 基本的に memory BBT のみの更新だが、 `td->reserved_block_code` に `0` 以外が指定されていると、 `nand_update_bbt` を呼び出して flash based BBT も更新する。 static int nand_update_bbt(struct mtd_info *mtd, loff_t offs) flash based BBT のバージョンをインクリメントし、 `write_bbt` を呼び出して、更新する。 `chip->md` が存在すれば、ミラーに対しても同じことをする。 `NAND_BBT_PERCHIP` フラグが立っている場合は、 `offs` 位置のブロックが属する BBT のみが更新される。 ### 実際の関数実装 (Bad Block 問い合わせ編) ### mtd_block_isbad `-- mtd->_block_isbad (=nand_block_isbad) `-- nand_block_checkbad |-- chip->block_bad (=nand_block_bad:overridable) `-- nand_isbad_bbt 各関数詳細 int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) MTD layer の bad block 判定関数。 オフセット位置 `ofs` のブロックが bad block なら `1`、正常ブロックなら `0`、エラーの場合は負の値を返す。 `mtd->_block_isbad` がなければ 0 を返し、存在すれば呼び出す。 int nand_block_isbad(struct mtd_info *mtd, loff_t offs) `mtd_block_isbad` の NAND layer 実装。`nand_block_checkbad` の wrapper。 int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt) memory BBT があればそれを読み、なければ `chip->block_bad` をコールして、直接 NAND chip の OOB を読む。 static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) OOB に直接アクセスして BBM を読み、 `0xFF` でなければ 1 を返す。 (TODO: `NAND_CMD_READOOB` のような低レベルコマンドだけでなく、 `chip->ecc.read_oob` に対応すべき??) int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) memory BBT にアクセスして Bad Block なら `1` を、正常ブロックなら `0` を返す。 `allowbbt` が `0` でないならば、 `BBT_BLOCK_RESERVED` (BBT を格納しているブロック) は正常ブロックとして扱い、 `allowbbt` が `1` ならば、 `BBT_BLOCK_RESERVED` は不良ブロックとして扱う。 ### 実際の関数実装 (BBMをつけるとき) ### mtd_block_markbad `-- mtd->_block_markbad (=nand_block_markbad) |-- nand_block_isbad `-- nand_block_markbad_lowlevel |-- nand_erase_nand |-- chip->block_markbad (=nand_default_block_markbad) `-- nand_markbad_bbt |-- bbt_mark_entry `-- nand_update_bbt `-- write_bbt 各関数詳細 int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) MTD layer 関数。`ofs` の位置のブロックに BBM をつける。 static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs) `mtd_block_markbad` の NAND layer 実装。 `nand_block_isbad` を呼び出して、`ofs` が bad block か調べ、すでに bad block なら何もしない。 bad block でないなら `nand_block_markbad_lowlevel` を呼び出して、マークをつける。 static int nand_block_markbad_lowlevel(struct mtd_info *mtd, loff_t ofs) `NAND_BBT_NO_OOB_BBM` フラグが立っていない場合は、いったんそのブロックを消去し、OOB 領域に BBM をつける。 さらに `chip->bbt` が存在すれば `nand_markbad_bbt` を呼び出して、memory BBT と flash based BBT を更新する。 (`chip->bbt` が存在しなくても flash based BBT は更新すべきでは?バグ??) int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) `ofs` 位置のブロックの先頭ページ の OOB に BBM (0x00) をつける。 `NAND_BBT_SCAN2NDPAGE` フラグが立っていると、2ページ目にも BBM をつける。 ただし、`NAND_BBT_SCANLASTPAGE` フラグが立っている場合は、ブロック内最終ページに BBM をつける。 int nand_markbad_bbt(struct mtd_info *mtd, loff_t offs) `bbt_mark_entry` を呼び出して memory BBT に `BBT_BLOCK_WORN` マークをつける。 `NAND_BBT_USE_FLASH` フラグが立っている場合には、`nand_update_bbt` を呼び出して、flash based BBT も更新する。
2014年11月20日木曜日
Google からの贈り物
先月のある日、 Google の Open Source Programs Office というところからメールが来まして。 読んでみると、 オープンソースで優れた仕事をしている人を表彰していて、あなたの仕事がそれに値すると認められました。 ついては、記念品と賞金を送りたい。 というような内容でした。 「お金あげます」という類のスパムは頻繁に来るのですが(特にオープンソースに参加しているとよくスパムが来る)、金額がまっとうな額な上に、 メールの内容が、自分のやったきたことに合致するので、本物のようです。 とりあえず、住所等をフォームから送信して、待つこと数週間。 本当に来ました! 予想外にでかい FeDex の箱を開けると、中から出てきたのは、、、 [](http://4.bp.blogspot.com/-jHvUJsHxOnQ/VGy9y9GB_tI/AAAAAAAANHU/aobPS-5vNX0/s1600/google_blanket.jpg) 暖かそうな、ブランケットのようです。 あまりにも実用的な品なので、かえって拍子抜けしてしまいました。(笑) プリペイドのクレジットカードも送られてきました。 賞金があらかじめチャージされていて、その分だけ使えるようです。 ただただ、自分の趣味でパッチを送り続けてきたのですが、このように認めてもらえるのは嬉しいものです。 このブランケットを使って、もっとパッチ送ってこい、という暗黙のメッセージなんでしょうか? これからの季節、夜な夜なキーボードに向かっていると体が冷えるので、ありががたく使わせていただきます。
2014年10月24日金曜日
ELCE 2014 & U-Boot Mini Summit in Düsseldorf
[昨年](/2013/10/elce-2013-u-boot-mini-summit-in.html)に引き続き、今年も [Embedded Linux Conference Europe](http://events.linuxfoundation.org/events/embedded-linux-conference-europe) と [U-Boot Mini Summit](http://www.denx.de/wiki/U-Boot/MiniSummitELCE2014) に行ってきました。 今年の開催地はドイツのデュッセルドルフでした。 最寄り駅。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvU2vUZ2dgEpz-wuYUUsTvIumWtBxsELBegJ4y75WZvcHSZzTirnBWhdycpE8coBahgIKOAn7QWILl7GxXzM5YwR0PzjEJdQcvl10kM0GG6URs7p9g7Icdqc1w9B5yuEtBmX88MEGnPYk/s1600/bahnhof.jpg) 会場までの道。のどかです。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUQH8TMoT8LSkCRlhMiuVHgahULjnx-gKpoS8dtJI12ruY8YBLQ5BnyBlt1XLzbmBGagEdcHgcL3co6xvnkkmcfdxjfpSqPI0FLQU4p9hgUWB0sCjzn3MwV3K07XUmXCaimjm2rKXL-8Q/s1600/strasse.jpg) 会場の Congress Centre Düsseldorf [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZQts4lTwctbF1jByHv61IZVZ87B_yfJLE9qBeXYFHwoxMX5s-7avzL4zSL2OUSrAhf93TH7ebQmPpKhgQA8PTlmgOyEQ_lzxA7NoV1GqQTxWqluXG-0GqXHuhp2ptgtqD-AHo9Wx3TOo/s1600/ccd.jpg) [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDtLsiM2sHt1SFff9dpk1v5BCLJZ3zGcRNubdaywjsXJrrNh2kQ-USOFtg3ZrmeD41vB_wKk3veNz8zkU3h2Voe6du3Q6C3qEi871JjeZGkf9PGejCAMu8Jy1A0UzBx1x8L1cgAExe5rQ/s1600/willkommen.jpg) メインのホール。もうすぐ始まります。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPKVf935Z5oz8ohCp0Ig1mSVs1tmbX9UHbkbzrNqLv8vF6qPK-snusFuaMCdpFTkuDA9ne_DXG3pA2QhQeen3MUJErtMSoWUsUZe6nlrI5l0NR3WbWpbeAWlntbFixASSHdLq4TgQaMjg/s1600/haupthalle.jpg) こちらは [U-Boot Mini Summit 2014](http://www.denx.de/wiki/U-Boot/MiniSummitELCE2014) の会場。14:30 からは自分のプレゼンです。ドキドキ。。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilphpMhijvWI1raCipv8tB-qRiKrUWPjPUUy1l3GR60uJvJcqv6vLRbxwYAQtyTE7_yRAKBTI9MGhh2NLpDsdxni9wROcq3ShU1ybPXyYGHWYNN3_Ik9KeZoe5MLmfMmLo6AbNYXwTzs8/s1600/uboot_summit2014.jpg) [ELCE2014](http://events.linuxfoundation.org/events/embedded-linux-conference-europe) の ARCHIVE ページにプレゼンしているところが載っていた。(なんだかビミョーな表情) [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0_rAhLrcAWGYxbXr97UfwmSbRe6YI8F_bjpN-OpWFNlIqnbKaa-GUwerqOZa5iqmLOO9VTYl6pv6r7bdZZ6czZOAt_qfaJAbthklSZ2lVlEkx5QLhBLTwXZDkfJXQydGoNE8AUmmxcUc/s1600/my_talk.jpg) プレゼンもなんとか無事に終えて、ホッ。 スライドは[こちら](http://www.denx.de/wiki/U-Boot/MiniSummitELCE2014)からダウンロード可能。 最後に集合写真。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoos58upKdKkAZoaaFFLL_TJH7W_K7ajNKR-h4_mVvQ_Uy7aogS9ZV1FT6cqifT1rMdcn2KnA6ob6cvv_qVvdPJgpfuN84mx0YirUey0MzEionCMmt8HMHACp4Pmhc9sW8o5qkUBfkFQw/s1600/U-Boot-Mini-Summit-2014.jpg) 夜は Füchschen というビアレストランで飲み会。「地球の歩き方」に載っていたので、結構有名なお店なんでしょうか。 [](http://3.bp.blogspot.com/-24yopBmKYuA/VEJ99dSgiLI/AAAAAAAAMTE/VqQMGbOw-zM/s1600/uboot_beer_summit2014.jpg) 3 日目のステージには Linus が登場。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMpV0FxTfzMAhF0O2t21PpAnE8foXjLOxNLcx3NSCQSBuypENbIuoL0tWsQPOapKcVldQF59ATgSr9emaNxroRsAMzmuSJXgT9BC4XNd0RYyP5cDAfYa61QVODq9s364i2KxVIKch0xVI/s1600/linus_elce2014.jpg) 去年(左)は「XL サイズしか残ってません!」と言われてしまったが、今年(右)はちゃんと MサイズのTシャツがもらえました。 [](http://3.bp.blogspot.com/-5v0pDM_PuEE/VEKFiIfyqeI/AAAAAAAAMTw/SWAGetnc7sY/s1600/elce_shirts.jpg) U-Boot の発展に貢献したということで、 DENX から素敵な贈り物を頂きました。一応 USBメモリとしても使えます。 [](http://1.bp.blogspot.com/-wqV3CcFKuPM/VEWo94kbeQI/AAAAAAAAMUo/eZGI-hKrcYo/s1600/denx_usb_memory.jpg)
2014年7月15日火曜日
U-Boot と Linux Kernel のメインラインで Zynq を動かす 2014年7月版
本日 U-Boot 2014.07 がリリースされました。 [前回](/2014/05/u-boot-linux-kernel-zynq-201404.html)から U-Boot の方のやり方が少し変わったので、今回もまとめておきます。 具体的には、 v2014.04 で動かなくなっていた DTB を使った U-Boot のブートシーケンスが動くようになっていたり、 煩雑な工程のいくつかが不要になっています。 使用するのは、 - U-Boot 2014.07 - Linux Kernel 3.15 - Zynq ZC706 ボード とします。 ### STEP0: DTC (Device Tree Compiler) の準備 ##### Input Files Required - None ##### Output Files Produced - `dtc`: Device Tree Compiler ##### Task Description U-Boot を Device Tree 付きでビルドするには version 1.4 以降の DTC が必要になる。 ディストリビューションに標準で用意されている DTC では不足するかもしれないのでバージョンを確認。 $ dtc -v Version: DTC 1.3.0 バージョンが 1.4.0 よりも古い場合は、自分でビルドします。 $ git clone git://git.kernel.org/pub/scm/utils/dtc/dtc.git でソースを取ってきて $ make $ make install でビルドとインストールができる。 `$(HOME)/bin` の下に `dtc` が入るので PATH を通しておく。 ### STEP1: U-Boot のビルド ##### Input Files Required - `dtc` - ARM Cross Compiler - `ps7_init.c`: ISE / Vivado で "Export Hardware for SDK" を実行すると出力される - `ps7_init.h`: ISE / Vivado で "Export Hardware for SDK" を実行すると出力される ##### Output Files Produced - `u-boot-dtb.bin`: U-Boot の RAWバイナリと u-boot をコンフィグレーションする DTB を連結したもの - `u-boot-dtb.img`: `u-boot-dtb.bin` に uImage ヘッダーをつけたもの - `spl/u-boot-spl.bin`: U-Boot SPLの RAWバイナリ - `tools/mkimage`: U-Boot で扱うイメージを生成するツール。 ##### Task Description $ git clone git://git.denx.de/u-boot.git $ cd u-boot $ git checkout v2014.07 でソース取得して、v2014.07 タグをチェックアウト。 (何かあっても自分で対処できる人は masterブランチでやってもOK) あとで、Kernel を TFTP でダウンロードしたいので、`include/configs/zynq-common.h` を開いて、適当なところに #define CONFIG_IPADDR 192.168.11.2 #define CONFIG_SERVERIP 192.168.11.1 #define CONFIG_ETHADDR 00:0a:35:00:01:22 の3行を足す。 `CONFIG_IPADDR` は Zynqボードに割り振る IPアドレス、 `CONFIG_SERVERIP` は TFTP サーバーのアドレスに合わせて下さい。 MACアドレスは、(他のネットワーク機器と被らなければ)適当でいい。 TFTP サーバーがなくても、動かすことはできるので、ない人は上記はスキップして下さい。 さらに、 `ps7_init.c`, `ps7_init.h` を U-Boot の `board/xilinx/zynq` ディレクトリにコピーする。 このファイルが、 FSBL の代わりをするための、肝になるファイルです。 あとは $ make zynq_zc70x_config $ make CROSS_COMPILE=arm-linux-gnueabi- DEVICE_TREE=zynq-zc706 のようにして、コンフィグレーションとビルドをする。 もしくは $ make zynq_zc70x_config all CROSS_COMPILE=arm-linux-gnueabi- DEVICE_TREE=zynq-zc706 のように 1行で、コンフィグレーションとビルドを同時にすることもできる。 `ps7_init.c` と `ps7_init.h` が warning を出しますが、気にしなくてもOK。 気になる人は、関数のプロトタイプの引数部に `void` を足してください。 ### STEP2: Linux Kernel のビルド ##### Input Files Required - ARM Cross Compiler ##### Output Files Produced - `arch/arm/boot/zImage`: Kernel Image - `arch/arm/boot/dts/zynq-zc706.dtb`: Kernel をコンフィグレーションする DTB (Device Tree Blob) (従来、 U-Boot から Kernel を起動するときは `arch/arm/boot/uImage` を使っていたが、これは使わない。) ##### Task Description $ git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git $ cd linux $ git checkout v3.15 でソース取得して、v3.15 タグをチェックアウト。 (何かあっても自分で対処できる人は masterブランチでやってもOK) 以下のようにして ARMv7 Multi な設定にする。 $ make ARCH=arm multi_v7_defconfig ここで $ make ARCH=arm menuconfig をして、少々設定をいじる。 Device Drivers ---> Block devices ---> [*] RAM block device support (16384) Default RAM disk size (kbytes) のようにたどり、 `RAM block device support` にチェックを入れ、 `Default RAM disk size` を `16384` に設定する。 もう一つ Device Drivers ---> Character devices ---> [ ] Legacy (BSD) PTY support のようにたどり、`Legacy (BSD) PTY support` のチェックを外す。 あとは $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- でビルド。 ### STEP3: Ramdisk のダウンロード ##### Input Files Required - None ##### Output Files Produced - `arm_ramdisk.image.gz`: Kernel がマウントする init ramdisk (を gzip 圧縮したもの) ##### Task Description [こちら](http://www.wiki.xilinx.com/Build+and+Modify+a+Rootfs) から arm_ramdisk.image.gz をダウンロードする。 ### STEP4: ITB (Image Tree Blob) の生成 ##### Input Files Required - `linux/arch/arm/boot/zImage` - `arm_ramdisk.image.gz` - `linux/arch/arm/boot/dts/zynq-zc706.dtb` - `u-boot/tools/mkimage` カレントディレクトリから見て、上記の配置になっているとする。 ##### Output Files Produced - `fit.itb`: U-Boot から Kernel を起動するためのイメージ ##### Task Description Kernel Image, Ramdisk, DTB (Device Tree Blob) を一つにまとめた ITB というのを作ります。 ITB を作るには、 ITS(Image Tree Source) を記述して、 `mkimage` に食わせます。 以下の内容を `fit.its` というファイルに記述する。 /dts-v1/; / { description = "Kernel, ramdisk and FDT blob"; #address-cells = <1>; images { kernel@1 { description = "Linux Kernel 3.15 configured with multi_v7_defconfig"; data = /incbin/("linux/arch/arm/boot/zImage"); type = "kernel"; arch = "arm"; os = "linux"; compression = "none"; load = <0x00008000>; entry = <0x00008000>; hash@1 { algo = "md5"; }; }; ramdisk@1 { description = "Ramdisk for Zynq"; data = /incbin/("arm_ramdisk.image.gz"); type = "ramdisk"; arch = "arm"; os = "linux"; compression = "gzip"; load = <0x00000000>; entry = <0x00000000>; hash@1 { algo = "sha1"; }; }; fdt@1 { description = "FDT for ZC706"; data = /incbin/("linux/arch/arm/boot/dts/zynq-zc706.dtb"); type = "flat_dt"; arch = "arm"; compression = "none"; hash@1 { algo = "crc32"; }; }; }; configurations { default = "config@1"; config@1 { description = "Zynq ZC706 Configuration"; kernel = "kernel@1"; ramdisk = "ramdisk@1"; fdt = "fdt@1"; }; }; }; あとは以下のようにすれば、`fit.itb` ができる。 $ u-boot/tools/mkimage -f fit.its fit.itb ### STEP5: JTAG (Slave Boot) から U-Boot と Linux を起動する ##### Input Files Required - `u-boot-dtb.bin`: STEP1 で作成したもの - `fit.itb`: STEP4 で作成したもの - `xmd`: ISE / Vivado のインストールディレクトリに入っている - `ps7_init.tcl`: ISE / Vivado から "Export Hardware for SDK" を実行すると出力される - `stub.tcl`: Xilinx のページからダウンロードできる `ug873-design-files.zip` の中に入っている - `fpga.bit`: ISE / Vivado で生成した FPGA bit file (Optional) ##### Task Description `fit.itb` を TFTP の公開ディレクトリに置く。(TFTP 環境のない人はスキップして下さい) Zynq ボードのブートモードの選択スイッチを JTAG に合わせて電源入れる。JTAG でボードと接続し、XMD を開く。 $ xmd XMD のプロンプトから以下を実行する。(FPGA は必要なければダウンロードしなくても良い) XMD% connect arm hw ;# Open JTAG connection XMD% rst -slcr ;# Reset the whole system XMD% fpga -f fpga.bit ;# Download FPGA bit file (Optional) XMD% source ps7_init.tcl XMD% ps7_init ;# Initialize DDR, IO pins, etc. XMD% ps7_post_config ;# Enable level shifter XMD% source stub.tcl ;# start CPU1 XMD% targets 64 ;# connect to CPU0 XMD% dow -data u-boot-dtb.bin 0x04000000 ;# Download u-boot to address 0x04000000 XMD% con 0x04000000 ;# start CPU0 from address 0x04000000 なお、毎回これを打ち込むのも面倒ですので、 `foo.tcl` に書いておきましょう。 XMD% source foo.tcl で XMD から読み込むか、シェルから $ xmd -tcl foo.tcl とすればよいです。 U-Boot のプロンプトが出た後、放っておくと、自動的に TFTPサーバーから `fit.itb` をダウンロードして、 Linux が起動する。 TFTP サーバーがない場合は、`con 0x04000000` の前に XMD% dow -data fit.itb 0x02000000 とすれば、 JTAG 経由で `fit.itb` をダウンロードできるので(時間かかりますが、、) あとは U-Boot のプロンプトから > bootm 2000000 と入力して Linux を起動させる。 ### STEP6: SDカード用のブートイメージを作成する ##### Input Files Required - `u-boot/spl/u-boot-spl.bin`: STEP1 で作成したもの ##### Output Files Produced - `boot.bin` ##### Task Description `bootgen` を使わずに `boot.bin` を作成する方法を紹介します。 u-boot-xlnx のコードを取ってきます。 git clone git://github.com/Xilinx/u-boot-xlnx.git `tools` ディレクトリの下に `zynq-boot-bin.py` という Python スクリプトが 入っているので、これを `~/bin` かどこか適当な PATH にコピーする。 あとは zynq-boot-bin.py -o boot.bin -u u-boot/spl/u-boot-spl.bin とすれば、 `boot.bin` ができます。 BIF ファイルを記述しなくてもいい分、こちらの方が簡単だと思います。 ### Step7: SDカードから U-Boot と Linux Kernel を起動する ##### Input Files Required - `boot.bin`: STEP6 または STEP6B で作成したもの - `u-boot-dtb.img`: STEP1 で作成したもの - `fit.itb`: STEP4 で作成したもの ##### Task Description FAT でフォーマットしたSDカードに `boot.bin`, `u-boot-dtb.img`, `fit.itb` をコピー。 SDカードを Zynq ボードに挿し、ブートモードの選択スイッチを SD カードに合わせて電源入れる。 ### 補足 U-Boot の起動時に以下の Warning メッセージが出ると思いますが、気にしなくていいです。 Warning: Your board does not use generic board. Please read doc/README.generic-board and take action. Boards not upgraded by the late 2014 may break or be removed. これは開発者向けの情報です。次回のリリースでは修正されているはず。 消したい人は `include/configs/zynq-common.h` に #define CONFIG_SYS_GENERIC_BOARD を足す。
2014年6月5日木曜日
Linux で Merge Window の開き方が変わった
LKML (Linux Kernel Mailing List) を watch していて、 まだ Linux 3.15 がリリースされていないのに、 やたらと Pull Request が飛んでいるなあ、と思っていたらすでに Merge Window が開いていた。 Linus のこのアナウンスメールです。 [https://lkml.org/lkml/2014/6/1/264](https://lkml.org/lkml/2014/6/1/264) 要約するとこういう話。(MW = Merge Window) ----------------------------------------------------------------------------------- 先週 rc7 をリリースして、本来は今日 3.15 をリリースするつもりだったけど、 ちょっとした修正があって、目論見に反して rc8 をリリースすることになってしまった。 rc8 をリリースしたくなかった本当の理由というのは、娘の学校が 2週間後に終わるので、 それに合わせてバカンスに出かけようとしていたため。 (今日 3.15 をリリースして、 MW を開いていたら、 MW が閉じるのと同時にバカンスに 出発できる、という目論見だった。) ネットがあるから、バカンス中に MW はできるが、できればやりたくない。 かといって、 3.15 をリリースするには、もう少し時間を置く必要がある。 そういうわけで、新しい試みとして、リリースの1週間前に MW を開いてみよう。 リリースの最後の1週間は、おかしなことが起こっていないかの確認期間みたいなものだから、 次のリリースの MW と重なっていたとしてもうまくいうだろう。 MW の最初の1週間は、 3.16 用の変更は 'next' branch へ Pull することになるが、 Subsystem のメンテナーはすでによく branch を使いこなしているから、混乱する人もいないだろう。 ----------------------------------------------------------------------------------- 補足入れつつ、かなり雰囲気で訳してしまったが、だいたいこんな感じだと思います。 図にするとこういうことだと思う。 #### これまでのやり方 #### Mainline には 'master' branch のみがある。 3.x のリリースから 2週間 Merge Window が開いた後、 rc1 リリースがされる。 ----o----------------o---------------------------------o----------------o---(master)---> 3.14-rc8 3.14 3.15-rc1 3.15-rc2 |---- 1 week ----|------------- 2 week ------------|---- 1 week ----| |<-------- [Merge Window] ------->| #### 今回のやり方 #### 3.15-rc8 から 'next' branch が分岐し、新しい機能はこちらにマージされる。 一方、 3.15 のリリースに向けた最後の修正は 'master' branch の方に入る。 3.15 のリリース後に、'next' branch は 'master' branch へとマージされ、 さらにもう 1週間の MW が続いたあと、 3.16-rc1 がリリースされる。 -----(next)------o--------------o----------------o---(master)---> / / 3.16-rc1 3.16-rc2 ----o----(master)----o 3.15-rc8 3.15 |---- 1 week ----|---- 1 week ----|---- 1 week ----| |<-------- [Merge Window] ------->|
2014年5月28日水曜日
Device Tree アクセス関数まとめ (DTC & U-Boot)
Device Tree Compiler や U-Boot に入っている Device Tree アクセス関数をまとめました。 ### `_fdt_nodename_eq` (fdt_ro.c:17) static int _fdt_nodename_eq(const void *fdt, int offset, const char *s, int len) DTB `fdt` の DT structure の先頭から `offset` の位置の文字列(`0` または `@` で終端されている)と 文字列 `s` (長さ `len`) を比較し、 一致すれば `1` を、一致しなければ `0` を返す。 ### `fdt_string` (fdt_ro.c:78) const char *fdt_string(const void *fdt, int stroffset) DTB `fdt` の DT strings の先頭から `stroffset` の位置の文字列へのポインタを返す。 ### `_fdt_string_eq` (fdt_ro.c:83) static int _fdt_string_eq(const void *fdt, int stroffset, const char *s, int len) DTB `fdt` の DT strings の先頭から `stroffset` の位置の文字列と、 文字列 `s` (長さ `len`) を比較する。 長さも、文字列も一致すれば `1` を、それ以外は `0` を返す。 `strcmp` と論理が逆なので注意する。 ### `_nextprop` (fdt_ro.c:108) static int _nextprop(const void *fdt, int offset) DTB `fdt` の DT structure の先頭から `offset` の位置のタグを調べ、プロパティならば `offset` を返す。 それ以外は負のエラーコードを返す。 ### `fdt_subnode_offset_namelen` (fdt_ro.c:132) int fdt_subnode_offset_namelen(const void *fdt, int offset, const char *name, int namelen) DTB `fdt` の DT structure の先頭から `offset` 位置 (ノード先頭を指していないといけない)のノードから見て、 1つ下の階層のサブノードのうち、ノード名 `name` (長さ `namelen`) のものを探す。 見つかれば、そのサブノードのオフセット位置を返し、見つからなかったり、エラーの場合は負のエラーコードを返す。 ### `fdt_subnode_offset` (fdt_ro.c:151) int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name) DTB `fdt` の DT structure の先頭から `offset` 位置 (ノード先頭をさしていないといけない)のノードから見て、 1つ下の階層のサブノードのうち、ノード名 `name` のものを探す。 見つかれば、そのサブノードのオフセット位置を返し、見つからなかったり、エラーの場合は負のエラーコードを返す。 ### `fdt_path_offset` (fdt_ro.c:157) int fdt_path_offset(const void *fdt, const char *path) DTB `fdt` から `path` のパスのノードを探し、そのオフセット位置を返す。 見つからない場合や、エラーの場合は負のエラーコードを返す。 `path` が `'/'` で始まらない場合はエイリアスとみなされる。 ### `fdt_get_name` (fdt_ro.c:201) const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) DTB `fdt` の DT structure の先頭から `nodeoffset` 位置 (ノード先頭を指していないといけない) のノードの名前を返す。 `len` に `NULL` 以外が与えられていると、ノード名の長さを詰めて返す。 エラーの場合は、`NULL` を返し、 `len` には負のエラーコードを詰める。 ### `fdt_first_property_offset` (fdt_ro.c:221) int fdt_first_property_offset(const void *fdt, int nodeoffset) DTB `fdt` の DT structure の先頭から `nodeoffset` 位置 (ノード先頭を指していないといけない) の最初のプロパティ位置のオフセットを返す。 見つからないときや、エラーの場合は負のエラーコードを返す。 ### `fdt_get_property_by_offset` (fdt_ro.c:239) const struct fdt_property *fdt_get_property_by_offset(const void *fdt, int offset, int *lenp) DTB `fdt` の DT structure の先頭から `offset` 位置がプロパティ先頭かを確認し、 プロパティならば、そのポインタを返し、 `lenp` (に `NULL` 以外が与えられていれば)にプロパティ値の長さを詰める。 プロパティでなければ、`NULL` を返し、`lenp` には エラーコードを詰める。 ### `fdt_get_property_namelen` (fdt_ro.c:260) const struct fdt_property *fdt_get_property_namelen(const void *fdt, int offset, const char *name, int namelen, int *lenp) DTB `fdt` の DT structure の先頭から `offset` 位置 (ノード先頭を指していないといけない) のノードからプロパティ名が `name` (長さ `namelen`)のものを探し、見つかれば、そのプロパティへのポインタを返し、 `lenp` (に`NULL` 以外が与えられていれば)にプロパティ値の長さを詰める。 見つからない場合やエラーの場合は、`NULL` を返し、`lenp` にエラーコードを詰める。 ### `fdt_get_property` (fdt_ro.c:284) const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, const char *name, int *lenp) DTB `fdt` の DT structure の先頭から `offset` 位置 (ノード先頭を指していないといけない) のノードからプロパティ名が `name` のものを探し、見つかれば、そのプロパティへのポインタを返し、 `lenp` (に`NULL` 以外が与えられていれば)にプロパティ値の長さを詰める。 見つからない場合やエラーの場合は、`NULL` を返し、`lenp` にエラーコードを詰める。 ### `fdt_getprop_namelen` (fdt_ro.c:251) const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, const char *name, int namelen, int *lenp) DTB `fdt` の DT structure の先頭から `offset` 位置 (ノード先頭を指していないといけない) のノードからプロパティ名が `name` (長さ `namelen`)のものを探し、見つかれば、そのプロパティの値へのポインタを返し、 `lenp` (に`NULL` 以外が与えられていれば)にプロパティ値の長さを詰める。 見つからない場合やエラーの場合は、`NULL` を返し、`lenp` にエラーコードを詰める ### `fdt_getprop_by_offset` (fdt_ro.c:265) const void *fdt_getprop_by_offset(const void *fdt, int offset, const char **namep, int *lenp) DTB `fdt` の DT structure の先頭から `offset` 位置 (プロパティ先頭を指していないといけない) のプロパティの値へのポインタを返す。 `namep` に `NULL` 以外が与えられていれば、プロパティ名を詰め、 `lenp` に `NULL` が与えられていれば、プロパティ値の長さを詰める。 エラーのときは、 `NULL` を返し、 `lenp` に (`NULL`以外が与えられていれば、エラーコードを詰める) ### `fdt_getprop` (fdt_ro.c:276) const void *fdt_getprop(const void *fdt, int nodeoffset, const char *name, int *lenp) DTB `fdt` の DT structure の先頭から `offset` 位置 (ノード先頭を指していないといけない) のノードからプロパティ名が `name` のものを探し、見つかれば、そのプロパティの値へのポインタを返し、 `lenp` (に`NULL` 以外が与えられていれば)にプロパティ値の長さを詰める。 見つからない場合やエラーの場合は、`NULL` を返し、`lenp` にエラーコードを詰める ### `fdt_get_phandle (fdt_ro.c:282)` uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) DTB `fdt` の DT structure の先頭から `offset` 位置 (ノード先頭を指していないといけない) のノードからプロパティ "phandle" の値を返す。 "phandle" が見つからない場合は、 "linux,phandle" の値を返す。 見つからない場合は `0` を返す。 ### `fdt_get_alias_namelen` (fdt_ro.c:299) const char *fdt_get_alias_namelen(const void *fdt, const char *name, int namelen) ノード `name` の別名(エイリアス)を文字列として返す。 エイリアスは `"/aliases"` ノードに格納されているので、そのノードからプロパティ名 `name` (長さ `namelen`) のものを探し、そのプロパティ値を返す。 見つからない場合は `NULL` を返す。 ### `fdt_supernode_atdepth_offset` (fdt_ro.c:368) int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, int supernodedepth, int *nodedepth) DTB `fdt` の DT structure の先頭から `nodeoffset` 位置 (ノード先頭を指していないといけない)のノード の親(直接の親とは限らない)のうち、 `supernodedepth` の深さのもののオフセットを返す。 見つからない場合は、負のエラーコードを返す。 `nodedepth` に `NULL` 以外が与えられていれば、自身の階層の深さを詰める。 ### `fdt_node_depth` (fdt_ro.c:404) int fdt_node_depth(const void *fdt, int nodeoffset) DTB `fdt` の DT structure の先頭から `nodeoffset` 位置 (ノード先頭を指していないといけない)のノード の深さを返す。 エラーの場合は、負のエラーコードを返す。 ### `int fdt_parent_offset(const void *fdt, int nodeoffset)` (fdt_ro.c:416) int fdt_parent_offset(const void *fdt, int nodeoffset) DTB `fdt` の DT structure の先頭から `nodeoffset` 位置 (ノード先頭を指していないといけない)のノード の直接の親ノードのオフセット位置を返す。 エラーの場合は、負のエラーコードを返す。 ### `fdt_node_offset_by_prop_value` (fdt_ro.c:425) int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, const char *propname, const void *propval, int proplen) DTB `fdt` の DT structure 領域の `startoffset` 位置から検索開始し、 プロパティ名 `propname` と プロパティ値 `propval` の組み合わせを持つノードが見つかると そのノードへのオフセットを返す。 見つからない場合やエラーの場合は、負のエラーコードを返す。 ### `fdt_node_offset_by_phandle` (fdt_ro.c:452) int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) DTB `fdt` の DT structure の先頭から探索し、phandle `phadle` を持つノードを返す。 見つからない場合や、エラーの場合は、府のエラー番号を返す。nodeoffset` 位置 (ノード先頭を指していないといけない)のノード の直接の親ノードのオフセット位置を返す。 ### `fdt_stringlist_contains` (rdt_ro.c:477) int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) 1つまたはそれ以上の文字列の連結 `strlist` (長さ `listlen`) 中に、文字列 `str` が含まれていれば `1`を、それ以外は `0` を返す。 ### `fdt_node_check_compatible` (rdt_ro.c:570) int fdt_node_check_compatible(const void *fdt, int nodeoffset, const char *compatible) DTB `fdt` の DT structure の先頭から `offset` 位置 (ノード先頭を指していないといけない) のノードから `"compatible"` という名前のプロパティを探し、その値中に文字列 `compatible` があれば(compatible であれば)、`0` を返す。 compatible でない場合は、 `1` を返す。 ノードが `"compatible"` というプロパティを持たない場合や、その他エラーの場合は、負のエラーコードを返す。 ### `fdt_node_offset_by_compatible` (fdt_ro.c:585) int fdt_node_offset_by_compatible(const void *fdt, int startoffset, const char *compatible) DTB `fdt` の DT structure 領域の `startoffset` 位置から検索開始し、 `compatible` に対して compatible なノードを返す。 見つからなかったり、エラーの場合は、負のエラーコードを返す。 ### `fdt_offset_ptr` (fdt.c:77) const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) DTB `fdt` の DT structure の先頭から `offset` バイト目の位置のポインタを返す。 DT structure 領域をオーバーランしないかチェックするため、アクセスする長さを `len` に与える。 ### `fdt_next_tag` (fdt.c:93) uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) DTB `fdt` の DT structure の先頭から `startoffset` バイト目にあるタグを返す。 戻り値は `FDT_BEGIN_NODE`, `FDT_END_NODE`, `FDT_PROP`, `FDT_END`, `FDT_NOP`。 `FDT_END` は DT structure の最後に達したか、エラーを意味する。 次のタグの位置を `nextoffset` に詰める。エラー終了の場合は `nextoffset` にエラーコードを詰める。 ### `_fdt_check_node_offset` (fdt.c:143) int _fdt_check_node_offset(const void *fdt, int offset) DTB `fdt` の DT structure の先頭から `offset` バイト目がノードの先頭かをチェックする。 ノード先頭であれば、次のタグの `offset` 値を返し、それ以外は `-FDT_ERR_BADOFFSET` を返す。 ### `_fdt_check_prop_offset` (fdt.c:152) int _fdt_check_prop_offset(const void *fdt, int offset) DTB `fdt` の DT structure の先頭から `offset` バイト目がプロパティの先頭かをチェックする。 プロパティの先頭であれば、次のタグの `offset` 値を返し、それ以外は `-FDT_ERR_BADOFFSET` を返す。 ### `fdt_next_node` (fdt.c:120) int fdt_next_node(const void *fdt, int offset, int *depth) DTB `fdt` の DT structure の先頭から `offset` バイト目 (ノードを指していないといけない)から探索を開始し、次のノードを探す。 見つかれば、そのノードのオフセット位置を返す。 `depth` に `NULL` 以外が与えられていれば、階層を降りたり昇ったりするたびに、 `depth` を増減させる。 これは、繰り返し探索するときに、現在の階層の深さを記憶するのに使われる。 `depth` が負になるとそれ以上探索しない。 DT structure の終端に達した時や、見つからないとき、エラーの時は負のエラーコードを返す。 ### `fdt_first_subnode` (fdt.c:160) int fdt_first_subnode(const void *fdt, int offset) DTB `fdt` の DT structure の先頭から `offset` バイト目 (ノードを指していないといけない)から探索を開始し、最初のサブノードを探す。 見つかれば、そのノードのオフセットを位置を返す。見つからないときは、 `-FDT_ERR_NOTFOUND' を返す。 ### `fdt_next_subnode` (fdt.c:171) int fdt_next_subnode(const void *fdt, int offset) DTB `fdt` の DT structure の先頭から `offset` バイト目 (ノードを指していないといけない)から探索を開始し、次のサブノードを探す。 見つかれば、そのノードのオフセットを位置を返す。見つからないときは、 `-FDT_ERR_NOTFOUND' を返す。 ### `_fdt_find_string` (fdt.c:229) const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) 1つまたはそれ以上の文字列の連結 `strtab` (長さ `tabsize`) 中に、文字列 `s` が含まれているか検索する。 見つかると、そのポインタ位置を返す。見つからない場合は `NULL` を返す。 ### `_fdt_blocks_misordered` (fdt_rw.c:58) static int _fdt_blocks_misordered(const void *fdt, int mem_rsv_size, int struct_size) DTB `fdt` の構造を再構築する必要があるか判定する。 FDB の構造は常に Mermory Reserve Map, DT structure, DT strings の順に並んでいる必要がある。 `mem_rsv_size` には Memory Reserve Map のサイズ、`struct_size` には DT structure のサイズを渡す。 Mermory Reserve Map が DT structure 領域にオーバーラップしないか、 DT structuret が DT strings 領域にオーバーラップしないか、さらには DT strings の最後が DTB 全体のサイズを超えないかをチェックする。 再構築の必要があれば、`1`、それ以外は `0` を返す。 ### `_fdt_splice` (fdt_rw.c:97) _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen) DTB `fdt` のポインタ位置 `splicepoint` から始まる、長さ `oldlen` 分のデータを、長さ `newlen` に変更するために、隙間を拡張したり、詰めたりする。 成功すると `0`、失敗すると負のエラーコードを返す。 ### `_fdt_splice_struct` (fdt_rw.c:123) static int _fdt_splice_struct(void *fdt, void *p, int oldlen, int newlen) DTB `fdt` のポインタ位置 `splicepoint` から始まる、長さ `oldlen` 分のデータを、長さ `newlen` に変更するために、隙間を拡張したり、詰めたりする。 さらに、DT structuret のサイズと、DT strings へのオフセットのヘッダー情報を更新する。 成功すると `0`、失敗すると負のエラーコードを返す。 ### `_fdt_splice_string` (fdt_rw.c:137) static int _fdt_splice_string(void *fdt, int newlen) DTB `fdt` の DT strings の末尾に、長さ `newlen` 分のスペースを確保し、 DT strings サイズのヘッダー情報を更新する。 成功すると `0`、失敗すると負のエラーコードを返す。 ### `_fdt_find_add_string` (fdt_rw.c:150) static int _fdt_find_add_string(void *fdt, const char *s) DTB `fdt` の DT strings 領域を検索し、文字列 `s` が見つかれば、そこへのオフセットを返す。 見つからない場合は、DT strings 領域の末尾に追加し、オフセットを返す。 失敗すると負のエラーコードを返す。 ### `_fdt_resize_property` (fdt_rw.c:205) static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name, int len, struct fdt_property **prop) DTB `fdt` の DT structuret の先頭から `nodeoffset` 位置 (ノード先頭を指していないといけない) のノードからプロパティ名が `name` のものを探し、そのプロパティの長さを `len` にリサイズし、 `prop` にそのプロパティのポインタを返す。 成功すれば `0`、失敗すれば負のエラーコードを返す。 ### `_fdt_add_property` (fdt_rw.c:223) static int _fdt_add_property(void *fdt, int nodeoffset, const char *name, int len, struct fdt_property **prop) DTB `fdt` の DT structuret の先頭から `nodeoffset` 位置(ノード先頭を指していないといけない) のノードに新しいプロパティ (プロパティ名 `name`、プロパティ値の長さ `len`)を追加する。 同名のプロパティがすでに存在しているかはチェックしない。 追加したプロパティへのポインタを `prop` に詰めて返す。 ### `fdt_setprop` (fdt_rw.c:274) int fdt_setprop(void *fdt, int nodeoffset, const char *name, const void *val, int len) DTB `fdt` の DT structuret の先頭から `nodeoffset` 位置(ノード先頭を指していないといけない) のノードの `name` というプロパティの値を `val` (長さ `len`) に書き変える。 `name` という名前のプロパティがまだ存在していない場合は新しく追加する。 成功すれば `0` を返し、失敗すれば負のエラーコードを返す。 ### `fdt_add_subnode_namelen` (fdt_rw.c:334) int fdt_add_subnode_namelen(void *fdt, int parentoffset, const char *name, int namelen) DTB `fdt` の DT structuret の先頭から `parentoffset` 位置(ノード先頭を指していないといけない)のノードに 名前 `name` (長さ `namelen`) のサブノードを追加する。 サブノードは、親ノードのプロパティの直後に追加される。 すでに同名のサブノードが存在する場合は失敗する。 成功すると、追加されたサブノードへのオフセットを返す。失敗すると、負のエラーコードを返す。 ### `fdt_add_subnode` (fdt_rw.c:375) int fdt_add_subnode(void *fdt, int parentoffset, const char *name) DTB `fdt` の DT structuret の先頭から `parentoffset` 位置(ノード先頭を指していないといけない)のノードに 名前 `name` のサブノードを追加する。 サブノードは、親ノードのプロパティの直後に追加される。 すでに同名のサブノードが存在する場合は失敗する。 成功すると、追加されたサブノードへのオフセットを返す。失敗すると、負のエラーコードを返す。 ### `_fdt_packblocks` (fdt_rw.c:394) static void _fdt_packblocks(const char *old, char *new, int mem_rsv_size, int struct_size) アドレス `old` 上の DTB をアドレス `new` 上に再構築する。 新しい DTB の Memory Reserve Map サイズは `mem_rsv_size` に、 DT structuret サイズは `struct_size` となる。 ### `fdt_open_into` (fdt_rw.c:416) int fdt_open_into(const void *fdt, void *buf, int bufsize) DTB `fdt` をバッファ `buf` (サイズ `bufsize`) に読み出す。 通常は、そのままコピーされるだけだが、必要があるときには `_fdt_packblocks` 関数を呼び出して、リフォーマットする。 ### `fdt_pack` (fdt_rw.c:480) int fdt_pack(void *fdt) DTB `fdt` の余分な Memory Reserve Map 領域を詰めて、再パッキングする。
Device Tree アクセス関数まとめ (Linux Kernel)
Linux Kernel の Device Tree 関連の関数をまとめました。 関数名は `of_` で始まり (OF = Open Firmware)、 `drivers/of/` ディレクトリ以下で定義されています。 ### `of_n_addr_cells` (base.c:53) int of_n_addr_cells(struct device_node *np) ノード `np` の一つ上の親から上流に向かって探索し、 `#address-cells` プロパティが見つかるとその値を返す。 見つからない場合は、 `OF_ROOT_NODE_ADDR_CELLS_DEFAULT` を返す。 ### `of_n_size_cells` (base.c:69) int of_n_size_cells(struct device_node *np) ノード `np` の一つ上の親から上流に向かって探索し、 `#size-cells` プロパティが見つかるとその値を返す。 見つからない場合は、 `OF_ROOT_NODE_SIZE_CELLS_DEFAULT` を返す。 ### `of_node_get` (base.c:100) struct device_node *of_node_get(struct device_node *node) ノード `np` の参照カウンタを 1増やす。 ### `of_find_property` (base.c:328) struct property *of_find_property(const struct device_node *np, const char *name, int *lenp) ノード `np` から `name` という名前のプロパティを探し、そのプロパティ構造体へのポインタを返す。 見つからなければ、`NULL` を返す。 `lenp` が与えられていれば、プロパティ値の長さを詰めて返す。 ### `of_get_property` (base.c:383) const void *of_get_property(const struct device_node *np, const char *name, int *lenp) ノード `np` から `name` という名前のプロパティを探し、そのプロパティ値へのポインタを返す。 該当するプロパティが見つからなければ `NULL` を返す。 `lenp` が与えられていれば、プロパティ値の長さを詰めて返す。 ### `of_device_is_compatible` (base.c:566) static int __of_device_is_compatible(const struct device_node *device, const char *compat) ノード `np` がデバイス名 `compat` に対して compatible かどうかを調べる。 compatible なら 1、それ以外は 0 を返す。 ノード `np` の "compatible" という名前のプロパティの値に、文字列 `compat` が含まれるかどうかで判断される。 ### `of_get_parent` (base.c:656) struct device_node *of_get_parent(const struct device_node *node) ノード `node` の親のノードを返す。 ### `of_find_compatible_node` (base.c:870) struct device_node *of_find_compatible_node(struct device_node *from, const char *type, const char *compatible) 全ノード中の `from` 以降から `compatible` に対して compatible なノードを探索する。 見つかれば、そのノードへのポインタを返す。 見つからない場合は、`NULL` を返す。 なお、`from` に `NULL` を渡した場合は、全ノードを探索する。 `from` は通常、前回の探索の続きを実行するときに用いられる。 `type` に `NULL` 以外が与えられていれば、 device_type に対してもマッチングが行われる。 (`type` と `compatible` のAND条件。) `type` は Device Source Tree 中で `device_type` プロパティによって指定される。 ### `of_match_node` (base.c:954) const struct of_device_id *of_match_node(const struct of_device_id *matches, const struct device_node *node) マッチングテーブル(の配列の先頭) `match` を探索し、ノード `node` に対して compatible かどうかを調べる。 compatible ならば、マッチングテーブルのエントリーを返し、 compatible でないなら `NULL` を返す。 ### `of_find_matching_node_and_match` (base.c:981) struct device_node *of_find_matching_node_and_match(struct device_node *from, const struct of_device_id *matches, const struct of_device_id **match) 全ノード中の `from` 以降から、マッチングテーブル `matches` に対して、 compatible なノードを探索する。 見つかると、マッチした マッチングテーブルのエントリーを `match` に詰め、ノードを返す。 見つからない場合は、 `NULL` を返す。 なお、`from` に `NULL` を渡した場合は、全ノードを探索する。 `from` は通常、前回の探索の続きを実行するときに用いられる。 ### `of_find_node_by_phandle` (base.c:1041) struct device_node *of_find_node_by_phandle(phandle handle) 全ノードの中から、phandle 番号 `handle` を持つノードを返す。 phandle は 1から始まる整数である。 見つからない場合は `NULL` を返す。 ### `of_find_property_value_of_size` (base.c:1101) static void *of_find_property_value_of_size(const struct device_node *np, const char *propname, u32 len) ノード `np` から `propname` という名前のプロパティを探し、そのプロパティ値へのポインタを返す。 その際、プロパティ値の長さチェックが行われる。 プロパティ値の長さが、 `len` に満たない場合は `-EOVERFLOW` を返す。 ### `of_property_read_u32_array` (base.c:1228) int of_property_read_u32_array(const struct device_node *np, const char *propname, u32 *out_values, size_t sz) ノード `np` から `propname` という名前のプロパティを探し、プロパティ値を `sz * 4` byte 分、 `out_value` へコピーする。 プロパティ値の長さが `sz * 4` byte に満たない場合は、コピーは行われず、 `-EOVERFLOW` を返す。 ### `of_property_read_u32` (of.h:692) static inline int of_property_read_u32(const struct device_node *np, const char *propname, u32 *out_value) ノード `np` から `propname` という名前のプロパティを探し、プロパティ値の先頭 4 byte を `out_value` へコピーする。 エラーが起きると、非ゼロを返す。 ### `of_property_read_string_index` (base.c:1319) int of_property_read_string_index(struct device_node *np, const char *propname, int index, const char **output) ノード `np` の `propname` という名前のプロパティの値から、`index` 番目の文字列へのポインタを `output` に詰めて返す。 成功すると `0` を返す。 例えば、Device Tree Source で node1 { prop1 = "foo", "bar"; } という記述になっていたとする。 `of_property_read_string_index(node1, "prop1", 1, output)` は `output` に `"bar"` をセットする。 ### `of_property_match_string` (base.c:1356) int of_property_match_string(struct device_node *np, const char *propname, const char *string) ノード `np` から `propname` という名前のプロパティを探し、そのプロパティ値のリスト中から文字列 `string` を検索する。 見つかった場合、リスト中の index を返す。 例えば、Device Tree Source の記述が clock-name = "foo", "bar"; のようであったとする。 その場合、 `of_property_match_string(np, "clock_name", "foo")` は `0` を `of_property_match_string(np, "clock_name", "bar")` は `1` を返す。 `of_property_match_string(np, "clock_name", "baz")` は `-ENODATA` (見つからない)を返す。 ### `of_parse_phandle_with_args` (base.c:1602) int of_parse_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name, int index, struct of_phandle_args *out_args) ノード `np` から `list_name` という名前のプロパティを探し、そのプロパティ値 (phandle のリストになっている) の中から、`index` 番目の phandle の情報を、 `out_args` に詰めて返す。 エラーが起きると、非ゼロを返す。 `out_args->np` には指している phandle のノードを、`out_args->args_count` には引数の個数、`out_args->args[]` には引数を詰める。 例えば、 Device Tree Source の記述が以下のようになっていたとする。 クロック情報を受け渡しするときの典型的な書き方である。 clk1: node1 { #clock-cells = <2>; } clk2: node2 { #clock-cells = <1>; } node3 { clocks = <&clk1 5 6>, <&clk2 7> } この時、 `of_parse_phandle_with_args(node3, "clocks", "#clock-cells", 0, out_args);` は `out_args` に `clk1` の情報を詰めて返す。 out_args->np = node1; out_args->args_count = 2; out_args->args[0] = 5; out_args->args[1] = 6; といった具合。 `of_parse_phandle_with_args(node3, "clocks", "#clock-cells", 1, out_args);` は `out_args` に `clk2` の情報を詰めて返す。 ### `of_count_phandle_with_args` (base.c:1606) int of_count_phandle_with_args(const struct device_node *np, const char *list_name, const char *cells_name) ノード `np` の `list_name` という名前のプロパティに、 phandle とその引数の組みが 何組含まれているかを返す。 ### `of_bus_default_count_cells` (address.c:50) static void of_bus_default_count_cells(struct device_node *dev, int *addrc, int *sizec) ノード `dev` の `#address-cells` および `#size-cells` の値をそれぞれ `addrc` と `sizec` に詰めて返す。 ### `of_translate_address` (address.c:555) u64 of_translate_address(struct device_node *dev, const __be32 *in_addr) ノード `dev` の アドレス `addr` を CPU View のアドレス空間に変換する。 ノードを上流に向かって探索し、`ranges` プロパティに記載されている変換ルールに従って、アドレス変換を行う。 この変換はルートノードに達するまで再帰的に行われる。 親ノードは必ず `range` プロパティを持っていなくてはならないが、値は空でもよい。 `range` プロパティの値が空の場合は 1:1変換が行われる。 ### `of_get_address` (address.c:586) const __be32 *of_get_address(struct device_node *dev, int index, u64 *size, unsigned int *flags) ノード `dev` の `index` 番目のアドレスとサイズ (通常は `reg` というプロパティの格納されている。PCIバスなどは異なる)を返す。 `size` が与えられていれば サイズを詰める。 `flags` が与えられていれば、通常バスの場合は `IORESOURCE_MEM` を詰める。 アドレスへのポインタを返す。 ### `of_address_to_resource` (address.c:669) int of_address_to_resource(struct device_node *dev, int index, struct resource *r) ノード `dev` の `index` 番目のメモリリソース情報を `r` に詰めて返す。 具体的には、`reg` プロパティと親ノードの `range` プロパティによって CPU View に変換された物理アドレスが `r->start` と `r->end` にセットされる。 `reg-names` プロパティを持つ場合は、その値が `r->name` にセットされ、持たない場合は `dev->full_name` がセットされる。 ### `of_iomap` (address.c:714) void __iomem *of_iomap(struct device_node *np, int index) ノード `np` の `index` 番目の物理アドレスを取得し、 `ioremap` を行い、仮想アドレスを返す。
Device Tree 入門
Device Tree というのは、ハードウェアの詳細を記述したデータ構造体です。 元々は PowerPC Sybsystem から始まったようなのですが、すでに ARM Linux は DeviceTree 一色になってしまっています。 そのため Device Tree を知らないと、 SoC の移植はおろか、ドライバの開発もできない。 そこで、 Device Tree の初歩についてまとめてみることにします。 ただし、私自身が初心者ですので、難しいことは説明できませんし、間違っている部分もあるかもしれませんが、ご了承ください。 ARM のことしかわかりませんので、 ARM を対象として書くことにします。 ### 何故に DeviceTree か? ### より Generic な OS を記述するためです。 ハードウェアを差分を吸収するのがドライバの役目なのですが、勘違いしてはいけないのは、ドライバは「ハードウェアの制御の仕方」を記述するものであって、 デバイスのベースアドレスや、クロック、割り込み番号といった「ハードウェアのプロパティ」を記述するものではありません。 たとえば 8250 とレジスタ互換の UART はいろいろな SoC に搭載されていますが、ベースアドレスや割り込み番号は SoC ごとに違うでしょう。 1つの SoC に同じハードウェア IP が複数個載っていて、ベースアドレスだけ違う、ということもあります。 したがって、ドライバをより汎用的に記述するには、ハードウェアのプロパティは別の場所に記述しておいて、ドライバの初期化時に引き渡す必要があります。 以前は、ハードウェアのプロパティはボードファイル (ARM でいうと `arch/arm/mach-*/` 以下のファイル)に記述していて、 platform_driver を経由してドライバに渡すというのがよく行われていました。 現在では、新しいボードファイルの追加は推奨されず、代わりに Device Tree に記述します。 つまり、ハードウェアの細かい情報を Kernel 内にハードコーディングするのではなくて、 Device Tree という別のデータ構造に押し出すということです。 これによって Kernel がよい汎用的な記述になります。 さらに ARM Linux はより汎用的な方向に向かっています。マルチプラットフォームです。 (`CONFIG_ARCH_MULTIPLATFORM` を選択する) 例えば `arch/arm/configs/multi_v7_defconfig` でコンフィグレーションすると、さまざまな SoC で動く Kernel ができます。 再コンパイルすることなく、同一の Kernel イメージで OMAP や Tegra や Zynq が動くということです。 今後、新しい SoC に移植する場合には、専用のコンフィグレーションではなくて、マルチプラットフォームを使うべきです。 ### Device Tree の記述の仕方 ### Deviece Tree Source (以下 DTS) を記述し、Device Tree Compiler (以下 DTC) でコンパイルすると、 Device Tree Blob (以下 DTB) というバイナリーができます。 この DTB をブート時に Kernel に渡します。 DTS それ自体は非常にシンプルな言語で、以下のような感じで記述します。 /dts-v1/; / { node1 { a-string-property = "A string"; a-string-list-property = "first string", "second string"; a-byte-data-property = [01 23 34 56]; child-node1 { first-child-property; second-child-property = <1>; a-string-property = "Hello, world"; }; child-node2 { }; }; node2 { an-empty-property; a-cell-property = <1 2 3 4>; /* each number (cell) is a uint32 */ child-node1 { }; }; }; 階層的にノードがあって、各ノードにプロパティ(とその値)を記述します。 XML みたいな構造のものと思っていいでしょう。 コンパイルの仕方ですが、上記のようなソースを `foo.dts` に書いて $ dtc -O dtb -o foo.dtb foo.dts とすると、 `foo.dtb` (DTB) ができます。 また、以下のようにして DTB から DTS へ戻すこともできます。 $ fdtdump foo.dtb DTC の公式リポジトリは `git://git.kernel.org/pub/scm/utils/dtc/dtc.git` です。 (以前は `git://jdl.com/software/dtc.git` だったが、こちらはもう更新されていないので注意!) ただし、 Kernel のビルド時には `scripts/dtc` に入っているものが使われますので、なくてもできます。 DTS の書き方ですが、[Device Tree Usage](http://www.devicetree.org/Device_Tree_Usage) のサイトがわかりやすいと思いますので、そちらで基本的な書き方を勉強するとよいです。 ### Device Tree の渡し方 ### Device Tree 以前は、 ATAGS という構造体にいろんな情報を格納して、Kernel の起動時に渡していました。 今でも ATAGS はサポートされているものの、より細かい記述のできる Device Tree に置き換わっています。 Kernel のエントリーポイントにおける レジスタは以下のようになっている必要があります。 | ATAGS | Device Tree ---|---------------------------------|---------------------------- r0 | 0 | 0 r1 | Machine type number | don't care r2 | Physical address of ATAGS | Physical address of DTB ### DTB の構造 ### DTS の記述の仕方は上記のとおりなのですが、DTB がどんな構造になっているのかを見てみます。 DTB は大きく見ると、以下のような構造になっています。 |----------------------------| | device-tree header | |----------------------------| | memory reserve map | |----------------------------| | | | device-tree structure | | | |----------------------------| | | | device-tree strings | | | |----------------------------| 前述の `foo.dtb` をダンプしてみると以下のようになった。 00000000 d0 0d fe ed 00 00 01 df 00 00 00 38 00 00 01 54 |...........8...T| 00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............| 00000020 00 00 00 8b 00 00 01 1c 00 00 00 00 00 00 00 00 |................| 00000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................| 00000040 00 00 00 01 6e 6f 64 65 31 00 00 00 00 00 00 03 |....node1.......| 00000050 00 00 00 09 00 00 00 00 41 20 73 74 72 69 6e 67 |........A string| 00000060 00 00 00 00 00 00 00 03 00 00 00 1b 00 00 00 12 |................| 00000070 66 69 72 73 74 20 73 74 72 69 6e 67 00 73 65 63 |first string.sec| 00000080 6f 6e 64 20 73 74 72 69 6e 67 00 00 00 00 00 03 |ond string......| 00000090 00 00 00 04 00 00 00 29 01 23 34 56 00 00 00 01 |.......).#4V....| 000000a0 63 68 69 6c 64 2d 6e 6f 64 65 31 00 00 00 00 03 |child-node1.....| 000000b0 00 00 00 00 00 00 00 3e 00 00 00 03 00 00 00 04 |.......>........| 000000c0 00 00 00 53 00 00 00 01 00 00 00 03 00 00 00 0d |...S............| 000000d0 00 00 00 00 48 65 6c 6c 6f 2c 20 77 6f 72 6c 64 |....Hello, world| 000000e0 00 00 00 00 00 00 00 02 00 00 00 01 63 68 69 6c |............chil| 000000f0 64 2d 6e 6f 64 65 32 00 00 00 00 02 00 00 00 02 |d-node2.........| 00000100 00 00 00 01 6e 6f 64 65 32 00 00 00 00 00 00 03 |....node2.......| 00000110 00 00 00 00 00 00 00 69 00 00 00 03 00 00 00 10 |.......i........| 00000120 00 00 00 7b 00 00 00 01 00 00 00 02 00 00 00 03 |...{............| 00000130 00 00 00 04 00 00 00 01 63 68 69 6c 64 2d 6e 6f |........child-no| 00000140 64 65 31 00 00 00 00 02 00 00 00 02 00 00 00 02 |de1.............| 00000150 00 00 00 09 61 2d 73 74 72 69 6e 67 2d 70 72 6f |....a-string-pro| 00000160 70 65 72 74 79 00 61 2d 73 74 72 69 6e 67 2d 6c |perty.a-string-l| 00000170 69 73 74 2d 70 72 6f 70 65 72 74 79 00 61 2d 62 |ist-property.a-b| 00000180 79 74 65 2d 64 61 74 61 2d 70 72 6f 70 65 72 74 |yte-data-propert| 00000190 79 00 66 69 72 73 74 2d 63 68 69 6c 64 2d 70 72 |y.first-child-pr| 000001a0 6f 70 65 72 74 79 00 73 65 63 6f 6e 64 2d 63 68 |operty.second-ch| 000001b0 69 6c 64 2d 70 72 6f 70 65 72 74 79 00 61 6e 2d |ild-property.an-| 000001c0 65 6d 70 74 79 2d 70 72 6f 70 65 72 74 79 00 61 |empty-property.a| 000001d0 2d 63 65 6c 6c 2d 70 72 6f 70 65 72 74 79 00 |-cell-property.| 000001df 以下では、各領域ごとに見ていこうと思う。 #### Header #### 先頭から 40 byte (0x28 byte) 分が Header 領域である。 各ワードの意味は以下の通りです。 +0 +4 +8 +C 00000000 magic totalsize off_dt_struct off_dt_string 00000010 off_mem_rsvmap version last_comp_version boot_cpuid_phys 00000020 size_dt_strings size_dt_struct - `magic`: 常に `FDT_MAGIC` (= `0xd00dfeed`) - `totalsize`: この DTB のサイズ - `off_dt_struct`: DT structure の開始位置 - `off_dt_strings`: DT strings の開始位置 - `offset_to`: Memory Reserve Map の開始位置 - `version`: Version。現在は 17 - `last_comp_version`: 互換性のある最後のバージョン - `boot_cpuid_phys`: ブートさせるCPUのID (使い方よくわかりませんが、たぶん気にしなくてOK) - `size_dt_strings`: DT strings のサイズ - `size_dt_struct`: DT structure のサイズ #### Memory Reserve Map #### Kernel が書き潰してはいけない、メモリの予約領域を表した配列。 struct fdt_reserve_entry { fdt64_t address; fdt64_t size; }; のように 64 bit 長の物理アドレスとサイズのペアで記述され、エントリーはいくつでも持つことができる。 `size` が 0 のエントリーが終了を意味する。 例えば、 DTB 自身や InitRamdisk が置かれているメモリ領域は Kernel に書き潰されないように、Memory Reserve Map に記述する必要がある。 上記の `foo.dtb` の例では 0x28 ~ 0x37 がこの領域に相当するが、単に 0 埋めされているだけである。(つまり予約領域なし) 実際のところ、 Memory Reserve Map は、DTC よりもブートローダーによって記録されることが多い。 コンパイル段階では DTB や InitRamdisk をメモリ上のどこに置くか決まっていないため。 通常はブートローダーが DTB や InitRamdisk を適切な物理メモリ上に置いたあと、 Memory Reserve Map を更新する。 #### DT structure #### ノードやプロパティの構造をバイナリ化した領域。 (ただしプロパティの名前だけは DT structure ではなくて、 DT strings に記録される) データ構造をパースできるように、ノードの開始、終了、プロパティなどタグが定義されている。 #define FDT_BEGIN_NODE 0x1 /* Start node: full name */ #define FDT_END_NODE 0x2 /* End node */ #define FDT_PROP 0x3 /* Property: name off, size, content */ #define FDT_NOP 0x4 /* nop */ #define FDT_END 0x9 上記の `foo.dtb` の例では、 `0x00000038` 移行が DT structure に相当します。 各アドレスのデータが何を意味しているのか、データ構造をインデントを入れてわかりやすく表現すると以下のようになる。 00000038 FDT_BEGIN_NODE 0000003c node_name = NULL (/) 00000040 FDT_BEGIN_NODE 00000044 node_name = "node1" 0000004c FDT_PROP 00000050 data_length = 9 00000054 nameoff = 0 00000058 data = "A string" 00000064 FDT_PROP 00000068 data_length = 0x1b 0000006c nameoff = 0x12 00000070 data = "first string", "second string" 0000008c FDT_PROP 00000090 data_length = 4 00000094 nameoff = 0x29 00000098 data = 0x01, 0x23, 0x23, 0x56 0000009c FDT_BEGIN_NODE 000000a0 node_name = "child-node1" 000000ac FDT_PROP 000000b0 data_length = 0 000000b4 nameoff = 3e 000000b8 FDT_PROP 000000bc data_length = 4 000000c0 nameoff = 0x53 000000c4 data = 0x00000001 000000c8 FDT_PROP 000000cc data_length = 0xd 000000d0 nameoff = 0 000000d4 data = "Hello,world" 000000e4 FDT_END_NODE 000000e8 FDT_BEGIN_NODE 000000ec node_name = "child-node2" 000000f8 FDT_END_NODE 000000fc FDT_END_NODE 00000100 FDT_BEGIN_NODE 00000104 node_name = "node2" ...... このように、DTS の構造をそっくりそのままバイナリに変換したものが DTB となっています。 ノードやプロパティの出現順もそのままで、非常に簡単な変換アルゴリズムになっていることがわかると思います。 その他、いくつかの特徴をまとめておきます。 - データの切れ目は常に 4バイト境界 データが 4バイトに満たない場合はパディングされる。 - ビッグエンディアン - 先頭からなめないと、構文解析できない 例えば、途中から読み始めて `0x00000001` というデータを見つけても、ノード先頭(`FDT_BEGIN_NODE`)を表しているのか、プロパティの値なのか判別できないため。 - データの頻繁な更新に向かない 隙間なくデータが配置されているため、途中に新しいノードやプロパティを挿入したり、より長いプロパティ値に書きかえるのが苦手。 DTB を更新する場合には、後続のデータを必要サイズ分後ろにずらしてから、該当箇所を書きかえる必要があるため、メモリコピーが発生する。 - データの型情報は記録されない `"Hello,world"` のような文字列や `<1>` のような 32 bit 値などはあくまで DTS 上での表現である。 DTB に変換されるときに、いずれも単なるバイトストリームとして記録されるので、 DTB だけからはそれが文字列を表しているのか、数字なのかはわからない。 どう解釈するかは、 Kernel 側が知っている。 プロパティ名が `compatible` なら値は文字列だろうし、 `reg` なら値はアドレス値だろうから、そもそも型を記録しなくても困らない。 #### DT strings #### プロパティ名の文字列を格納している領域。 どうして、ノード名は DT structure の方に埋め込まれているのに、 プロパティ名は DT strings に格納し、 DT structure にはプロパティ名へのポインタを格納しているのでしょうか? おそらく、プロパティ名には、`compatible`, `reg`, `interrupts` のように同じ名前のものが繰り返し出現するので、 同じ文字列は 1回だけ DT strings に記録し、それへのポインタを持つ方がサイズが小さくなるからでしょう。 ### phandle ### Device Tree Source は階層的な構造を取りますが、何に基づいて階層化するかというと、基本的に CPU から見たアドレス空間です。 もうちょっと具体的にいうと、バスの構造に基づいて階層化して記述するのが普通です。 ですので、バス構造に基づいた親子関係(例えば 「I2Cコントローラー」と「I2Cバスにぶら下がっている EEPROM」)は Device Tree の構造で表すことができます。 ところが、バスの構造に現れない親子関係はうまく表現できません。例えば、 - 割り込みコントローラ(親)とその割り込みを使うデバイス(子) - クロックを供給するブロック(親)とそのクロックを使うデバイス(子) といった関係です。 例として以下のようなソースを見てみます。 /dts-v1/; / { amba { Label0: interrupt-controller { }; Label1: pll { }; FooDevice { clocks = <&Label1 100 200>; interrupt-parent = <&Label0 10>; }; }; external_bus { BarDevice { interrupt-parent = <&Label0 5>; }; }; }; `amba` と `external_bus` はバスを、 `interrupt-controller`, `pll`, `FooDevice`, `BarDevice` はデバイスを表していると思って下さい。 ノードの階層構造から、 `interrupt-controller`, `pll`, `FooDevice` は AMBAバスに、 `BarDevice` は外部バスにぶら下がっている といったことは読み取れます。 割り込みの関係、例えば `FooDevice` と `BarDevice` は `interrupt-controller` という割り込みコントローラにつながっている、 といったことは phandle という仕組みを使って記述します。 割り込みコントローラーが複数あることもありうるので、ちゃんと指定してあげないと、どれに繋がっているかわからないのです。 まず、親となる `interrupt-controller` になんでもいいのでラベルをつける。 ここでは `Label0: `としたが、実際はもうちょっとわかりやすいラベルをつけます。 子である `FooDevice` と `BarDevice` からは `<&Label0>` といった感じで親を指定する。 これを phandle といいます。通常は、いくつかの引数も合わせて指定します。 例えば、割り込みの場合は、つながっている割り込みコントローラの他に、割り込み番号が何番か、 といった情報も必要なので、 `10` とか `5` とかはそういったパラメータのつもりです。 同様にクロックの関係も、 親である `pll` ノードに `Label1: `というラベルをつけ、クロックを使う `FooDevice` には `<&Label1>` で指定する。 こういう風に記述すると、いったい何が起きるのかを見るために、いったんコンパイルして、それをダンプします。 $ dtc -O dtb -o foo.dtb foo.dts && fdtdump foo.dtb 以下のように表示された。 /dts-v1/; // magic: 0xd00dfeed // totalsize: 0x16a (362) // off_dt_struct: 0x38 // off_dt_strings: 0x144 // off_mem_rsvmap: 0x28 // version: 17 // last_comp_version: 16 // boot_cpuid_phys: 0x0 // size_dt_strings: 0x26 // size_dt_struct: 0x10c / { amba { interrupt-controller { linux,phandle = <0x00000002>; phandle = <0x00000002>; }; pll { linux,phandle = <0x00000001>; phandle = <0x00000001>; }; FooDevice { clocks = <0x00000001 0x00000008 0x00000002>; interrupt-parent = <0x00000002 0x00000001>; }; }; external_bus { BarDevice { interrupt-parent = <0x00000002 0x00000002>; }; }; }; `interrupt-controller` と `pll` ノードに、勝手にプロパティが追加されているのに気づいたでしょうか? これが phandle の正体です。phandle というのは単なる整数なのです。 Device Tree Compiler は `<&Label>` といった記述を見つけると、指し示された親に `linux,phandle"` と `plandle` というプロパティを追加し、1 から始まる整数を割り当てます。 そして、子の `<&Label>` の記述をその整数に置き換えます。 `linux,phandle` または `phandle` の値を元に検索すれば、所望の親を見つけられるというわけです。 phandle は `1` から割り振るというのは味噌です。 `< >` という記号は 32bit幅の unsigned integer を表すのに使います。 phandle を探す関数は、 phandle を見つけられない場合は `0` を返すようになっています。 割り込みコントローラを指すのに `interrupt-parent` というプロパティ名を使いますよとか、クロックを指すのには `clocks` というプロパティ名を使いますよ、とかいうのはもう一段上のレベルの取り決めです。 それぞれのプロパティ名をどう解釈するかはデバイスドライバの実装の話ですが、いちいちソースコードを読むのも大変なので、 Linux の `Documentation/devicetree/bindings` の下のドキュメントを読むとたいてい書いてあります。 なんで `linux,plandle` と `plandle` と2つも追加するのかは、詳しくは知らないのですが、後方互換性とかの問題だと思います。 一応 Device Tree というのは Open Firmware という規格で決まっているらしく、Linux 独自のものではないです。 `linux,phandle` みたいに `linux,` で始まるのは Linux の拡張機能で、最初は `linux,plandle` を使っていたけど、 途中でそれが Open Firmware 本家に取り込まれて `plandle` を使うようになったという事情だと想像します。 (間違っていたらすいません。。)
2014年5月27日火曜日
LinuxCon Japan 2014
5月20-22日に LinuxCon Japan に行ってきました。 [](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnDSE3YSGlVd92Y1Jy3qIN4-f8H-rtNjf3zHLLehaJKp60Cf97irXzB1PCU7FJD3lcAtIIYpU7ldZVSPybHiK62jZ28NxpdtMqLHNErrkmD7Q7CX9Mq56SMBQnkSCHLBu4iyU3if9LtWg/s1600/lcj2014_board.jpg)
[](http://2.bp.blogspot.com/-PS8zFY4Lm6c/U4KCVMzxQxI/AAAAAAAAKlM/QnoL2icWPzE/s1600/lcj2014.jpg) 2日目最後のステージに Linus が登場。 会場が一斉に写真を取り出すも、「カメラは後にしてくれ」と言っているところ。
[](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1e2YaUhEUWCpTcfAvfGqK130rwo0CpFOMMYFm_yMBt1609wybYQ0xNAw1wOYzSwkcoXnUbA2tGDHEUGwwEXTYi5ycJCkwPi_i9YvrCapa35t1OWmpSAXGyqZ0dRbBrriIQmrenukhyEU/s1600/lcj2014_tshirt.jpg) クールなメッセージ入りの Tシャツ。 今回は日本人向け Mサイズがもらえたのでよかった。 [](http://3.bp.blogspot.com/-MTwKQ9qrExM/U4KCreXG2DI/AAAAAAAAKlw/DocyhdH2hHU/s1600/uboot_summit.jpg) U-Boot の開発者と再会。 屋形船での飲み会。
2014年5月14日水曜日
sed で遊ぶ
それなりに実用的だと思われる例を書き留めておきます。 ほとんど、 Linux Kernel のコーディングスタイル絡みですが。。 #### 2 行以上連続する空行を 1 行の空行にまとめる #### /^$/ { N /^\n$/D } `N` は次の行をパターンスペースに読み込む。 `D` はパターンスペース中に最初に現れる `\n` までを削除し、スクリプトの先頭に戻る。 #### 8 スペースによるインデントを タブに置き換える #### 視認性のために、以下のコードではスペースを `SPC`, タブを `TAB` と書いている。 :foo s/^\(TAB*\)SPC\{8\}/\1TAB/ t foo `:foo` はラベル。 `t foo` は直前の置換が成功したら、ラベル `foo` に分岐するという意味です。 例えば、行頭にスペースが 16 個あったら、タブ 2個に置き換えて欲しいので、ループを使ってみた。 #### 行末の空白文字を削除する #### これは簡単。 s/[[:space:]]*$// `[[:space:]]` はスペースとタブにマッチします。 #### ファイル末の空行を削除する #### :top /^\n*$/ { $d N b top } ファイル末に 2 行以上連続する空行がある場合には、すべて削除したいので、 上記のようなスクリプトになりました。 ${ /^$/d } というスクリプトだと、最終行の空行のみ削除します。 これでも、最初の例の「複数行の空行を1行にまとめる」と組み合わせると、十分目的は達成できます。 #### ファイル先頭の空行を削除する #### 0,/^..*$/ { /^$/d } ファイル先頭に 2 行以上連続する空業がある場合にも、すべて削除されます。 `0,/^..*$/` は範囲指定で、ファイル先頭から、空行でない行が出現するまでマッチします。
2014年5月13日火曜日
U-Boot と Linux Kernel のメインラインで Zynq を動かす 2014年4月版
[以前](/2014/01/u-boot-linux-kernel-zynq.html)、2014年1月時点での U-Boot と Linux のメインラインで Zynq を動かす方法を紹介しました。 その後、コード(特に U-Boot)が大きく変わっているので、 2014年4月時点での動かし方を紹介することにします。 使用するのは、 - U-Boot 2014.04 - Linux Kernel 3.14 - Zynq ZC706 ボード です。 その前に、 Zynq のブートシーケンスを復習しておきましょう。 Active Boot (= JTAG ブート以外)は 1. Boot ROM 2. FSBL 3. U-Boot 4. Linux Kernel というのが、Xilinx が公式にサポートしているブートシーケンスで、 [Xilinx Wiki ページ](http://www.wiki.xilinx.com/)もこのやり方を紹介しています。 最近の U-Boot では 1. Boot ROM 2. U-Boot SPL 3. U-Boot 4. Linux Kernel というブートシーケンスも可能になっています。 SPL というのは Secondary Progmam Loader の略です。 DRAM 等のメモリを初期化し、U-Boot 本体を NAND, MMC 等のデバイスからロードするための、 より小さなブートローダーといったものです。 U-Boot の標準のインフラとして用意されています。 詳しく知りたい人は U-Boot の `doc/README.SPL` を読んでください。 これを選択するメリットは、 FSBL (First Stage Boot Loader) を生成するために、 XSDK (Xilinx SDK) を起動しなくても済む、ということです。 さらに簡略化した 1. Boot ROM 2. U-Boot SPL 3. Linux Kernel というブートシーケンスもあります。 (Falcon ブートといいます) これのメリットは U-Boot 本体をスキップすることで、より高速に Linux を起動できることです。 ただし、現時点ではサポートが限定的でまともに動かすのは難しいので、今回は割愛します。 おそらく、そう遠くないうちにまともに動かせるようになると思いますが。 以下では、 U-Boot 2014.04 から可能になった SPL を用いたブートのやり方を紹介します。 ### STEP1: U-Boot のビルド ##### Input Files Required - ARM Cross Compiler - `ps7_init.c`: ISE / Vivado で "Export Hardware for SDK" を実行すると出力される - `ps7_init.h`: ISE / Vivado で "Export Hardware for SDK" を実行すると出力される ##### Output Files Produced - `u-boot.bin`: U-Boot 本体の RAWバイナリ - `u-boot.img`: `u-boot.bin` に uImage ヘッダーをつけたもの - `spl/u-boot-spl.bin`: U-Boot SPLの RAWバイナリ - `tools/mkimage`: U-Boot で扱うイメージを生成するツール。 ##### Task Description $ git clone git://git.denx.de/u-boot.git $ cd u-boot $ git checkout v2014.04 でソース取得して、v2014.04 タグをチェックアウト。 (何かあっても自分で対処できる人は masterブランチでやってもOK) まず、少々ソースコードをいじらなくてはなりません。 `include/configs/zynq-common.h` を開き、以下のように `CONFIG_OF_CONTROL` ~ `CONFIG_RSA` までを無効にする。 --- a/include/configs/zynq-common.h +++ b/include/configs/zynq-common.h @@ -199,6 +199,7 @@ #define CONFIG_FIT #define CONFIG_FIT_VERBOSE 1 /* enable fit_format_{error,warning}() */ +#if 0 /* FDT support */ #define CONFIG_OF_CONTROL #define CONFIG_OF_SEPARATE @@ -207,6 +208,7 @@ /* RSA support */ #define CONFIG_FIT_SIGNATURE #define CONFIG_RSA +#endif /* Extend size of kernel image for uncompression */ #define CONFIG_SYS_BOOTM_LEN (20 * 1024 * 1024) なんで、上記を無効にするかというと、前回の U-Boot 2014.01 では U-Boot を DeviceTree 付きで動かしたのですが、コードのマージが中途半端に行われたために、 2014.04 で再び `CONFIG_OF_CONTROL` が動かなくなってしまったためです。 頑張って動かすことはできるのですが、 Linux Kernel から DeviceTree の記述をいろいろと持ってこなくてはいけなかったり、 SPL から u-boot.img + DeviceTree をロードするのにコード修正したりと、 いろいろと修正箇所が多いので、無効にしてしまった方が楽です。 また、あとで、Kernel を TFTP でダウンロードしたいので、`include/configs/zynq-common.h` の適当なところに #define CONFIG_IPADDR 192.168.11.2 #define CONFIG_SERVERIP 192.168.11.1 #define CONFIG_ETHADDR 00:0a:35:00:01:22 の3行を足す。 `CONFIG_IPADDR` は Zynqボードに割り振る IPアドレス、 `CONFIG_SERVERIP` は TFTP サーバーのアドレスに合わせて下さい。 MACアドレスは、(他のネットワーク機器と被らなければ)適当でいい。 TFTP サーバーがなくても、動かすことはできるので、ない人は上記はスキップして下さい。 さらに、 `ps7_init.c`, `ps7_init.h` を U-Boot の `board/xilinx/zynq` ディレクトリにコピーする。 このファイルが、 FSBL の代わりをするための、肝になるファイルです。 また touch board/xilinx/xil_io.h で、空の `xil_io.h` を作る。 (`ps7_init.c` が `xil_io.h` をインクルードしているので、これがないとエラーになる。) あとは $ make zynq_zc70x_config $ make CROSS_COMPILE=arm-linux-gnueabi- のようにして、コンフィグレーションとビルドをする。 もしくは $ make zynq_zc70x_config all CROSS_COMPILE=arm-linux-gnueabi- のように 1行で、コンフィグレーションとビルドを同時にすることもできる。 `ps7_init.c` と `ps7_init.h` が warning を出しますが、気にしなくてもOK。 気になる人は、関数のプロトタイプの引数部に `void` を足してください。 ### STEP2: Linux Kernel のビルド ##### Input Files Required - ARM Cross Compiler ##### Output Files Produced - `arch/arm/boot/zImage`: Kernel Image - `arch/arm/boot/dts/zynq-zc706.dtb`: Kernel をコンフィグレーションする DTB (Device Tree Blob) (従来、 U-Boot から Kernel を起動するときは `arch/arm/boot/uImage` を使っていたが、これは使わない。) ##### Task Description $ git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git $ cd linux $ git checkout v3.14 でソース取得して、v3.14 タグをチェックアウト。 (何かあっても自分で対処できる人は masterブランチでやってもOK) 以下のようにして ARMv7 Multi な設定にする。 $ make ARCH=arm multi_v7_defconfig ここで $ make ARCH=arm menuconfig をして、少々設定をいじる。 Device Drivers ---> Block devices ---> [*] RAM block device support (16384) Default RAM disk size (kbytes) のようにたどり、 `RAM block device support` にチェックを入れ、 `Default RAM disk size` を `16384` に設定する。 もう一つ Device Drivers ---> Character devices ---> [ ] Legacy (BSD) PTY support のようにたどり、`Legacy (BSD) PTY support` のチェックを外す。 あとは $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- でビルド。 ### STEP3: Ramdisk のダウンロード ##### Input Files Required - None ##### Output Files Produced - `arm_ramdisk.image.gz`: Kernel がマウントする init ramdisk (を gzip 圧縮したもの) ##### Task Description [こちら](http://www.wiki.xilinx.com/Build+and+Modify+a+Rootfs) から arm_ramdisk.image.gz をダウンロードする。 ### STEP4: ITB (Image Tree Blob) の生成 ##### Input Files Required - `linux/arch/arm/boot/zImage` - `arm_ramdisk.image.gz` - `linux/arch/arm/boot/dts/zynq-zc706.dtb` - `u-boot/tools/mkimage` カレントディレクトリから見て、上記の配置になっているとする。 ##### Output Files Produced - `fit.itb`: U-Boot から Kernel を起動するためのイメージ ##### Task Description Kernel Image, Ramdisk, DTB (Device Tree Blob) を一つにまとめた ITB というのを作ります。 ITB を作るには、 ITS(Image Tree Source) を記述して、 `mkimage` に食わせます。 以下の内容を `fit.its` というファイルに記述する。 /dts-v1/; / { description = "Kernel, ramdisk and FDT blob"; #address-cells = <1>; images { kernel@1 { description = "Linux Kernel 3.14 configured with multi_v7_defconfig"; data = /incbin/("linux/arch/arm/boot/zImage"); type = "kernel"; arch = "arm"; os = "linux"; compression = "none"; load = <0x00008000>; entry = <0x00008000>; hash@1 { algo = "md5"; }; }; ramdisk@1 { description = "Ramdisk for Zynq"; data = /incbin/("arm_ramdisk.image.gz"); type = "ramdisk"; arch = "arm"; os = "linux"; compression = "gzip"; load = <0x00000000>; entry = <0x00000000>; hash@1 { algo = "sha1"; }; }; fdt@1 { description = "FDT for ZC706"; data = /incbin/("linux/arch/arm/boot/dts/zynq-zc706.dtb"); type = "flat_dt"; arch = "arm"; compression = "none"; hash@1 { algo = "crc32"; }; }; }; configurations { default = "config@1"; config@1 { description = "Zynq ZC706 Configuration"; kernel = "kernel@1"; ramdisk = "ramdisk@1"; fdt = "fdt@1"; }; }; }; あとは以下のようにすれば、`fit.itb` ができる。 $ u-boot/tools/mkimage -f fit.its fit.itb ### STEP5: JTAG (Slave Boot) から U-Boot と Linux を起動する ##### Input Files Required - `u-boot.bin`: STEP1 で作成したもの - `fit.itb`: STEP4 で作成したもの - `xmd`: ISE / Vivado のインストールディレクトリに入っている - `ps7_init.tcl`: ISE / Vivado から "Export Hardware for SDK" を実行すると出力される - `stub.tcl`: Xilinx のページからダウンロードできる `ug873-design-files.zip` の中に入っている - `fpga.bit`: ISE / Vivado で生成した FPGA bit file (Optional) ##### Task Description `fit.itb` を TFTP の公開ディレクトリに置く。(TFTP 環境のない人はスキップして下さい) Zynq ボードのブートモードの選択スイッチを JTAG に合わせて電源入れる。JTAG でボードと接続し、XMD を開く。 $ xmd XMD のプロンプトから以下を実行する。(FPGA は必要なければダウンロードしなくても良い) XMD% connect arm hw ;# Open JTAG connection XMD% rst -slcr ;# Reset the whole system XMD% fpga -f fpga.bit ;# Download FPGA bit file (Optional) XMD% source ps7_init.tcl XMD% ps7_init ;# Initialize DDR, IO pins, etc. XMD% ps7_post_config ;# Enable level shifter XMD% source stub.tcl ;# start CPU1 XMD% targets 64 ;# connect to CPU0 XMD% dow -data u-boot.bin 0x04000000 ;# Download u-boot to address 0x04000000 XMD% con 0x04000000 ;# start CPU0 from address 0x04000000 なお、毎回これを打ち込むのも面倒ですので、 `foo.tcl` に書いておきましょう。 XMD% source foo.tcl で XMD から読み込むか、シェルから $ xmd -tcl foo.tcl とすればよいです。 U-Boot のプロンプトが出た後、放っておくと、自動的に TFTPサーバーから `fit.itb` をダウンロードして、 Linux が起動する。 TFTP サーバーがない場合は、`con 0x04000000` の前に XMD% dow -data fit.itb 0x02000000 とすれば、 JTAG 経由で `fit.itb` をダウンロードできるので(時間かかりますが、、) あとは U-Boot のプロンプトから > bootm 2000000 と入力して Linux を起動させる。 ### STEP6: SDカード用のブートイメージを作成する ##### Input Files Required - `u-boot/spl/u-boot-spl.bin`: STEP1 で作成したもの - `bootgen`: ISE / Vivado のインストールディレクトリに入っている ##### Output Files Produced - `boot.bin` ##### Task Description `foo.bif` というファイル(名前適当でよい)に以下のように記述する。 image: { [bootloader,load=0x00000000,startup=0x00000000]u-boot/spl/u-boot-spl.bin } そして $ bootgen -image foo.bif -w on -o boot.bin とすると、`boot.bin` ができる。SDカードのブートイメージは必ず `boot.bin` というファイル名でないといけないので注意する。 ### STEP6B: SDカード用のブートイメージを作成する (もうちょっと簡単なやり方) ##### Input Files Required - `u-boot/spl/u-boot-spl.bin`: STEP1 で作成したもの ##### Output Files Produced - `boot.bin` ##### Task Description `bootgen` を使わずに `boot.bin` を作成する方法を紹介します。 u-boot-xlnx のコードを取ってきます。 git clone git://github.com/Xilinx/u-boot-xlnx.git `tools` ディレクトリの下に `zynq-boot-bin.py` という Python スクリプトが 入っているので、これを `~/bin` かどこか適当な PATH にコピーする。 あとは zynq-boot-bin.py -o boot.bin -u u-boot/spl/u-boot-spl.bin とすれば、 `boot.bin` ができます。 BIF ファイルを記述しなくてもいい分、こちらの方が簡単だと思います。 なお、 u-boot-xlnx だと、 `zynq-boot-bin.py` が Makefile からフックされていて、 make すると自動で `boot.bin` までできるので、更に楽なのですが、 (しかもローカルでいろいろとソースをいじくらなくても動く) ここのページではあくまで、メインラインでやることを目指しています。 ### Step7: SDカードから U-Boot と Linux Kernel を起動する ##### Input Files Required - `boot.bin`: STEP6 または STEP6B で作成したもの - `u-boot.img`: STEP1 で作成したもの - `fit.itb`: STEP4 で作成したもの ##### Task Description FAT でフォーマットしたSDカードに `boot.bin`, `u-boot.img`, `fit.itb` をコピー。 SDカードを Zynq ボードに挿し、ブートモードの選択スイッチを SD カードに合わせて電源入れる。 ### STEP8: FSBL を使ったブートシーケンス 上記の通り、 SPL を使ったブートシーケンスを紹介しましたが、 従来通りの FSBL を使ったブートシーケンスも可能です。 その場合は U-Boot に `ps7_init.c` と `ps7_init.h` をコピーしたり、 `xil_io.h` を作ったりする必要はないです。 STEP6 の部分を以下のようにアレンジすればよいです。 ##### Input Files Required - `fsbl.elf`: FSBL (First Stage Boot Loader)。XSDK で生成。 - `fpga.bit`: FPGA bit file (Optional) - `u-boot/u-boot.bin`: STEP1 で作成したもの - `bootgen`: ISE / Vivado のインストールディレクトリに入っている ##### Output Files Produced - `boot.bin` ##### Task Description `foo.bif` というファイル(名前適当でよい)に以下のように記述する。 image: { [bootloader]fsbl.elf fpga.bit [load=0x04000000,startup=0x04000000]u-boot/u-boot.bin } FPGA Bit file のダウンロードが不要なら `fpga.bit` の行は削除してよい。 あとは $ bootgen -image foo.bif -w on -o boot.bin とすると、`boot.bin` ができるので、 FAT でフォーマットしたSDカードに `boot.bin` と `fit.itb` をコピー。 ### 今後の開発の行方 Zynq は U-Boot の中でも、特に頻繁に更新されている SoC で、今後もやり方が変わっていく可能性が高いです。 今回、ローカルでソースをいじくっている部分のいくつかは、修正パッチを投稿しておいたので、 次のリリースではもうちょっと楽になるはず。 U-Boot 2014.04 ではまともに動いてませんが、 DeviceTree を使った U-Boot のコンフィグレーションに移行していくでしょう。 最終的には、 `ps7_init.c` と `ps7_init.h` をコピーすることもなくなり、 すべての情報を DeviceTree から読み込むようになるだろう。 (と、コードを書いている Xilinx のエンジニアは語っておりました。)
新しい投稿
前の投稿
ホーム
登録:
投稿 (Atom)