Set MAC-Address on boot


Is it possible to set MAC via cmdline?

On raspberry pi i can set it via


Regards Frank

No. You can set it from dts

If i set ot via dts,all users using my kernels having my mac if they don’t change dts and recompile. This is only problematic if 2 such devices are in same lan-segment (like in my lan).

But maybe its a start to get rid of random mac.

How can it be done?

Maybe its a good start for testing dto :slight_smile: can you give me such example?

I’m trying to set via uboot,passing as self-defined param in cmdline and parsing cmdline in linux to set via ip-command

found here an interesting article regarding it:

basicly we can set mac by dts

local-mac-address = [00 0a 35 00 00 01];
mac-address = [00 0a 35 00 00 01];

used second one:

            mac@0 {                                                                                                                              
                    compatible = "mediatek,eth-mac";                                                                                             
                    mac-address = [02 02 02 02 02 02];                                                                                           
                    phandle = <0x39>;                                                                                                            
                    reg = <0x0>;                                                                                                                 
                    phy-mode = "trgmii";  

root@bpi-r2:~# ls /sys/firmware/devicetree/base/ethernet\@1b100000/mac\@0/                                                                           
compatible  fixed-link  mac-address  name  phandle  phy-mode  reg
cat /sys/firmware/devicetree/base/ethernet\@1b100000/mac\@0/mac-adddress | hexdump
0000000 0202 0202 0202

root@bpi-r2:~# ip link show eth0              
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 02:02:02:02:02:02 brd ff:ff:ff:ff:ff:ff

works so far

now the second info from the thread…they say i can pass the mac-adress set in uboot via ethaddr (+saveenv)

U-Boot> printenv ethaddr

root@bpi-r2:~# cat /sys/firmware/devicetree/base/aliases/ethernet0                                            

but mac is the one from dts, not from uboot

root@bpi-r2:~# ip link show eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
    link/ether 02:02:02:02:02:02 brd ff:ff:ff:ff:ff:ff

any idea what i’m making wrong? if i leave the mac-address in dts, i got the random mac, but not the one set in uboot… do i need to set alias in dts from uboot? here (2018-11) i have only the eth for linking not the gmac-subnode…

alias {
    ethernet0 = &eth;

is that right? still not working…maybe because dts-ethernet-node in uboot is not the same as in kernel (in uboot link to eth,in kernel to gmac0)

@weijiegao can you help here?

can you give me an example how to set mac-addr via devicetree overlay?

have read this:

i need to overlay the node gmac0 and dt-overlay-source should look like this, right?

/ {
    fragment@0 {
        target = <&gmac0>;
        __overlay__ {
            mac-address = [02 01 02 03 04 05];

dtc -@ -I dts -O dtb -o bpi-r2-mac.dtb bpi-r2-mac.dts

to load in uboot (untested):

fatload ${device} ${partition} ${fdt_addr_r} ${bpi}/${board}/${service}/dtb/${fdtfile}; // full dtb maybe not used if appended DTB (r2), how to deal with the following commands (does dto work with appended DTB)?
fdt addr ${fdt_addr_r}
fdt resize // why this without parameter (bytes to add or target-size)

setexpr fdtovaddr ${fdt_addr_r} + F000 //why F000, why not using ${filesize}??
fatload ${device} ${partition} ${fdtovaddr} ${bpi}/${board}/${service}/dtb/bpi-r2-mac.dtb && fdt apply ${fdtovaddr}

as far as i understand, fdtovaddr is temporary address to load a fdto before apply it to the base-fdt it is set 60kb after base (base-fdt+ previous loaded fdto should not exceed 60kb)

how are the ranges to define ${fdt_addr_r}? can i use scriptaddr (0x83000000) here? i only use it for loading environment…kernel will be loaded in ${loadaddr} (also for tftp)

maybe i can use

for <name> in <word list> ; do <list> ; done

to load multiple dto’s

currently hang on separating kernelimage and fdt

after getting kernel with separate dtb running, i continue with DTO…

compiling above dts gives me this warning:

bpi-r2-mac.dtb: Warning (unit_address_vs_reg): Node /fragment@0 has a unit name, but no reg property

is this needed or how to fix this?

i tried to load it, but in uboot there seem no fdt apply

fdt - flattened device tree utility commands

fdt addr [-c]  <addr> [<length>]   - Set the [control] fdt location to <addr>
fdt move   <fdt> <newaddr> <length> - Copy the fdt to <addr> and make it active
fdt resize [<extrasize>]            - Resize fdt to size + padding to 4k addr + some optional <extrasize> if needed
fdt print  <path> [<prop>]          - Recursive print starting at <path>
fdt list   <path> [<prop>]          - Print one level starting at <path>
fdt get value <var> <path> <prop>   - Get <property> and store in <var>
fdt get name <var> <path> <index>   - Get name of node <index> and store in <var>
fdt get addr <var> <path> <prop>    - Get start address of <property> and store in <var>
fdt get size <var> <path> [<prop>]  - Get size of [<property>] or num nodes and store in <var>
fdt set    <path> <prop> [<val>]    - Set <property> [to <val>]                                                                                      
fdt mknode <path> <node>            - Create a new node after <path>                                                                                 
fdt rm     <path> [<prop>]          - Delete the node or <property>                                                                                  
fdt header                          - Display header info                                                                                            
fdt bootcpu <id>                    - Set boot cpuid                                                                                                 
fdt memory <addr> <size>            - Add/Update memory node                                                                                         
fdt rsvmem print                    - Show current mem reserves                                                                                      
fdt rsvmem add <addr> <size>        - Add a mem reserve                                                                                              
fdt rsvmem delete <index>           - Delete a mem reserves                                                                                          
fdt chosen [<start> <end>]          - Add/update the /chosen branch in the tree                                                                      
                                        <start>/<end> - initrd start/end addr

dto gets loaded, but after it i get “usage”…seems apply does trigger it

loaddto=echo "loaddto:${dto}";fdt addr ${dtaddr};fdt resize; setexpr fdtovaddr ${dtaddr} + F000; fatload ${device} ${partition} ${fdtovaddr} ${bpi}/${board}/${service}/dtb/${dto} && fdt apply ${fdtovaddr}

documentation of uboot uses also apply (doc/README.fdt-overlays)

  1. You are now ready to apply the overlay. => fdt apply $fdtovaddr

seems i need “OF_LIBFDT_OVERLAY”-option in uboot…

after that i got this:

27836 bytes read in 10 ms (2.7 MiB/s)
229 bytes read in 7 ms (31.3 KiB/s)
failed on fdt_overlay_apply(): FDT_ERR_NOTFOUND
base fdt does did not have a /__symbols__ node
make sure you've compiled with -@
## Booting kernel from Legacy Image at 82000000 ...
   Image Name:   Linux Kernel 4.19.13-main
   Image Type:   ARM Linux Kernel Image (uncompressed)
   Data Size:    7311880 Bytes = 7 MiB
   Load Address: 80008000
   Entry Point:  80008000
   Verifying Checksum ... OK
ERROR: Did not find a cmdline Flattened Device Tree
Could not find a valid device tree

i checked it via

[11:36:58]$ fdtdump bpi-r2-4.19.13-main_nodt.dtb | grep -C3 __symbols__

**** fdtdump is a low-level debugging tool, not meant for general use.
**** If you want to decompile a dtb, you probably want
****     dtc -I dtb -O dts <filename>

so it seems i need to compile base dtb by myself…

[11:37:14]$ dtc -@ -I dts -O dtb -o bpi-r2-base.dtb arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts
Error: arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts:8.1-9 syntax error
FATAL ERROR: Unable to parse input tree

line 8 is first include…so it seems i need to pass include-dir

i tried this, because include-folder in root contains the dt-bindings subfolder (including the input/input.h), but same result

dtc -i ./include -@ -I dts -O dtb -o bpi-r2-base.dtb arch/arm/boot/dts/mt7623n-bananapi-bpi-r2.dts

also tried to build dtb with “make dtbs”…same

kernel seems to use make to build dtb’s


%.dtb: | scripts
        $(Q)$(MAKE) $(build)=$(boot)/dts MACHINE=$(MACHINE) $(boot)/dts/$@

but how to add “-@” to generate symbols? and modifying Makefile is maybe not the best way…only for testing

tried also moving from phandle to path like mentioned here:


export DTC_FLAGS=-@

to my and now i see the symbols

[09:59:40]$ fdtdump bpi-r2.dtb | grep -C3 __symbols__

**** fdtdump is a low-level debugging tool, not meant for general use.
**** If you want to decompile a dtb, you probably want
****     dtc -I dtb -O dts <filename>

        device_type = "memory";
        reg = <0x00000000 0x80000000 0x00000000 0x80000000>;
    __symbols__ {
        cpu_opp_table = "/opp-table";
        cpu0 = "/cpus/cpu@0";
        cpu1 = "/cpus/cpu@1";

uboot boots it…and i see mac set via dto :slight_smile:

 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP g0
    link/ether 02:01:02:03:04:05 brd ff:ff:ff:ff:ff:ff                          
3: wan@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 0
    link/ether 02:01:02:03:04:05 brd ff:ff:ff:ff:ff:ff

i tested uboot’s ethaddr-way now and it seems that it needs separate fdt, also overwrites address set by dto…

BPI-R2> printenv ethaddr
BPI-R2> printenv eth1addr

root@bpi-r2:~# ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP g0
    link/ether 02:04:04:04:04:04 brd ff:ff:ff:ff:ff:ff
3: wan@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 0
    link/ether 02:04:04:04:04:04 brd ff:ff:ff:ff:ff:ff

but maybe it needs the mac-address-property and alias

aliases {
    serial2 = &uart2;
    ethernet0 = &gmac0;
gmac0: mac@0 {
    mac-address = [02 02 02 02 02 02];

i have only set mac-address in dto+alias (4.19-main) and it works there. if dto is not loaded maybe mac-address is needed in bpi-r2’s base dts

have added it now also for 4.14 (both cpu-ports):

root@bpi-r2:~# ip a
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP g0
    link/ether 02:04:04:04:04:04 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP g0
    link/ether 02:05:05:05:05:05 brd ff:ff:ff:ff:ff:ff

I am using systemd to change mac addresses: /etc/systemd/network/


1 Like

Is it possible to get a clearer explanation of how to set mac addresses on the bpi without recompiling the kernel? Are you using a different kernel (the one with no_dt) to do this? Does this require compiling or otherwise creating a .dtb file and placing it in the /boot partition? I imagine there are some u-boot things as well that are needed?

If you want to use the dto-way you need _nodt kernel, the full dtb and your dto.

I have some functions in my uboot to load them

This is the function you nees to call

Basicly the bootall-function does the most work.

Prior to calling newboot2 you need to define thos variables:

  • kernel
  • fdt
  • dtolist

This are all the filenames…dtolist is space-separated (or only one like in my tests).

fdt and dto are located in dtb-folder (subdir to kernel-folder). _nodt kernel and fdt are compiled in my packages,so you need only change the mac.dts and compile this

I don’t understand. I will clarify my environment. I have the 4.14-main branch checked out of your git repository and also your u-boot repository checked out on branch 2019-07-bpi-r2.

I’ve taken your ubuntu 1804 preview image and put a 4.14-main kernel on it (with some additional .config parameters applied). I used this kernel instead of 4.19 because the VLAN box is not checked on the support matrix in the git repository.

I can find your uEnv.txt file in the u-boot repository, but in neither repository can I find the dto-folder.

In the preview image, there is a .dts file in /boot/bananapi called bpi-r2.dts. I’ve tried unpacking it, changing mac addresses and repacking it, but this seems to have no effect after rebooting.

as i’ve wrote, i have not tested vlan in kernels >4.14, so i have not checked the vlan in the matrix…that does not mean it is not supported…only not tested

as for the dto…

your kernel is in BPI-ROOT (mounted to /boot) in folder bananapi/bpi-r2/linux. dtb are located in subfolder dtb (bananapi/bpi-r2/linux/dtb). here you need the full dtb and your dt-overlay (filename also *.dtb). then you need to change the uenv.txt setting the variables i wrote to load the 3 files

something like this:


I’ve attempted to merge the original uEnv.txt with the changes you noted in the github repository. I can get the kernel to boot, but only if I don’t specify a fdt parameter. There are some magic numbers I don’t fully understand, and I think I might need to change one of them in order to get it to work.

There’s specifically the dtaddr=0x83f00000 parameter, copied from the uEnv you provided, and then I am keeping the others from the Ubuntu image. The other magic numbers are in the loaddto function.

I also tried building and installing that branch of u-boot and was also not able to boot the device this way. I’ve some Serial UARTS on the way to enhance my troubleshooting capability, but right now I can just tell you that I get the blank screen.

## uEnv.txt

#newloadenv=mmc init; run loadbootenv; env import -t ${scriptaddr} ${filesize}
root=/dev/mmcblk0p2 rootfstype=ext4 rootwait
console=earlyprintk console=tty1 fbcon=map:0 console=ttyS0,115200
bootopts=vmalloc=496M debug=7 initcall_debug=0 video=1920x1080 drm.debug=0x7

#abootargs=setenv bootargs board=${board} console=${console} root=${root} service=${service} ${bootopts}
#aboot=if fatload $device $partition $rdaddr ${bpi}/berryboot.img; then bootm $kaddr $rdaddr ; else bootm $kaddr; fi
#aload_kernel=fatload $device $partition $kaddr ${bpi}/${board}/${service}/${kernel}
#uenvcmd=run abootargs aload_kernel aboot

#separate fdt+dto
loadfdt=fatload ${device} ${partition} ${dtaddr} ${bpi}/${board}/${service}/dtb/${fdt}
loaddto=echo "loaddto:${dto}";fdt addr ${dtaddr};fdt resize 8192; setexpr fdtovaddr ${dtaddr} + F000;fatload ${device} ${partition} ${fdtovaddr} ${bpi}/${board}/${service}/dtb/${dto} && fdt apply ${fdtovaddr}
loaddtolist=for dto in ${dtolist} ; do run loaddto ; done

buildargs=setenv bootargs "board=${board} ${console} root=${root} service=${service} ${bootopts}"
loadkernel=echo "loading kernel ${kernel}...";fatload ${device} ${partition} ${kaddr} ${bpi}/${board}/${service}/${kernel}

bootall=if printenv fdt; then run loadfdt; if printenv dtolist;then run loaddtolist;fi; bootm ${kaddr} - ${dtaddr} ;else bootm; fi
uenvcmd=run buildargs; printenv bootargs; run loadkernel; run bootall;

Here is my partition table, in case it matters.

    Device                Boot                          Start                 End             Sectors             Size           Id Type
>>  Free space                                           2048              204799              202752              99M
    /dev/sdb1                                          204800              729087              524288             256M            c W95 FAT32 (LBA)
    /dev/sdb2                                          729088            15269887            14540800               7G           83 Linux
    Free space                                       15269888            62521343            47251456            22.5G

Dtaddr is memory-address to load fdt. Loadfdt basicly load dtb-file to this address, loaddto load it in fdtlib from memory,makes it larger calculate a temporary memory-address,load dto-file from fat to this and apply dto on fdt.

You need nodt-kernel (kernel-var) to load separate dt.

Which image do you use? You need at least fdtlib and setexpr options which are not available in older (and official images).but you can compile and flash my uboot over it

Although not shown in the config file above, I did edit uEnv.txt to boot from the nodt kernel and gave it the fdt bpi-r2.conf when testing booting in that way.

I started with your ubuntu-1804 preview image. I have a different kernel and slightly different bootargs, have added universe and updated it, but the u-boot is from that.

I have tried to compile and flash your u-boot without success. I used origin/2019-07-bpi-r2 with a toolchain from an Ubuntu 18.04 machine.

I tried it with and without the edited CONFIG_ENV_SIZE parameter. I’m flying a little blind here unfortunately without the serial UART.

config_env_size is only interesting if you save your environment inside uboot…afair only the newest buster-image has dto-support. The others should be older

Without debug-uart its hard because you don’t see any errors…try to get such cable/adapter.

Btw how do you call newboot2 without debug-uart? Have you also modified default bootmenu-entry?

How to call the functions in newboot2 is actually one of the open questions I have about the uEnv.txt file. I tried to copy the structure of the originally provided file as much as possible–meaning that I kept the uenvcmd= the same as from the original uEnv.txt file, except with the newboot2 definition:

uenvcmd=run buildargs; printenv bootargs; run loadkernel; run bootall;

I definitely want the device to boot the kernel without any interaction needed on the UART. I suspect these memory offsets have something to do with it.

My uboot has the most already in so you need only set what needs to be overridden. To boot automaticly just change default bootmenu-entry or add new and set this to the default. without debug-uart you are blind and don’t see whats happening and on which part it breaks…very bad on testing such complex behaviour like dto…

Thanks. I know it is not ideal. The UART is on its way, but trying to inch along until then.

I have still been unable to get this to work. The output of the ./ script tells me that the kernel load address is 0x80008000. So I am going to try modifying that variable. I’ll leave the others alone, and have changed bootcmd to

bootcmd=run newboot2;

I have tried the most minimal change I can think of to the uEnv.txt file provided in the u-boot tree. Still no luck.

I’ve tried this with the following variations:

  • Flashing both u-boot.bin and u-boot-nodtb.bin
  • Leaving the default loadaddr and trying 0x80008000 as given in my kernel build report.
  • Different default boot options, since it wasn’t entirely clear if the numbering starts at 1 or 0.
  • I tried running just newboot2 for the boot1 option.

Here’s the diff. Its possible at this point I will need to wait until I have UART access.

--- BPI-BOOT/bananapi/bpi-r2/linux/uEnv.txt.default_newuboot	2019-09-17 09:42:36.000000000 -0700
+++ BPI-BOOT/bananapi/bpi-r2/linux/uEnv.txt	2019-09-17 10:06:26.000000000 -0700
\@@ -5,7 +5,9 @@
 #default bootargs will be overidden by buildargs
 bootargs=console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait ip=dhcp
\@@ -43,7 +45,7 @@

 boot0=run lskernel;run askkernel;if printenv kernelinput ;then setenv kernel ${kernelinput};run lsdtb;run askdtb;if printenv dtbinput ;then setenv fdt ${dtbinput};fi; run newboot2; fi;
-boot1=run selectmmc; run loadbootenv; run importenv; run newboot;
+boot1=run selectmmc; run loadbootenv; run importenv; run newboot2;
 boot14=setenv kernel ${kernel414}; run newboot;
 boot19=setenv kernel ${kernel419}; run newboot;
 boot19_dt=setenv kernel ${kernel419_nodt}; setenv fdt ${fdt419}; setenv dtolist ${dtolist419}; run newboot2;

Loadaddr in uboot is for loading files to memory…it’s independ of kernels loadaddress.

bootmenu_default numbering is same as numbering for entries (0=first).But thos depends on bootmenu_x entries…by default it is set to 2 so bootmenu_2 is called by default which points to boot1

Why do you have a uboot_nodt?

Btw see boot19_dt to see what i have done (dynamicly assign kernel+fdt+dtolist),basicly you need only call newboot2 if you have defined kernel+fdt before (no need for setenv).

You also do not need the loadenv-stuff before because this is already done if you use my uboot…uenv.txt on boot-partition is already loaded by reloadmenu