« 展示会・FPGAカンファレンスに出展します | トップページ | ZYNQ LinuxのGigabitEtherのMACアドレスはどのように設定されるのか(後編) »

2018.01.17

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アドレスが設定されます。

Ubootrandom

それゆえ、EEPROMを使いたいならば、config.hに以下の2行を加えます。

#define CONFIG_ZYNQ_GEM_EEPROM_ADDR     0x57
#define CONFIG_ZYNQ_GEM_I2C_MAC_OFFSET  0xF2

なお、これはMCP79411という、RTCチップとMACアドレスチップを兼ねているICを使った場合のアドレスです。

Ubooteeprom

③ 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()を呼び出し、デバイスツリーから以下の順序で情報を探します。

  1. mac-address
  2. local-mac-address
  3. 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オリジナルスタータキット

https://www.trenz.jp/product/zbstart/

|

« 展示会・FPGAカンファレンスに出展します | トップページ | ZYNQ LinuxのGigabitEtherのMACアドレスはどのように設定されるのか(後編) »

コメント

コメントを書く



(ウェブ上には掲載しません)




« 展示会・FPGAカンファレンスに出展します | トップページ | ZYNQ LinuxのGigabitEtherのMACアドレスはどのように設定されるのか(後編) »