From nobody Fri Nov 14 17:01:13 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1761946115; cv=none; d=zohomail.com; s=zohoarc; b=JQQbh9+PQd5Dj7m/RhcahP3WXoED01yEY7vyBRv+JUB7ONuYF1DZ0CBwEHiVzJC2ynDPGD3rQN9lxlozObxr0PT0oJXxkdqVmUqeadpnjqWT6meUmIjKXIOb4KbA463IJQFfv30BBnWz9G9anFMzq7pvt1dWZVnbvT3l1DI7kdU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1761946115; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=CPCl8R+I/SKh5BgbKo9T2ZRm8RcVBHVWYZqEpXNjYEs=; b=RqgOtRaRMSC1fiCPQEBYwu+xda3RCjxFoZVlYmgzcmW9oeGMaonTX9/QiCMP45ImczNiEaTJOKu8Mp90nZ+Hxv8k1pbdcYV5e4l/JNXnZcoFIckdpsgY+R0RqkdWQ7dNk/w1xiO6SOSYDo8V9ebNdNDUe+cltAxHe+8vH3C65wc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1761946115959192.7482832891269; Fri, 31 Oct 2025 14:28:35 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vEweA-0006Yg-Qv; Fri, 31 Oct 2025 17:26:58 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vEwT9-0000HG-Dy for qemu-devel@nongnu.org; Fri, 31 Oct 2025 17:15:35 -0400 Received: from mail-pl1-x641.google.com ([2607:f8b0:4864:20::641]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vEwT3-0001xD-HN for qemu-devel@nongnu.org; Fri, 31 Oct 2025 17:15:35 -0400 Received: by mail-pl1-x641.google.com with SMTP id d9443c01a7336-290ab379d48so27396845ad.2 for ; Fri, 31 Oct 2025 14:15:29 -0700 (PDT) Received: from fedora.. ([103.2.232.250]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-b93b8aa3ff1sm3023320a12.14.2025.10.31.14.15.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 31 Oct 2025 14:15:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1761945326; x=1762550126; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=CPCl8R+I/SKh5BgbKo9T2ZRm8RcVBHVWYZqEpXNjYEs=; b=HpdTJWazIvAAoIsaHzDAZhjsC2EtX7Uzib4ECZjrzB7w6o7AVO1JRtlkB2yYN23tOu 1tUACkJR8FDEl7kjCe1RBSD4+tcZPYYMqQcgAF0VQvCkMscnTsZhtw69+2IpiOV5FIzL c/v8QUjWeC9usS9FaJhd0VluqYM54b9keMKP1LEpf7yl4csKaT3tMwlG5XWiIyQXuR6O I1Mw5b3ljsOw5ChVli5opXvxHJZ5mmvHGWTA0w48W4KbPHC0ByXzVjLYRN6ZI2eUAGKA coWo5xzDdJCFbbITHmdbCI4LmzcMF7nIWsKBq9GZpkJN9xFLqTG91INQG+U1rDtBfHf2 am+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1761945326; x=1762550126; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=CPCl8R+I/SKh5BgbKo9T2ZRm8RcVBHVWYZqEpXNjYEs=; b=bc7+A0HGutiUdShE0Iv0H6+zzBDFyGsVtlL3/7UEnRLhnQz3EBSCpWPSHxLgtEzsy1 6+QIuXSWr0SYtw9c6VzMQMekFljifAR3Rlvae+oSjmz2bNE6EwiRIcpsOyuvKm1SKKvh /pnc8vEfwHb+bQ67M8+MnXoNkrB0z6VHqQN29ySOlBw40JgJOU+A6pPL5WVu6STuCfzX phtCCukuVYepCuXMZVDIRDoDvUOYBh56fMkjhssXWsJQStmr33eHlmHiuOzG0fahq4iK P6CVXxCEICrp9acq+xyj+3v8xuRxC9YzXiQ2MHGhSO+jAuXdarrAq1WsBE50fdgnVp5s vIrQ== X-Forwarded-Encrypted: i=1; AJvYcCV/VvgnH5k6gqrgnj+v1TM2qBa3jNO6UGhEKEJlZ2XIEeuilVlio2Dy//CwwsDNDOLfG4OempNQAOm7@nongnu.org X-Gm-Message-State: AOJu0Yzcql6gj4AtvwtD/28Ceu7PVFnc3H+ElbMUvppSfKADAjqxonaL nT1CFSPOlAsK/1R/7bOO+MUDLcNo99oVWcjd30snujH3CbuT3FgeulSp X-Gm-Gg: ASbGncuNimEtz4mZS+xlRY5nNGLwRUSXIONd1X2fBpbiONLYvymiKnl8VdSJ99fFGeZ Qn1QSullKKc/rZjupNTg9uYiSIADqDFjhGtqGkp31L0Epdhn3Nl+zUfpXSxRzx1/7YAkQHPUfC7 NUYrbC5hnFtJItDU/6I+ldtx1/M6q0tR0mh+4VapvhCiOzD5f46bJyFPKQAKp7w592PTK8S5O4a 54Slo0P08KFRdLk6RCEz3kBLO3apZL1BHCGJYwxKydSVPr20aAQ6dlSb+v65GuUsl5yhX8Tv006 eQ5o4fDTdMxHWsJjeF2w7FIGmu92UF2qK1PvDcWERGQ/tapFMwtBBaQnwSP7jac0nfILxSkZxmY QDxvoKIF8k5XB03KRH2qiJpQZQ2xm1IjPChC4d2NaevYYNk6/JHT161S1vawSLPiV4fpv/Sn6SZ rERhJEEWY4NspTlS3I6MCD380nODrBD3+YjbLKKluckeaHdbWpow== X-Google-Smtp-Source: AGHT+IEq3lfre9hD92z9FNfx9DNUM/AdLVlGEkeIARsp/WDSLXluMDWOdqWB4RdC003cJPByynEH4w== X-Received: by 2002:a17:902:c408:b0:295:54cb:a8df with SMTP id d9443c01a7336-29554cba9d6mr8834745ad.36.1761945326428; Fri, 31 Oct 2025 14:15:26 -0700 (PDT) From: Soumyajyotii Ssarkar To: deller@gmx.de, qemu-devel@nongnu.org Cc: sarkarsoumyajyoti23@gmail.com, Richard Henderson , Jason Wang , RemZapCypher Subject: [PATCH 1/3] hw/hppa: Enable LASI i82596 network on 715 machine Date: Sat, 1 Nov 2025 02:45:14 +0530 Message-ID: <20251031211516.208488-2-soumyajyotisarkar23@gmail.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20251031211516.208488-1-soumyajyotisarkar23@gmail.com> References: <20251031211516.208488-1-soumyajyotisarkar23@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::641; envelope-from=soumyajyotisarkar23@gmail.com; helo=mail-pl1-x641.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Fri, 31 Oct 2025 17:26:56 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1761946118859154100 Content-Type: text/plain; charset="utf-8" From: RemZapCypher Enable the i82596 network chip which is included in the LASI multi I/O chip. Since LASI has different start addresses on the various machines, always initialize the LASI components by their offsets. Signed-off-by: Helge Deller --- hw/hppa/hppa_hardware.h | 20 ++++++++++++-------- hw/hppa/machine.c | 25 +++++++++++-------------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h index d422af0429..a8a496c0e1 100644 --- a/hw/hppa/hppa_hardware.h +++ b/hw/hppa/hppa_hardware.h @@ -21,14 +21,6 @@ #define DINO_SCSI_HPA 0xfff8c000 #define LASI_HPA_715 0xf0100000 #define LASI_HPA 0xffd00000 -#define LASI_UART_HPA 0xffd05000 -#define LASI_SCSI_HPA 0xffd06000 -#define LASI_LAN_HPA 0xffd07000 -#define LASI_RTC_HPA 0xffd09000 -#define LASI_LPT_HPA 0xffd02000 -#define LASI_AUDIO_HPA 0xffd04000 -#define LASI_PS2KBD_HPA 0xffd08000 -#define LASI_PS2MOU_HPA 0xffd08100 #define LASI_GFX_HPA 0xf8000000 #define ARTIST_FB_ADDR 0xf9000000 #define CPU_HPA 0xfffb0000 @@ -44,6 +36,18 @@ =20 #define SCSI_HPA 0xf1040000 /* emulated SCSI, needs to be in f= region */ =20 +#define HPA_DISABLED_DEVICE 1 /* add to HPA to disable */ + +/* LASI offsets */ +#define LASI_LPT 0x02000 +#define LASI_AUDIO 0x04000 +#define LASI_UART 0x05000 +#define LASI_SCSI 0x06000 +#define LASI_LAN 0x07000 +#define LASI_PS2 0x08000 +#define LASI_RTC 0x09000 +#define LASI_FDC 0x0A000 + /* offsets to DINO HPA: */ #define DINO_PCI_ADDR 0x064 #define DINO_CONFIG_DATA 0x068 diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index cbca0026ab..7c5297c071 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -50,8 +50,6 @@ struct HppaMachineState { #define HPA_POWER_BUTTON (FIRMWARE_END - 0x10) static hwaddr soft_power_reg; =20 -#define enable_lasi_lan() 0 - static DeviceState *lasi_dev; =20 static void hppa_powerdown_req(Notifier *n, void *opaque) @@ -376,13 +374,6 @@ static void machine_HP_common_init_tail(MachineState *= machine, PCIBus *pci_bus, } } =20 - /* Network setup. */ - if (lasi_dev) { - lasi_82596_init(addr_space, translate(NULL, LASI_LAN_HPA), - qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA), - enable_lasi_lan()); - } - if (pci_bus) { pci_init_nic_devices(pci_bus, mc->default_nic); =20 @@ -595,6 +586,11 @@ static void machine_HP_715_init(MachineState *machine) lasi_ncr710_handle_legacy_cmdline(dev); } =20 + /* LASI i82596 network */ + lasi_82596_init(addr_space, translate(NULL, LASI_HPA_715 + LASI_LAN), + qdev_get_gpio_in(lasi_dev, LASI_IRQ_LAN_HPA), + true); + /* Add NICs, graphics & load firmware */ machine_HP_common_init_tail(machine, NULL, translate); } @@ -638,7 +634,7 @@ static void machine_HP_B160L_init(MachineState *machine) assert(isa_bus); =20 /* Serial ports: Lasi and Dino use a 7.272727 MHz clock. */ - serial_mm_init(addr_space, translate(NULL, LASI_UART_HPA + 0x800), 0, + serial_mm_init(addr_space, translate(NULL, LASI_HPA + LASI_UART + 0x80= 0), 0, qdev_get_gpio_in(lasi_dev, LASI_IRQ_UART_HPA), 7272727 / 16, serial_hd(0), DEVICE_BIG_ENDIAN); =20 @@ -647,7 +643,8 @@ static void machine_HP_B160L_init(MachineState *machine) serial_hd(1), DEVICE_BIG_ENDIAN); =20 /* Parallel port */ - parallel_mm_init(addr_space, translate(NULL, LASI_LPT_HPA + 0x800), 0, + parallel_mm_init(addr_space, + translate(NULL, LASI_HPA + LASI_LPT + 0x800), 0, qdev_get_gpio_in(lasi_dev, LASI_IRQ_LPT_HPA), parallel_hds[0]); =20 @@ -657,11 +654,11 @@ static void machine_HP_B160L_init(MachineState *machi= ne) sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(lasi_dev, LASI_IRQ_PS2KBD_HPA)); memory_region_add_subregion(addr_space, - translate(NULL, LASI_PS2KBD_HPA), + translate(NULL, LASI_HPA + LASI_PS2), sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0)); memory_region_add_subregion(addr_space, - translate(NULL, LASI_PS2KBD_HPA + 0x100), + translate(NULL, LASI_HPA + LASI_PS2 + 0x10= 0), sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1)); =20 @@ -832,7 +829,7 @@ static void HP_715_machine_init_class_init(ObjectClass = *oc, const void *data) /* can only support up to max. 8 CPUs due inventory major numbers */ mc->max_cpus =3D MIN_CONST(HPPA_MAX_CPUS, 8); mc->default_ram_size =3D 256 * MiB; - mc->default_nic =3D NULL; + mc->default_nic =3D "lasi_82596"; } =20 =20 --=20 2.49.0 From nobody Fri Nov 14 17:01:13 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1761946115; cv=none; d=zohomail.com; s=zohoarc; b=jjCnnHhLd6otti6cqYthYT9twBW9aFAI+XhFx8ex1Of0yymX9iGS+ym3tdWVlFBy6ZdX2hR1nsP+GcjZZwBPDQ9VZxf5h+lS/9oKDtuTOCvNzKbvQTbhOAcPNNxLq40oM7fQ5KBGT0EVkkdbU3W1GUk9otbMmfbNiTjiRfePby8= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1761946115; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=SJ+5JLakhh68GqpUQ5rqgqP7MYjWUeLMuLL+Ypn52xs=; b=Ceq6rqamTTyeiBhc7YfgYDGPFs4ikcAnFIxdfOFgnd0s3HYEe0pC2pqyBzfIZakR7sV39298VK+ijlhRnIVAneqotlWFFUKTkRbwBWSvsQAbRjrkvVH7pEzW9Fvs9kgfZzud0MW1VU76Oyju7skVZ06EO1Q57Kmt3zdx71mDJXQ= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1761946115953129.85255362523606; Fri, 31 Oct 2025 14:28:35 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vEweA-0006Yb-9R; Fri, 31 Oct 2025 17:26:58 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vEwTF-0000Hm-F1 for qemu-devel@nongnu.org; Fri, 31 Oct 2025 17:15:41 -0400 Received: from mail-pf1-x442.google.com ([2607:f8b0:4864:20::442]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vEwT8-0001xK-8s for qemu-devel@nongnu.org; Fri, 31 Oct 2025 17:15:41 -0400 Received: by mail-pf1-x442.google.com with SMTP id d2e1a72fcca58-7a26b9a936aso2065180b3a.0 for ; Fri, 31 Oct 2025 14:15:33 -0700 (PDT) Received: from fedora.. ([103.2.232.250]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-b93b8aa3ff1sm3023320a12.14.2025.10.31.14.15.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 31 Oct 2025 14:15:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1761945330; x=1762550130; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SJ+5JLakhh68GqpUQ5rqgqP7MYjWUeLMuLL+Ypn52xs=; b=KL+k/VTTWow2zHhIIQB9N+rim8qnp7zo5BDc8gOmyQnsjCeiTrNyNd7DqlXaXyQT5A hHUMUFwZzqA9+1tH1ac/U9ACnd3j2XHh+K9g+O6X9vImA7aa90BFlbjpSBEXj7ffoXxB R3z9WmO+jbrGH4rlV2PGANY/7Va+9YLj0rsnUEqvQU41X8Bn7yDPFwG5ZmQlOYwmcgEu FqcKKlP8qSeSeGKLL+q8bSD0Vjd2UuoSUMBYaL9gE8Z2zp9az6k9x8F7i+/N4sl3SQM9 bnJTUyG6AeLqhHCOUFcVUcRskOtXeWWcmob1RAtrTimRU9zZ5plIw+iA6rXdy527tA8A xNWg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1761945330; x=1762550130; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=SJ+5JLakhh68GqpUQ5rqgqP7MYjWUeLMuLL+Ypn52xs=; b=gLTHn0wl9bN55A6ffYwN7bEg22ARsDdJCmEPm2b6+8co1k0oJPOoEbsMcLY4Tvknjw aHPyBqpn5iKVguxKeE95EJar/cpSi8EHvbdCHB0jA2Y8R6hGWUbqMAx3aug9raLFRCqi Yw7pbg5C76Jbjg5OrqhIK5BCrwTwyKCSmTnEti3UVKYYa7vcxPsFkBZmmIM5uiV0JTNw fg46mSdAgV15/MN2VrfAR0RdFIYbkLUEXeB5JTQU0SXKd6+CbwJgvcyiOcQVjS5rCXs/ FNNNihstR7rAcRjXLmsU1+uj3NPJ0Ww3mULHVPiLHSYzrE/m/qEsOwHZbfh696OPG6cr DCTA== X-Forwarded-Encrypted: i=1; AJvYcCWj7KBAWBY0/ya5mZgWZ7OtQooU4RBTv62vbwClT9wpflSmq+qpu41MKrwTm0Lxlgb3rcW5dPBWCOOM@nongnu.org X-Gm-Message-State: AOJu0Yy99bSkNoGk3lmchbdqLbjfR8nH+zBgd+aM08xH4+7bCrTvb8fi zeGZScgLMLFysE7RsonMmk8igfRCTczfBSj9X+hIOdGS3Ya5EQWe++BH X-Gm-Gg: ASbGnct51I0gCvK6aXk0mZ3E52NsE723ht+5J1mSwug+pHvKI2/+n1pm8/svjKVhq1w 6RkQBCwnZ0i3WG8MlsmNSzIHUmF9Mti7Fo7nzgO3LcVCFXS5bufYDGWqpfYw352adIDBAKq961W mv4A+13Fsj4p/uZBZlr8sWrUkgEuzQz4d+fMM2nBVHsZhG9XhaDV7TpZCoXA+Fmgvwqk/K8L+l5 4LidPKWpSivnj/8cZQG9MGlu62F4Hn7DUrR3Fy36L+ScdOh6/iW8Bx0tXzfwc7WIhsisyRVWou5 FpNSaBMmk4CIDdgMhEJ05zPoDnzQ95DvH5IewKeyBQzD2JWWuAAvhNonWiUJehKdmz0iiPPZqKK sIY80NOaDwTL7Yfu/Krx0OzhHpbITk/dVAnkS6wi986i1hlqpuS5OHLsjHUAFAzdD309uwfpSjl HRMn8OSLh+dUQ4mgx/nIHi1w7sGWhycGCyzj+9p00= X-Google-Smtp-Source: AGHT+IGkwpMnHgT1HSuGoebpIcfdL9i4UXl+dUl2D2ktRsJ5y0DWquT5FxzIteOSoO3K6Kc24s3d7w== X-Received: by 2002:a05:6a20:3d07:b0:334:a9ff:ca32 with SMTP id adf61e73a8af0-348c9f671cemr7064642637.2.1761945329653; Fri, 31 Oct 2025 14:15:29 -0700 (PDT) From: Soumyajyotii Ssarkar To: deller@gmx.de, qemu-devel@nongnu.org Cc: sarkarsoumyajyoti23@gmail.com, Richard Henderson , Jason Wang , RemZapCypher Subject: [PATCH 2/3] i82596: Added core infrastructure and helper functions Date: Sat, 1 Nov 2025 02:45:15 +0530 Message-ID: <20251031211516.208488-3-soumyajyotisarkar23@gmail.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20251031211516.208488-1-soumyajyotisarkar23@gmail.com> References: <20251031211516.208488-1-soumyajyotisarkar23@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::442; envelope-from=soumyajyotisarkar23@gmail.com; helo=mail-pf1-x442.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Fri, 31 Oct 2025 17:26:56 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1761946118968154100 Content-Type: text/plain; charset="utf-8" From: RemZapCypher As a part of GSOC 2025 I have done a massive rewrite of what was the 82596 NIC. This has been done to add the missing functionality according to the 82596 Manual and making the code more production ready. In this patch I have done the following: - Added comprehensive 82596 constants and configuration macros - Implemented address translation for segmented/linear memory modes - Added error recording and statistics tracking infrastructure - Implemented CRC-16/32 calculation and appending functions - Added CSMA/CD collision detection and backoff logic - Implemented bus throttle timer framework - Added enhanced reset with full state initialization - Added receive_iov and polling support functions - Updated VMState for migration of all new fields Note: This patch primarily includes placeholder code. To achieve full 82596 emulation, the complete 82596 patch series is require= d. Nevertheless, QEMU is able to load and boot successfully with this patch. --- hw/net/i82596.c | 667 ++++++++++++++++++++++++++++++++++++++----- hw/net/i82596.h | 74 ++++- hw/net/lasi_i82596.c | 6 + 3 files changed, 655 insertions(+), 92 deletions(-) diff --git a/hw/net/i82596.c b/hw/net/i82596.c index c1ff3e6c56..859cc88b2e 100644 --- a/hw/net/i82596.c +++ b/hw/net/i82596.c @@ -2,10 +2,38 @@ * QEMU Intel i82596 (Apricot) emulation * * Copyright (c) 2019 Helge Deller - * This work is licensed under the GNU GPL license version 2 or later. * + * Additional functionality added by: + * Soumyajyotii Ssarkar + * During GSOC 2025 under mentorship of Helge Deller. + * + * This work is licensed under the GNU GPL license version 2 or later. * This software was written to be compatible with the specification: * https://parisc.docs.kernel.org/en/latest/_downloads/96672be0650d9fc046b= bcea40b92482f/82596CA.pdf + * + * INDEX: + * 1. Reset + * 2. Address Translation + * 3. Transmit functions + * 4. Receive Helper functions + * 5. Receive functions + * 6. Misc Functionality Functions + * 6.1 Individual Address + * 6.2 Multicast Address List + * 6.3 Link Status + * 6.4 CSMA/CD functions + * 6.5 Unified CRC Calculation + * 6.6 Unified Statistics Update + * 7. Bus Throttling Timer + * 8. Dump functions + * 9. Configure + * 10. Command Loop + * 11. Examine SCB + * 12. Channel attention (CA) + * 13. LASI interface + * 14. Polling functions + * 15. QOM and interface functions + * */ =20 #include "qemu/osdep.h" @@ -21,50 +49,90 @@ #include "i82596.h" #include /* for crc32 */ =20 +#define ENABLE_DEBUG 0 + #if defined(ENABLE_DEBUG) #define DBG(x) x #else #define DBG(x) do { } while (0) #endif =20 -#define USE_TIMER 0 - -#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) +#define USE_TIMER 1 =20 -#define PKT_BUF_SZ 1536 #define MAX_MC_CNT 64 - -#define ISCP_BUSY 0x0001 - #define I596_NULL ((uint32_t)0xffffffff) - -#define SCB_STATUS_CX 0x8000 /* CU finished command with I bit */ -#define SCB_STATUS_FR 0x4000 /* RU finished receiving a frame */ -#define SCB_STATUS_CNA 0x2000 /* CU left active state */ -#define SCB_STATUS_RNR 0x1000 /* RU left active state */ - -#define SCB_COMMAND_ACK_MASK \ - (SCB_STATUS_CX | SCB_STATUS_FR | SCB_STATUS_CNA | SCB_STATUS_RNR) - +#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m) + +#define SCB_STATUS_CX 0x8000 /* CU finished command with I bit */ +#define SCB_STATUS_FR 0x4000 /* RU finished receiving a frame */ +#define SCB_STATUS_CNA 0x2000 /* CU left active state */ +#define SCB_STATUS_RNR 0x1000 /* RU left active state */ +#define SCB_ACK_MASK 0xF000 /* All interrupt acknowledge bits */ + +/* 82596 Operational Modes */ +#define I82586_MODE 0x00 +#define I82596_MODE_SEGMENTED 0x01 +#define I82596_MODE_LINEAR 0x02 + +/* Monitor Options */ +#define MONITOR_NORMAL 0x00 +#define MONITOR_FILTERED 0x01 +#define MONITOR_ALL 0x02 +#define MONITOR_DISABLED 0x03 + +/* Operation mode flags from SYSBUS byte */ +#define SYSBUS_LOCK_EN 0x08 +#define SYSBUS_INT_ACTIVE_LOW 0x10 +#define SYSBUS_BIG_ENDIAN_32 0x80 +#define SYSBUS_THROTTLE_MASK 0x60 + +/* SCB commands - Command Unit (CU) */ +#define SCB_CUC_NOP 0x00 +#define SCB_CUC_START 0x01 +#define SCB_CUC_RESUME 0x02 +#define SCB_CUC_SUSPEND 0x03 +#define SCB_CUC_ABORT 0x04 +#define SCB_CUC_LOAD_THROTTLE 0x05 +#define SCB_CUC_LOAD_START 0x06 + +/* SCB commands - Receive Unit (RU) */ +#define SCB_RUC_NOP 0x00 +#define SCB_RUC_START 0x01 +#define SCB_RUC_RESUME 0x02 +#define SCB_RUC_SUSPEND 0x03 +#define SCB_RUC_ABORT 0x04 + +/* SCB statuses - Command Unit (CU) */ #define CU_IDLE 0 #define CU_SUSPENDED 1 #define CU_ACTIVE 2 =20 -#define RX_IDLE 0 -#define RX_SUSPENDED 1 -#define RX_READY 4 +/* SCB statuses - Receive Unit (RU) */ +#define RX_IDLE 0x00 +#define RX_SUSPENDED 0x01 +#define RX_NO_RESOURCES 0x02 +#define RX_READY 0x04 +#define RX_NO_RESO_RBD 0x0A +#define RX_NO_MORE_RBD 0x0C + +#define CMD_FLEX 0x0008 +#define CMD_MASK 0x0007 + +#define CMD_EOL 0x8000 +#define CMD_SUSP 0x4000 +#define CMD_INTR 0x2000 =20 -#define CMD_EOL 0x8000 /* The last command of the list, stop. */ -#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */ -#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */ +#define ISCP_BUSY 0x01 +#define NANOSECONDS_PER_MICROSECOND 1000 =20 -#define CMD_FLEX 0x0008 /* Enable flexible memory model */ +#define DUMP_BUF_SZ 304 =20 enum commands { CmdNOp =3D 0, CmdSASetup =3D 1, CmdConfigure =3D 2, CmdMulticastLi= st =3D 3, CmdTx =3D 4, CmdTDR =3D 5, CmdDump =3D 6, CmdDiagnose =3D 7 }; =20 + #define STAT_C 0x8000 /* Set to 0 after execution */ #define STAT_B 0x4000 /* Command being executed */ #define STAT_OK 0x2000 /* Command executed ok */ @@ -73,15 +141,60 @@ enum commands { #define I596_EOF 0x8000 #define SIZE_MASK 0x3fff =20 -/* various flags in the chip config registers */ -#define I596_PREFETCH (s->config[0] & 0x80) -#define I596_PROMISC (s->config[8] & 0x01) -#define I596_BC_DISABLE (s->config[8] & 0x02) /* broadcast disable */ -#define I596_NOCRC_INS (s->config[8] & 0x08) -#define I596_CRCINM (s->config[11] & 0x04) /* CRC appended */ -#define I596_MC_ALL (s->config[11] & 0x20) -#define I596_MULTIIA (s->config[13] & 0x40) - +#define CSMA_SLOT_TIME 51 +#define CSMA_MAX_RETRIES 16 +#define CSMA_BACKOFF_LIMIT 10 + +/* Global Flags fetched from config bytes */ +#define I596_PREFETCH (s->config[0] & 0x80) +#define SAVE_BAD_FRAMES (s->config[2] & 0x80) +#define I596_NO_SRC_ADD_IN (s->config[3] & 0x08) +#define I596_LOOPBACK (s->config[3] >> 6) +#define I596_PROMISC (s->config[8] & 0x01) +#define I596_BC_DISABLE (s->config[8] & 0x02) +#define I596_NOCRC_INS (s->config[8] & 0x08) +#define I596_CRC16_32 (s->config[8] & 0x10) +#define I596_PADDING (s->config[8] & 0x80) +#define I596_MIN_FRAME_LEN (s->config[10]) +#define I596_CRCINM (s->config[11] & 0x04) +#define I596_MONITOR_MODE ((s->config[11] >> 6) & 0x03) +#define I596_MC_ALL (s->config[11] & 0x20) +#define I596_FULL_DUPLEX (s->config[12] & 0x40) +#define I596_MULTIIA (s->config[13] & 0x40) + +/* RX Error flags */ +#define RX_COLLISIONS 0x0001 +#define RX_LENGTH_ERRORS 0x0080 +#define RX_OVER_ERRORS 0x0100 +#define RX_FIFO_ERRORS 0x0400 +#define RX_FRAME_ERRORS 0x0800 +#define RX_CRC_ERRORS 0x1000 +#define RX_LENGTH_ERRORS_ALT 0x2000 +#define RFD_STATUS_TRUNC 0x0020 +#define RFD_STATUS_NOBUFS 0x0200 + +/* TX Error flags */ +#define TX_COLLISIONS 0x0020 +#define TX_HEARTBEAT_ERRORS 0x0040 +#define TX_CARRIER_ERRORS 0x0400 +#define TX_COLLISIONS_ALT 0x0800 +#define TX_ABORTED_ERRORS 0x1000 + +static void i82596_update_scb_irq(I82596State *s, bool trigger); +static void i82596_update_cu_status(I82596State *s, uint16_t cmd_status, + bool generate_interrupt); +static void update_scb_status(I82596State *s); +static void examine_scb(I82596State *s); +static bool i82596_check_medium_status(I82596State *s); +static int i82596_csma_backoff(I82596State *s, int retry_count); +static uint16_t i82596_calculate_crc16(const uint8_t *data, size_t len); +static size_t i82596_append_crc(I82596State *s, uint8_t *buffer, size_t le= n); +static void i82596_bus_throttle_timer(void *opaque); +static void i82596_flush_queue_timer(void *opaque); +static int i82596_flush_packet_queue(I82596State *s); +static void i82596_update_statistics(I82596State *s, bool is_tx, + uint16_t error_flags, + uint16_t collision_count); =20 static uint8_t get_byte(uint32_t addr) { @@ -116,7 +229,44 @@ static void set_uint32(uint32_t addr, uint32_t val) set_uint16(addr + 2, val >> 16); } =20 +/* Centralized error detection and update mechanism */ +static void i82596_record_error(I82596State *s, uint16_t error_type, bool = is_tx) +{ + if (is_tx) { + if (error_type & TX_ABORTED_ERRORS) { + s->tx_aborted_errors++; + set_uint32(s->scb + 28, s->tx_aborted_errors); + } + } else { + if (error_type & RX_CRC_ERRORS) { + s->crc_err++; + set_uint32(s->scb + 16, s->crc_err); + } =20 + if (error_type & (RX_LENGTH_ERRORS | RX_LENGTH_ERRORS_ALT | + RX_FRAME_ERRORS)) { + s->align_err++; + set_uint32(s->scb + 18, s->align_err); + } + + if (error_type & RFD_STATUS_NOBUFS) { + s->resource_err++; + set_uint32(s->scb + 20, s->resource_err); + } + + if (error_type & (RX_OVER_ERRORS | RX_FIFO_ERRORS)) { + s->over_err++; + set_uint32(s->scb + 22, s->over_err); + } + + if (error_type & RFD_STATUS_TRUNC) { + s->short_fr_error++; + set_uint32(s->scb + 26, s->short_fr_error); + } + } +} + +/* Packet Header Debugger */ struct qemu_ether_header { uint8_t ether_dhost[6]; uint8_t ether_shost[6]; @@ -124,12 +274,122 @@ struct qemu_ether_header { }; =20 #define PRINT_PKTHDR(txt, BUF) do { \ - struct qemu_ether_header *hdr =3D (void *)(BUF); \ - printf(txt ": packet dhost=3D" MAC_FMT ", shost=3D" MAC_FMT ", type=3D= 0x%04x\n",\ - MAC_ARG(hdr->ether_dhost), MAC_ARG(hdr->ether_shost), \ - be16_to_cpu(hdr->ether_type)); \ } while (0) =20 +static void i82596_cleanup(I82596State *s) +{ + if (s->throttle_timer) { + timer_del(s->throttle_timer); + } + if (s->flush_queue_timer) { + timer_del(s->flush_queue_timer); + } + s->queue_head =3D 0; + s->queue_tail =3D 0; + s->queue_count =3D 0; +} + +static void i82596_s_reset(I82596State *s) +{ + trace_i82596_s_reset(s); + i82596_cleanup(s); + + /* Clearing config bits */ + memset(s->config, 0, sizeof(s->config)); + s->scp =3D 0x00FFFFF4; + s->scb =3D 0; + s->scb_base =3D 0; + s->scb_status =3D 0; + s->cu_status =3D CU_IDLE; + s->rx_status =3D RX_IDLE; + s->cmd_p =3D I596_NULL; + s->lnkst =3D 0x8000; + s->ca =3D s->ca_active =3D 0; + s->send_irq =3D 0; + + /* Statistical Counters */ + s->crc_err =3D 0; + s->align_err =3D 0; + s->resource_err =3D 0; + s->over_err =3D 0; + s->rcvdt_err =3D 0; + s->short_fr_error =3D 0; + s->total_frames =3D 0; + s->total_good_frames =3D 0; + s->collision_events =3D 0; + s->total_collisions =3D 0; + s->tx_good_frames =3D 0; + s->tx_collisions =3D 0; + s->tx_aborted_errors =3D 0; + s->last_tx_len =3D 0; + + s->last_good_rfa =3D 0; + s->current_rx_desc =3D 0; + s->current_tx_desc =3D 0; + s->tx_retry_addr =3D 0; + s->tx_retry_count =3D 0; + + s->rnr_signaled =3D false; + s->flushing_queue =3D false; + + memset(s->tx_buffer, 0, sizeof(s->tx_buffer)); + memset(s->rx_buffer, 0, sizeof(s->rx_buffer)); + s->tx_frame_len =3D 0; + s->rx_frame_len =3D 0; +} + +void i82596_h_reset(void *opaque) +{ + I82596State *s =3D opaque; + + i82596_s_reset(s); +} + +/* + * Address Translation Implementation + * Handles segmented and linear memory modes for i82596. + * Returns physical address for DMA operations. + * Returns I596_NULL (0xffffffff) on invalid addresses. + */ +static inline uint32_t i82596_translate_address(I82596State *s, + uint32_t logical_addr, + bool is_data_buffer) +{ + if (logical_addr =3D=3D I596_NULL || logical_addr =3D=3D 0) { + return logical_addr; + } + + switch (s->mode) { + case I82596_MODE_LINEAR: + return logical_addr; + + case I82596_MODE_SEGMENTED: { + uint32_t base =3D (logical_addr >> 16) & 0xFFFF; + uint32_t offset =3D logical_addr & 0xFFFF; + + if (is_data_buffer) { + return (base << 4) + offset; + } else { + if (base =3D=3D 0xFFFF && offset =3D=3D 0xFFFF) { + return I596_NULL; + } + return s->scb_base + ((base << 4) + offset); + } + } + + case I82586_MODE: + default: + if (is_data_buffer) { + return logical_addr; + } else { + if ((logical_addr & 0xFFFF0000) =3D=3D 0xFFFF0000) { + return I596_NULL; + } + return s->scb_base + logical_addr; + } + } +} + static void i82596_transmit(I82596State *s, uint32_t addr) { uint32_t tdb_p; /* Transmit Buffer Descriptor */ @@ -223,32 +483,198 @@ static void set_multicast_list(I82596State *s, uint3= 2_t addr) =20 void i82596_set_link_status(NetClientState *nc) { - I82596State *d =3D qemu_get_nic_opaque(nc); + I82596State *s =3D qemu_get_nic_opaque(nc); + bool was_up =3D s->lnkst !=3D 0; + + s->lnkst =3D nc->link_down ? 0 : 0x8000; + bool is_up =3D s->lnkst !=3D 0; =20 - d->lnkst =3D nc->link_down ? 0 : 0x8000; + if (!was_up && is_up && s->rx_status =3D=3D RX_READY) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } } =20 -static void update_scb_status(I82596State *s) +static bool G_GNUC_UNUSED i82596_check_medium_status(I82596State *s) { - s->scb_status =3D (s->scb_status & 0xf000) - | (s->cu_status << 8) | (s->rx_status << 4); - set_uint16(s->scb, s->scb_status); + if (I596_FULL_DUPLEX) { + return true; + } + + if (!s->throttle_state) { + return false; + } + + if (!I596_LOOPBACK && (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) % 100 < 5= )) { + s->collision_events++; + return false; + } + + return true; } =20 +static int G_GNUC_UNUSED i82596_csma_backoff(I82596State *s, int retry_cou= nt) +{ + int backoff_factor, slot_count, backoff_time; + + backoff_factor =3D MIN(retry_count + 1, CSMA_BACKOFF_LIMIT); + slot_count =3D qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) % (1 << backoff_f= actor); + backoff_time =3D slot_count * CSMA_SLOT_TIME; =20 -static void i82596_s_reset(I82596State *s) + return backoff_time; +} + +static uint16_t i82596_calculate_crc16(const uint8_t *data, size_t len) { - trace_i82596_s_reset(s); - s->scp =3D 0; - s->scb_status =3D 0; - s->cu_status =3D CU_IDLE; - s->rx_status =3D RX_SUSPENDED; - s->cmd_p =3D I596_NULL; - s->lnkst =3D 0x8000; /* initial link state: up */ - s->ca =3D s->ca_active =3D 0; - s->send_irq =3D 0; + uint16_t crc =3D 0xFFFF; + size_t i, j; + + for (i =3D 0; i < len; i++) { + crc ^=3D data[i] << 8; + for (j =3D 0; j < 8; j++) { + if (crc & 0x8000) { + crc =3D (crc << 1) ^ 0x1021; + } else { + crc <<=3D 1; + } + } + } + return crc; +} + +static size_t G_GNUC_UNUSED i82596_append_crc(I82596State *s, uint8_t *buf= fer, size_t len) +{ + if (len + 4 > PKT_BUF_SZ) { + return len; + } + + if (I596_CRC16_32) { + uint32_t crc =3D crc32(~0, buffer, len); + crc =3D cpu_to_be32(crc); + memcpy(&buffer[len], &crc, sizeof(crc)); + return len + sizeof(crc); + } else { + uint16_t crc =3D i82596_calculate_crc16(buffer, len); + crc =3D cpu_to_be16(crc); + memcpy(&buffer[len], &crc, sizeof(crc)); + return len + sizeof(crc); + } +} + +static void G_GNUC_UNUSED i82596_update_statistics(I82596State *s, bool is= _tx, + uint16_t error_flags, + uint16_t collision_count) +{ + if (is_tx) { + if (collision_count > 0) { + s->tx_collisions +=3D collision_count; + s->collision_events++; + s->total_collisions +=3D collision_count; + set_uint32(s->scb + 32, s->tx_collisions); + } + if (error_flags) { + i82596_record_error(s, error_flags, true); + } + if (!(error_flags & (TX_ABORTED_ERRORS | TX_CARRIER_ERRORS))) { + s->tx_good_frames++; + set_uint32(s->scb + 36, s->tx_good_frames); + } + } else { + s->total_frames++; + set_uint32(s->scb + 40, s->total_frames); + if (error_flags) { + i82596_record_error(s, error_flags, false); + } else { + s->total_good_frames++; + set_uint32(s->scb + 44, s->total_good_frames); + } + } +} + +/* Bus Throttle Functionality */ +static void G_GNUC_UNUSED i82596_bus_throttle_timer(void *opaque) +{ + I82596State *s =3D opaque; + + if (s->throttle_state) { + s->throttle_state =3D false; + if (s->t_off > 0) { + timer_mod(s->throttle_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (s->t_off * NANOSECONDS_PER_MICROSECOND)); + } + } else { + s->throttle_state =3D true; + if (s->t_on > 0) { + timer_mod(s->throttle_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + (s->t_on * NANOSECONDS_PER_MICROSECOND)); + } + + if (s->cu_status =3D=3D CU_ACTIVE || s->rx_status =3D=3D RX_READY)= { + examine_scb(s); + } + } +} + +static int G_GNUC_UNUSED i82596_flush_packet_queue(I82596State *s) +{ + /* Stub for now - will be implemented in Patch 2 */ + return 0; +} + +static void G_GNUC_UNUSED i82596_flush_queue_timer(void *opaque) +{ + /* Stub for now - will be implemented in Patch 2 */ +} + +static void i82596_update_scb_irq(I82596State *s, bool trigger) +{ + if (trigger) { + s->send_irq =3D 1; + qemu_set_irq(s->irq, 1); + } +} + +static void G_GNUC_UNUSED i82596_update_cu_status(I82596State *s, uint16_t= cmd_status, + bool generate_interrupt) +{ + if (cmd_status & STAT_C) { + if (cmd_status & STAT_OK) { + if (s->cu_status =3D=3D CU_ACTIVE && s->cmd_p =3D=3D I596_NULL= ) { + s->cu_status =3D CU_IDLE; + s->scb_status |=3D SCB_STATUS_CNA; + } + } else { + s->cu_status =3D CU_IDLE; + s->scb_status |=3D SCB_STATUS_CNA; + } + + if (generate_interrupt) { + s->scb_status |=3D SCB_STATUS_CX; + i82596_update_scb_irq(s, true); + } + } + + update_scb_status(s); } =20 +static void update_scb_status(I82596State *s) +{ + s->scb_status =3D (s->scb_status & 0xf000) + | (s->cu_status << 8) | (s->rx_status << 4) | (s->lnkst >> 8); + set_uint16(s->scb, s->scb_status); + + set_uint32(s->scb + 28, s->tx_aborted_errors); + set_uint32(s->scb + 32, s->tx_collisions); + set_uint32(s->scb + 36, s->tx_good_frames); + + set_uint32(s->scb + 16, s->crc_err); + set_uint32(s->scb + 18, s->align_err); + set_uint32(s->scb + 20, s->resource_err); + set_uint32(s->scb + 22, s->over_err); + set_uint32(s->scb + 24, s->rcvdt_err); + set_uint32(s->scb + 26, s->short_fr_error); +} =20 static void command_loop(I82596State *s) { @@ -330,17 +756,6 @@ static void command_loop(I82596State *s) qemu_flush_queued_packets(qemu_get_queue(s->nic)); } =20 -static void i82596_flush_queue_timer(void *opaque) -{ - I82596State *s =3D opaque; - if (0) { - timer_del(s->flush_queue_timer); - qemu_flush_queued_packets(qemu_get_queue(s->nic)); - timer_mod(s->flush_queue_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); - } -} - static void examine_scb(I82596State *s) { uint16_t command, cuc, ruc; @@ -353,7 +768,7 @@ static void examine_scb(I82596State *s) /* and clear the scb command word */ set_uint16(s->scb + 2, 0); =20 - s->scb_status &=3D ~(command & SCB_COMMAND_ACK_MASK); + s->scb_status &=3D ~(command & SCB_ACK_MASK); =20 switch (cuc) { case 0: /* no change */ @@ -465,13 +880,6 @@ uint32_t i82596_ioport_readw(void *opaque, uint32_t ad= dr) return -1; } =20 -void i82596_h_reset(void *opaque) -{ - I82596State *s =3D opaque; - - i82596_s_reset(s); -} - bool i82596_can_receive(NetClientState *nc) { I82596State *s =3D qemu_get_nic_opaque(nc); @@ -595,7 +1003,6 @@ ssize_t i82596_receive(NetClientState *nc, const uint8= _t *buf, size_t sz) rbd =3D get_uint32(rfd_p + 8); assert(rbd && rbd !=3D I596_NULL); =20 - trace_i82596_receive_packet(len); /* PRINT_PKTHDR("Receive", buf); */ =20 while (len) { @@ -714,14 +1121,113 @@ ssize_t i82596_receive(NetClientState *nc, const ui= nt8_t *buf, size_t sz) return sz; } =20 +ssize_t i82596_receive_iov(NetClientState *nc, const struct iovec *iov, + int iovcnt) +{ + size_t sz =3D 0; + uint8_t *buf; + int i; + for (i =3D 0; i < iovcnt; i++) { + sz +=3D iov[i].iov_len; + } + if (sz =3D=3D 0) { + return -1; + } + buf =3D g_malloc(sz); + if (!buf) { + return -1; + } + size_t offset =3D 0; + for (i =3D 0; i < iovcnt; i++) { + if (iov[i].iov_base =3D=3D NULL) { + g_free(buf); + return -1; + } + memcpy(buf + offset, iov[i].iov_base, iov[i].iov_len); + offset +=3D iov[i].iov_len; + } + DBG(PRINT_PKTHDR("Receive IOV:", buf)); + i82596_receive(nc, buf, sz); + g_free(buf); + return sz; +} + +void i82596_poll(NetClientState *nc, bool enable) +{ + I82596State *s =3D qemu_get_nic_opaque(nc); + + if (!enable) { + return; + } + + if (s->send_irq) { + qemu_set_irq(s->irq, 1); + } + + if (s->rx_status =3D=3D RX_NO_RESOURCES) { + if (s->cmd_p !=3D I596_NULL) { + s->rx_status =3D RX_READY; + update_scb_status(s); + } + } + + if (s->cu_status =3D=3D CU_ACTIVE && s->cmd_p !=3D I596_NULL) { + examine_scb(s); + } + qemu_set_irq(s->irq, 0); +} =20 const VMStateDescription vmstate_i82596 =3D { .name =3D "i82596", .version_id =3D 1, .minimum_version_id =3D 1, - .fields =3D (const VMStateField[]) { + .fields =3D (VMStateField[]) { + VMSTATE_UINT8(mode, I82596State), + VMSTATE_UINT16(t_on, I82596State), + VMSTATE_UINT16(t_off, I82596State), + VMSTATE_BOOL(throttle_state, I82596State), + VMSTATE_UINT32(iscp, I82596State), + VMSTATE_UINT8(sysbus, I82596State), + VMSTATE_UINT32(scb, I82596State), + VMSTATE_UINT32(scb_base, I82596State), + VMSTATE_UINT16(scb_status, I82596State), + VMSTATE_UINT8(cu_status, I82596State), + VMSTATE_UINT8(rx_status, I82596State), VMSTATE_UINT16(lnkst, I82596State), - VMSTATE_TIMER_PTR(flush_queue_timer, I82596State), + VMSTATE_UINT32(cmd_p, I82596State), + VMSTATE_INT32(ca, I82596State), + VMSTATE_INT32(ca_active, I82596State), + VMSTATE_INT32(send_irq, I82596State), + VMSTATE_BUFFER(mult, I82596State), + VMSTATE_BUFFER(config, I82596State), + VMSTATE_BUFFER(tx_buffer, I82596State), + VMSTATE_UINT32(tx_retry_addr, I82596State), + VMSTATE_INT32(tx_retry_count, I82596State), + VMSTATE_UINT32(tx_good_frames, I82596State), + VMSTATE_UINT32(tx_collisions, I82596State), + VMSTATE_UINT32(tx_aborted_errors, I82596State), + VMSTATE_UINT32(last_tx_len, I82596State), + VMSTATE_UINT32(collision_events, I82596State), + VMSTATE_UINT32(total_collisions, I82596State), + VMSTATE_UINT32(crc_err, I82596State), + VMSTATE_UINT32(align_err, I82596State), + VMSTATE_UINT32(resource_err, I82596State), + VMSTATE_UINT32(over_err, I82596State), + VMSTATE_UINT32(rcvdt_err, I82596State), + VMSTATE_UINT32(short_fr_error, I82596State), + VMSTATE_UINT32(total_frames, I82596State), + VMSTATE_UINT32(total_good_frames, I82596State), + VMSTATE_BUFFER(rx_buffer, I82596State), + VMSTATE_UINT16(tx_frame_len, I82596State), + VMSTATE_UINT16(rx_frame_len, I82596State), + VMSTATE_UINT64(current_tx_desc, I82596State), + VMSTATE_UINT64(current_rx_desc, I82596State), + VMSTATE_UINT32(last_good_rfa, I82596State), + VMSTATE_INT32(queue_head, I82596State), + VMSTATE_INT32(queue_tail, I82596State), + VMSTATE_INT32(queue_count, I82596State), + VMSTATE_BOOL(rnr_signaled, I82596State), + VMSTATE_BOOL(flushing_queue, I82596State), VMSTATE_END_OF_LIST() } }; @@ -736,8 +1242,15 @@ void i82596_common_init(DeviceState *dev, I82596State= *s, NetClientInfo *info) qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); =20 if (USE_TIMER) { - s->flush_queue_timer =3D timer_new_ns(QEMU_CLOCK_VIRTUAL, - i82596_flush_queue_timer, s); + if (!s->flush_queue_timer) { + s->flush_queue_timer =3D timer_new_ns(QEMU_CLOCK_VIRTUAL, + i82596_flush_queue_timer, s); + } + if (!s->throttle_timer) { + s->throttle_timer =3D timer_new_ns(QEMU_CLOCK_VIRTUAL, + i82596_bus_throttle_timer, s); + } } + s->lnkst =3D 0x8000; /* initial link state: up */ } diff --git a/hw/net/i82596.h b/hw/net/i82596.h index dc1fa1a1dc..67b18f957a 100644 --- a/hw/net/i82596.h +++ b/hw/net/i82596.h @@ -6,11 +6,15 @@ #include "system/memory.h" #include "system/address-spaces.h" =20 -#define PORT_RESET 0x00 /* reset 82596 */ -#define PORT_SELFTEST 0x01 /* selftest */ -#define PORT_ALTSCP 0x02 /* alternate SCB address */ -#define PORT_ALTDUMP 0x03 /* Alternate DUMP address */ -#define PORT_CA 0x10 /* QEMU-internal CA signal */ +#define PACKET_QUEUE_SIZE 64 +#define RX_RING_SIZE 16 +#define PKT_BUF_SZ 1536 + +#define PORT_RESET 0x00 +#define PORT_SELFTEST 0x01 +#define PORT_ALTSCP 0x02 +#define PORT_ALTDUMP 0x03 +#define PORT_CA 0x10 =20 typedef struct I82596State_st I82596State; =20 @@ -21,35 +25,75 @@ struct I82596State_st { NICState *nic; NICConf conf; QEMUTimer *flush_queue_timer; + uint8_t mode; + + QEMUTimer *throttle_timer; + uint16_t t_on; + uint16_t t_off; + bool throttle_state; =20 - hwaddr scp; /* pointer to SCP */ + hwaddr scp; + uint32_t iscp; uint8_t sysbus; - uint32_t scb; /* SCB */ + uint32_t scb; + uint32_t scb_base; uint16_t scb_status; uint8_t cu_status, rx_status; uint16_t lnkst; + uint32_t last_tx_len; + uint32_t collision_events; + uint32_t total_collisions; + + uint32_t tx_retry_addr; + int tx_retry_count; + uint32_t tx_good_frames; + uint32_t tx_collisions; + uint32_t tx_aborted_errors; =20 - uint32_t cmd_p; /* addr of current command */ + uint32_t cmd_p; int ca; int ca_active; int send_irq; =20 - /* Hash register (multicast mask array, multiple individual addresses)= . */ uint8_t mult[8]; - uint8_t config[14]; /* config bytes from CONFIGURE command */ + uint8_t config[14]; + + uint32_t crc_err; + uint32_t align_err; + uint32_t resource_err; + uint32_t over_err; + uint32_t rcvdt_err; + uint32_t short_fr_error; + uint32_t total_frames; + uint32_t total_good_frames; + + uint8_t tx_buffer[PKT_BUF_SZ]; + uint8_t rx_buffer[PKT_BUF_SZ]; + uint16_t tx_frame_len; + uint16_t rx_frame_len; =20 - uint8_t tx_buffer[0x4000]; + hwaddr current_tx_desc; + hwaddr current_rx_desc; + uint32_t last_good_rfa; + uint8_t packet_queue[PACKET_QUEUE_SIZE][PKT_BUF_SZ]; + size_t packet_queue_len[PACKET_QUEUE_SIZE]; + int queue_head; + int queue_tail; + int queue_count; + bool rnr_signaled; + bool flushing_queue; }; =20 void i82596_h_reset(void *opaque); void i82596_ioport_writew(void *opaque, uint32_t addr, uint32_t val); uint32_t i82596_ioport_readw(void *opaque, uint32_t addr); -void i82596_ioport_writel(void *opaque, uint32_t addr, uint32_t val); -uint32_t i82596_ioport_readl(void *opaque, uint32_t addr); -uint32_t i82596_bcr_readw(I82596State *s, uint32_t rap); ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t size= _); +ssize_t i82596_receive_iov(NetClientState *nc, const struct iovec *iov, + int iovcnt); bool i82596_can_receive(NetClientState *nc); void i82596_set_link_status(NetClientState *nc); -void i82596_common_init(DeviceState *dev, I82596State *s, NetClientInfo *i= nfo); +void i82596_poll(NetClientState *nc, bool enable); +void i82596_common_init(DeviceState *dev, I82596State *s, + NetClientInfo *info); extern const VMStateDescription vmstate_i82596; #endif diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c index 9e1dd21546..fc06588ade 100644 --- a/hw/net/lasi_i82596.c +++ b/hw/net/lasi_i82596.c @@ -86,6 +86,10 @@ static const MemoryRegionOps lasi_82596_mem_ops =3D { .min_access_size =3D 4, .max_access_size =3D 4, }, + .impl =3D { + .min_access_size =3D 4, + .max_access_size =3D 4, + }, }; =20 static NetClientInfo net_lasi_82596_info =3D { @@ -93,6 +97,8 @@ static NetClientInfo net_lasi_82596_info =3D { .size =3D sizeof(NICState), .can_receive =3D i82596_can_receive, .receive =3D i82596_receive, + .receive_iov =3D i82596_receive_iov, + .poll =3D i82596_poll, .link_status_changed =3D i82596_set_link_status, }; =20 --=20 2.49.0 From nobody Fri Nov 14 17:01:13 2025 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1761946086; cv=none; d=zohomail.com; s=zohoarc; b=UTc+ZrCUuyve5cO6IAvxUJnB7OIWKtlWkD5clhn/VuK7+HKa/x2TE3xlQROnQjUF/X3XpdwgHLQ7m+oJ5AAF8JEf7r79svlgLqyr+1uEJMQo9d0jhgJwQOcV59Qr804TNgagZN/PBHKbQ8uX+chpWDfi3XN1BC7VgM7v4LorvQg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1761946086; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=fAM4FZoxb0MBH5ZAtKRWKw7Q0rXwyIxHIyxwj872NFQ=; b=iyXQPDB8duwmJE5Ug2s0hg7MRklg7fNb4VFGkwJ7ah8E5Kb6DcTrjbhVmABlz5qx4zyvUBZRixi2YIPpWHTAGNizVHVjyns6H9tAYMA9jxbzHztc5HhwRNsrM5bBYysLrJ4QGF3cKTjOCLy3aDuT+sCmbg/21V8jeEFyKAwOGe0= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1761946086607734.0994004306191; Fri, 31 Oct 2025 14:28:06 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vEweB-0006Yu-FJ; Fri, 31 Oct 2025 17:26:59 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1vEwTF-0000IA-PI for qemu-devel@nongnu.org; Fri, 31 Oct 2025 17:15:41 -0400 Received: from mail-pg1-x544.google.com ([2607:f8b0:4864:20::544]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vEwTA-0001xY-Co for qemu-devel@nongnu.org; Fri, 31 Oct 2025 17:15:41 -0400 Received: by mail-pg1-x544.google.com with SMTP id 41be03b00d2f7-b6ce806af3eso2649755a12.0 for ; Fri, 31 Oct 2025 14:15:35 -0700 (PDT) Received: from fedora.. ([103.2.232.250]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-b93b8aa3ff1sm3023320a12.14.2025.10.31.14.15.30 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 31 Oct 2025 14:15:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1761945334; x=1762550134; darn=nongnu.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=fAM4FZoxb0MBH5ZAtKRWKw7Q0rXwyIxHIyxwj872NFQ=; b=JJXURV2A3uGpjPdXVHtTwSo2HTjVDve+XkbREAH9ygfeQivzhOCu7lpDdPrR6StVUC tVR3EG6yZi/dQHCBWXdLuYghdQw9nNg0HxVBmWVJPd5KMbmZTDDQmzbH5mCuEA7vPlut JqccvI+afdmK5MIwAw6tqQCgjEr7J/WsKaLyixRVdVlom0bHXROixySGXX2DwwaIzlzu 9xUPp79dlx4u5CJ5+5wSOQrtRz39SmdmBxMhIA6fSt5z10FJht+x+fh7xm0QNXevYQbA 45TejaRIsGTdV4PIVTLNVGNOorOcQc6A5RI2JJtedXuS8K2hUTLnMU/L1hEteUuhTber saPw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1761945334; x=1762550134; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=fAM4FZoxb0MBH5ZAtKRWKw7Q0rXwyIxHIyxwj872NFQ=; b=GP9jil7dgS0h0XeKdoGqpUEtfAUlklSBF9VRr0JR/FDigctLYVvD3xaooft9W0/hn3 9pIaYB2ksNh9+iXf2I4GGNvN1INmKGT4h6AG0ZyPeWCrwc2w+D58stQCfVh8AlkZO9u6 JyVXCgwkcTkvhQcia+jvh41oY8S30HXJzfbUYmrmbFiVWgE565H3wj4iR7zDd1wANPzF o6xe8jL5cAYuQkT/jHig2IrBPuM69+zUrGdpwdEM/sjKvNqEg83MmNvclWeTOTI1Uuxd YpgsH3nOjTqq4KKjyfDQThiEjdbYGbDSpjRmQbV2EFCOrpf1RamvMaT+RZRbYLF+3DNi Tjtg== X-Forwarded-Encrypted: i=1; AJvYcCUyF37LSwPKsaloq7+PHL65yUVna3hk7RpnNnpL38ASNBaGMfxIHGfmzq44qzDUNnplQws9QzKKA2rX@nongnu.org X-Gm-Message-State: AOJu0Ywx1x5Zee7y4TAyRxmaorH7h3JOqqLTE66vW2/wWOUpHhWM+MhO JJhnHaD44T3GWvsZnXRU+sQdkm6YiWeqZHA5wIi3lJcGtRZy1se3Vd8Q X-Gm-Gg: ASbGnctWWdna82v9cHzkLJYHlbR/XPZfgiyyhMDAXkZ/X2D/RzLcfOIDRP/cbqg/33m TKmfrm7iu4g32td7xdYddcUYLlcfWhHzP0hOxmCwcFcZUFQ/iGVz73NNXauwCmYPjYN8ZXcC6lS /iWS7GoSU8IfRO4lpktSOTiLl8NCKRIy1aJywFtKpTc8sTZOMdbVx+lAaZiFirkyB8ZlmdSVss3 GQQOgqbfS/Ot5OU2htLduEgUwz/JWX98mvX2rVqHGALgJlQYUQqZL1xnzaWgLa1LCe5+Qd/HV9S JmUdEyTh0oRfFLJCophaLoC3bUa6Uk/yAbB4qc9fBcig7j/xIdx7vnReuZfkqdebfbhK2faxoC7 SsamWXn6Psl2ZRSxjhZXtsEt8PkhPwXXIP470MdETqMZo0aJ4P2jzSXAKXedKlIKTEUuwdMre0H bQNXSiIUTZn0+bS1ekZs2uDAC/t7W2of+BglGjrud5lrC6wX/SGw== X-Google-Smtp-Source: AGHT+IG1ytLpUPhlitUTGeeDBPoXofMiZynUXPgp0YkdMTUKnxFwmmsxNBSWE6G84aVYOLBHwXV0tg== X-Received: by 2002:a17:902:c401:b0:24b:182b:7144 with SMTP id d9443c01a7336-2951a38fdf9mr55212845ad.7.1761945333096; Fri, 31 Oct 2025 14:15:33 -0700 (PDT) From: Soumyajyotii Ssarkar To: deller@gmx.de, qemu-devel@nongnu.org Cc: sarkarsoumyajyoti23@gmail.com, Richard Henderson , Jason Wang , RemZapCypher Subject: [PATCH 3/3] i82596: Implement enhanced TX/RX with packet queuing and filtering Date: Sat, 1 Nov 2025 02:45:16 +0530 Message-ID: <20251031211516.208488-4-soumyajyotisarkar23@gmail.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20251031211516.208488-1-soumyajyotisarkar23@gmail.com> References: <20251031211516.208488-1-soumyajyotisarkar23@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=2607:f8b0:4864:20::544; envelope-from=soumyajyotisarkar23@gmail.com; helo=mail-pg1-x544.google.com X-Spam_score_int: -17 X-Spam_score: -1.8 X-Spam_bar: - X-Spam_report: (-1.8 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-Mailman-Approved-At: Fri, 31 Oct 2025 17:26:56 -0400 X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @gmail.com) X-ZM-MESSAGEID: 1761946087794158500 Content-Type: text/plain; charset="utf-8" From: RemZapCypher In this patch I have done the following: - Rewrote transmit path with CSMA/CD collision handling and retry logic - Implemented flexible TX buffer descriptor (TBD) chain processing - Rewrote receive path with packet filtering and monitor mode support - Added RX packet queue for handling resource exhaustion - Implemented queue flush timer - Added RX state machine with proper state transitions - Implemented packet filtering - Added SCB RU_START enhancement to find a usable RFDs - Implemented dump & diagnostics functionality=20 - Added bus throttle timer - Enhanced signal_ca with a proper initialization sequence - And finally, adding self-test functionality Note: With this patch, and the previous ones in the patch series, we would be able to achive proper 82596 NIC emulation. --- hw/net/i82596.c | 2003 ++++++++++++++++++++++++++++++++----------- hw/net/trace-events | 8 +- 2 files changed, 1512 insertions(+), 499 deletions(-) diff --git a/hw/net/i82596.c b/hw/net/i82596.c index 859cc88b2e..f6ee28e7c7 100644 --- a/hw/net/i82596.c +++ b/hw/net/i82596.c @@ -51,7 +51,7 @@ =20 #define ENABLE_DEBUG 0 =20 -#if defined(ENABLE_DEBUG) +#if ENABLE_DEBUG #define DBG(x) x #else #define DBG(x) do { } while (0) @@ -274,6 +274,10 @@ struct qemu_ether_header { }; =20 #define PRINT_PKTHDR(txt, BUF) do { \ + struct qemu_ether_header *hdr =3D (void *)(BUF); \ + printf(txt ": packet dhost=3D" MAC_FMT ", shost=3D" MAC_FMT ", type=3D= 0x%04x\n",\ + MAC_ARG(hdr->ether_dhost), MAC_ARG(hdr->ether_shost), \ + be16_to_cpu(hdr->ether_type)); \ } while (0) =20 static void i82596_cleanup(I82596State *s) @@ -324,104 +328,1022 @@ static void i82596_s_reset(I82596State *s) s->last_tx_len =3D 0; =20 s->last_good_rfa =3D 0; - s->current_rx_desc =3D 0; - s->current_tx_desc =3D 0; - s->tx_retry_addr =3D 0; - s->tx_retry_count =3D 0; + s->queue_head =3D 0; + s->queue_tail =3D 0; + s->queue_count =3D 0; =20 - s->rnr_signaled =3D false; - s->flushing_queue =3D false; + s->t_on =3D 0xFFFF; + s->t_off =3D 0; + s->throttle_state =3D true; + + if (!s->throttle_timer) { + s->throttle_timer =3D timer_new_ns(QEMU_CLOCK_VIRTUAL, + i82596_bus_throttle_timer, s); + } else { + timer_del(s->throttle_timer); + } + + if (!I596_FULL_DUPLEX && s->t_on !=3D 0xFFFF) { + timer_mod(s->throttle_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + s->t_on * NANOSECONDS_PER_MICROSECOND); + } +} + +void i82596_h_reset(void *opaque) +{ + I82596State *s =3D opaque; + + i82596_s_reset(s); +} + +/* + * Mode Transition of address functionality. + * Note: As of now the 82596 is tested only for Linear Mode as it is most + * widely used by Linux and HPUX systems. This function is here for + * futureproofing our 82596 device model. + * According to the documentation the translation of addresses based on mo= de + * are done for the following cases: ISCP, SCB, CBP, RFD, TFD, + * RBD, TBD, Rx Buffers, Tx Buffers + * Please refer to the documentation for more details. + */ +static inline uint32_t i82596_translate_address(I82596State *s, + uint32_t addr, + bool is_data_buffer) +{ + if (addr =3D=3D I596_NULL || addr =3D=3D 0) { + return addr; + } + switch (s->mode) { + case I82586_MODE: + if (is_data_buffer) { + return addr & 0x00FFFFFF; + } else { + if (s->scb_base) { + return (s->scb_base & 0x00FFFFFF) + (addr & 0xFFFF); + } else { + return addr & 0x00FFFFFF; + } + } + break; + case I82596_MODE_SEGMENTED: + if (is_data_buffer) { + return addr; + } else { + if (s->scb_base) { + return s->scb_base + (addr & 0xFFFF); + } else { + return addr; + } + } + break; + case I82596_MODE_LINEAR: + default: + return addr; + } +} + +/* (TFD) Transmit Frame Descriptor */ +struct i82596_tx_descriptor { + uint16_t status_bits; + uint16_t command; + uint32_t link_addr; + uint32_t tbd_addr; + uint16_t tcb_count; + uint8_t dest_addr[6]; + uint16_t length_field; +}; + +/* (TBD) Transmit Buffer Descriptor */ +struct i82596_tx_buffer_desc { + uint16_t size; + uint32_t link; + uint32_t buffer; +}; + +/* (RFD) Receive Frame Descriptor */ +struct i82596_rx_descriptor { + uint16_t status_bits; + uint16_t command; + uint32_t link; + uint32_t rbd_addr; + uint16_t actual_count; + uint16_t size; + uint8_t dest_addr[6]; + uint8_t src_addr[6]; + uint16_t length_field; +}; + +/* (RBD) Receive Buffer Descriptor */ +struct i82596_rx_buffer_desc { + uint16_t actual_count; + uint32_t next_rbd_addr; + uint32_t buffer_addr; + uint16_t size; +}; + +static void i82596_tx_tfd_read(I82596State *s, hwaddr addr, + struct i82596_tx_descriptor *desc) +{ + desc->status_bits =3D get_uint16(addr + 0); + desc->command =3D get_uint16(addr + 2); + desc->link_addr =3D get_uint32(addr + 4); + desc->tbd_addr =3D get_uint32(addr + 8); + desc->tcb_count =3D get_uint16(addr + 12); + address_space_read(&address_space_memory, addr + 14, + MEMTXATTRS_UNSPECIFIED, desc->dest_addr, 6); + desc->length_field =3D get_uint16(addr + 20); +} + +static void i82596_tx_tfd_write(I82596State *s, hwaddr addr, + struct i82596_tx_descriptor *desc) +{ + set_uint16(addr + 0, desc->status_bits); + set_uint16(addr + 2, desc->command); + set_uint32(addr + 4, desc->link_addr); + set_uint32(addr + 8, desc->tbd_addr); + set_uint16(addr + 12, desc->tcb_count); + address_space_write(&address_space_memory, addr + 14, + MEMTXATTRS_UNSPECIFIED, desc->dest_addr, 6); + set_uint16(addr + 20, desc->length_field); +} + +static void i82596_tbd_read(I82596State *s, hwaddr addr, + struct i82596_tx_buffer_desc *tbd) +{ + tbd->size =3D get_uint16(addr + 0); + tbd->link =3D get_uint32(addr + 4); + tbd->buffer =3D get_uint32(addr + 8); +} + +#if ENABLE_DEBUG +static void i82596_tx_tfd_dump(I82596State *s, hwaddr addr, + struct i82596_tx_descriptor *desc) +{ + DBG(printf("TFD @0x%08lx:\n", (unsigned long)addr)); + DBG(printf(" Status=3D0x%04x Cmd=3D0x%04x Link=3D0x%08x TBD=3D0x%08x\= n", + desc->status_bits, desc->command, desc->link_addr, + desc->tbd_addr)); + DBG(printf(" Count=3D%d (0x%04x)\n", + desc->tcb_count & SIZE_MASK, desc->tcb_count)); + DBG(printf(" Dest MAC=3D" MAC_FMT "\n", MAC_ARG(desc->dest_addr))); + DBG(printf(" Length/Type=3D0x%04x\n", desc->length_field)); +} +#endif + +#if ENABLE_DEBUG +static void i82596_tbd_dump(I82596State *s, hwaddr addr, + struct i82596_tx_buffer_desc *tbd) +{ + DBG(printf(" TBD @0x%08lx: size=3D%d EOF=3D%d EL=3D%d buf=3D0x%08x\n", + (unsigned long)addr, + tbd->size & 0x3FFF, + !!(tbd->size & I596_EOF), + !!(tbd->size & CMD_EOL), + tbd->buffer)); +} +#endif + +static void i82596_rx_rfd_read(I82596State *s, hwaddr addr, + struct i82596_rx_descriptor *desc) +{ + desc->status_bits =3D get_uint16(addr + 0x0); + desc->command =3D get_uint16(addr + 0x2); + desc->link =3D get_uint32(addr + 0x4); + desc->rbd_addr =3D get_uint32(addr + 0x8); + desc->actual_count =3D get_uint16(addr + 0xC); + desc->size =3D get_uint16(addr + 0xE); + + address_space_read(&address_space_memory, addr + 0x10, + MEMTXATTRS_UNSPECIFIED, desc->dest_addr, 6); + address_space_read(&address_space_memory, addr + 0x16, + MEMTXATTRS_UNSPECIFIED, desc->src_addr, 6); + desc->length_field =3D get_uint16(addr + 28); +} + +static void i82596_rx_desc_write(I82596State *s, hwaddr addr, + struct i82596_rx_descriptor *desc, + bool write_full) +{ + set_uint16(addr + 0x0, desc->status_bits); + set_uint16(addr + 0xC, desc->actual_count); + + if (write_full) { + set_uint16(addr + 0x2, desc->command); + set_uint32(addr + 0x4, desc->link); + set_uint32(addr + 0x8, desc->rbd_addr); + set_uint16(addr + 0xE, desc->size); + + address_space_write(&address_space_memory, addr + 0x10, + MEMTXATTRS_UNSPECIFIED, desc->dest_addr, 6); + address_space_write(&address_space_memory, addr + 0x16, + MEMTXATTRS_UNSPECIFIED, desc->src_addr, 6); + set_uint16(addr + 0x1C, desc->length_field); + } +} + +static void i82596_rbd_read(I82596State *s, hwaddr addr, + struct i82596_rx_buffer_desc *rbd) +{ + rbd->actual_count =3D get_uint16(addr + 0x0); + rbd->next_rbd_addr =3D get_uint32(addr + 0x4); + rbd->buffer_addr =3D get_uint32(addr + 0x8); + rbd->size =3D get_uint16(addr + 0xC); +} + +static void i82596_rbd_write(I82596State *s, hwaddr addr, + struct i82596_rx_buffer_desc *rbd) +{ + set_uint16(addr + 0x0, rbd->actual_count); + set_uint32(addr + 0x4, rbd->next_rbd_addr); + set_uint32(addr + 0x8, rbd->buffer_addr); + set_uint16(addr + 0xC, rbd->size); +} + +#if ENABLE_DEBUG +static void i82596_rx_rfd_dump(I82596State *s, hwaddr addr, + struct i82596_rx_descriptor *desc) +{ + DBG(printf("RFD @0x%08lx:\n", (unsigned long)addr)); + DBG(printf(" Status=3D0x%04x Cmd=3D0x%04x Link=3D0x%08x RBD=3D0x%08x\= n", + desc->status_bits, desc->command, desc->link, desc->rbd_add= r)); + DBG(printf(" Count=3D%d Size=3D%d EOF=3D%d F=3D%d\n", + desc->actual_count & 0x3FFF, + desc->size & 0x3FFF, + !!(desc->actual_count & I596_EOF), + !!(desc->actual_count & 0x4000))); + DBG(printf(" Dest=3D" MAC_FMT " Src=3D" MAC_FMT " Len=3D0x%04x\n", + MAC_ARG(desc->dest_addr), MAC_ARG(desc->src_addr), + desc->length_field)); +} + +static void i82596_rbd_dump(I82596State *s, hwaddr addr, + struct i82596_rx_buffer_desc *rbd) +{ + DBG(printf(" RBD @0x%08lx: count=3D%d EOF=3D%d F=3D%d buf=3D0x%08x si= ze=3D%d " + "EL=3D%d P=3D%d\n", (unsigned long)addr, + rbd->actual_count & 0x3FFF, + !!(rbd->actual_count & I596_EOF), + !!(rbd->actual_count & 0x4000), + rbd->buffer_addr, + rbd->size & 0x3FFF, + !!(rbd->size & CMD_EOL), + !!(rbd->size & 0x4000))); +} +#endif + +static int i82596_tx_copy_buffers(I82596State *s, hwaddr tfd_addr, + struct i82596_tx_descriptor *desc) +{ + bool simplified_mode =3D !(desc->command & CMD_FLEX); + uint32_t total_len =3D 0; + uint32_t tbd_addr; + struct i82596_tx_buffer_desc tbd; + + s->tx_frame_len =3D 0; + + if (simplified_mode) { + uint16_t frame_len =3D desc->tcb_count & SIZE_MASK; + if (frame_len =3D=3D 0 || frame_len > sizeof(s->tx_buffer)) { + return -1; + } + address_space_read(&address_space_memory, tfd_addr + 16, + MEMTXATTRS_UNSPECIFIED, s->tx_buffer, frame_len); + total_len =3D frame_len; + + } else { + tbd_addr =3D desc->tbd_addr; + while (tbd_addr !=3D I596_NULL && tbd_addr !=3D 0) { + uint16_t buf_size; + uint32_t buf_addr; + tbd_addr =3D i82596_translate_address(s, tbd_addr, false); + if (tbd_addr =3D=3D 0 || tbd_addr =3D=3D I596_NULL) { + return -1; + } + i82596_tbd_read(s, tbd_addr, &tbd); +#if ENABLE_DEBUG + DBG(i82596_tbd_dump(s, tbd_addr, &tbd)); +#endif + buf_size =3D tbd.size & SIZE_MASK; + buf_addr =3D i82596_translate_address(s, tbd.buffer, true); + + if (total_len + buf_size > sizeof(s->tx_buffer)) { + return -1; + } + + if (buf_size > 0 && buf_addr !=3D 0 && buf_addr !=3D I596_NULL= ) { + address_space_read(&address_space_memory, buf_addr, + MEMTXATTRS_UNSPECIFIED, + s->tx_buffer + total_len, buf_size); + total_len +=3D buf_size; + } + if (tbd.size & I596_EOF) { + break; + } + tbd_addr =3D tbd.link; + } + } + + s->tx_frame_len =3D total_len; + return total_len; +} + +static int i82596_tx_process_frame(I82596State *s, bool insert_crc) +{ + uint32_t total_len =3D s->tx_frame_len; + + if (total_len =3D=3D 0) { + return 0; + } + + if (I596_NO_SRC_ADD_IN =3D=3D 0 && total_len >=3D ETH_ALEN * 2) { + memcpy(&s->tx_buffer[ETH_ALEN], s->conf.macaddr.a, ETH_ALEN); + } + + if (I596_PADDING && total_len < I596_MIN_FRAME_LEN) { + size_t pad_len =3D I596_MIN_FRAME_LEN - total_len; + memset(s->tx_buffer + total_len, 0, pad_len); + total_len =3D I596_MIN_FRAME_LEN; + } + + if (insert_crc) { + total_len =3D i82596_append_crc(s, s->tx_buffer, total_len); + } + + s->tx_frame_len =3D total_len; + return total_len; +} + +static void i82596_tx_update_status(I82596State *s, hwaddr tfd_addr, + struct i82596_tx_descriptor *desc, + uint16_t tx_status, + uint16_t collision_count, + bool success) +{ + desc->status_bits =3D STAT_C; + + if (success) { + desc->status_bits |=3D STAT_OK; + } else { + desc->status_bits |=3D STAT_A; + } + + if (collision_count > 0) { + desc->status_bits |=3D (collision_count & 0x0F); + } + + i82596_tx_tfd_write(s, tfd_addr, desc); +} + +static int i82596_tx_csma_cd(I82596State *s, uint16_t *tx_status) +{ + int retry_count =3D 0; + bool medium_available; + + if (I596_FULL_DUPLEX || I596_LOOPBACK) { + return 0; + } + + while (retry_count < CSMA_MAX_RETRIES) { + medium_available =3D i82596_check_medium_status(s); + + if (medium_available) { + break; + } + i82596_csma_backoff(s, retry_count); + retry_count++; + s->total_collisions++; + } + if (retry_count >=3D CSMA_MAX_RETRIES) { + *tx_status |=3D TX_ABORTED_ERRORS; + return -1; + } + if (retry_count > 0) { + *tx_status |=3D TX_COLLISIONS; + s->collision_events++; + } + + return retry_count; +} + +static void i82596_transmit(I82596State *s, uint32_t addr) +{ + struct i82596_tx_descriptor tfd; + hwaddr tfd_addr =3D addr; + uint16_t tx_status =3D 0; + int collision_count =3D 0; + int frame_len; + bool success =3D true; + bool insert_crc; + + i82596_tx_tfd_read(s, tfd_addr, &tfd); + +#if ENABLE_DEBUG + DBG(i82596_tx_tfd_dump(s, tfd_addr, &tfd)); +#endif + + s->current_tx_desc =3D tfd_addr; + insert_crc =3D (I596_NOCRC_INS =3D=3D 0) && ((tfd.command & 0x10) =3D= =3D 0) && + !I596_LOOPBACK; + collision_count =3D i82596_tx_csma_cd(s, &tx_status); + if (collision_count < 0) { + success =3D false; + goto tx_complete; + } + frame_len =3D i82596_tx_copy_buffers(s, tfd_addr, &tfd); + if (frame_len < 0) { + tx_status |=3D TX_ABORTED_ERRORS; + success =3D false; + goto tx_complete; + } + frame_len =3D i82596_tx_process_frame(s, insert_crc); + if (frame_len <=3D 0) { + tx_status |=3D TX_ABORTED_ERRORS; + success =3D false; + goto tx_complete; + } + s->last_tx_len =3D frame_len; + trace_i82596_transmit(frame_len, addr); + + if (I596_LOOPBACK) { + i82596_receive(qemu_get_queue(s->nic), s->tx_buffer, frame_len); + } else { + if (s->nic) { + qemu_send_packet_raw(qemu_get_queue(s->nic), s->tx_buffer, + frame_len); + } + } + +tx_complete: + i82596_tx_update_status(s, tfd_addr, &tfd, tx_status, collision_count, + success); + i82596_update_statistics(s, true, tx_status, collision_count); + if (tfd.command & CMD_INTR) { + i82596_update_cu_status(s, tfd.status_bits, true); + } +} + +bool i82596_can_receive(NetClientState *nc) +{ + I82596State *s =3D qemu_get_nic_opaque(nc); + + if (I596_LOOPBACK || !s->lnkst) { + return false; + } + + if (s->rx_status =3D=3D RX_SUSPENDED || s->rx_status =3D=3D RX_IDLE || + s->rx_status =3D=3D RX_NO_RESOURCES) { + return true; + } + + if (!s->throttle_state && !I596_FULL_DUPLEX) { + return (s->queue_count < PACKET_QUEUE_SIZE); + } + + return true; +} + +static int i82596_validate_receive_state(I82596State *s, size_t *sz, + bool from_queue) +{ + if (*sz < 14 || *sz > PKT_BUF_SZ - 4) { + trace_i82596_receive_analysis(">>> Packet size invalid"); + return -1; + } + + if (!from_queue && s->rx_status =3D=3D RX_SUSPENDED) { + trace_i82596_receive_analysis(">>> Receiving is suspended"); + return -1; + } + + if (s->rx_status !=3D RX_READY && s->rx_status !=3D RX_SUSPENDED) { + trace_i82596_receive_analysis(">>> RU not ready"); + return -1; + } + + if (!s->lnkst) { + trace_i82596_receive_analysis(">>> Link is down"); + return -1; + } + + return 1; +} + +static bool i82596_check_packet_filter(I82596State *s, const uint8_t *buf, + uint16_t *is_broadcast) +{ + static const uint8_t broadcast_macaddr[6] =3D { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + if (I596_PROMISC || I596_LOOPBACK) { + trace_i82596_receive_analysis( + ">>> packet received in promiscuous mode"); + return true; + } else { + if (!memcmp(buf, broadcast_macaddr, 6)) { + /* broadcast address */ + if (I596_BC_DISABLE) { + trace_i82596_receive_analysis(">>> broadcast packet reject= ed"); + return false; + } + trace_i82596_receive_analysis(">>> broadcast packet received"); + *is_broadcast =3D 1; + return true; + } else if (buf[0] & 0x01) { + /* multicast */ + if (!I596_MC_ALL) { + trace_i82596_receive_analysis(">>> multicast packet reject= ed"); + return false; + } + + int mcast_idx =3D (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2; + assert(mcast_idx < 8 * sizeof(s->mult)); + + if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) { + trace_i82596_receive_analysis(">>> multicast address misma= tch"); + return false; + } + + trace_i82596_receive_analysis(">>> multicast packet received"); + *is_broadcast =3D 1; + return true; + } else if (!memcmp(s->conf.macaddr.a, buf, 6)) { + /* match */ + trace_i82596_receive_analysis( + ">>> physical address matching packet received"); + return true; + } else { + trace_i82596_receive_analysis(">>> unknown packet"); + return false; + } + } +} + +/* MONITOR MODE */ +static bool i82596_monitor(I82596State *s, const uint8_t *buf, size_t sz, + bool packet_passes_filter) +{ + if (I596_MONITOR_MODE =3D=3D MONITOR_DISABLED) { + return true; + } + if (sz < I596_MIN_FRAME_LEN) { + s->short_fr_error++; + } + if ((sz % 2) !=3D 0) { + s->align_err++; + } + + switch (I596_MONITOR_MODE) { + case MONITOR_NORMAL: /* No monitor, just add to total frames */ + if (packet_passes_filter) { + s->total_good_frames++; + return true; + } else { + return false; + } + break; + case MONITOR_FILTERED: /* Monitor only filtered packets */ + s->total_frames++; + if (packet_passes_filter) { + s->total_good_frames++; + } + return false; + case MONITOR_ALL: /* Monitor all packets */ + s->total_frames++; + if (packet_passes_filter) { + s->total_good_frames++; + } + return false; + + default: + return true; + } +} + +static void i82596_update_rx_state(I82596State *s, int new_state) +{ + if (s->rx_status !=3D new_state) { + trace_i82596_rx_state_change(s->rx_status, new_state); + } + + s->rx_status =3D new_state; + + switch (new_state) { + case RX_NO_RESOURCES: + if (!s->rnr_signaled) { + s->scb_status |=3D SCB_STATUS_RNR; + s->rnr_signaled =3D true; + } + break; + case RX_SUSPENDED: + if (!s->rnr_signaled) { + s->scb_status |=3D SCB_STATUS_RNR; + s->rnr_signaled =3D true; + } + + if (s->queue_count > 0 && !s->flushing_queue) { + i82596_flush_packet_queue(s); + if (s->queue_count > 0) { + timer_mod(s->flush_queue_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 50000); + } + } + break; + case RX_READY: + /* When RU becomes ready, flush buffered packets */ + if (s->queue_count > 0) { + i82596_flush_packet_queue(s); + } + break; + default: + break; + } +} + +static void i82596_rx_store_frame_header(I82596State *s, + struct i82596_rx_descriptor *rfd, + const uint8_t *buf, size_t size) +{ + memcpy(rfd->dest_addr, buf, 6); + if (size >=3D 12) { + memcpy(rfd->src_addr, buf + 6, 6); + } + if (size >=3D 14) { + rfd->length_field =3D (buf[12] << 8) | buf[13]; + } +} + +static size_t i82596_rx_copy_to_rfd(I82596State *s, hwaddr rfd_addr, + const uint8_t *buf, size_t size, + size_t rfd_size) +{ + size_t to_copy =3D MIN(size, rfd_size); + size_t data_offset =3D 0x1E; /* Bypassing the header */ + + if (to_copy > 0) { + address_space_write(&address_space_memory, rfd_addr + data_offset, + MEMTXATTRS_UNSPECIFIED, buf, to_copy); + } + return to_copy; +} + +static size_t i82596_rx_copy_to_rbds(I82596State *s, hwaddr rbd_addr, + const uint8_t *buf, size_t size, + bool *out_of_resources, + hwaddr *remaining_rbd) +{ + size_t bytes_copied =3D 0; + hwaddr current_rbd =3D rbd_addr; + *out_of_resources =3D false; + *remaining_rbd =3D I596_NULL; + + while (bytes_copied < size && current_rbd !=3D I596_NULL && + current_rbd !=3D 0) { + struct i82596_rx_buffer_desc rbd; + i82596_rbd_read(s, current_rbd, &rbd); + if (rbd.size & 0x4000) { /* P bit set */ + break; + } + + uint16_t buf_size =3D rbd.size & 0x3FFF; + + if (buf_size =3D=3D 0) { + current_rbd =3D i82596_translate_address(s, rbd.next_rbd_addr,= false); + continue; + } + + hwaddr buf_addr =3D i82596_translate_address(s, rbd.buffer_addr, t= rue); +#if ENABLE_DEBUG + i82596_rbd_dump(s, current_rbd, &rbd); +#endif + if (buf_addr =3D=3D 0 || buf_addr =3D=3D I596_NULL) { + *out_of_resources =3D true; + break; + } + size_t remaining =3D size - bytes_copied; + size_t to_copy =3D MIN(remaining, buf_size); + if (to_copy > 0) { + address_space_write(&address_space_memory, buf_addr, + MEMTXATTRS_UNSPECIFIED, + buf + bytes_copied, to_copy); + bytes_copied +=3D to_copy; + } + rbd.actual_count =3D to_copy | 0x4000; /* Set F (filled) bit */ + if (bytes_copied >=3D size) { + rbd.actual_count |=3D 0x8000; /* Set EOF bit (bit 15) */ + } + i82596_rbd_write(s, current_rbd, &rbd); + if (rbd.size & CMD_EOL) { /* EL bit */ + if (bytes_copied < size) { + *out_of_resources =3D true; + } + current_rbd =3D I596_NULL; + break; + } + current_rbd =3D i82596_translate_address(s, rbd.next_rbd_addr, fal= se); + } =20 - memset(s->tx_buffer, 0, sizeof(s->tx_buffer)); - memset(s->rx_buffer, 0, sizeof(s->rx_buffer)); - s->tx_frame_len =3D 0; - s->rx_frame_len =3D 0; + *remaining_rbd =3D current_rbd; + return bytes_copied; } =20 -void i82596_h_reset(void *opaque) +static inline size_t i82596_get_crc_size(I82596State *s) { - I82596State *s =3D opaque; - - i82596_s_reset(s); + return I596_CRC16_32 ? 4 : 2; } =20 -/* - * Address Translation Implementation - * Handles segmented and linear memory modes for i82596. - * Returns physical address for DMA operations. - * Returns I596_NULL (0xffffffff) on invalid addresses. - */ -static inline uint32_t i82596_translate_address(I82596State *s, - uint32_t logical_addr, - bool is_data_buffer) +static ssize_t i82596_receive_packet(I82596State *s, const uint8_t *buf, + size_t size, bool from_queue) { - if (logical_addr =3D=3D I596_NULL || logical_addr =3D=3D 0) { - return logical_addr; + struct i82596_rx_descriptor rfd; + uint32_t rfd_addr, rbd_addr; + uint16_t rx_status =3D 0; + uint16_t is_broadcast =3D 0; + bool packet_completed =3D true; + bool simplified_mode =3D false; + size_t frame_size =3D size; + size_t payload_size =3D 0; + size_t bytes_copied =3D 0; + const uint8_t *packet_data =3D buf; + bool crc_valid =3D true; + bool out_of_resources =3D false; + size_t crc_size =3D i82596_get_crc_size(s); + + DBG(PRINT_PKTHDR("[RX]", buf)); + + if (i82596_validate_receive_state(s, &size, from_queue) < 0) { + return -1; } =20 - switch (s->mode) { - case I82596_MODE_LINEAR: - return logical_addr; + bool passes_filter =3D i82596_check_packet_filter(s, buf, &is_broadcas= t); =20 - case I82596_MODE_SEGMENTED: { - uint32_t base =3D (logical_addr >> 16) & 0xFFFF; - uint32_t offset =3D logical_addr & 0xFFFF; + if (!i82596_monitor(s, buf, size, passes_filter) && (!passes_filter)) { + return size; + } + + + rfd_addr =3D get_uint32(s->scb + 8); + + if (rfd_addr =3D=3D 0 || rfd_addr =3D=3D I596_NULL) { + i82596_update_rx_state(s, RX_NO_RESOURCES); + s->resource_err++; + set_uint16(s->scb, get_uint16(s->scb) | SCB_STATUS_RNR); + i82596_update_scb_irq(s, true); + return -1; + } + + i82596_rx_rfd_read(s, rfd_addr, &rfd); +#if ENABLE_DEBUG + i82596_rx_rfd_dump(s, rfd_addr, &rfd); +#endif + + s->current_rx_desc =3D rfd_addr; + + if (rfd.status_bits & STAT_C) { + return -1; + } + + DBG(printf("RX: Using RFD=3D0x%08lx\n", (unsigned long)rfd_addr)); + + /* 0: Simplified Mode 1: Flexible Mode */ + simplified_mode =3D !(rfd.command & CMD_FLEX); + + set_uint16(rfd_addr, STAT_B); + + if (frame_size < 14) { + DBG(printf("RX: Frame too short (%zu bytes)\n", frame_size)); + rx_status |=3D RX_LENGTH_ERRORS; + i82596_record_error(s, RX_LENGTH_ERRORS, false); + s->short_fr_error++; + packet_completed =3D false; + goto rx_complete; + } + + payload_size =3D frame_size; + do { + if (simplified_mode && I596_LOOPBACK) { + uint16_t rfd_size =3D rfd.size & 0x3FFF; + + if (rfd_size % 2 !=3D 0) { + rx_status |=3D RX_LENGTH_ERRORS; + i82596_record_error(s, RX_LENGTH_ERRORS, false); + s->align_err++; + packet_completed =3D false; + goto rx_complete; + } + + if (payload_size > rfd_size) { + rx_status |=3D RFD_STATUS_TRUNC; + payload_size =3D rfd_size; + packet_completed =3D !SAVE_BAD_FRAMES ? false : true; + } + + if (payload_size > 0) { + bytes_copied =3D i82596_rx_copy_to_rfd(s, rfd_addr, packet= _data, + payload_size, rfd_si= ze); + } + + i82596_rx_store_frame_header(s, &rfd, packet_data, frame_size); =20 - if (is_data_buffer) { - return (base << 4) + offset; } else { - if (base =3D=3D 0xFFFF && offset =3D=3D 0xFFFF) { - return I596_NULL; + uint16_t rfd_size =3D rfd.size & 0x3FFF; /* SIZE_MASK */ + size_t rfd_frame_size =3D 0; + size_t remaining_to_copy =3D payload_size - bytes_copied; + if (rfd_size > 0 && remaining_to_copy > 0) { + size_t data_offset =3D 0x10; + + rfd_frame_size =3D MIN(remaining_to_copy, rfd_size); + address_space_write(&address_space_memory, + rfd_addr + data_offset, + MEMTXATTRS_UNSPECIFIED, + packet_data + bytes_copied, + rfd_frame_size); + bytes_copied +=3D rfd_frame_size; + } + + if (bytes_copied < payload_size) { + size_t remaining =3D payload_size - bytes_copied; + rbd_addr =3D i82596_translate_address(s, rfd.rbd_addr, fal= se); + + if (rbd_addr =3D=3D I596_NULL || rbd_addr =3D=3D 0) { + rx_status |=3D RFD_STATUS_TRUNC | RFD_STATUS_NOBUFS; + i82596_record_error(s, RFD_STATUS_NOBUFS, false); + packet_completed =3D true; + } else { + hwaddr remaining_rbd =3D I596_NULL; + size_t rbd_bytes =3D i82596_rx_copy_to_rbds( + s, rbd_addr, + packet_data + bytes_copied, + remaining, + &out_of_resources, + &remaining_rbd); + bytes_copied +=3D rbd_bytes; + + uint32_t next_rfd =3D i82596_translate_address(s, rfd.= link, + false); + if (next_rfd !=3D I596_NULL && next_rfd !=3D 0) { + if (remaining_rbd !=3D I596_NULL && remaining_rbd = !=3D 0) { + DBG(printf("RX: Updating next RFD 0x%08x to po= int " + "to remaining RBD 0x%08lx\n", + next_rfd, (unsigned long)remaining_rbd)= ); + set_uint32(next_rfd + 8, remaining_rbd); + } else { + DBG(printf("RX: Next RFD 0x%08x has no RBDs le= ft, " + "set NULL\n", next_rfd)); + set_uint32(next_rfd + 8, I596_NULL); + } + } + + if (out_of_resources) { + DBG(printf("RX: Out of RBDs mid-frame\n")); + i82596_record_error(s, RFD_STATUS_NOBUFS, false); + rx_status |=3D RFD_STATUS_TRUNC | RFD_STATUS_NOBUF= S; + packet_completed =3D true; + } + + if (bytes_copied < payload_size) { + DBG(printf("RX: Incomplete copy (%zu/%zu bytes)\n", + bytes_copied, payload_size)); + rx_status |=3D RFD_STATUS_TRUNC; + packet_completed =3D true; + } + } } - return s->scb_base + ((base << 4) + offset); } - } + break; =20 - case I82586_MODE: - default: - if (is_data_buffer) { - return logical_addr; + } while (bytes_copied < payload_size); + +rx_complete: + if (I596_CRCINM && !I596_LOOPBACK && packet_completed) { + uint8_t crc_data[4]; + size_t crc_len =3D crc_size; + + if (I596_CRC16_32) { + uint32_t crc =3D crc32(~0, packet_data, frame_size); + crc =3D cpu_to_be32(crc); + memcpy(crc_data, &crc, 4); } else { - if ((logical_addr & 0xFFFF0000) =3D=3D 0xFFFF0000) { - return I596_NULL; - } - return s->scb_base + logical_addr; + uint16_t crc =3D i82596_calculate_crc16(packet_data, frame_siz= e); + crc =3D cpu_to_be16(crc); + memcpy(crc_data, &crc, 2); + } + + if (simplified_mode) { + address_space_write(&address_space_memory, + rfd_addr + 0x1E + bytes_copied, + MEMTXATTRS_UNSPECIFIED, crc_data, crc_len); } } -} =20 -static void i82596_transmit(I82596State *s, uint32_t addr) -{ - uint32_t tdb_p; /* Transmit Buffer Descriptor */ + if (packet_completed && crc_valid) { + rx_status |=3D STAT_C | STAT_OK; + if (is_broadcast) { + rx_status |=3D 0x0001; + } + } else if (packet_completed) { + rx_status |=3D STAT_C; + if (!crc_valid) { + rx_status |=3D RX_CRC_ERRORS; + } + } else { + rx_status |=3D STAT_B; + } + + rfd.status_bits =3D rx_status & ~STAT_B; + rfd.actual_count =3D (bytes_copied & 0x3FFF) | 0x4000; + if (packet_completed) { + rfd.actual_count |=3D I596_EOF; + } =20 - /* TODO: Check flexible mode */ - tdb_p =3D get_uint32(addr + 8); - while (tdb_p !=3D I596_NULL) { - uint16_t size, len; - uint32_t tba; + i82596_rx_desc_write(s, rfd_addr, &rfd, (simplified_mode || I596_LOOPB= ACK)); =20 - size =3D get_uint16(tdb_p); - len =3D size & SIZE_MASK; - tba =3D get_uint32(tdb_p + 8); - trace_i82596_transmit(len, tba); + if (rfd.command & CMD_SUSP) { + i82596_update_rx_state(s, RX_SUSPENDED); + return size; + } + + if (rfd.command & CMD_EOL) { + i82596_update_rx_state(s, RX_SUSPENDED); + return size; + } =20 - if (s->nic && len) { - assert(len <=3D sizeof(s->tx_buffer)); - address_space_read(&address_space_memory, tba, - MEMTXATTRS_UNSPECIFIED, s->tx_buffer, len); - DBG(PRINT_PKTHDR("Send", &s->tx_buffer)); - DBG(printf("Sending %d bytes\n", len)); - qemu_send_packet(qemu_get_queue(s->nic), s->tx_buffer, len); + if (packet_completed && crc_valid && s->rx_status =3D=3D RX_READY) { + uint32_t next_rfd_addr =3D i82596_translate_address(s, rfd.link, f= alse); + if (next_rfd_addr !=3D 0 && next_rfd_addr !=3D I596_NULL) { + set_uint32(s->scb + 8, next_rfd_addr); } =20 - /* was this the last package? */ - if (size & I596_EOF) { - break; + s->scb_status |=3D SCB_STATUS_FR; + i82596_update_scb_irq(s, true); + } + DBG(printf("=3D=3D=3D RX: Complete (errors: CRC=3D%d align=3D%d res=3D= %d) =3D=3D=3D\n\n", + s->crc_err, s->align_err, s->resource_err)); + return size; +} + +ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t size) +{ + I82596State *s =3D qemu_get_nic_opaque(nc); + + if (!I596_FULL_DUPLEX && !s->throttle_state) { + if (s->queue_count < PACKET_QUEUE_SIZE) { + goto queue_packet; + } + trace_i82596_receive_suspended(); + return size; + } + + if (s->rx_status !=3D RX_READY) { + if (s->queue_count >=3D PACKET_QUEUE_SIZE) { + trace_i82596_receive_queue_full(); + s->over_err++; + set_uint32(s->scb + 22, s->over_err); + i82596_record_error(s, RX_OVER_ERRORS, false); + return size; + } +queue_packet: + if (size <=3D PKT_BUF_SZ) { + memcpy(s->packet_queue[s->queue_head], buf, size); + s->packet_queue_len[s->queue_head] =3D size; + s->queue_head =3D (s->queue_head + 1) % PACKET_QUEUE_SIZE; + s->queue_count++; } + return size; + } + + return i82596_receive_packet(s, buf, size, false); +} =20 - /* get next buffer pointer */ - tdb_p =3D get_uint32(tdb_p + 4); +ssize_t i82596_receive_iov(NetClientState *nc, const struct iovec *iov, + int iovcnt) +{ + size_t sz =3D 0; + uint8_t *buf; + int i; + for (i =3D 0; i < iovcnt; i++) { + sz +=3D iov[i].iov_len; + } + if (sz =3D=3D 0) { + return -1; + } + buf =3D g_malloc(sz); + if (!buf) { + return -1; + } + size_t offset =3D 0; + for (i =3D 0; i < iovcnt; i++) { + if (iov[i].iov_base =3D=3D NULL) { + g_free(buf); + return -1; + } + memcpy(buf + offset, iov[i].iov_base, iov[i].iov_len); + offset +=3D iov[i].iov_len; } + DBG(PRINT_PKTHDR("Receive IOV:", buf)); + i82596_receive(nc, buf, sz); + g_free(buf); + return sz; } =20 static void set_individual_address(I82596State *s, uint32_t addr) @@ -437,26 +1359,6 @@ static void set_individual_address(I82596State *s, ui= nt32_t addr) trace_i82596_new_mac(nc->info_str); } =20 -static void i82596_configure(I82596State *s, uint32_t addr) -{ - uint8_t byte_cnt; - byte_cnt =3D get_byte(addr + 8) & 0x0f; - - byte_cnt =3D MAX(byte_cnt, 4); - byte_cnt =3D MIN(byte_cnt, sizeof(s->config)); - /* copy byte_cnt max. */ - address_space_read(&address_space_memory, addr + 8, - MEMTXATTRS_UNSPECIFIED, s->config, byte_cnt); - /* config byte according to page 35ff */ - s->config[2] &=3D 0x82; /* mask valid bits */ - s->config[2] |=3D 0x40; - s->config[7] &=3D 0xf7; /* clear zero bit */ - assert(I596_NOCRC_INS =3D=3D 0); /* do CRC insertion */ - s->config[10] =3D MAX(s->config[10], 5); /* min frame length */ - s->config[12] &=3D 0x40; /* only full duplex field valid */ - s->config[13] |=3D 0x3f; /* set ones in byte 13 */ -} - static void set_multicast_list(I82596State *s, uint32_t addr) { uint16_t mc_count, i; @@ -471,8 +1373,6 @@ static void set_multicast_list(I82596State *s, uint32_= t addr) uint8_t multicast_addr[ETH_ALEN]; address_space_read(&address_space_memory, addr + i * ETH_ALEN, MEMTXATTRS_UNSPECIFIED, multicast_addr, ETH_ALE= N); - DBG(printf("Add multicast entry " MAC_FMT "\n", - MAC_ARG(multicast_addr))); unsigned mcast_idx =3D (net_crc32(multicast_addr, ETH_ALEN) & BITS(7, 2)) >> 2; assert(mcast_idx < 8 * sizeof(s->mult)); @@ -494,7 +1394,7 @@ void i82596_set_link_status(NetClientState *nc) } } =20 -static bool G_GNUC_UNUSED i82596_check_medium_status(I82596State *s) +static bool i82596_check_medium_status(I82596State *s) { if (I596_FULL_DUPLEX) { return true; @@ -512,7 +1412,7 @@ static bool G_GNUC_UNUSED i82596_check_medium_status(I= 82596State *s) return true; } =20 -static int G_GNUC_UNUSED i82596_csma_backoff(I82596State *s, int retry_cou= nt) +static int i82596_csma_backoff(I82596State *s, int retry_count) { int backoff_factor, slot_count, backoff_time; =20 @@ -541,7 +1441,7 @@ static uint16_t i82596_calculate_crc16(const uint8_t *= data, size_t len) return crc; } =20 -static size_t G_GNUC_UNUSED i82596_append_crc(I82596State *s, uint8_t *buf= fer, size_t len) +static size_t i82596_append_crc(I82596State *s, uint8_t *buffer, size_t le= n) { if (len + 4 > PKT_BUF_SZ) { return len; @@ -560,7 +1460,7 @@ static size_t G_GNUC_UNUSED i82596_append_crc(I82596St= ate *s, uint8_t *buffer, s } } =20 -static void G_GNUC_UNUSED i82596_update_statistics(I82596State *s, bool is= _tx, +static void i82596_update_statistics(I82596State *s, bool is_tx, uint16_t error_flags, uint16_t collision_count) { @@ -591,7 +1491,7 @@ static void G_GNUC_UNUSED i82596_update_statistics(I82= 596State *s, bool is_tx, } =20 /* Bus Throttle Functionality */ -static void G_GNUC_UNUSED i82596_bus_throttle_timer(void *opaque) +static void i82596_bus_throttle_timer(void *opaque) { I82596State *s =3D opaque; =20 @@ -600,42 +1500,210 @@ static void G_GNUC_UNUSED i82596_bus_throttle_timer= (void *opaque) if (s->t_off > 0) { timer_mod(s->throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (s->t_off * NANOSECONDS_PER_MICROSECOND)); + s->t_off * NANOSECONDS_PER_MICROSECOND); + } else { + s->throttle_state =3D true; } } else { s->throttle_state =3D true; - if (s->t_on > 0) { + if (s->t_on > 0 && s->t_on !=3D 0xFFFF) { + timer_mod(s->throttle_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + s->t_on * NANOSECONDS_PER_MICROSECOND); + } + } +} + +static void i82596_load_throttle_timers(I82596State *s, bool start_now) +{ + uint16_t previous_t_on =3D s->t_on; + uint16_t previous_t_off =3D s->t_off; + s->t_on =3D get_uint16(s->scb + 36); + s->t_off =3D get_uint16(s->scb + 38); + + bool values_changed =3D (s->t_on !=3D previous_t_on || + s->t_off !=3D previous_t_off); + if (start_now || (values_changed && s->throttle_timer)) { + timer_del(s->throttle_timer); + s->throttle_state =3D true; + if (s->t_on > 0 && s->t_on !=3D 0xFFFF && !I596_FULL_DUPLEX) { timer_mod(s->throttle_timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + - (s->t_on * NANOSECONDS_PER_MICROSECOND)); + s->t_on * NANOSECONDS_PER_MICROSECOND); } + } +} + +static void i82596_init_dump_area(I82596State *s, uint8_t *buffer) +{ + memset(buffer, 0, DUMP_BUF_SZ); + + printf("This is the dump area function for i82596 QEMU side\n" + "If you are seeing this message, please contact:\n" + "Soumyajyotii Sarkar \n" + "With the process in which you encountered this issue:\n" + "This still needs developement so,\n" + "I will be more than delighted to help you out!\n" + ); + + auto void write_uint16(int offset, uint16_t value) + { + buffer[offset] =3D value >> 8; + buffer[offset + 1] =3D value & 0xFF; + } + auto void write_uint32(int offset, uint32_t value) + { + write_uint16(offset, value >> 16); + write_uint16(offset + 2, value & 0xFFFF); + } + + write_uint16(0x00, (s->config[5] << 8) | s->config[4]); + write_uint16(0x02, (s->config[3] << 8) | s->config[2]); + write_uint16(0x04, (s->config[9] << 8) | s->config[8]); + write_uint16(0x06, (s->config[7] << 8) | s->config[6]); + write_uint16(0x08, (s->config[13] << 8) | s->config[12]); + write_uint16(0x0A, (s->config[11] << 8) | s->config[10]); + + buffer[0x0C] =3D s->conf.macaddr.a[0]; + buffer[0x0D] =3D s->conf.macaddr.a[1]; + buffer[0x10] =3D s->conf.macaddr.a[2]; + buffer[0x11] =3D s->conf.macaddr.a[3]; + buffer[0x12] =3D s->conf.macaddr.a[4]; + buffer[0x13] =3D s->conf.macaddr.a[5]; + + if (s->last_tx_len > 0) { + uint32_t tx_crc =3D crc32(~0, s->tx_buffer, s->last_tx_len); + write_uint16(0x14, tx_crc & 0xFFFF); + write_uint16(0x16, tx_crc >> 16); + } + + memcpy(&buffer[0x24], s->mult, sizeof(s->mult)); + + buffer[0xB0] =3D s->cu_status; + buffer[0xB1] =3D s->rx_status; + + write_uint32(0xB4, s->crc_err); + write_uint32(0xB8, s->align_err); + write_uint32(0xBC, s->resource_err); + write_uint32(0xC0, s->over_err); + + write_uint32(0xC4, s->short_fr_error); + write_uint32(0xC8, s->total_frames); + write_uint32(0xCC, s->total_good_frames); =20 - if (s->cu_status =3D=3D CU_ACTIVE || s->rx_status =3D=3D RX_READY)= { - examine_scb(s); + buffer[0xD0] =3D I596_PROMISC ? 1 : 0; + buffer[0xD1] =3D I596_BC_DISABLE ? 1 : 0; + buffer[0xD2] =3D I596_FULL_DUPLEX ? 1 : 0; + buffer[0xD3] =3D I596_LOOPBACK; + + uint8_t mc_count =3D 0; + for (int i =3D 0; i < sizeof(s->mult); i++) { + uint8_t byte =3D s->mult[i]; + while (byte) { + if (byte & 0x01) { + mc_count++; + } + byte >>=3D 1; } } + buffer[0xD4] =3D mc_count; + buffer[0xD5] =3D I596_NOCRC_INS ? 1 : 0; + buffer[0xD6] =3D I596_CRC16_32 ? 1 : 0; + + write_uint16(0xD8, s->lnkst); + buffer[0xDA] =3D I596_MONITOR_MODE; + write_uint32(0xDC, s->collision_events); + + write_uint16(0x110, s->t_on); + write_uint16(0x112, s->t_off); + write_uint16(0x114, s->throttle_state ? 0x0001 : 0x0000); + write_uint16(0x120, s->sysbus); + write_uint16(0x128, s->scb_status); + write_uint32(0, 0xFFFF0000); +} + +static void i82596_port_dump(I82596State *s, uint32_t dump_addr) +{ + uint8_t dump_buffer[DUMP_BUF_SZ]; + + i82596_init_dump_area(s, dump_buffer); + + address_space_write(&address_space_memory, dump_addr, + MEMTXATTRS_UNSPECIFIED, dump_buffer, sizeof(dump_buf= fer)); + + set_uint32(dump_addr, 0xFFFF0000); + s->scb_status |=3D SCB_STATUS_CX; + s->send_irq =3D 1; } =20 -static int G_GNUC_UNUSED i82596_flush_packet_queue(I82596State *s) +static void i82596_command_dump(I82596State *s, uint32_t cmd_addr) { - /* Stub for now - will be implemented in Patch 2 */ - return 0; + uint32_t dump_addr; + uint8_t dump_buffer[DUMP_BUF_SZ]; + uint16_t cmd =3D get_uint16(cmd_addr + 2); + uint16_t status; + + dump_addr =3D get_uint32(cmd_addr + 8); + + i82596_init_dump_area(s, dump_buffer); + address_space_write(&address_space_memory, dump_addr, + MEMTXATTRS_UNSPECIFIED, dump_buffer, sizeof(dump_buf= fer)); + status =3D STAT_C | STAT_OK; + set_uint16(cmd_addr, status); + if (cmd & CMD_INTR) { + s->scb_status |=3D SCB_STATUS_CX; + s->send_irq =3D 1; + } + if (cmd & CMD_SUSP) { + s->cu_status =3D CU_SUSPENDED; + s->scb_status |=3D SCB_STATUS_CNA; + } } =20 -static void G_GNUC_UNUSED i82596_flush_queue_timer(void *opaque) +static void i82596_configure(I82596State *s, uint32_t addr) { - /* Stub for now - will be implemented in Patch 2 */ + uint8_t byte_cnt; + byte_cnt =3D get_byte(addr + 8) & 0x0f; + byte_cnt =3D MAX(byte_cnt, 4); + byte_cnt =3D MIN(byte_cnt, sizeof(s->config)); + s->config[2] &=3D 0x82; /* mask valid bits */ + s->config[2] |=3D 0x40; + s->config[7] &=3D 0xf7; /* clear zero bit */ + + address_space_read(&address_space_memory, addr + 8, + MEMTXATTRS_UNSPECIFIED, s->config, byte_cnt); + + if (byte_cnt > 12) { + s->config[12] &=3D 0x40; + + if (byte_cnt > 11) { + uint8_t monitor_mode =3D I596_MONITOR_MODE; + s->config[11] &=3D ~0xC0; /* Clear bits 6-7 */ + s->config[11] |=3D (monitor_mode << 6); /* Set monitor mode */ + } + } + + if (s->rx_status =3D=3D RX_READY) { + timer_mod(s->flush_queue_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 100000000); + } + + s->scb_status |=3D SCB_STATUS_CNA; + s->config[13] |=3D 0x3f; + qemu_set_irq(s->irq, 1); } =20 static void i82596_update_scb_irq(I82596State *s, bool trigger) { + update_scb_status(s); + if (trigger) { s->send_irq =3D 1; qemu_set_irq(s->irq, 1); } } =20 -static void G_GNUC_UNUSED i82596_update_cu_status(I82596State *s, uint16_t= cmd_status, +static void i82596_update_cu_status(I82596State *s, uint16_t cmd_status, bool generate_interrupt) { if (cmd_status & STAT_C) { @@ -658,6 +1726,11 @@ static void G_GNUC_UNUSED i82596_update_cu_status(I82= 596State *s, uint16_t cmd_s update_scb_status(s); } =20 +/** + * Update SCB Status + * Synchronizes device state with SCB status word and statistics counters. + * This function is called frequently to keep the kernel driver updated. + */ static void update_scb_status(I82596State *s) { s->scb_status =3D (s->scb_status & 0xf000) @@ -678,21 +1751,26 @@ static void update_scb_status(I82596State *s) =20 static void command_loop(I82596State *s) { - uint16_t cmd; - uint16_t status; - - DBG(printf("STARTING COMMAND LOOP cmd_p=3D%08x\n", s->cmd_p)); - - while (s->cmd_p !=3D I596_NULL) { - /* set status */ - status =3D STAT_B; - set_uint16(s->cmd_p, status); - status =3D STAT_C | STAT_OK; /* update, but write later */ - - cmd =3D get_uint16(s->cmd_p + 2); - DBG(printf("Running command %04x at %08x\n", cmd, s->cmd_p)); - - switch (cmd & 0x07) { + while (s->cu_status =3D=3D CU_ACTIVE && s->cmd_p !=3D I596_NULL && + s->cmd_p !=3D 0) { + uint16_t status =3D get_uint16(s->cmd_p); + if (status & (STAT_C | STAT_B)) { + uint32_t next =3D get_uint32(s->cmd_p + 4); + if (next =3D=3D 0 || next =3D=3D s->cmd_p) { + s->cmd_p =3D I596_NULL; + s->cu_status =3D CU_IDLE; + s->scb_status |=3D SCB_STATUS_CNA; + break; + } + s->cmd_p =3D i82596_translate_address(s, next, false); + continue; + } + set_uint16(s->cmd_p, STAT_B); + uint16_t cmd =3D get_uint16(s->cmd_p + 2); + uint32_t next_addr =3D get_uint32(s->cmd_p + 4); + next_addr =3D (next_addr =3D=3D 0) ? I596_NULL : + i82596_translate_address(s, next_addr, false); + switch (cmd & CMD_MASK) { case CmdNOp: break; case CmdSASetup: @@ -702,454 +1780,334 @@ static void command_loop(I82596State *s) i82596_configure(s, s->cmd_p); break; case CmdTDR: - /* get signal LINK */ set_uint32(s->cmd_p + 8, s->lnkst); break; case CmdTx: i82596_transmit(s, s->cmd_p); - break; + goto skip_status_update; case CmdMulticastList: set_multicast_list(s, s->cmd_p); break; case CmdDump: + i82596_command_dump(s, s->cmd_p); + break; case CmdDiagnose: - printf("FIXME Command %d !!\n", cmd & 7); - g_assert_not_reached(); - } - - /* update status */ - set_uint16(s->cmd_p, status); - - s->cmd_p =3D get_uint32(s->cmd_p + 4); /* get link address */ - DBG(printf("NEXT addr would be %08x\n", s->cmd_p)); - if (s->cmd_p =3D=3D 0) { - s->cmd_p =3D I596_NULL; - } - - /* Stop when last command of the list. */ - if (cmd & CMD_EOL) { - s->cmd_p =3D I596_NULL; - } - /* Suspend after doing cmd? */ - if (cmd & CMD_SUSP) { - s->cu_status =3D CU_SUSPENDED; - printf("FIXME SUSPEND !!\n"); - } - /* Interrupt after doing cmd? */ - if (cmd & CMD_INTR) { - s->scb_status |=3D SCB_STATUS_CX; - } else { - s->scb_status &=3D ~SCB_STATUS_CX; - } - update_scb_status(s); - - /* Interrupt after doing cmd? */ - if (cmd & CMD_INTR) { - s->send_irq =3D 1; - } - - if (s->cu_status !=3D CU_ACTIVE) { + break; + default: + printf("CMD_LOOP: Unknown command %d\n", cmd & CMD_MASK); break; } - } - DBG(printf("FINISHED COMMAND LOOP\n")); - qemu_flush_queued_packets(qemu_get_queue(s->nic)); -} - -static void examine_scb(I82596State *s) -{ - uint16_t command, cuc, ruc; - - /* get the scb command word */ - command =3D get_uint16(s->scb + 2); - cuc =3D (command >> 8) & 0x7; - ruc =3D (command >> 4) & 0x7; - DBG(printf("MAIN COMMAND %04x cuc %02x ruc %02x\n", command, cuc, ruc= )); - /* and clear the scb command word */ - set_uint16(s->scb + 2, 0); - - s->scb_status &=3D ~(command & SCB_ACK_MASK); - - switch (cuc) { - case 0: /* no change */ - break; - case 1: /* CUC_START */ - s->cu_status =3D CU_ACTIVE; - break; - case 4: /* CUC_ABORT */ - s->cu_status =3D CU_SUSPENDED; - s->scb_status |=3D SCB_STATUS_CNA; /* CU left active state */ - break; - default: - printf("WARNING: Unknown CUC %d!\n", cuc); - } - - switch (ruc) { - case 0: /* no change */ - break; - case 1: /* RX_START */ - case 2: /* RX_RESUME */ - s->rx_status =3D RX_IDLE; - if (USE_TIMER) { - timer_mod(s->flush_queue_timer, qemu_clock_get_ms( - QEMU_CLOCK_VIRTUAL) + 1000); - } - break; - case 3: /* RX_SUSPEND */ - case 4: /* RX_ABORT */ - s->rx_status =3D RX_SUSPENDED; - s->scb_status |=3D SCB_STATUS_RNR; /* RU left active state */ - break; - default: - printf("WARNING: Unknown RUC %d!\n", ruc); - } - - if (command & 0x80) { /* reset bit set? */ - i82596_s_reset(s); - } =20 - /* execute commands from SCBL */ - if (s->cu_status !=3D CU_SUSPENDED) { - if (s->cmd_p =3D=3D I596_NULL) { - s->cmd_p =3D get_uint32(s->scb + 4); + status =3D get_uint16(s->cmd_p); + if (!(status & STAT_C)) { + set_uint16(s->cmd_p, STAT_C | STAT_OK); } - } =20 - /* update scb status */ - update_scb_status(s); +skip_status_update: + if (cmd & CMD_INTR) { + s->scb_status |=3D SCB_STATUS_CX; + s->send_irq =3D 1; + } =20 - command_loop(s); -} + bool stop =3D false; =20 -static void signal_ca(I82596State *s) -{ - uint32_t iscp =3D 0; + if (cmd & CMD_SUSP) { + s->cu_status =3D CU_SUSPENDED; + s->scb_status |=3D SCB_STATUS_CNA; + stop =3D true; + } =20 - /* trace_i82596_channel_attention(s); */ - if (s->scp) { - /* CA after reset -> do init with new scp. */ - s->sysbus =3D get_byte(s->scp + 3); /* big endian */ - DBG(printf("SYSBUS =3D %08x\n", s->sysbus)); - if (((s->sysbus >> 1) & 0x03) !=3D 2) { - printf("WARNING: NO LINEAR MODE !!\n"); - } - if ((s->sysbus >> 7)) { - printf("WARNING: 32BIT LINMODE IN B-STEPPING NOT SUPPORTED !!\= n"); - } - iscp =3D get_uint32(s->scp + 8); - s->scb =3D get_uint32(iscp + 4); - set_byte(iscp + 1, 0); /* clear BUSY flag in iscp */ - s->scp =3D 0; - } + if (cmd & CMD_EOL) { + s->cmd_p =3D I596_NULL; + s->cu_status =3D CU_IDLE; + s->scb_status |=3D SCB_STATUS_CNA; + stop =3D true; + } else if (!stop) { + if (next_addr =3D=3D 0 || next_addr =3D=3D I596_NULL || + next_addr =3D=3D s->cmd_p) { + s->cmd_p =3D I596_NULL; + s->cu_status =3D CU_IDLE; + s->scb_status |=3D SCB_STATUS_CNA; + stop =3D true; + } else { + s->cmd_p =3D next_addr; + } + } + + update_scb_status(s); =20 - s->ca++; /* count ca() */ - if (!s->ca_active) { - s->ca_active =3D 1; - while (s->ca) { - examine_scb(s); - s->ca--; + if (stop || s->cu_status !=3D CU_ACTIVE) { + break; } - s->ca_active =3D 0; } =20 - if (s->send_irq) { - s->send_irq =3D 0; - qemu_set_irq(s->irq, 1); - } -} + update_scb_status(s); =20 -void i82596_ioport_writew(void *opaque, uint32_t addr, uint32_t val) -{ - I82596State *s =3D opaque; - /* printf("i82596_ioport_writew addr=3D0x%08x val=3D0x%04x\n", addr, v= al); */ - switch (addr) { - case PORT_RESET: /* Reset */ - i82596_s_reset(s); - break; - case PORT_ALTSCP: - s->scp =3D val; - break; - case PORT_CA: - signal_ca(s); - break; + if (s->rx_status =3D=3D RX_READY && s->nic) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } -} =20 -uint32_t i82596_ioport_readw(void *opaque, uint32_t addr) -{ - return -1; } =20 -bool i82596_can_receive(NetClientState *nc) +static void examine_scb(I82596State *s) { - I82596State *s =3D qemu_get_nic_opaque(nc); + uint16_t command =3D get_uint16(s->scb + 2); + uint8_t cuc =3D (command >> 8) & 0x7; + uint8_t ruc =3D (command >> 4) & 0x7; =20 - if (s->rx_status =3D=3D RX_SUSPENDED) { - return false; - } + trace_i82596_scb_command(cuc, ruc); =20 - if (!s->lnkst) { - return false; - } + set_uint16(s->scb + 2, 0); + s->scb_status &=3D ~(command & SCB_ACK_MASK); =20 - if (USE_TIMER && !timer_pending(s->flush_queue_timer)) { - return true; + if (command & SCB_STATUS_RNR) { + s->rnr_signaled =3D false; } =20 - return true; -} + /* Process Command Unit (CU) commands */ + switch (cuc) { + case SCB_CUC_NOP: + break; =20 -ssize_t i82596_receive(NetClientState *nc, const uint8_t *buf, size_t sz) -{ - I82596State *s =3D qemu_get_nic_opaque(nc); - uint32_t rfd_p; - uint32_t rbd; - uint16_t is_broadcast =3D 0; - size_t len =3D sz; /* length of data for guest (including CRC) */ - size_t bufsz =3D sz; /* length of data in buf */ - uint32_t crc; - uint8_t *crc_ptr; - static const uint8_t broadcast_macaddr[6] =3D { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + case SCB_CUC_START: { + uint32_t cmd_ptr =3D get_uint32(s->scb + 4); + s->cmd_p =3D i82596_translate_address(s, cmd_ptr, false); + s->cu_status =3D CU_ACTIVE; + break; + } =20 - DBG(printf("i82596_receive() start\n")); + case SCB_CUC_RESUME: + if (s->cu_status =3D=3D CU_SUSPENDED) { + s->cu_status =3D CU_ACTIVE; + } + break; =20 - if (USE_TIMER && timer_pending(s->flush_queue_timer)) { - return 0; - } + case SCB_CUC_SUSPEND: + s->cu_status =3D CU_SUSPENDED; + s->scb_status |=3D SCB_STATUS_CNA; + break; =20 - /* first check if receiver is enabled */ - if (s->rx_status =3D=3D RX_SUSPENDED) { - trace_i82596_receive_analysis(">>> Receiving suspended"); - return -1; - } + case SCB_CUC_ABORT: + s->cu_status =3D CU_IDLE; + s->scb_status |=3D SCB_STATUS_CNA; + break; =20 - if (!s->lnkst) { - trace_i82596_receive_analysis(">>> Link down"); - return -1; + case SCB_CUC_LOAD_THROTTLE: { + bool external_trigger =3D (s->sysbus & I82586_MODE); + i82596_load_throttle_timers(s, !external_trigger); + break; } =20 - /* Received frame smaller than configured "min frame len"? */ - if (sz < s->config[10]) { - printf("Received frame too small, %zu vs. %u bytes\n", - sz, s->config[10]); - return -1; + case SCB_CUC_LOAD_START: + i82596_load_throttle_timers(s, true); + break; } =20 - DBG(printf("Received %lu bytes\n", sz)); - - if (I596_PROMISC) { - - /* promiscuous: receive all */ - trace_i82596_receive_analysis( - ">>> packet received in promiscuous mode"); + /* Process Receive Unit (RU) commands */ + switch (ruc) { + case SCB_RUC_NOP: + break; =20 - } else { + case SCB_RUC_START: { + uint32_t rfd_log =3D get_uint32(s->scb + 8); + hwaddr rfd =3D i82596_translate_address(s, rfd_log, false); =20 - if (!memcmp(buf, broadcast_macaddr, 6)) { - /* broadcast address */ - if (I596_BC_DISABLE) { - trace_i82596_receive_analysis(">>> broadcast packet reject= ed"); + if (rfd =3D=3D 0 || rfd =3D=3D I596_NULL) { + s->rx_status =3D RX_NO_RESOURCES; + s->scb_status |=3D SCB_STATUS_RNR; + break; + } =20 - return len; + /* Find first usable RFD with valid RBD */ + struct i82596_rx_descriptor test_rfd; + hwaddr test_rfd_addr =3D rfd; + uint32_t test_rfd_log =3D rfd_log; + hwaddr first_usable_rfd =3D 0; + uint32_t first_usable_rfd_log =3D 0; + bool found =3D false; + + for (int i =3D 0; i < 10 && test_rfd_addr !=3D 0 && + test_rfd_addr !=3D I596_NULL; i++) { + i82596_rx_rfd_read(s, test_rfd_addr, &test_rfd); + + if (test_rfd.command & CMD_FLEX) { + hwaddr rbd =3D i82596_translate_address(s, test_rfd.rbd_ad= dr, + false); + if (rbd !=3D I596_NULL && rbd !=3D 0) { + first_usable_rfd =3D test_rfd_addr; + first_usable_rfd_log =3D test_rfd_log; + found =3D true; + break; + } } =20 - trace_i82596_receive_analysis(">>> broadcast packet received"); - is_broadcast =3D 1; + test_rfd_log =3D test_rfd.link; + test_rfd_addr =3D i82596_translate_address(s, test_rfd.link, f= alse); + } =20 - } else if (buf[0] & 0x01) { - /* multicast */ - if (!I596_MC_ALL) { - trace_i82596_receive_analysis(">>> multicast packet reject= ed"); + if (found) { + s->current_rx_desc =3D first_usable_rfd; + s->last_good_rfa =3D first_usable_rfd_log; + i82596_update_rx_state(s, RX_READY); =20 - return len; + if (first_usable_rfd !=3D rfd) { + set_uint32(s->scb + 8, first_usable_rfd_log); } =20 - int mcast_idx =3D (net_crc32(buf, ETH_ALEN) & BITS(7, 2)) >> 2; - assert(mcast_idx < 8 * sizeof(s->mult)); - - if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) { - trace_i82596_receive_analysis(">>> multicast address misma= tch"); - - return len; + if (s->queue_count > 0) { + trace_i82596_flush_queue(s->queue_count); + i82596_flush_packet_queue(s); } =20 - trace_i82596_receive_analysis(">>> multicast packet received"); - is_broadcast =3D 1; - - } else if (!memcmp(s->conf.macaddr.a, buf, 6)) { - - /* match */ - trace_i82596_receive_analysis( - ">>> physical address matching packet received"); - + qemu_flush_queued_packets(qemu_get_queue(s->nic)); } else { - - trace_i82596_receive_analysis(">>> unknown packet"); - - return len; + s->rx_status =3D RX_NO_RESOURCES; + s->scb_status |=3D SCB_STATUS_RNR; } + break; } =20 - /* Calculate the ethernet checksum (4 bytes) */ - len +=3D 4; - crc =3D cpu_to_be32(crc32(~0, buf, sz)); - crc_ptr =3D (uint8_t *) &crc; - - rfd_p =3D get_uint32(s->scb + 8); /* get Receive Frame Descriptor */ - assert(rfd_p && rfd_p !=3D I596_NULL); - - /* get first Receive Buffer Descriptor Address */ - rbd =3D get_uint32(rfd_p + 8); - assert(rbd && rbd !=3D I596_NULL); - - /* PRINT_PKTHDR("Receive", buf); */ - - while (len) { - uint16_t command, status; - uint32_t next_rfd; - - command =3D get_uint16(rfd_p + 2); - assert(command & CMD_FLEX); /* assert Flex Mode */ - /* get first Receive Buffer Descriptor Address */ - rbd =3D get_uint32(rfd_p + 8); - assert(get_uint16(rfd_p + 14) =3D=3D 0); - - /* printf("Receive: rfd is %08x\n", rfd_p); */ - - while (len) { - uint16_t buffer_size, num; - uint32_t rba; - size_t bufcount, crccount; - - /* printf("Receive: rbd is %08x\n", rbd); */ - buffer_size =3D get_uint16(rbd + 12); - /* printf("buffer_size is 0x%x\n", buffer_size); */ - assert(buffer_size !=3D 0); - - num =3D buffer_size & SIZE_MASK; - if (num > len) { - num =3D len; - } - rba =3D get_uint32(rbd + 8); - /* printf("rba is 0x%x\n", rba); */ - /* - * Calculate how many bytes we want from buf[] and how many - * from the CRC. - */ - if ((len - num) >=3D 4) { - /* The whole guest buffer, we haven't hit the CRC yet */ - bufcount =3D num; - } else { - /* All that's left of buf[] */ - bufcount =3D len - 4; - } - crccount =3D num - bufcount; - - if (bufcount > 0) { - /* Still some of the actual data buffer to transfer */ - assert(bufsz >=3D bufcount); - bufsz -=3D bufcount; - address_space_write(&address_space_memory, rba, - MEMTXATTRS_UNSPECIFIED, buf, bufcount); - rba +=3D bufcount; - buf +=3D bufcount; - len -=3D bufcount; - } - - /* Write as much of the CRC as fits */ - if (crccount > 0) { - address_space_write(&address_space_memory, rba, - MEMTXATTRS_UNSPECIFIED, crc_ptr, crcco= unt); - rba +=3D crccount; - crc_ptr +=3D crccount; - len -=3D crccount; + case SCB_RUC_RESUME: + if (s->rx_status =3D=3D RX_SUSPENDED) { + i82596_update_rx_state(s, RX_READY); + if (s->queue_count > 0) { + trace_i82596_flush_queue(s->queue_count); + i82596_flush_packet_queue(s); } + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + break; =20 - num |=3D 0x4000; /* set F BIT */ - if (len =3D=3D 0) { - num |=3D I596_EOF; /* set EOF BIT */ - } - set_uint16(rbd + 0, num); /* write actual count with flags */ + case SCB_RUC_SUSPEND: + s->rx_status =3D RX_SUSPENDED; + s->scb_status |=3D SCB_STATUS_RNR; + break; =20 - /* get next rbd */ - rbd =3D get_uint32(rbd + 4); - /* printf("Next Receive: rbd is %08x\n", rbd); */ + case SCB_RUC_ABORT: + s->rx_status =3D RX_IDLE; + s->scb_status |=3D SCB_STATUS_RNR; + break; + } =20 - if (buffer_size & I596_EOF) /* last entry */ - break; + if (command & 0x80) { + i82596_s_reset(s); + return; + } + if (s->cu_status =3D=3D CU_ACTIVE) { + if (s->cmd_p =3D=3D I596_NULL) { + s->cmd_p =3D get_uint32(s->scb + 4); } + update_scb_status(s); + command_loop(s); + } else { + update_scb_status(s); + } +} + +static void signal_ca(I82596State *s) +{ + if (s->scp) { + /* CA after reset -> initialize with new SCP */ + s->sysbus =3D get_byte(s->scp + 3); + s->mode =3D (s->sysbus >> 1) & 0x03; /* Extract mode bits (m0, m1= ) */ + s->iscp =3D get_uint32(s->scp + 8); + + s->scb =3D get_uint32(s->iscp + 4); + + s->scb_base =3D (s->mode =3D=3D I82596_MODE_LINEAR) ? 0 : + get_uint32(s->iscp + 8); + s->scb =3D i82596_translate_address(s, s->scb, false); + DBG(printf("CA: Initialization - SCB=3D0x%08x, mode=3D%d, base=3D0= x%08x\n", + s->scb, s->mode, s->scb_base)); + + /* + * Complete initialization sequence: + * - Clear BUSY flag in ISCP + * - Set CX and CNA in SCB status + * - Clear SCB command word + * - Signal interrupt + */ + set_byte(s->iscp + 1, 0); + s->scb_status |=3D SCB_STATUS_CX | SCB_STATUS_CNA; + update_scb_status(s); + set_uint16(s->scb + 2, 0); + s->scp =3D 0; + qemu_set_irq(s->irq, 1); + return; + } =20 - /* Housekeeping, see pg. 18 */ - next_rfd =3D get_uint32(rfd_p + 4); - set_uint32(next_rfd + 8, rbd); + if (s->ca_active) { + s->ca++; + return; + } + s->ca_active =3D 1; + s->ca++; =20 - status =3D STAT_C | STAT_OK | is_broadcast; - set_uint16(rfd_p, status); + while (s->ca > 0) { + s->ca--; + examine_scb(s); + } =20 - if (command & CMD_SUSP) { /* suspend after command? */ - s->rx_status =3D RX_SUSPENDED; - s->scb_status |=3D SCB_STATUS_RNR; /* RU left active state */ - break; - } - if (command & CMD_EOL) /* was it last Frame Descriptor? */ - break; + s->ca_active =3D 0; =20 - assert(len =3D=3D 0); + if (s->send_irq) { + s->send_irq =3D 0; + qemu_set_irq(s->irq, 1); } +} =20 - assert(len =3D=3D 0); +static void i82596_self_test(I82596State *s, uint32_t val) +{ + /* + * The documentation for the self test is a bit unclear, + * we are currently doing this and it seems to work. + */ + set_uint32(val, 0xFFC00000); + set_uint32(val + 4, 0); =20 - s->scb_status |=3D SCB_STATUS_FR; /* set "RU finished receiving frame"= bit. */ - update_scb_status(s); + s->scb_status &=3D ~SCB_STATUS_CNA; + s->scb_status |=3D SCB_STATUS_CNA; =20 - /* send IRQ that we received data */ qemu_set_irq(s->irq, 1); - /* s->send_irq =3D 1; */ + update_scb_status(s); +} =20 - if (0) { - DBG(printf("Checking:\n")); - rfd_p =3D get_uint32(s->scb + 8); /* get Receive Frame Descriptor = */ - DBG(printf("Next Receive: rfd is %08x\n", rfd_p)); - rfd_p =3D get_uint32(rfd_p + 4); /* get Next Receive Frame Descrip= tor */ - DBG(printf("Next Receive: rfd is %08x\n", rfd_p)); - /* get first Receive Buffer Descriptor Address */ - rbd =3D get_uint32(rfd_p + 8); - DBG(printf("Next Receive: rbd is %08x\n", rbd)); - } +/* + * LASI specific interfaces + */ +static uint32_t bit_align_16(uint32_t val) +{ + return val & ~0x0f; +} =20 - return sz; +uint32_t i82596_ioport_readw(void *opaque, uint32_t addr) +{ + return -1; } =20 -ssize_t i82596_receive_iov(NetClientState *nc, const struct iovec *iov, - int iovcnt) +void i82596_ioport_writew(void *opaque, uint32_t addr, uint32_t val) { - size_t sz =3D 0; - uint8_t *buf; - int i; - for (i =3D 0; i < iovcnt; i++) { - sz +=3D iov[i].iov_len; - } - if (sz =3D=3D 0) { - return -1; - } - buf =3D g_malloc(sz); - if (!buf) { - return -1; - } - size_t offset =3D 0; - for (i =3D 0; i < iovcnt; i++) { - if (iov[i].iov_base =3D=3D NULL) { - g_free(buf); - return -1; - } - memcpy(buf + offset, iov[i].iov_base, iov[i].iov_len); - offset +=3D iov[i].iov_len; + I82596State *s =3D opaque; + DBG(printf("i82596_ioport_writew addr=3D0x%08x val=3D0x%04x\n", addr, = val)); + switch (addr) { + case PORT_RESET: + i82596_s_reset(s); + break; + case PORT_SELFTEST: + val =3D bit_align_16(val); + i82596_self_test(s, val); + break; + case PORT_ALTSCP: + s->scp =3D bit_align_16(val); + break; + case PORT_ALTDUMP: + trace_i82596_dump(val); + i82596_port_dump(s, bit_align_16(val)); + break; + case PORT_CA: + signal_ca(s); + break; } - DBG(PRINT_PKTHDR("Receive IOV:", buf)); - i82596_receive(nc, buf, sz); - g_free(buf); - return sz; } =20 void i82596_poll(NetClientState *nc, bool enable) @@ -1166,7 +2124,7 @@ void i82596_poll(NetClientState *nc, bool enable) =20 if (s->rx_status =3D=3D RX_NO_RESOURCES) { if (s->cmd_p !=3D I596_NULL) { - s->rx_status =3D RX_READY; + i82596_update_rx_state(s, RX_READY); update_scb_status(s); } } @@ -1232,6 +2190,57 @@ const VMStateDescription vmstate_i82596 =3D { } }; =20 +static int i82596_flush_packet_queue(I82596State *s) +{ + if (s->flushing_queue) { + return 0; + } + + s->flushing_queue =3D true; + int processed =3D 0; + + while (s->queue_count > 0) { + int tail =3D s->queue_tail; + size_t len =3D s->packet_queue_len[tail]; + + ssize_t ret =3D i82596_receive_packet(s, s->packet_queue[tail], le= n, + true); + + if (ret < 0) { + break; + } + + s->queue_tail =3D (s->queue_tail + 1) % PACKET_QUEUE_SIZE; + s->queue_count--; + processed++; + } + + s->flushing_queue =3D false; + trace_i82596_flush_queue(processed); + + return processed; +} + +static void i82596_flush_queue_timer(void *opaque) +{ + I82596State *s =3D opaque; + + if (s->queue_count =3D=3D 0) { + return; + } + + int processed =3D i82596_flush_packet_queue(s); + + if (processed > 0 && s->rx_status =3D=3D RX_READY) { + qemu_flush_queued_packets(qemu_get_queue(s->nic)); + } + + if (s->queue_count > 0 && s->rx_status !=3D RX_READY) { + timer_mod(s->flush_queue_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 50000); + } +} + void i82596_common_init(DeviceState *dev, I82596State *s, NetClientInfo *i= nfo) { if (s->conf.macaddr.a[0] =3D=3D 0) { diff --git a/hw/net/trace-events b/hw/net/trace-events index e82d7490c3..495528e814 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -421,10 +421,14 @@ lasi_82596_mem_writew(uint64_t addr, uint32_t val) "a= ddr=3D0x%"PRIx64" val=3D0x%04x" i82596_s_reset(void *s) "%p Reset chip" i82596_transmit(uint32_t size, uint32_t addr) "size %u from addr 0x%04x" i82596_receive_analysis(const char *s) "%s" -i82596_receive_packet(size_t sz) "len=3D%zu" +i82596_receive_queue_full(void) "Packet queue full, dropping packet" +i82596_receive_suspended(void) "RX unit suspended, queueing packet" +i82596_rx_state_change(uint8_t old_state, uint8_t new_state) "RX state cha= nged from %d to %d" i82596_new_mac(const char *id_with_mac) "New MAC for: %s" i82596_set_multicast(uint16_t count) "Added %d multicast entries" -i82596_channel_attention(void *s) "%p: Received CHANNEL ATTENTION" +i82596_scb_command(uint8_t cuc, uint8_t ruc) "SCB command CUC=3D0x%02x RUC= =3D0x%02x" +i82596_dump(uint32_t addr) "Dump command to addr 0x%08x" +i82596_flush_queue(int count) "Flushing %d packets from queue" =20 # imx_fec.c imx_phy_read_num(int phy, int configured) "read request from unconfigured = phy %d (configured %d)" --=20 2.49.0