From nobody Fri Nov 14 21:03:08 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