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=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