From nobody Fri Nov 14 18:24:39 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=1762169377; cv=none; d=zohomail.com; s=zohoarc; b=byo3vzcqYIb2NRrCtm165fPR/YrIibJlAP51KfN68LucE8U4fFz46YpJiO2HOZWArdnpo2feYKr9GTs779nGkRW/JrOLEi/BXBBKo9LNQ5jO5ca1E76D/7TUVP3amdKtFmnNc7VA1eNyKy/GQyN38R5QsMMcROtXZ7MeVOngvkk= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1762169377; 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=X8WX2sg5nTax3ZLKZ6Cx5NJgpnH1Y/0Ecrl/738nU0w=; b=fssroaqVI/JqGcNoZfR3Vmv0SfIkzUCI2T6fGrin585pMHuLnoZLkEmer5+oNvZ6pX6cJfkJe4So3qA+GJ/cGe1GPgjjO4esnGMRqC53O/pVo7Ko4fwGqM8EApp4wj6jYl8xMr91Z6u/NtOPguFrw70j8ooYMvIIm7N8GEMWlio= 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 1762169377544659.0763345013812; Mon, 3 Nov 2025 03:29:37 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1vFsj1-0001Q7-08; Mon, 03 Nov 2025 06:27:51 -0500 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 1vFsiy-0001Px-Cy for qemu-devel@nongnu.org; Mon, 03 Nov 2025 06:27:48 -0500 Received: from mail-pj1-x1043.google.com ([2607:f8b0:4864:20::1043]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1vFsio-0001YN-Ff for qemu-devel@nongnu.org; Mon, 03 Nov 2025 06:27:48 -0500 Received: by mail-pj1-x1043.google.com with SMTP id 98e67ed59e1d1-340bcc92c7dso1631600a91.0 for ; Mon, 03 Nov 2025 03:27:38 -0800 (PST) Received: from fedora.. ([103.2.232.250]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-341599f14bbsm741661a91.13.2025.11.03.03.27.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 03 Nov 2025 03:27:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1762169256; x=1762774056; 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=X8WX2sg5nTax3ZLKZ6Cx5NJgpnH1Y/0Ecrl/738nU0w=; b=QrJW2pK+bLZ7m+bwKY9G3vMrU3HTU6J/SBO41kJrtap9FegxwFTqjfIWx5eXN3p9Hf nrP0WdqEzzJZzbcffyShPE/NmDpZ0EMoUEUf+xGh9dY9uq8m/2qecRn3VGiC7DANXtsq 5wWuaBuCYrhw0I3fQbf0PWh2f5na58EmlG8uzQZ6yUO9NcmCNMXcspdBi+2OANmI1c1M Y6hnWrZd3pOh8PPaZI9xl/Up5PMEK6fTONHxYQuFpMLvzGjzRAu5D2GmDxb0gtZdb9Hp WOsApPYbTYukN/rRC89o5BSBIl69PMND3wBGsiIN/nTK+g9uSEdZ2ux9PHUn1mgIkbW8 JPpQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1762169256; x=1762774056; 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=X8WX2sg5nTax3ZLKZ6Cx5NJgpnH1Y/0Ecrl/738nU0w=; b=wBCXjahjcDjgX2ctRSis25R5GqWKBfF6KYwuByjWA1iLcY7hH5bdrXwNJ5zuSn4C9p ZJTPzHImgRsF8fynI24+1/aA8WLRDno30azD4tkxy8NePRqvtFZ0Gzmwv2iL6uHJryTr oCT9GsV19qI9M3VAELAk7Os2Psv1aRag2XAHK8kpMjT/Rw5tkK1tZFY8bhztMbGzwyWR zx2JkrMcOOVnfxHdLquypdj+eN8RZz47I7zhb5dHL8I8qLemfW8uPg/QY7pk9J1JaX+V YXyFUnbfDfAMzEtW0iNSPObftS3coX2Anri9ZYwcSqA8ZCrrB+IDpx9yALlzk8XK7wwr Y2Wg== X-Forwarded-Encrypted: i=1; AJvYcCVbA2qiUQZYN4lbRawnZuaPACg+YxhhCzYRUG/9ogMeNh/KvBP6nlzQ8EbbxC2yPPU/Ixh30d0dCjG+@nongnu.org X-Gm-Message-State: AOJu0YzdSQEHiop/Tt5qa/qDtj+y5mCEcUbXfhpV9PwKzkVaM2NhVKIc ekwSMZWQEeaWwCu7rvMXwIh/MmrxZO9Nx5usyFGZ7m21PImD7iOWnb7Ql/ERNqb9Yb8= X-Gm-Gg: ASbGnctx6nqYp+234rVZz6+vuKNKBxTWH1Tqbi7T8imv9E4WzQ/L367Rb74MfkQQoZ5 1d5qcJnjmet9nWmjzrmL28nQGlGVQGRCBWL824rubSJgOFe/nnunJ9twr/JV3q806PVIRXC9sP2 5B1xydynHyvJ7AX3e17lUphtytk4mC+SQOeVRcdbxeQiPghKspl3MaBTwPWm7J2r+0OTqhy4zYa PYCJ2TtzgtpoBDRdW0G+C1QSOp1Up03SFGtGu5uAA4vycCiLUWdGmGWvguy+6IVNzQgKKx9ErEW pDkJdyuRII1S+nXHlnMyEIGhOJQ9FgEcP096Gvxr3xOdaC1cAxcY7381ZGEg8RsZM2nbQ7leiIi rPeY2NmQFTA9EfaK9jHWT6bE5iKXG+1S8K5boP7C3Wcv+iqhiWBcykTDY8oh9l7nw2rrwXLtt9B TVqhPC7MwrLKefzuzT1Lj+cUpx1Tyn4K5J+SIeM6JcB2KAKOt/5A== X-Google-Smtp-Source: AGHT+IHZPCYxVS8/zhqrfPjV2s8miBB2nlN3Lvu1HRo06Px+uv+calUgkYSLrnAGDcDuEbB3iy9Sxg== X-Received: by 2002:a17:90b:224d:b0:340:e8ce:7557 with SMTP id 98e67ed59e1d1-340e8ce765cmr6991591a91.13.1762169255814; Mon, 03 Nov 2025 03:27:35 -0800 (PST) From: Soumyajyotii Ssarkar To: mark.cave-ayland@ilande.co.uk, sarkarsoumyajyoti23@gmail.com, linux@roeck-us.net, richard.henderson@linaro.org, qemu-devel@nongnu.org, deller@gmx.de Cc: Jason Wang , Soumyajyotii Ssarkar Subject: [PATCH v3 2/3] i82596: Added core infrastructure and helper functions Date: Mon, 3 Nov 2025 16:57:22 +0530 Message-ID: <20251103112723.12256-3-soumyajyotisarkar23@gmail.com> X-Mailer: git-send-email 2.49.0 In-Reply-To: <20251103112723.12256-1-soumyajyotisarkar23@gmail.com> References: <20251103112723.12256-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::1043; envelope-from=soumyajyotisarkar23@gmail.com; helo=mail-pj1-x1043.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_PASS=-0.001, T_SPF_HELO_TEMPERROR=0.01 autolearn=ham autolearn_force=no X-Spam_action: no action 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: 1762169379666158500 Content-Type: text/plain; charset="utf-8" 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 production ready. In this patch I have added 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. Signed-off-by: Soumyajyotii Ssarkar Tested-by: Helge Deller Tested-by: Guenter Roeck --- 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 b87cea411a..2f212a7ed3 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