All of lore.kernel.org
 help / color / mirror / Atom feed
From: Ana Bobillo <anabobillo3@gmail.com>
To: Marek Vasut <marek.vasut+usb@mailbox.org>
Cc: Tom Rini <trini@konsulko.com>, Simon Glass <sjg@chromium.org>,
	Jonas Karlman <jonas@kwiboo.se>,
	u-boot@lists.u-boot-project.org
Subject: [PATCH RESEND 4/4] test: dm: Add tests for the USB Ethernet rx header checks
Date: Thu, 29 Oct 2026 06:07:05 +0000	[thread overview]
Message-ID: <81f03167-12d5-4bb3-b2ae-03633a399f9d@gmail.com> (raw)
In-Reply-To: <f9afa215-645f-400f-8743-eaeca3f6a2f5@gmail.com>

From: Ana Bobillo <anabobillo3@gmail.com>

Add DM tests that bind the r8152, ax88179, lan75xx and lan78xx drivers
without probing them and hand their recv() and free_pkt() prepared
transfers in the receive buffer: well-formed ones, which must come back
as the packets they hold, and ones whose header or lengths run past the
transfer, which must be dropped.

Enable CONFIG_USB_HOST_ETHER and these drivers in sandbox_defconfig so
that they are built and tested under sandbox.

Signed-off-by: Ana Bobillo <anabobillo3@gmail.com>
---
 configs/sandbox_defconfig |   5 +
 test/dm/Makefile          |   1 +
 test/dm/usb_eth.c         | 375 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 381 insertions(+)
 create mode 100644 test/dm/usb_eth.c

diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index a3fa5246082..1d409891580 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -359,6 +359,11 @@ CONFIG_USB=y
 CONFIG_DM_USB_GADGET=y
 CONFIG_USB_EMUL=y
 CONFIG_USB_KEYBOARD=y
+CONFIG_USB_HOST_ETHER=y
+CONFIG_USB_ETHER_ASIX88179=y
+CONFIG_USB_ETHER_LAN75XX=y
+CONFIG_USB_ETHER_LAN78XX=y
+CONFIG_USB_ETHER_RTL8152=y
 CONFIG_USB_GADGET=y
 CONFIG_USB_GADGET_DOWNLOAD=y
 CONFIG_USB_ETHER=y
diff --git a/test/dm/Makefile b/test/dm/Makefile
index 1b3a20aa932..e22cbf2c2ba 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -150,6 +150,7 @@ obj-$(CONFIG_TEE) += tee.o
 obj-$(CONFIG_TIMER) += timer.o
 obj-$(CONFIG_TPM_V2) += tpm.o
 obj-$(CONFIG_DM_USB) += usb.o
+obj-$(CONFIG_USB_HOST_ETHER) += usb_eth.o
 obj-$(CONFIG_VIDEO_SANDBOX_SDL) += video.o
 ifeq ($(CONFIG_VIRTIO_SANDBOX),y)
 obj-y += virtio.o
diff --git a/test/dm/usb_eth.c b/test/dm/usb_eth.c
new file mode 100644
index 00000000000..2f8903b71b6
--- /dev/null
+++ b/test/dm/usb_eth.c
@@ -0,0 +1,375 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for the receive header checks of the USB Ethernet drivers
+ *
+ * Each driver is bound without being probed, so that no USB device sits
+ * behind it, and its recv() is handed a prepared transfer in the receive
+ * buffer.
+ */
+
+#include <dm.h>
+#include <net.h>
+#include <usb_ether.h>
+#include <asm/unaligned.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include "../../drivers/usb/eth/lan7x.h"
+#include "../../drivers/usb/eth/r8152.h"
+
+static u8 rxbuf[256] __maybe_unused;
+
+/*
+ * Bind @drv to a new device and present the first @len bytes of rxbuf to it
+ * as a received transfer. device_of_to_plat() allocates the private data
+ * without probing the device; @ueth_offset is where the driver keeps its
+ * struct ueth_data in there.
+ */
+static int __maybe_unused usb_eth_test_bind(struct unit_test_state *uts,
+					    struct driver *drv,
+					    size_t ueth_offset, int len,
+					    struct udevice **devp,
+					    struct ueth_data **uethp)
+{
+	struct ueth_data *ueth;
+
+	ut_assertok(device_bind(dm_root(), drv, drv->name, NULL, ofnode_null(),
+				devp));
+	ut_assertok(device_of_to_plat(*devp));
+	ueth = dev_get_priv(*devp) + ueth_offset;
+	ueth->rxbuf = rxbuf;
+	ueth->rxsize = sizeof(rxbuf);
+	ueth->rxlen = len;
+	ueth->rxptr = 0;
+	*uethp = ueth;
+
+	return 0;
+}
+
+static int __maybe_unused usb_eth_test_recv(struct udevice *dev,
+					    uchar **packetp)
+{
+	return eth_get_ops(dev)->recv(dev, 0, packetp);
+}
+
+static int __maybe_unused usb_eth_test_free_pkt(struct udevice *dev,
+						uchar *packet, int len)
+{
+	return eth_get_ops(dev)->free_pkt(dev, packet, len);
+}
+
+#if IS_ENABLED(CONFIG_USB_ETHER_RTL8152)
+static int r8152_test_bind(struct unit_test_state *uts, int len,
+			   struct udevice **devp, struct ueth_data **uethp)
+{
+	return usb_eth_test_bind(uts, DM_DRIVER_GET(r8152_eth),
+				 offsetof(struct r8152, ueth), len, devp,
+				 uethp);
+}
+
+/* r8152_eth_recv() returns each packet of an aggregated transfer */
+static int dm_test_r8152_eth_recv(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	/* A packet of 60 bytes, then one of 42 at the next 8-byte boundary */
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(60 + CRC_SIZE, rxbuf);
+	put_unaligned_le32(42 + CRC_SIZE, rxbuf + 88);
+	ut_assertok(r8152_test_bind(uts, 88 + sizeof(struct rx_desc) + 42 +
+				    CRC_SIZE, &dev, &ueth));
+
+	ut_asserteq(60, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + sizeof(struct rx_desc), packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 60));
+	ut_asserteq(42, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 88 + sizeof(struct rx_desc), packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 42));
+	ut_asserteq(-EAGAIN, usb_eth_test_recv(dev, &packet));
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_r8152_eth_recv, 0);
+
+/* r8152_eth_recv() rejects a transfer too short for the rx descriptor */
+static int dm_test_r8152_eth_recv_short(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(CRC_SIZE + 2, rxbuf);
+	ut_assertok(r8152_test_bind(uts, 8, &dev, &ueth));
+
+	ut_asserteq(-ENOSPC, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_r8152_eth_recv_short, 0);
+
+/* r8152_eth_recv() rejects a descriptor length shorter than the CRC */
+static int dm_test_r8152_eth_recv_crc(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(CRC_SIZE - 2, rxbuf);
+	ut_assertok(r8152_test_bind(uts, sizeof(struct rx_desc) + 64, &dev,
+				    &ueth));
+
+	ut_asserteq(-ENOSPC, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_r8152_eth_recv_crc, 0);
+#endif
+
+#if IS_ENABLED(CONFIG_USB_ETHER_ASIX88179)
+/*
+ * struct asix_private is local to the driver; its struct ueth_data comes
+ * first
+ */
+static int ax88179_test_bind(struct unit_test_state *uts, int len,
+			     struct udevice **devp, struct ueth_data **uethp)
+{
+	return usb_eth_test_bind(uts, DM_DRIVER_GET(ax88179_eth), 0, len, devp,
+				 uethp);
+}
+
+/*
+ * Prepare a transfer of 64 bytes of packet data, two packet headers, the
+ * first giving @pkt_len, and the rx header giving @pkt_cnt of them
+ */
+static int ax88179_test_rx(int pkt_cnt, int pkt_len)
+{
+	const int hdr_off = 64;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(pkt_len << 16, rxbuf + hdr_off);
+	put_unaligned_le32(hdr_off << 16 | pkt_cnt, rxbuf + hdr_off + 8);
+
+	return hdr_off + 12;
+}
+
+/* ax88179_eth_recv() returns each packet of the transfer */
+static int dm_test_ax88179_eth_recv(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+	int len;
+
+	/* A packet of 22 bytes padded to 24, then one of 40 up to the headers */
+	len = ax88179_test_rx(2, 22);
+	put_unaligned_le32(40 << 16, rxbuf + 68);
+	ut_assertok(ax88179_test_bind(uts, len, &dev, &ueth));
+
+	ut_asserteq(22, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 2, packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 22));
+	ut_asserteq(40, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 24 + 2, packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 40));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_ax88179_eth_recv, 0);
+
+/* ax88179_eth_recv() rejects packet headers running past the rx header */
+static int dm_test_ax88179_eth_recv_pkt_cnt(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+	int len;
+
+	len = ax88179_test_rx(3, 22);
+	ut_assertok(ax88179_test_bind(uts, len, &dev, &ueth));
+
+	ut_asserteq(-EIO, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_ax88179_eth_recv_pkt_cnt, 0);
+
+/* ax88179_eth_recv() rejects a packet running past the packet headers */
+static int dm_test_ax88179_eth_recv_pkt_len(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+	int len;
+
+	len = ax88179_test_rx(1, 65);
+	ut_assertok(ax88179_test_bind(uts, len, &dev, &ueth));
+
+	ut_asserteq(-EIO, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_ax88179_eth_recv_pkt_len, 0);
+#endif
+
+#if IS_ENABLED(CONFIG_USB_ETHER_LAN75XX) || IS_ENABLED(CONFIG_USB_ETHER_LAN78XX)
+static int lan7x_test_bind(struct unit_test_state *uts, struct driver *drv,
+			   u32 chipid, int len, struct udevice **devp,
+			   struct ueth_data **uethp)
+{
+	struct lan7x_private *priv;
+
+	ut_assertok(usb_eth_test_bind(uts, drv,
+				      offsetof(struct lan7x_private, ueth), len,
+				      devp, uethp));
+	priv = dev_get_priv(*devp);
+	priv->chipid = chipid;
+
+	return 0;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_USB_ETHER_LAN78XX)
+static int lan78xx_test_bind(struct unit_test_state *uts, int len,
+			     struct udevice **devp, struct ueth_data **uethp)
+{
+	return lan7x_test_bind(uts, DM_DRIVER_GET(lan78xx_eth),
+			       ID_REV_CHIP_ID_7800, len, devp, uethp);
+}
+
+/* lan7x_eth_recv() returns the frame behind command A, B and C */
+static int dm_test_lan7x_eth_recv(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(60, rxbuf);
+	ut_assertok(lan78xx_test_bind(uts, 10 + 60, &dev, &ueth));
+
+	ut_asserteq(60, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 10, packet);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_eth_recv, 0);
+
+/* lan7x_eth_recv() rejects a transfer too short for command A, B and C */
+static int dm_test_lan7x_eth_recv_short(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(2, rxbuf);
+	ut_assertok(lan78xx_test_bind(uts, 6, &dev, &ueth));
+
+	ut_asserteq(-EINVAL, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_eth_recv_short, 0);
+
+/* lan7x_eth_recv() rejects a frame running past the transfer */
+static int dm_test_lan7x_eth_recv_len(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(64, rxbuf);
+	ut_assertok(lan78xx_test_bind(uts, 4 + 64, &dev, &ueth));
+
+	ut_asserteq(-EINVAL, usb_eth_test_recv(dev, &packet));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_eth_recv_len, 0);
+
+/* lan7x_free_pkt() steps over the header, the frame and its padding */
+static int dm_test_lan7x_free_pkt(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	/* A frame of 61 bytes, then one of 60 at the next 4-byte boundary */
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(61, rxbuf);
+	put_unaligned_le32(60, rxbuf + 72);
+	ut_assertok(lan78xx_test_bind(uts, 72 + 10 + 60, &dev, &ueth));
+
+	ut_asserteq(61, usb_eth_test_recv(dev, &packet));
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 61));
+	ut_asserteq(60, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 72 + 10, packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 60));
+	ut_asserteq(0, ueth->rxlen);
+	ut_asserteq(-EAGAIN, usb_eth_test_recv(dev, &packet));
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_free_pkt, 0);
+#endif
+
+#if IS_ENABLED(CONFIG_USB_ETHER_LAN75XX)
+/*
+ * lan7x_eth_recv() returns the LAN75xx frame behind command A, B and the
+ * padding, which its length counts
+ */
+static int dm_test_lan7x_eth_recv_lan75xx(struct unit_test_state *uts)
+{
+	struct ueth_data *ueth;
+	struct udevice *dev;
+	uchar *packet;
+
+	memset(rxbuf, 0, sizeof(rxbuf));
+	put_unaligned_le32(2 + 60, rxbuf);
+	ut_assertok(lan7x_test_bind(uts, DM_DRIVER_GET(lan75xx_eth),
+				    ID_REV_CHIP_ID_7500, 10 + 60, &dev, &ueth));
+
+	ut_asserteq(60, usb_eth_test_recv(dev, &packet));
+	ut_asserteq_ptr(rxbuf + 10, packet);
+	ut_assertok(usb_eth_test_free_pkt(dev, packet, 60));
+	ut_asserteq(0, ueth->rxlen);
+
+	ut_assertok(device_unbind(dev));
+
+	return 0;
+}
+DM_TEST(dm_test_lan7x_eth_recv_lan75xx, 0);
+#endif

      parent reply	other threads:[~2026-10-29  6:07 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29  6:06 [PATCH RESEND 0/4] usb: eth: Check rx header lengths in r8152, asix88179 and lan7x Ana Bobillo
2026-10-29  6:06 ` [PATCH RESEND 1/4] usb: eth: r8152: Check the rx descriptor fits in the transfer Ana Bobillo
2026-10-29  6:06 ` [PATCH RESEND 2/4] usb: eth: asix88179: Bound the rx packet headers and packets Ana Bobillo
2026-10-29  6:06 ` [PATCH RESEND 3/4] usb: eth: lan7x: Account for the whole rx command header Ana Bobillo
2026-10-29  6:07 ` Ana Bobillo [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=81f03167-12d5-4bb3-b2ae-03633a399f9d@gmail.com \
    --to=anabobillo3@gmail.com \
    --cc=jonas@kwiboo.se \
    --cc=marek.vasut+usb@mailbox.org \
    --cc=sjg@chromium.org \
    --cc=trini@konsulko.com \
    --cc=u-boot@lists.u-boot-project.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.