[Linux][U-boot] Ethernet の MAC アドレスの決め方

ethernet にはインターフェースごとに必ず MAC アドレスが割り当てられています。しかしドライバが正しく取得できないとランダムに選ばれたり、U-boot と Linux でアドレスが一致しないためにネットワークの混乱を引き起こしたりします。どのように MAC アドレスが決められるのかを追ってみます。

U-boot の MAC アドレス取得

U-boot の MAC アドレスの取り扱い方は下記のドキュメントに説明があります。

u-boot/doc/README.enetaddr at master · u-boot/u-boot
"Das U-Boot" Source Tree. Contribute to u-boot/u-boot development by creating an account on GitHub.

デバイスのドライバによって取得方法がまちまちですが、だいたい以下のような手段で取得します。

  • ボードのどこか(flash memoryや eepromなど) に書かれている情報を取り込む
  • U-boot の環境変数 "ethaddr" を取り込む
  • eth_device->enetaddr から取得する (ドライバ起動時に設定済の BIOS や firmware 値を格納)

このどれからも取得できなければ、CONFIG_NET_RANDOM_ETHADDR の定義により乱数でアドレスが決められます。取得したアドレスは環境変数 "ethaddr" に再設定されます。

Linux kernel の MAC アドレス取得方法

Linux kernel の場合も同様にドライバによりますが、だいたい以下のような手段で取得します。

  • devictree の "mac-address", "local-mac-address", "address" プロパティから取得
    • device_get_mac_address()
  • ボードのどこか(flash memoryや eepromなど) に書かれている情報を取り込む

このどちらからも取得できなければ、eth_hw_addr_random() により乱数でアドレスが決められます。

と、ここで疑問なのが U-boot と Linux で取得方法が異なる場合はどうなるのか? ということです。

U-boot の MAC アドレスを Linux kernel に反映

U-boot で設定された MAC アドレスを Linux kernel が継承すればシステム上で矛盾がなくなりますが、それぞれで取得方法が独立しているため異なる可能性があります。U-boot はそれを避けるために Linux kernel に与える devicetree を強制的に書き換えるようになっています。

しかしどうもうまくいかず、U-boot で設定したアドレスが Linux 側では ランダムアドレスになってしまうため、原因を追ってみました。

fdt_fixup_ethernet() を見ると以下のようになっています。

/* FDT might have been edited, recompute the offset */
offset = fdt_first_property_offset(fdt, fdt_path_offset(fdt, "/aliases"));
	:
path = fdt_getprop_by_offset(fdt, offset, &name, NULL);
if (!strncmp(name, "ethernet", 8)) {
	:
	strcpy(mac, "ethaddr");
	:
	tmp = env_get(mac);
	:
	for (j = 0; j < 6; j++) {
		mac_addr[j] = tmp ? simple_strtoul(tmp, &end, 16) : 0;
		if (tmp)
			tmp = (*end) ? end + 1 : end;
	}
	do_fixup_by_path(fdt, path, "mac-address", &mac_addr, 6, 0);
	do_fixup_by_path(fdt, path, "local-mac-address", &mac_addr, 6, 1);
}

よく見ると、aliases に設定された ethernet プロパティの phandle に対して、そのノード上の "mac-address" と "local-mac-address" プロパティを変数 "ethaddr" の内容に強制的に書き換えています。

aliases {
	serial0 = &serial0;
	serial1 = &serial1;
	ethernet0 = &eth0
};

このように Linux 側の devicetree にて ethernet ノードの phandle (&eth0) を aliases に設定することで、U-boot の MAC アドレス値を Linux kernel 継承できるようになっていました。また、U-boot を使う場合はこの aliases が設定されていれば、Linux kernel の devicetree に MAC アドレスを設定していても上書きされてしまうことに注意が必要です。

Linux kernel で常にランダムアドレスで設定されてしまう場合や devicetree の設定値が反映できないときは、devicetree の aliases ノードを見直してみるとよいかもしれません。


コメント

タイトルとURLをコピーしました