espressif / qemu

Fork of QEMU with Espressif patches. See Wiki for details.
https://github.com/espressif/esp-toolchain-docs/blob/main/qemu/README.md
Other
230 stars 61 forks source link

Unable to set Ethernet mac address from qemu command line (QEMU-225) #107

Open mzakharocsc opened 2 days ago

mzakharocsc commented 2 days ago

Checklist

How often does this bug occurs?

always

Expected behavior

when specifying custom mac on qemu command line like this: -nic user,model=open_eth,mac=00:00:00:00:00:05 , the value does not reflect expected bytes when read using the following api: esp_read_mac(mac_addr, ESP_MAC_ETH)

Actual behavior (suspected bug)

mac_addr always shows up as "00:00:00:00:00:03", regardless of what qemu's parameter 'mac=XX:XX;XX:XX:XX:XX' is set to.

Error logs or terminal output

No response

Steps to reproduce the behavior

idf.py qemu monitor

Project release version

latest

System architecture

Intel/AMD 64-bit (modern PC, older Mac)

Operating system

Linux

Operating system version

Ubuntu 20.04

Shell

Bash

Additional context

No response

igrr commented 2 days ago

@mzakharocsc That's right, IDF's esp_read_mac doesn't query the MAC address from the network device, instead it derives the address from the base MAC address stored in eFuse. Therefore setting the MAC address on the network device side doesn't affect the result returned by esp_read_mac.

To extract the MAC address from the network device you can use esp_eth_ioctl with ETH_CMD_G_MAC_ADDR argument:

esp_err_t get_addr(esp_eth_handle_t eth_handle, uint8_t eth_mac[6]) {
    return esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, eth_mac)
}

Or, directly from the esp_eth_mac_t MAC instance, if you have that instead of esp_eth_handle_t:

esp_err_t get_addr(esp_eth_mac_t *mac_handle, uint8_t eth_mac[6]) {
    return mac_handle->read_addr(mac_handle, eth_mac);
}

Once you have read the address from the device, you either use it to configure your base MAC address (which is absent from eFuses) or just set the custom MAC address for the interface.

Edit: I'll also see if we can provide reasonable behavior (to use the MAC address configured via QEMU arguments) at least in IDF examples (which rely on examples/common_components/protocol_examples_common/eth_connect.c) or if we can add a --mac-addr argument to idf.py qemu which will pre-set the MAC address in eFuse.

mzakharocsc commented 2 days ago

Running examples/ethernet/basic, which does esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); returns '00:00:00:00:00:03' also, thats because I suspect in static esp_err_t emac_opencores_init(esp_eth_mac_t *mac in esp_eth_mac_openeth.c it also uses esp_read_mac() to set mac address of the interface.

Please confirm

igrr commented 2 days ago

Ah yes, you are totally right, I have missed that part.

I see two things we could do here:

  1. Modify emac_opencores_init, adding more complex logic around the selection of the MAC address:
    • Get the base MAC address. If it is non-zero, then use esp_read_mac to obtain the address derived for the Ethernet interface. This would handle the case when the emulated eFuses do contain a valid MAC address.
    • If the base MAC address is zero, try to get the address from the (emulated) ethernet MAC. If non-zero, use that address. This would allow setting the address via -nic user,model=open_eth,mac=.... argument.
    • If both are zero, fall back to some default address.
  2. Add --mac-addr option in idf.py qemu wrapper to allow setting the base address in eFuses.

Does that sound reasonable to you?

(If we do the above, we also need to fix the ifdef in protocols_examples_common so that the address doesn't get overridden in the example projects which use this component. Currently we check for !CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET which includes the openeth case.)

mzakharocsc commented 2 days ago
  1. is the preferred option in my opinion

I tried to read the MAC address like so:

    // Initialize the MAC
    openeth_reset();
    openeth_set_tx_desc_cnt(TX_BUF_COUNT);

    uint32_t mac0 = REG_READ(OPENETH_MAC_ADDR0_REG);
    uint32_t mac1 = REG_READ(OPENETH_MAC_ADDR1_REG);

    printf("MAC0=%#lx  MAC1=%#lx\n", mac0, mac1);

inside esp_eth_mac_openeth.c, however I am getting all 0's even when -nic user,model=open_eth,mac=00:00:00:00:00:05 is set on QEMU parameters

Please advise.

Edit. it looks like MAC_ADDR0 and MAC_ADDR1 are never set on init inside QEMU emulator: opencores_eth.c . How else would one retreive the mac address set by QEMU parameters?

igrr commented 2 days ago

Looks like openeth emulation doesn't call qemu_macaddr_default_if_unset to initialize the MAC address, like other ethernet adapter emulations in QEMU do, for example

https://github.com/espressif/qemu/blob/d2f893f982067986ef444998a824c853592a1d95/hw/net/ne2000-pci.c#L69

Will fix that as well.

(And we have to copy the MAC address from NICInfo into the registers.)