とあるエンジニアの備忘log
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 に行ってきました。 [![Welcome Board](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnDSE3YSGlVd92Y1Jy3qIN4-f8H-rtNjf3zHLLehaJKp60Cf97irXzB1PCU7FJD3lcAtIIYpU7ldZVSPybHiK62jZ28NxpdtMqLHNErrkmD7Q7CX9Mq56SMBQnkSCHLBu4iyU3if9LtWg/s320/lcj2014_board.jpg)](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnDSE3YSGlVd92Y1Jy3qIN4-f8H-rtNjf3zHLLehaJKp60Cf97irXzB1PCU7FJD3lcAtIIYpU7ldZVSPybHiK62jZ28NxpdtMqLHNErrkmD7Q7CX9Mq56SMBQnkSCHLBu4iyU3if9LtWg/s1600/lcj2014_board.jpg)
[![Linus](http://2.bp.blogspot.com/-PS8zFY4Lm6c/U4KCVMzxQxI/AAAAAAAAKlM/QnoL2icWPzE/s320/lcj2014.jpg)](http://2.bp.blogspot.com/-PS8zFY4Lm6c/U4KCVMzxQxI/AAAAAAAAKlM/QnoL2icWPzE/s1600/lcj2014.jpg) 2日目最後のステージに Linus が登場。 会場が一斉に写真を取り出すも、「カメラは後にしてくれ」と言っているところ。
[![T-shirt](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgromlCrySVZIibIKkhvzsN2mIPOE-WahzuVSN5X2_4s19mGWoJp9sgPJwBrLZgU2d0YmwtRFM56NxOTyKdrLT-_-qTnibuixlJXNm-HygWnB2jtI53bR-kohZS7IB-2XOoxFrey_XoOsg/s320/lcj2014_tshirt.jpg)](https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1e2YaUhEUWCpTcfAvfGqK130rwo0CpFOMMYFm_yMBt1609wybYQ0xNAw1wOYzSwkcoXnUbA2tGDHEUGwwEXTYi5ycJCkwPi_i9YvrCapa35t1OWmpSAXGyqZ0dRbBrriIQmrenukhyEU/s1600/lcj2014_tshirt.jpg) クールなメッセージ入りの Tシャツ。 今回は日本人向け Mサイズがもらえたのでよかった。 [![U-Boot mini-mini-beer summit](http://3.bp.blogspot.com/-MTwKQ9qrExM/U4KCreXG2DI/AAAAAAAAKlw/DocyhdH2hHU/s320/uboot_summit.jpg)](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)