ZYNQ LinuxのGigabitEtherのMACアドレスはどのように設定されるのか(前編)
ZYNQ Linuxを作ったとき、何も考えずに作るとeth0のMACアドレスが00:0a:35:00:01:22になってしまいます。
ボードをたくさん並べたい場合や、ボードを販売したい場合に、このアドレスはどのように変更すればよいのでしょうか?
ifconfig eth0 hw ether 02:11:22:33:44:55
のように設定する方法はうまくいきません。自動的に戻ってしまうからです。
そんなZYNQ LinuxのMACアドレスを変更する方法をついに解明しました。
まず、Linux起動時に、MACアドレスの設定には以下の3つのプログラムが関わっています。
① FSBL
② U-Boot
③ Linux
順番に見ていきましょう。
●FSBL
FSBLではXEmacPs_GetMacAddress()とXEmacPs_GetSacAddress()という関数が使えます。
unsigned char mac_addr[6]; mac_addr[0] = 0x02; mac_addr[1] = 0x03; mac_addr[2] = 0x04; mac_addr[3] = 0x05; mac_addr[4] = 0x06; mac_addr[5] = 0x07; EmacPs_SetMacAddress(&Emac, mac_addr, 1);
のようなプログラムを書けば、確かにPSの中のGigabitEtherモジュール(XEmacPsという)にMACアドレスをセットすることができます。
しかし、後段のU-Bootで上書きされてしまうので、通常は効果を持ちません。
●U-Boot
U-Bootのソースで、include\configs\zynq-common.h というファイルがあります。
このファイルに
#ifndef CONFIG_EXTRA_ENV_SETTINGS
#define CONFIG_EXTRA_ENV_SETTINGS \
"ethaddr=00:0a:35:00:01:22\0" \
"kernel_image=uImage\0" \
"kernel_load_address=0x2080000\0" \
"ramdisk_image=uramdisk.image.gz\0" \
"ramdisk_load_address=0x4000000\0" \
"devicetree_image=devicetree.dtb\0" \
で定義されたethaddrがあるため、このMACアドレスになってしまいます。ethaddrが
U-BootがどのようにしてMACアドレスを設定しているかを追ってみると、
① net/eth-uclass.cにおけるeth_post_probe()での処理
- 環境変数のethaddrが設定されているなら、環境変数を使う。
pdata(EEPROMのデータ)もあるなら、表示だけは一応する。 - 環境変数がなくて、pdataがあるなら、pdataを使う。
- pdataがなくて、環境変数も無効な値なら、乱数を使う
となっています。つまり、環境変数 > EEPROM > 乱数という優先順位です。
なお、U-Bootのデバイスツリーの読み込みでは、iobase、emio、phyaddr、phy_mode、interface、int_pcsというプロパティは読み出されますが、MACアドレスは設定されません。つまり、u-bootのデバイスツリーでMACアドレスの設定はできません。
② zynq_board_read_rom_ethaddr()での処理
同じファイルの中で定義されているzynq_board_read_rom_ethaddr()によってboard/xilinx/zynq/board.c の中にあるzynq_board_read_rom_ethaddr()が呼び出されます。
この関数は
int zynq_board_read_rom_ethaddr(unsigned char *ethaddr) { #if defined(CONFIG_ZYNQ_GEM_EEPROM_ADDR) && \ defined(CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET) if (eeprom_read(CONFIG_ZYNQ_GEM_EEPROM_ADDR, CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET, ethaddr, 6)) printf("I2C EEPROM MAC address read failed\n"); #endif return 0; }
つまり、CONFIG_ZYNQ_GEM_EEPROM_ADDRと、CONFIG_ZYNQ_GEM_I2C_MAC_OFFSETというマクロが定義されていれば、zynq_board_read_rom_ethaddr()によってROMから読み込んだMACアドレスが設定されます。
それゆえ、EEPROMを使いたいならば、config.hに以下の2行を加えます。
#define CONFIG_ZYNQ_GEM_EEPROM_ADDR 0x57
#define CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET 0xF2
なお、これはMCP79411という、RTCチップとMACアドレスチップを兼ねているICを使った場合のアドレスです。
③ drivers/net/zynq_gem.cにおけるzynq_gem_setup_mac()での処理
最後にzynq_gem_setup_mac()が呼び出され、MACアドレスをXEmacPsのレジスタに書き込まれます。ここで実際にI/OのWriteが行われます。
U-Bootの中での優先順位は「環境変数 > EEPROM > 乱数」なので、環境変数を設定すればよいわけです。
環境変数ethaddrは、実はuEnv.txtで変更できます。
●Linuxにおける扱い
LinuxでMACアドレスの設定は以下のようにして行われます。
(1) xemacps_probe()の中で、of_get_mac_address()を呼び出し、デバイスツリーから以下の順序で情報を探します。
- mac-address
- local-mac-address
- address
(2) xemacps_probe()関数はデバイスツリーからMACアドレスが見つかれば、その値をMACアドレスとして採用します。そして、xemacps_set_hwaddr()を呼び出し、I/Oレジスタを操作してMACアドレスを設定する。
(3) デバイスツリーに有効なMACアドレスが見つからなければ、xemacps_update_hwaddr()を呼び出します。xemacps_update_hwaddrは、I/Oレジスタを操作して現在のMACアドレスを読み出し、それが有効なものだったら採用し、有効でなければランダムなものを使います。
なお、xemacps_update_hwaddrはxemacps_probe()でしか呼ばれません。
つまり、デバイスツリーにmac-addressやlocal-mac-addressが無ければ、ハードウェアレジスタにすでに設定されたものが採用されるはずです。
確かに、デバイスツリーに
ps7-ethernet@e000b000 { local-mac-address = [00 0a 35 00 01 22];
というのがあるかもしれません。
しかし、これを消しても、書き換えても、MACアドレスは変わりません。
実はここには深いからくりがあるのです。
(続く)
[PR]
※このようなZYNQ Linuxの深い部分の構築や実験は、ZynqBerryオリジナルスタータキットでの楽しむことができます。
● Zynqberryオリジナルスタータキット
| 固定リンク
コメント