From 14fa832937de4339953e02c310c4e2f654b44c5f Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Sun, 23 Aug 2026 14:03:39 +0200 Subject: [PATCH 1/8] qemu: give the virt machine an Ethernet MAC SO3 can drive QEMU's 'virt' machine only offers virtio-net, and SO3 has no driver for it: its single Ethernet driver is smc911x. So networking under QEMU has never worked, which is why CONFIG_NET was off in every defconfig. Add an SMSC LAN9118 to the machine model, next to the PL111/PL050/ absmouse devices the so3 patch already adds, at 0x08804000 (VIRT_ETH) with SPI 15. Two things the obvious version gets wrong: - 0x1a000000, the address the SO3 device trees inherited from vexpress-a15, does not work here. It is inside VIRT_PCIE_MMIO, and the gpex alias covers the whole window and shadows anything mapped underneath it (visible in "info mtree -f"): the guest reads back nothing and never detects the chip. Hence an address in the block the so3 patch already owns. - the device has to be created BEFORE create_pcie(), which hands every nd_table entry no on-board device has claimed to pci_nic_init_nofail(), and that one exits with "Unsupported NIC model: lan9118". Slots an on-board NIC has taken are now skipped there too. It is only instantiated when the command line fills the legacy on-board NIC slot, so a run without -nic/-net leaves the machine exactly as it was. st.sh passes "-nic user,model=lan9118" on its own slirp stack, alongside the virtio-net device the Linux agency uses (which keeps the 2222 -> 22 host forward). Each guest ignores the NIC it cannot drive. The new Kconfig patch of the set exposed a .gitignore trap: the "qemu" entry meant for the source tree build.sh downloads is unanchored, so it also matches the directories of that name inside the layers (meta-qemu/recipes-qemu/qemu/). The two existing patches survive because they were already tracked, but the third was silently swallowed by "git add". Anchor the entry as "/qemu". --- .gitignore | 6 +- .../qemu/files/0001-qemu-8.2.2-r0-patches.inc | 1 + .../0001-qemu-8.2.2-r0/0001-virt.c.patch | 66 +++++++++++++++++-- .../0001-qemu-8.2.2-r0/0002-virt.h.patch | 3 +- .../0001-qemu-8.2.2-r0/0003-Kconfig.patch | 9 +++ scripts/st.sh | 24 +++++++ 6 files changed, 101 insertions(+), 8 deletions(-) create mode 100644 build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0003-Kconfig.patch diff --git a/.gitignore b/.gitignore index 1811775e79..24d2434eae 100755 --- a/.gitignore +++ b/.gitignore @@ -60,8 +60,10 @@ modules.order vmlinux.*dom* **/include/config/** -# QEMU downloaded by build.sh script -qemu +# QEMU downloaded by build.sh script. Anchored: an unanchored "qemu" also +# matches the DIRECTORIES of that name inside the layers (meta-qemu/ +# recipes-qemu/qemu/), which silently swallowed the recipe's patch files. +/qemu # Backup files *~ diff --git a/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0-patches.inc b/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0-patches.inc index 2e9696e540..3dd7bb5dca 100644 --- a/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0-patches.inc +++ b/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0-patches.inc @@ -3,4 +3,5 @@ SRC_URI += "\ file://0001-virt.c.patch \ file://0002-virt.h.patch \ + file://0003-Kconfig.patch \ " diff --git a/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0001-virt.c.patch b/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0001-virt.c.patch index 4f73c201be..0b049dc709 100644 --- a/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0001-virt.c.patch +++ b/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0001-virt.c.patch @@ -1,6 +1,14 @@ --- a/hw/arm/virt.c +++ b/hw/arm/virt.c -@@ -82,6 +82,10 @@ +@@ -42,6 +42,7 @@ + #include "hw/vfio/vfio-amd-xgbe.h" + #include "hw/display/ramfb.h" + #include "net/net.h" ++#include "hw/net/lan9118.h" + #include "sysemu/device_tree.h" + #include "sysemu/numa.h" + #include "sysemu/runstate.h" +@@ -82,6 +83,10 @@ #include "hw/char/pl011.h" #include "qemu/guest-random.h" @@ -11,7 +19,7 @@ #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ void *data) \ -@@ -145,6 +149,12 @@ +@@ -145,6 +150,20 @@ [VIRT_GIC_ITS] = { 0x08080000, 0x00020000 }, /* This redistributor space allows up to 2*64kB*123 CPUs */ [VIRT_GIC_REDIST] = { 0x080A0000, 0x00F60000 }, @@ -20,11 +28,19 @@ + [VIRT_KMI0] = { 0x08801000, 0x00001000 }, + [VIRT_KMI1] = { 0x08802000, 0x00001000 }, + [VIRT_ABSMOUSE] = { 0x08803000, 0x00001000 }, ++ ++ /* SMSC LAN9118 Ethernet MAC. It sits with the other SO3-added devices ++ * rather than at the 0x1a000000 the SO3 device trees inherited from ++ * vexpress-a15: that address falls inside VIRT_PCIE_MMIO below, whose ++ * gpex window covers the whole range and shadows anything mapped ++ * underneath it (the guest then reads back nothing and the chip is ++ * never detected). */ ++ [VIRT_ETH] = { 0x08804000, 0x00001000 }, + [VIRT_UART] = { 0x09000000, 0x00001000 }, [VIRT_RTC] = { 0x09010000, 0x00001000 }, [VIRT_FW_CFG] = { 0x09020000, 0x00000018 }, -@@ -199,6 +209,11 @@ +@@ -199,6 +218,12 @@ [VIRT_SECURE_UART] = 8, [VIRT_ACPI_GED] = 9, [VIRT_MMIO] = 16, /* ...to 16 + NUM_VIRTIO_TRANSPORTS - 1 */ @@ -32,11 +48,25 @@ + [VIRT_CLCD] = 10, + [VIRT_KMI0] = 11, + [VIRT_KMI1] = 12, ++ [VIRT_ETH] = 15, + [VIRT_GIC_V2M] = 48, /* ...to 48 + NUM_GICV2M_SPIS - 1 */ [VIRT_SMMU] = 74, /* ...to 74 + NUM_SMMU_IRQS - 1 */ [VIRT_PLATFORM_BUS] = 112, /* ...to 112 + PLATFORM_BUS_NUM_IRQS -1 */ -@@ -2022,6 +2037,109 @@ +@@ -1487,6 +1512,12 @@ + vms->bus = pci->bus; + if (vms->bus) { + for (i = 0; i < nb_nics; i++) { ++ /* Skip a slot an on-board NIC already took (the SO3 LAN9118 ++ * created just before this call): the PCI code would reject its ++ * model with "Unsupported NIC model: lan9118". */ ++ if (nd_table[i].instantiated) { ++ continue; ++ } + pci_nic_init_nofail(&nd_table[i], pci->bus, mc->default_nic, NULL); + } + } +@@ -2022,6 +2053,109 @@ } } @@ -146,7 +176,33 @@ static void machvirt_init(MachineState *machine) { VirtMachineState *vms = VIRT_MACHINE(machine); -@@ -2326,6 +2444,51 @@ +@@ -2287,6 +2421,25 @@ + + create_rtc(vms); + ++ /* SMSC LAN9118 Ethernet MAC — the NIC the SO3 smc911x driver ++ * (devices/net/smc911x_lwip.c, "smsc,smc911x") binds to, at the address ++ * and SPI the SO3 device trees declare. QEMU's own virt machine offers ++ * only virtio-net, which SO3 has no driver for. ++ * ++ * Taken from the legacy on-board NIC slot, so a run without -nic/-net ++ * leaves the machine exactly as it was and the guest simply finds no chip ++ * (smc911x_detect_chip() fails cleanly). scripts/st.sh passes ++ * "-nic user,model=lan9118". ++ * ++ * This has to happen BEFORE create_pcie(): that one hands every nd_table ++ * entry no on-board device has claimed to pci_nic_init_nofail(), which ++ * bails out with "Unsupported NIC model: lan9118". ++ */ ++ if (nd_table[0].used) { ++ lan9118_init(&nd_table[0], vms->memmap[VIRT_ETH].base, ++ qdev_get_gpio_in(vms->gic, vms->irqmap[VIRT_ETH])); ++ } ++ + create_pcie(vms); + + if (has_ged && aarch64 && firmware_loaded && virt_is_acpi_enabled(vms)) { +@@ -2326,6 +2479,51 @@ vms->fw_cfg, OBJECT(vms)); } diff --git a/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0002-virt.h.patch b/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0002-virt.h.patch index dcac85baf2..4bc2f97580 100644 --- a/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0002-virt.h.patch +++ b/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0002-virt.h.patch @@ -1,6 +1,6 @@ --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h -@@ -58,6 +58,12 @@ +@@ -58,6 +58,13 @@ VIRT_GIC_VCPU, VIRT_GIC_ITS, VIRT_GIC_REDIST, @@ -9,6 +9,7 @@ + VIRT_KMI0, + VIRT_KMI1, + VIRT_ABSMOUSE, ++ VIRT_ETH, + VIRT_SMMU, VIRT_UART, diff --git a/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0003-Kconfig.patch b/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0003-Kconfig.patch new file mode 100644 index 0000000000..cd09fd3606 --- /dev/null +++ b/build/meta-qemu/recipes-qemu/qemu/files/0001-qemu-8.2.2-r0/0003-Kconfig.patch @@ -0,0 +1,9 @@ +--- a/hw/arm/Kconfig ++++ b/hw/arm/Kconfig +@@ -1,5 +1,6 @@ + config ARM_VIRT + bool ++ select LAN9118 # SO3 Ethernet (smsc,smc911x) + imply PCI_DEVICES + imply TEST_DEVICES + imply VFIO_AMD_XGBE diff --git a/scripts/st.sh b/scripts/st.sh index 7b581e21b3..0efd89f507 100755 --- a/scripts/st.sh +++ b/scripts/st.sh @@ -38,6 +38,10 @@ N_QEMU_INSTANCES=`ps -A | grep qemu-system | wc -l` launch_qemu() { QEMU_MAC_ADDR="$(printf 'DE:AD:BE:EF:%02X:%02X\n' $((N_QEMU_INSTANCES)) $((N_QEMU_INSTANCES)))" + # Second NIC (SO3's LAN9118, see ETH_OPT below) — must not collide with + # the virtio-net one above. + QEMU_ETH_MAC_ADDR="$(printf 'DE:AD:BE:EF:%02X:%02X\n' $((0x10 + N_QEMU_INSTANCES)) $((N_QEMU_INSTANCES)))" + GDB_PORT=$((${GDB_PORT_BASE} + ${N_QEMU_INSTANCES})) echo -e "\033[01;36mMAC addr: " ${QEMU_MAC_ADDR} "\033[0;37m" @@ -81,6 +85,24 @@ launch_qemu() { DISPLAY_OPT="-display none" fi + # Networking. Two NICs are attached, because the two kinds of guest this + # script boots need different ones: + # + # * virtio-net for the Linux agency (AVZ boot chain) — it has no + # smc911x node in its device tree, and hostfwd puts guest ssh on host + # port 2222; + # * an SMSC LAN9118 for SO3, whose only Ethernet driver is smc911x + # (devices/net/smc911x_lwip.c). QEMU's stock 'virt' machine has no + # such device; the so3 QEMU patch adds one at 0x08804000 / SPI 15 — + # the address and IRQ the SO3 device trees declare — and creates it + # only when the legacy on-board NIC slot is filled, which is what + # -nic does here. + # + # Both sit on their own user-mode (slirp) stack: QEMU plays DHCP, DNS and + # NAT internally, so the guest gets 10.0.2.15 with no host setup and no + # sudo. Whichever guest is booted simply ignores the NIC it cannot drive. + ETH_OPT="-nic user,model=lan9118,mac=${QEMU_ETH_MAC_ADDR}" + if [ "$IB_PLATFORM" == "virt64" ]; then QEMU_BIN="$IB_ROOT_DIR/qemu/build/qemu-system-aarch64" echo Starting on virt64 @@ -135,6 +157,7 @@ launch_qemu() { ${DISPLAY_OPT} \ -netdev user,id=n1,hostfwd=tcp::2222-:22 \ -device virtio-net-device,netdev=n1,mac=${QEMU_MAC_ADDR} \ + ${ETH_OPT} \ -gdb tcp::${GDB_PORT} fi @@ -158,6 +181,7 @@ launch_qemu() { ${DISPLAY_OPT} \ -netdev user,id=n1,hostfwd=tcp::2222-:22 \ -device virtio-net-device,netdev=n1,mac=${QEMU_MAC_ADDR} \ + ${ETH_OPT} \ -gdb tcp::${GDB_PORT} fi From 8b888fbf9a33fe562512d1e4d977cb22e41c050b Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Sun, 23 Aug 2026 14:03:52 +0200 Subject: [PATCH 2/8] so3: enable Ethernet on virt64 Point the ethernet nodes of the QEMU virt device trees at the LAN9118 the so3 QEMU patch now instantiates (0x08804000, SPI 15) instead of the vexpress-a15 0x1a000000 they carried, which is shadowed by the PCIe MMIO window. The stale xvisor/vexpress reference links go with them. CONFIG_SMC911X was unreachable on 64-bit ("depends on VIRT32"), so drop that restriction, and turn CONFIG_NET + CONFIG_SMC911X on in virt64_defconfig and virt64_fb_defconfig. virt32 keeps them off: the device tree is ready but nothing has been tested there. The driver had not been compiled in a long time and no longer built: DBG() used to come from soo/debug.h, which is only on the include path under CONFIG_SOO. Give it a local definition (the call sites also passed a status argument the format string had no conversion for). --- so3/so3/configs/virt64_defconfig | 3 ++- so3/so3/configs/virt64_fb_defconfig | 3 ++- so3/so3/devices/net/Kconfig | 2 +- so3/so3/devices/net/smc911x_lwip.c | 29 +++++++++++++++++++--------- so3/so3/dts/virt32.dts | 30 +++++++++++++++-------------- so3/so3/dts/virt32_lvperf.dts | 28 ++++++++++++++------------- so3/so3/dts/virt64.dts | 30 +++++++++++++++-------------- so3/so3/dts/virt64_lvperf.dts | 28 ++++++++++++++------------- 8 files changed, 87 insertions(+), 66 deletions(-) diff --git a/so3/so3/configs/virt64_defconfig b/so3/so3/configs/virt64_defconfig index 47bc26280d..bd524066f9 100644 --- a/so3/so3/configs/virt64_defconfig +++ b/so3/so3/configs/virt64_defconfig @@ -41,7 +41,7 @@ CONFIG_SCHED_FREQ_PREEMPTION=y CONFIG_UART=y CONFIG_IO_MAPPING_BASE=0xffff900000000000 # CONFIG_I2C is not set -# CONFIG_NET is not set +CONFIG_NET=y # CONFIG_FB is not set # CONFIG_INPUT is not set # CONFIG_NS16550 is not set @@ -55,6 +55,7 @@ CONFIG_GIC_V2=y # CONFIG_PL111_CLCD is not set # CONFIG_QEMU_RAMFB is not set # CONFIG_PL050_KMI is not set +CONFIG_SMC911X=y # # SO3 Applications diff --git a/so3/so3/configs/virt64_fb_defconfig b/so3/so3/configs/virt64_fb_defconfig index 00fde9945f..0777c9349a 100644 --- a/so3/so3/configs/virt64_fb_defconfig +++ b/so3/so3/configs/virt64_fb_defconfig @@ -41,7 +41,7 @@ CONFIG_SCHED_FREQ_PREEMPTION=y CONFIG_UART=y CONFIG_IO_MAPPING_BASE=0xffff900000000000 # CONFIG_I2C is not set -# CONFIG_NET is not set +CONFIG_NET=y CONFIG_FB=y CONFIG_INPUT=y # CONFIG_NS16550 is not set @@ -56,6 +56,7 @@ CONFIG_PL111_CLCD=y # CONFIG_QEMU_RAMFB is not set # CONFIG_VIRTFB is not set CONFIG_PL050_KMI=y +CONFIG_SMC911X=y # CONFIG_VIRT_INPUT is not set # diff --git a/so3/so3/devices/net/Kconfig b/so3/so3/devices/net/Kconfig index 916901c8a3..fae8b53161 100644 --- a/so3/so3/devices/net/Kconfig +++ b/so3/so3/devices/net/Kconfig @@ -1,7 +1,7 @@ config SMC911X bool "smc911x network interface" - depends on VIRT32 + depends on VIRT32 || VIRT64 depends on NET diff --git a/so3/so3/devices/net/smc911x_lwip.c b/so3/so3/devices/net/smc911x_lwip.c index 472cf098d2..3faae444a4 100644 --- a/so3/so3/devices/net/smc911x_lwip.c +++ b/so3/so3/devices/net/smc911x_lwip.c @@ -54,6 +54,17 @@ #include +/* Interrupt-path tracing, compiled out unless DEBUG is defined above. The + * kernel-wide DBG() lives in soo/debug.h, which is only on the include path + * under CONFIG_SOO, so this driver carries its own. */ +#ifdef DEBUG +#define DBG(fmt, ...) printk("%s:%i > " fmt, __func__, __LINE__, ##__VA_ARGS__) +#else +#define DBG(fmt, ...) \ + do { \ + } while (0) +#endif + #define ETHERNET_LAYER_2_MAX_LENGTH 1522 /* Basic mode control register. */ @@ -303,46 +314,46 @@ static irq_return_t smc911x_so3_interrupt(int irq, void *dummy) } if (status & INT_STS_SW_INT) { - DBG("STS_SW\n", status); + DBG("STS_SW: status %08x\n", status); smc911x_reg_write(dev, INT_STS, INT_STS_SW_INT); } /* Handle various error conditions */ if (status & INT_STS_RXE) { - DBG("STS_RXE\n", status); + DBG("STS_RXE: status %08x\n", status); smc911x_reg_write(dev, INT_STS, INT_STS_RXE); } if (status & INT_STS_RXDFH_INT) { - DBG("STS_RXDFH\n", status); + DBG("STS_RXDFH: status %08x\n", status); smc911x_reg_write(dev, INT_STS, INT_STS_RXDFH_INT); } /* Undocumented interrupt-what is the right thing to do here? */ if (status & INT_STS_RXDF_INT) { - DBG("STS_RXDF\n", status); + DBG("STS_RXDF: status %08x\n", status); smc911x_reg_write(dev, INT_STS, INT_STS_RXDF_INT); } /* Incoming frame */ if (status & INT_STS_RSFL) { - DBG("STS_RSFL\n", status); + DBG("STS_RSFL: status %08x\n", status); irq_return = IRQ_BOTTOM; smc911x_reg_write(dev, INT_STS, INT_STS_RSFL); } /* Rx Data FIFO exceeds set level */ if (status & INT_STS_RDFL) { - DBG("STS_RDFL\n", status); + DBG("STS_RDFL: status %08x\n", status); smc911x_reg_write(dev, INT_STS, INT_STS_RDFL); } if (status & INT_STS_RDFO) { - DBG("STS_RDFO\n", status); + DBG("STS_RDFO: status %08x\n", status); smc911x_reg_write(dev, INT_STS, INT_STS_RDFO); } if (status & (INT_STS_TSFL | INT_STS_GPT_INT)) { - DBG("STS_TSFL\n", status); + DBG("STS_TSFL: status %08x\n", status); smc911x_reg_write(dev, INT_STS, INT_STS_TSFL | INT_STS_GPT_INT); } if (status & INT_STS_PHY_INT) { - DBG("PHY_INT\n", status); + DBG("PHY_INT: status %08x\n", status); smc911x_reg_write(dev, INT_STS, INT_STS_PHY_INT); } } while (--timeout); diff --git a/so3/so3/dts/virt32.dts b/so3/so3/dts/virt32.dts index d6d16231a7..00ecd248a3 100644 --- a/so3/so3/dts/virt32.dts +++ b/so3/so3/dts/virt32.dts @@ -147,19 +147,21 @@ status = "ok"; }; - /* - https://github.com/psawargaonkar/xvisor-next/blob/95b887c82a37c8d9ee126e061cf4d8f383ec7d01/arch/arm/board/generic/dts/vexpress/a15/vexpress-a15.dtsi - https://github.com/avpatel/xvisor-next/blob/master/tests/arm32/vexpress-a15/vexpress-a15-guest.dts - */ - ethernet@1a000000 { - compatible = "smsc,smc911x"; - reg = <0x1a000000 0x1000>; - - interrupt-parent = <&gic>; - interrupts = <0 15 4>; - switch = "br0"; - - status = "ok"; - }; + /* SMSC LAN9118 Ethernet MAC (so3 QEMU patch, VIRT_ETH @ 0x08804000). + * QEMU's stock 'virt' machine has no Ethernet controller SO3 can drive — + * only virtio-net — so the machine model adds one next to the other SO3 + * devices. Not at the 0x1a000000 vexpress-a15 uses: that address falls + * inside the PCIe MMIO window, whose gpex alias shadows anything mapped + * underneath it. */ + ethernet@08804000 { + compatible = "smsc,smc911x"; + reg = <0x08804000 0x1000>; + + interrupt-parent = <&gic>; + /* SPI 15 — matches irqmap[VIRT_ETH] in the so3 QEMU patch. */ + interrupts = <0 15 4>; + + status = "ok"; + }; }; diff --git a/so3/so3/dts/virt32_lvperf.dts b/so3/so3/dts/virt32_lvperf.dts index 96cea89856..98ce88117b 100644 --- a/so3/so3/dts/virt32_lvperf.dts +++ b/so3/so3/dts/virt32_lvperf.dts @@ -108,19 +108,21 @@ }; - /* - https://github.com/psawargaonkar/xvisor-next/blob/95b887c82a37c8d9ee126e061cf4d8f383ec7d01/arch/arm/board/generic/dts/vexpress/a15/vexpress-a15.dtsi - https://github.com/avpatel/xvisor-next/blob/master/tests/arm32/vexpress-a15/vexpress-a15-guest.dts - */ - ethernet@1a000000 { - compatible = "smsc,smc911x"; - reg = <0x1a000000 0x1000>; - - interrupt-parent = <&gic>; - interrupts = <0 15 4>; - switch = "br0"; + /* SMSC LAN9118 Ethernet MAC (so3 QEMU patch, VIRT_ETH @ 0x08804000). + * QEMU's stock 'virt' machine has no Ethernet controller SO3 can drive — + * only virtio-net — so the machine model adds one next to the other SO3 + * devices. Not at the 0x1a000000 vexpress-a15 uses: that address falls + * inside the PCIe MMIO window, whose gpex alias shadows anything mapped + * underneath it. */ + ethernet@08804000 { + compatible = "smsc,smc911x"; + reg = <0x08804000 0x1000>; - status = "ok"; - }; + interrupt-parent = <&gic>; + /* SPI 15 — matches irqmap[VIRT_ETH] in the so3 QEMU patch. */ + interrupts = <0 15 4>; + + status = "ok"; + }; }; diff --git a/so3/so3/dts/virt64.dts b/so3/so3/dts/virt64.dts index 709fcc8d85..d14ed9ea34 100644 --- a/so3/so3/dts/virt64.dts +++ b/so3/so3/dts/virt64.dts @@ -162,19 +162,21 @@ status = "ok"; }; - /* - https://github.com/psawargaonkar/xvisor-next/blob/95b887c82a37c8d9ee126e061cf4d8f383ec7d01/arch/arm/board/generic/dts/vexpress/a15/vexpress-a15.dtsi - https://github.com/avpatel/xvisor-next/blob/master/tests/arm32/vexpress-a15/vexpress-a15-guest.dts - */ - ethernet@1a000000 { - compatible = "smsc,smc911x"; - reg = <0x0 0x1a000000 0x0 0x1000>; - - interrupt-parent = <&gic>; - interrupts = <0 15 4>; - switch = "br0"; - - status = "ok"; - }; + /* SMSC LAN9118 Ethernet MAC (so3 QEMU patch, VIRT_ETH @ 0x08804000). + * QEMU's stock 'virt' machine has no Ethernet controller SO3 can drive — + * only virtio-net — so the machine model adds one next to the other SO3 + * devices. Not at the 0x1a000000 vexpress-a15 uses: that address falls + * inside the PCIe MMIO window, whose gpex alias shadows anything mapped + * underneath it. */ + ethernet@08804000 { + compatible = "smsc,smc911x"; + reg = <0x0 0x08804000 0x0 0x1000>; + + interrupt-parent = <&gic>; + /* SPI 15 — matches irqmap[VIRT_ETH] in the so3 QEMU patch. */ + interrupts = <0 15 4>; + + status = "ok"; + }; }; diff --git a/so3/so3/dts/virt64_lvperf.dts b/so3/so3/dts/virt64_lvperf.dts index 07f1f2c866..4d4a0ae72b 100644 --- a/so3/so3/dts/virt64_lvperf.dts +++ b/so3/so3/dts/virt64_lvperf.dts @@ -116,19 +116,21 @@ status = "ok"; }; - /* - https://github.com/psawargaonkar/xvisor-next/blob/95b887c82a37c8d9ee126e061cf4d8f383ec7d01/arch/arm/board/generic/dts/vexpress/a15/vexpress-a15.dtsi - https://github.com/avpatel/xvisor-next/blob/master/tests/arm32/vexpress-a15/vexpress-a15-guest.dts - */ - ethernet@1a000000 { - compatible = "smsc,smc911x"; - reg = <0x0 0x1a000000 0x0 0x1000>; - - interrupt-parent = <&gic>; - interrupts = <0 15 4>; - switch = "br0"; + /* SMSC LAN9118 Ethernet MAC (so3 QEMU patch, VIRT_ETH @ 0x08804000). + * QEMU's stock 'virt' machine has no Ethernet controller SO3 can drive — + * only virtio-net — so the machine model adds one next to the other SO3 + * devices. Not at the 0x1a000000 vexpress-a15 uses: that address falls + * inside the PCIe MMIO window, whose gpex alias shadows anything mapped + * underneath it. */ + ethernet@08804000 { + compatible = "smsc,smc911x"; + reg = <0x0 0x08804000 0x0 0x1000>; - status = "ok"; - }; + interrupt-parent = <&gic>; + /* SPI 15 — matches irqmap[VIRT_ETH] in the so3 QEMU patch. */ + interrupts = <0 15 4>; + + status = "ok"; + }; }; From aa95106d173465fd934ad43f3102394c662ae1c8 Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Sun, 23 Aug 2026 14:03:52 +0200 Subject: [PATCH 3/8] net: hand the peer address back to user space recvfrom() converted the caller's sockaddr into an lwIP one, let lwip_recvfrom() fill it in, and then dropped it: the source address never left the kernel, so ping reported every reply as coming from 0.0.0.0. accept() did copy back, but with a memcpy of the lwIP layout, whose leading sa_len byte shifts sin_family. Add the reverse of user_to_lwip_sockadd() and use it in both. --- so3/so3/net/net.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/so3/so3/net/net.c b/so3/so3/net/net.c index 21effb26c0..5251263589 100644 --- a/so3/so3/net/net.c +++ b/so3/so3/net/net.c @@ -156,6 +156,26 @@ struct sockaddr *user_to_lwip_sockadd(const struct usr_sockaddr_in *usr, struct return (struct sockaddr *) lwip; } +/** + * Adapt a lwip sockaddr back to a userspace one. + * The two layouts differ by lwip's leading sa_len byte, so the peer address + * an lwip call filled in cannot simply be memcpy'd out to user space. + * @param lwip + * @param usr + */ +void lwip_to_user_sockadd(const struct sockaddr_in *lwip, struct usr_sockaddr_in *usr) +{ + if (usr == NULL) { + return; + } + + memset(usr, 0, sizeof(struct usr_sockaddr_in)); + + usr->sin_family = lwip->sin_family; + usr->sin_port = lwip->sin_port; + usr->sin_addr = lwip->sin_addr; +} + int ioctl_sock(int fd, unsigned long cmd, unsigned long args) { int ret; @@ -504,9 +524,8 @@ SYSCALL_DEFINE3(accept, int, sockfd, struct usr_sockaddr_in *, addr, socklen_t * /* TODO check fd ok */ lwip_fds[gfd] = lwip_bind_fd; - /* Copy back our sockaddr info in the usr data */ - if (addr) - memcpy(addr, addr_ptr, sizeof(struct sockaddr_in)); + /* Copy the peer address back out, converting the layout */ + lwip_to_user_sockadd(&addr_lwip, addr); return fd; } @@ -539,6 +558,12 @@ SYSCALL_DEFINE6(recvfrom, int, sockfd, void *, mem, size_t, len, int, flags, str from_ptr = user_to_lwip_sockadd(from, &from_lwip); ret = lwip_recvfrom(lwip_fd, mem, len, flags, from_ptr, fromlen); + + /* lwip_recvfrom() filled the SOURCE address into our local sockaddr; + * without this it never reached the caller (ping printed 0.0.0.0). */ + if (ret >= 0) + lwip_to_user_sockadd(&from_lwip, from); + return lwip_return(ret); } From 10f6b9077949e43026b17ed33a2b109b6cf390c4 Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Sun, 23 Aug 2026 14:03:52 +0200 Subject: [PATCH 4/8] doc: describe the QEMU virt NIC in the lwIP chapter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chapter still said QEMU's virt machine provides no NIC SO3 can drive and that a different driver would be needed. Replace that with what the machine now has, where the address and IRQ are defined, why the MAC is not at the vexpress address, and what a working run looks like — including the one known rough edge, the second echo request of a ping run being lost. --- doc/source/lwip.rst | 59 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/doc/source/lwip.rst b/doc/source/lwip.rst index 5a20de7ade..a91a40fd50 100644 --- a/doc/source/lwip.rst +++ b/doc/source/lwip.rst @@ -23,18 +23,57 @@ chapter. .. note:: - Networking is **opt-in**: it is enabled with ``CONFIG_NET`` and is *off* in - the default ``virt64_defconfig``. The smc911x MAC is present on boards such as - the ARM Versatile Express; QEMU's ``virt`` machine does not provide one, so a - different NIC driver is needed to exercise the stack there. + Networking is **opt-in**: ``CONFIG_NET`` plus ``CONFIG_SMC911X``. Both are on + in ``virt64_defconfig`` and ``virt64_fb_defconfig``, and off everywhere else — + the other platforms have no driver for their own MAC yet. + +The NIC on QEMU virt +==================== + +QEMU's stock ``virt`` machine offers only virtio-net, which SO3 has no driver +for. The so3 QEMU patch therefore adds an **SMSC LAN9118** to the machine model, +next to the PL111/PL050 devices it already adds: + +- MMIO base ``0x08804000`` (``VIRT_ETH`` in the ``hw/arm/virt.c`` memmap, + ``ethernet@08804000`` in ``dts/virt{32,64}.dts``); +- interrupt SPI 15 (``irqmap[VIRT_ETH]``, and ``interrupts = <0 15 4>`` in the + same DT nodes). + +Two details are worth knowing. The MAC is **not** at the ``0x1a000000`` the SO3 +device trees inherited from vexpress-a15: that address is inside the PCIe MMIO +window, whose gpex alias covers the whole range and shadows anything mapped +underneath, so the guest read back nothing and never detected the chip. And it +is only instantiated when the command line fills the legacy on-board NIC slot +(``-nic``/``-net``); without it the machine is exactly as it was and +``smc911x_detect_chip()`` simply fails. Trying it out ============= -With ``CONFIG_NET`` enabled and a supported NIC, the ``ping`` application -exercises the stack end to end. Under QEMU, ``st.sh`` attaches a **user-mode -(slirp)** network device — QEMU itself plays DHCP, DNS and NAT, and forwards host -port ``2222`` to the guest's port ``22`` — so nothing has to be set up on the host -and no ``sudo`` is needed. The trade-off is that the guest is NAT'd and not -reachable from the LAN. (``scripts/qemu-ifup.sh`` / ``qemu-ifdown.sh`` are +``ping`` exercises the stack end to end:: + + / % ping -c 4 10.0.2.2 + 64 bytes from 10.0.2.2: icmp_seq=1 ttl=255 time=0.302979 ms + +``st.sh`` attaches two NICs on **user-mode (slirp)** networking — QEMU itself +plays DHCP, DNS and NAT, so nothing has to be set up on the host and no ``sudo`` +is needed. The LAN9118 (``-nic user,model=lan9118``) is the one SO3 drives; the +virtio-net device is there for the Linux agency of the AVZ boot chain, and +carries the host port ``2222`` → guest port ``22`` forward. Each guest ignores +the NIC it cannot drive. The trade-off of slirp is that the guest is NAT'd and +not reachable from the LAN. (``scripts/qemu-ifup.sh`` / ``qemu-ifdown.sh`` are leftovers from the earlier ``tap``-and-bridge setup and are no longer used.) + +The interface comes up by DHCP during ``netif_add()``, so the address is +announced on the console shortly after boot:: + + Network Interface Controller (NIC) found LAN9118 + smc911x: detected LAN9118 controller + IP Network up and running with address 10.0.2.15 + +.. note:: + + One known rough edge: the second echo request of a fresh ``ping`` run is + consistently lost (the first is answered, so are all the following ones). + The reply never reaches the raw socket; the receive path of + ``devices/net/smc911x_lwip.c`` is the suspect. From 942133d3bce863ee096f06e5c7dc4a86d408a3da Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Sun, 23 Aug 2026 14:09:41 +0200 Subject: [PATCH 5/8] smc911x: interrupt on the first received frame, not the second MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every ping run lost exactly one packet — the reply to the second echo request — and every reply after that was reported with an implausible sub-0.2 ms round-trip time. The driver programmed an RX Status Level of 1 into FIFO_INT. The controller raises RSFL when the RX status FIFO holds MORE entries than that level, so the interrupt only came once a SECOND frame had arrived. Reception was therefore permanently one frame behind: the ARP reply sat in the FIFO until echo reply 1 arrived and released both, echo reply 2 sat there alone with nothing to trigger the interrupt until echo request 3 was answered, and from then on every recvfrom() returned the PREVIOUS reply, already queued, hence the near-zero round-trip times. Write level 0 (also the chip's reset value) so a single waiting frame raises RSFL. ping now reports 0% loss and honest timings — 6.5 ms for the first request, ~0.5 ms afterwards, instead of 300 ms then 0.1 ms. --- doc/source/lwip.rst | 15 +++++++++------ so3/so3/devices/net/smc911x_lwip.c | 7 ++++++- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/doc/source/lwip.rst b/doc/source/lwip.rst index a91a40fd50..869400f1e7 100644 --- a/doc/source/lwip.rst +++ b/doc/source/lwip.rst @@ -52,8 +52,11 @@ Trying it out ``ping`` exercises the stack end to end:: - / % ping -c 4 10.0.2.2 - 64 bytes from 10.0.2.2: icmp_seq=1 ttl=255 time=0.302979 ms + / % ping -c 6 10.0.2.2 + 64 bytes from 10.0.2.2: icmp_seq=1 ttl=255 time=6.497070 ms + 64 bytes from 10.0.2.2: icmp_seq=2 ttl=255 time=0.601074 ms + ... + 6 packets transmitted, 6 received, 0.000000% packet loss ``st.sh`` attaches two NICs on **user-mode (slirp)** networking — QEMU itself plays DHCP, DNS and NAT, so nothing has to be set up on the host and no ``sudo`` @@ -73,7 +76,7 @@ announced on the console shortly after boot:: .. note:: - One known rough edge: the second echo request of a fresh ``ping`` run is - consistently lost (the first is answered, so are all the following ones). - The reply never reaches the raw socket; the receive path of - ``devices/net/smc911x_lwip.c`` is the suspect. + The **RX Status Level** the driver programs into ``FIFO_INT`` must be 0: the + controller raises the RSFL interrupt when the RX status FIFO holds *more* + entries than that level, so a level of 1 only interrupts once a second frame + has arrived, and every reception stays one frame behind. diff --git a/so3/so3/devices/net/smc911x_lwip.c b/so3/so3/devices/net/smc911x_lwip.c index 3faae444a4..13d8a2ae33 100644 --- a/so3/so3/devices/net/smc911x_lwip.c +++ b/so3/so3/devices/net/smc911x_lwip.c @@ -461,8 +461,13 @@ err_t smc911x_lwip_init(struct netif *netif) netif_set_link_up(netif); netif_set_up(netif); + /* RX Status Level 0. The controller raises RSFL when the RX status FIFO + * holds MORE entries than this level, so the 1 that used to be written + * here only interrupted once a SECOND frame had arrived: every + * reception stayed one frame behind, and the reply to the second ping + * of a run was not delivered until the third one showed up. */ fifo = smc911x_reg_read(eth_dev, FIFO_INT); - smc911x_reg_write(eth_dev, FIFO_INT, 0x01 | (fifo & 0xFFFFFF00)); + smc911x_reg_write(eth_dev, FIFO_INT, fifo & ~FIFO_INT_RX_STS_LEVEL); /* Turn on relevant interrupts */ smc911x_reg_write(eth_dev, INT_EN, INT_EN_RSFL_EN | INT_EN_RSFF_EN); From 82d1d494f73f34156347037994985ebd68286870 Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Sun, 23 Aug 2026 15:03:33 +0200 Subject: [PATCH 6/8] net: use the global descriptor in the socket file operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VFS calls fops->read/write/close with the GLOBAL file descriptor (fs/vfs.c), while the socket syscalls get the process-local one. read_sock(), write_sock() and close_sock() ran the local-to-global translation anyway, so they looked up an unrelated entry of the process fd table — and in close_sock()'s case usually -1, since sys_close() releases fd_array[fd] before calling the operation, which then indexed lwip_fds[] out of bounds. lwip_close() therefore never reached the socket. Every ping run leaked its raw pcb and the packets queued on it, and with MEMP_NUM_NETBUF at 4 the third run in a session started losing replies for good. Give the fops their own accessor, clear the slot on close so a recycled gfd cannot be taken for the old socket, and initialise lwip_fds[] to -1: 0 is a valid lwip socket id, so a zeroed table made every descriptor look like socket 0. --- so3/so3/net/net.c | 55 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/so3/so3/net/net.c b/so3/so3/net/net.c index 5251263589..6b8665db78 100644 --- a/so3/so3/net/net.c +++ b/so3/so3/net/net.c @@ -56,6 +56,20 @@ static int lwip_return(int ret) return ret; } +/** + * lwip_fds[] is indexed by GLOBAL file descriptor. + * + * @param Global file descriptor (gfd) + * @return Associated socket ID from lwip + */ +static int get_lwip_fd_from_gfd(int gfd) +{ + if ((gfd < 0) || (gfd >= MAX_FDS)) + return -1; + + return lwip_fds[gfd]; +} + /** * * @param Local file descriptor (fd) @@ -63,23 +77,25 @@ static int lwip_return(int ret) */ static int get_lwip_fd(int fd) { - int gfd; - /* Get the gfd from this fd */ - gfd = current()->pcb->fd_array[fd]; - - if (gfd < MAX_FDS) - return lwip_fds[gfd]; - else - return -1; + return get_lwip_fd_from_gfd(current()->pcb->fd_array[fd]); } /**************************** Network subsystem ***************************************/ -int read_sock(int fd, void *buffer, int count) +/* + * The file operations below are the ones the VFS calls, and it passes them the + * GLOBAL descriptor (see the fops->read/write/close call sites in fs/vfs.c) — + * unlike the socket syscalls, which get the process-local one. Translating a + * gfd a second time used to land on an unrelated entry of the process fd table, + * or on -1 once close() had already released it: lwip_close() then never + * reached the socket, leaking its pcb and every packet queued on it. + */ + +int read_sock(int gfd, void *buffer, int count) { int ret; - int lwip_fd = get_lwip_fd(fd); + int lwip_fd = get_lwip_fd_from_gfd(gfd); if (lwip_fd < 0) { return -EBADF; @@ -89,10 +105,10 @@ int read_sock(int fd, void *buffer, int count) return lwip_return(ret); } -int write_sock(int fd, const void *buffer, int count) +int write_sock(int gfd, const void *buffer, int count) { int ret; - int lwip_fd = get_lwip_fd(fd); + int lwip_fd = get_lwip_fd_from_gfd(gfd); if (lwip_fd < 0) { return -EBADF; @@ -102,15 +118,19 @@ int write_sock(int fd, const void *buffer, int count) return lwip_return(ret); } -int close_sock(int fd) +int close_sock(int gfd) { int ret; - int lwip_fd = get_lwip_fd(fd); + int lwip_fd = get_lwip_fd_from_gfd(gfd); if (lwip_fd < 0) { return -EBADF; } + /* Release the slot before the socket is gone, so a gfd handed out again + * later cannot be mistaken for this socket. */ + lwip_fds[gfd] = -1; + ret = lwip_close(lwip_fd); return lwip_return(ret); } @@ -618,6 +638,13 @@ static void network_tcpip_done(void *args) void net_init(void) { + int gfd; + + /* 0 is a perfectly valid lwip socket id, so the zero-initialised table + * would make every descriptor look like socket 0. */ + for (gfd = 0; gfd < MAX_FDS; gfd++) + lwip_fds[gfd] = -1; + tcpip_init(network_tcpip_done, NULL); } From 20d448e2f14b87fb7c6922a770f0c05f034453db Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Sun, 23 Aug 2026 15:03:33 +0200 Subject: [PATCH 7/8] usr: report what ping actually got Two things ping got wrong once there was a network to test it on. A failed recvfrom() was only reported from the second request onwards ("&& msg_count > 1"), so the first timeout of a run fell through to the reply parsing and came out as "Truncated reply of -1 bytes". Report it whatever the sequence number. An ICMP error was printed as raw numbers: pinging an address slirp cannot reach gave "Error... Packet received with ICMP type 3 code 3". Decode the types a ping is likely to meet and print the usual form, "From icmp_seq= Destination Port Unreachable", falling back to the numbers for anything unexpected. The lwIP chapter gains the host-side reason for that particular one: net.ipv4.ping_group_range is empty by default on several distributions, so slirp cannot open an ICMP socket and emulates the echo over UDP. --- doc/source/lwip.rst | 11 ++++++++ so3/usr/src/ping.c | 61 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/doc/source/lwip.rst b/doc/source/lwip.rst index 869400f1e7..0812a54cf0 100644 --- a/doc/source/lwip.rst +++ b/doc/source/lwip.rst @@ -74,6 +74,17 @@ announced on the console shortly after boot:: smc911x: detected LAN9118 controller IP Network up and running with address 10.0.2.15 +Pinging past the slirp gateway (``10.0.2.2``) needs the **host** to let QEMU +open ICMP sockets, otherwise slirp emulates the echo over UDP and relays back +the port-unreachable it gets:: + + From 192.168.1.1 icmp_seq=1 Destination Port Unreachable + +The kernel decides that with ``net.ipv4.ping_group_range``, whose default on +several distributions is the empty range ``1 0``. Widening it (``sysctl -w +net.ipv4.ping_group_range="0 2147483647"``) lets slirp forward the echo for +real. Nothing in SO3 is involved either way. + .. note:: The **RX Status Level** the driver programs into ``FIFO_INT`` must be 0: the diff --git a/so3/usr/src/ping.c b/so3/usr/src/ping.c index ebe17144c9..c7cb8511e8 100644 --- a/so3/usr/src/ping.c +++ b/so3/usr/src/ping.c @@ -55,6 +55,47 @@ struct ping_pkt { char msg[PING_PKT_LEN - sizeof(struct icmphdr)]; }; +/** + * Human-readable reason for an ICMP message that is not an echo reply, or NULL + * when the type is one we have nothing better to say about than its numbers. + */ +static const char *icmp_reason(unsigned char type, unsigned char code) +{ + switch (type) { + case ICMP_DEST_UNREACH: + switch (code) { + case ICMP_NET_UNREACH: + return "Destination Net Unreachable"; + case ICMP_HOST_UNREACH: + return "Destination Host Unreachable"; + case ICMP_PROT_UNREACH: + return "Destination Protocol Unreachable"; + case ICMP_PORT_UNREACH: + return "Destination Port Unreachable"; + case ICMP_FRAG_NEEDED: + return "Fragmentation needed but DF set"; + case ICMP_SR_FAILED: + return "Source Route Failed"; + case ICMP_NET_ANO: + case ICMP_HOST_ANO: + case ICMP_PKT_FILTERED: + return "Communication administratively prohibited"; + default: + return "Destination Unreachable"; + } + case ICMP_SOURCE_QUENCH: + return "Source Quench"; + case ICMP_REDIRECT: + return "Redirect"; + case ICMP_TIME_EXCEEDED: + return (code == ICMP_EXC_TTL) ? "Time to live exceeded" : "Fragment reassembly time exceeded"; + case ICMP_PARAMETERPROB: + return "Parameter problem"; + default: + return NULL; + } +} + /** * Compute the checksum * From https://www.geeksforgeeks.org/ping-in-c/ @@ -170,6 +211,7 @@ int main(int argc, char **argv) unsigned int size = 0; float rtt = 0, rtt_total = 0.0, rtt_min = 1000000.0, rtt_max = 0.0; char ip[100]; + const char *reason; struct ping_pkt packet; char reply[sizeof(struct iphdr) + PING_PKT_LEN]; struct iphdr *iph; @@ -231,7 +273,9 @@ int main(int argc, char **argv) len = recvfrom(s, reply, sizeof(reply), 0, (struct sockaddr *) &recv_addr, &size); - if (len <= 0 && msg_count > 1) { + /* A timeout (SO_RCVTIMEO) lands here too, which is the normal + * outcome for a host that never answers. */ + if (len <= 0) { printf("Packet receive failed!!\n"); continue; } @@ -256,9 +300,7 @@ int main(int argc, char **argv) icmph = (struct icmphdr *) (reply + hlen); - if (!(icmph->type == ICMP_ECHOREPLY && icmph->code == 0)) { - printf("Error... Packet received with ICMP type %d code %d\n", icmph->type, icmph->code); - } else { + if (icmph->type == ICMP_ECHOREPLY && icmph->code == 0) { printf("%d bytes from %s: icmp_seq=%d ttl=%d time=%f ms\n", len - hlen, ip, msg_count, iph->ttl, rtt); rtt_max = fmaxf(rtt_max, rtt); @@ -266,6 +308,17 @@ int main(int argc, char **argv) rtt_total += rtt; msg_count_succeed++; + } else { + /* Not an echo reply: an ICMP error about the request we + * just sent, reported by a router or by the stack of the + * destination itself. */ + + reason = icmp_reason(icmph->type, icmph->code); + + if (reason != NULL) + printf("From %s icmp_seq=%d %s\n", ip, msg_count, reason); + else + printf("From %s icmp_seq=%d ICMP type %d code %d\n", ip, msg_count, icmph->type, icmph->code); } } From 2a91937dbae43f4fec0429288d86f436f56939b9 Mon Sep 17 00:00:00 2001 From: Daniel Rossier Date: Sun, 23 Aug 2026 15:11:20 +0200 Subject: [PATCH 8/8] so3: enable Ethernet on virt32 too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 32-bit QEMU virt machine gets the same LAN9118 as the 64-bit one — it is the same machine model — and virt32.dts already points at it, so only the defconfigs were missing. Turn CONFIG_NET + CONFIG_SMC911X on in virt32_defconfig and virt32_fb_defconfig. While here, move CONFIG_SMC911X after the input symbols in virt64_fb_defconfig: devices/Kconfig sources input/ before net/, so that is where savedefconfig puts it. --- doc/source/lwip.rst | 5 +++-- so3/so3/configs/virt32_defconfig | 3 ++- so3/so3/configs/virt32_fb_defconfig | 3 ++- so3/so3/configs/virt64_fb_defconfig | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/source/lwip.rst b/doc/source/lwip.rst index 0812a54cf0..680c8d9d82 100644 --- a/doc/source/lwip.rst +++ b/doc/source/lwip.rst @@ -24,8 +24,9 @@ chapter. .. note:: Networking is **opt-in**: ``CONFIG_NET`` plus ``CONFIG_SMC911X``. Both are on - in ``virt64_defconfig`` and ``virt64_fb_defconfig``, and off everywhere else — - the other platforms have no driver for their own MAC yet. + in the four QEMU virt defconfigs (``virt32``/``virt64``, plain and ``_fb``), + and off everywhere else — rpi4 and verdin have no driver for their own MAC + yet. The NIC on QEMU virt ==================== diff --git a/so3/so3/configs/virt32_defconfig b/so3/so3/configs/virt32_defconfig index 4e8b9060f5..c8328d9a8a 100644 --- a/so3/so3/configs/virt32_defconfig +++ b/so3/so3/configs/virt32_defconfig @@ -38,7 +38,7 @@ CONFIG_SCHED_FREQ_PREEMPTION=y CONFIG_UART=y CONFIG_IO_MAPPING_BASE=0xe0000000 # CONFIG_I2C is not set -# CONFIG_NET is not set +CONFIG_NET=y # CONFIG_FB is not set # CONFIG_INPUT is not set # CONFIG_NS16550 is not set @@ -52,6 +52,7 @@ CONFIG_GIC=y CONFIG_GIC_V2=y # CONFIG_PL050_KMI is not set # CONFIG_VIRT_INPUT is not set +CONFIG_SMC911X=y # # SO3 Applications diff --git a/so3/so3/configs/virt32_fb_defconfig b/so3/so3/configs/virt32_fb_defconfig index 89482db88a..0a9fb53a49 100644 --- a/so3/so3/configs/virt32_fb_defconfig +++ b/so3/so3/configs/virt32_fb_defconfig @@ -38,7 +38,7 @@ CONFIG_SCHED_FREQ_PREEMPTION=y CONFIG_UART=y CONFIG_IO_MAPPING_BASE=0xe0000000 # CONFIG_I2C is not set -# CONFIG_NET is not set +CONFIG_NET=y CONFIG_FB=y CONFIG_INPUT=y # CONFIG_NS16550 is not set @@ -55,6 +55,7 @@ CONFIG_PL111_CLCD=y # CONFIG_VIRTFB is not set CONFIG_PL050_KMI=y # CONFIG_VIRT_INPUT is not set +CONFIG_SMC911X=y # # SO3 Applications diff --git a/so3/so3/configs/virt64_fb_defconfig b/so3/so3/configs/virt64_fb_defconfig index 0777c9349a..d8f1ba5f7e 100644 --- a/so3/so3/configs/virt64_fb_defconfig +++ b/so3/so3/configs/virt64_fb_defconfig @@ -56,8 +56,8 @@ CONFIG_PL111_CLCD=y # CONFIG_QEMU_RAMFB is not set # CONFIG_VIRTFB is not set CONFIG_PL050_KMI=y -CONFIG_SMC911X=y # CONFIG_VIRT_INPUT is not set +CONFIG_SMC911X=y # # SO3 Applications