* [Qemu-devel] [PATCH][RFC] E100 emulator
@ 2007-11-22 9:41 Zhang, Xing Z
2007-11-22 21:16 ` Stefan Weil
0 siblings, 1 reply; 2+ messages in thread
From: Zhang, Xing Z @ 2007-11-22 9:41 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 525 bytes --]
Hi All:
I know there is an eepro100 implementation in QEMU upstream. But it only supports x86 linux guest.
Base on the old version, I re-wrote a new E100 emulator. It should support linux/windows guest on x86, x86_64 and IA64 platform.
I tested it in linux/windows guest of IA64/XEN and got 30% faster than rtl8139. It also works fine in windows guest of QEMU on x86_64 box(I don't test it on x86 box, but it should be ok).
Good good study,day day up ! ^_^
-Wing(zhang xin)
OTC,Intel Corporation
[-- Attachment #2: e100.c --]
[-- Type: application/octet-stream, Size: 79555 bytes --]
/*
* QEMU E100(i82557) ethernet card emulation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Copyright (c) 2006-2007 Stefan Weil
* Copyright (c) 2006-2007 Zhang Xin(xing.z.zhang@intel.com)
*
* Support OS:
* x86 linux and windows
* PAE linux and windows
* x86_64 linux and windows
* IA64 linux and windows
*
* Untested:
* Big-endian machine
*
* References:
*
* Intel 8255x 10/100 Mbps Ethernet Controller Family
* Open Source Software Developer Manual
*/
#include <assert.h>
#include "vl.h"
enum
{
E100_PCI_VENDOR_ID = 0x00, /* 16 bits */
E100_PCI_DEVICE_ID = 0x02, /* 16 bits */
E100_PCI_COMMAND = 0x04, /* 16 bits */
E100_PCI_STATUS = 0x06, /* 16 bits */
E100_PCI_REVISION_ID = 0x08, /* 8 bits */
E100_PCI_CLASS_CODE = 0x0b, /* 8 bits */
E100_PCI_SUBCLASS_CODE = 0x0a, /* 8 bits */
E100_PCI_HEADER_TYPE = 0x0e, /* 8 bits */
E100_PCI_BASE_ADDRESS_0 = 0x10, /* 32 bits */
E100_PCI_BASE_ADDRESS_1 = 0x14, /* 32 bits */
E100_PCI_BASE_ADDRESS_2 = 0x18, /* 32 bits */
E100_PCI_BASE_ADDRESS_3 = 0x1c, /* 32 bits */
E100_PCI_BASE_ADDRESS_4 = 0x20, /* 32 bits */
E100_PCI_BASE_ADDRESS_5 = 0x24 /* 32 bits */
}PCI_CONFIGURE_SPACE;
#define PCI_CONFIG_8(offset, value) \
(*(uint8_t *)&pci_conf[offset] = (value))
#define PCI_CONFIG_16(offset, value) \
(*(uint16_t *)&pci_conf[offset] = cpu_to_le16(value))
#define PCI_CONFIG_32(offset, value) \
(*(uint32_t *)&pci_conf[offset] = cpu_to_le32(value))
// Alias for Control/Status register read/write
#define CSR_STATUS scb_status
#define CSR_CMD scb_cmd
#define CSR_POINTER scb_pointer
#define CSR_PORT port
#define CSR_EEPROM eeprom_ctrl
#define CSR_MDI mdi_ctrl
#define CSR_PM pm_reg
#define CSR(class, field) \
(s->pci_mem.csr.class.u.field)
#define CSR_VAL(class) \
(s->pci_mem.csr.class.val)
#define CSR_READ(x, type) \
({ \
type t; \
memcpy(&t, &s->pci_mem.mem[x], sizeof(type)); \
t; \
})
#define CSR_WRITE(x, val, type) \
({ \
type t = val; \
memcpy(&s->pci_mem.mem[x], &t, sizeof(type)); \
})
#define SET_CU_STATE(val) \
(CSR(CSR_STATUS, cus) = val)
#define GET_CU_STATE \
(CSR(CSR_STATUS, cus))
#define SET_RU_STATE(val) \
(CSR(CSR_STATUS, rus) = val)
#define GET_RU_STATE \
(CSR(CSR_STATUS, rus))
#define KiB 1024
#define EEPROM_SIZE 64
#define BIT(n) (1U << (n))
#define USE_BUFFER_TCP
/* debug E100 card */
//#define DEBUG_E100
#ifdef DEBUG_E100
#define logout(fmt, args...) fprintf(stderr, "EE100\t%-28s" fmt, __func__, ##args)
#else
#define logout(fmt, args...) ((void)0)
#endif
#define MAX_ETH_FRAME_SIZE 1514
/* This driver supports several different devices which are declared here. */
#define i82551 0x82551
#define i82557B 0x82557b
#define i82557C 0x82557c
#define i82558B 0x82558b
#define i82559C 0x82559c
#define i82559ER 0x82559e
#define i82562 0x82562
#define PCI_MEM_SIZE (4 * KiB)
#define PCI_IO_SIZE (64)
#define PCI_FLASH_SIZE (128 * KiB)
enum
{
OP_READ,
OP_WRITE,
} OPERTAION_DIRECTION;
/* The SCB accepts the following controls for the Tx and Rx units: */
enum
{
CU_NOP = 0x0000, /* No operation */
CU_START = 0x0010, /* CU start */
CU_RESUME = 0x0020, /* CU resume */
CU_STATSADDR = 0x0040, /* Load dump counters address */
CU_SHOWSTATS = 0x0050, /* Dump statistical counters */
CU_CMD_BASE = 0x0060, /* Load CU base address */
CU_DUMPSTATS = 0x0070, /* Dump and reset statistical counters */
CU_S_RESUME = 0x00a0 /* CU static resume */
}CONTROL_UNIT_COMMAND;
enum
{
RU_NOP = 0x0000,
RU_START = 0x0001,
RU_RESUME = 0x0002,
RU_DMA_REDIRECT = 0x0003,
RU_ABORT = 0x0004,
RU_LOAD_HDS = 0x0005,
RU_ADDR_LOAD = 0x0006,
RU_RESUMENR = 0x0007,
}RECEIVE_UNIT_COMMAND;
/* SCB status word descriptions */
enum
{
CU_IDLE = 0,
CU_SUSPENDED = 1,
CU_LPQ_ACTIVE = 2,
CU_HQP_ACTIVE = 3
} CONTROL_UINT_STATE;
enum
{
RU_IDLE = 0,
RU_SUSPENDED = 1,
RU_NO_RESOURCES =2,
RU_READY = 4
} RECEIVE_UNIT_STATE;
enum
{
PORT_SOFTWARE_RESET = 0,
PORT_SELF_TEST = 1,
PORT_SELECTIVE_RESET = 2,
PORT_DUMP = 3,
PORT_DUMP_WAKE_UP = 7,
}SCB_PORT_SELECTION_FUNCTION;
enum
{
CBL_NOP = 0,
CBL_IASETUP = 1,
CBL_CONFIGURE = 2,
CBL_MULTCAST_ADDR_SETUP = 3,
CBL_TRANSMIT = 4,
CBL_LOAD_MICROCODE = 5,
CBL_DUMP = 6,
CBL_DIAGNOSE = 7,
}CBL_COMMAND;
enum
{
SCB_STATUS = 0, /* SCB base + 0x00h, RU states + CU states + STAT/ACK */
SCB_ACK = 1, /* SCB ack/stat */
SCB_CMD = 2, /* RU command + CU command + S bit + M bit */
SCB_INTERRUPT_MASK = 3, /* Interrupts mask bits */
SCB_POINTER = 4, /* SCB general pointer, depending on command type */
SCB_PORT = 8, /* SCB port register */
SCB_EEPROM = 0xe, /* SCB eeprom control register */
SCB_MDI =0x10, /* SCB MDI control register */
} CSR_OFFSETS;
enum
{
EEPROM_SK = 0x01,
EEPROM_CS = 0x02,
EEPROM_DI = 0x04,
EEPROM_DO = 0x08,
} EEPROM_CONTROL_REGISTER;
enum
{
EEPROM_READ = 0x2,
EEPROM_WRITE = 0x1,
EEPROM_ERASE = 0x3,
} EEPROM_OPCODE;
enum
{
MDI_WRITE = 0x1,
MDI_READ = 0x2,
} MDI_OPCODE;
enum
{
INT_FCP = BIT(8),
INT_SWI = BIT(10),
INT_MDI = BIT(11),
INT_RNR = BIT(12),
INT_CNA = BIT(13),
INT_FR = BIT(14),
INT_CX_TNO = BIT(15),
} E100_INTERRUPT;
enum
{
CSR_MEMORY_BASE,
CSR_IO_BASE,
FLASH_MEMORY_BASE,
REGION_NUM
}E100_PCI_MEMORY_REGION;
typedef struct {
uint32_t tx_good_frames, // Good frames transmitted
tx_max_collisions, // Fatal frames -- had max collisions
tx_late_collisions, // Fatal frames -- had a late coll.
tx_underruns, // Transmit underruns (fatal or re-transmit)
tx_lost_crs, // Frames transmitted without CRS
tx_deferred, // Deferred transmits
tx_single_collisions, // Transmits that had 1 and only 1 coll.
tx_multiple_collisions,// Transmits that had multiple coll.
tx_total_collisions, // Transmits that had 1+ collisions.
rx_good_frames, // Good frames received
rx_crc_errors, // Aligned frames that had a CRC error
rx_alignment_errors, // Receives that had alignment errors
rx_resource_errors, // Good frame dropped due to lack of resources
rx_overrun_errors, // Overrun errors - bus was busy
rx_cdt_errors, // Received frames that encountered coll.
rx_short_frame_errors, // Received frames that were to short
complete_word; // A005h indicates dump cmd completion,
// A007h indicates dump and reset cmd completion.
// TODO: Add specific field for i82558, i82559
} __attribute__ ((packed)) e100_stats_t;
#define EEPROM_I82557_ADDRBIT 6
/* Below data is dumped from a real I82557 card */
static const uint16_t eeprom_i82557[] =
{
0x300, 0xe147, 0x2fa4, 0x203, 0x0, 0x201, 0x4701, 0x0, 0x7414, 0x6207,
0x4082, 0xb, 0x8086, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x128, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc374,
};
static const uint8_t e100_pci_configure[] =
{
0x86, 0x80, 0x29, 0x12, 0x17, 0x00, 0x90, 0x02, 0x08, 0x00, 0x00, 0x02, 0x10, 0x20, 0x00, 0x00,
0x00, 0x00, 0x10, 0x50, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0x80, 0x0b, 0x00,
0x00, 0x00, 0xf0, 0xff, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x01, 0x08, 0x38,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0xfe,
0x00, 0x40, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
typedef struct
{
#define OPCODE 0xb
#define ADDR 0xc
#define DATA 0xd
#define NOP 0xe
#define EEPROM_RESET_ALL 0xfe
#define EEPROM_SELECT_RESET 0xff
uint8_t start_bit;
uint8_t opcode;
uint8_t address;
uint16_t data; //This must be 16 bit represents a register in eeprom
uint32_t val;
uint32_t val_len;
uint8_t val_type; // What data type is in DI. opcode?address?data?
uint8_t cs;
uint8_t sk;
// This two fileds only be reset when device init
uint16_t addr_len;
uint16_t contents[256]; // 256 is enough to all device(i82557 ... i82559)
} eeprom_t;
// Control/Status register structure
typedef struct
{
/* SCB status word */
union
{
uint16_t val;
struct
{
uint8_t rs1:2; // Reserved
uint8_t rus:4; // RU status
uint8_t cus:2; // CU status
uint8_t stat_ack; // Stat/ACK
}u;
}scb_status;
/* SCB command word */
union
{
uint16_t val;
struct
{
uint8_t ru_cmd:3; // RU command
uint8_t rs1:1; // Reserved
uint8_t cu_cmd:4; // CU command
uint8_t m:1; // Interrup mask bit(1:mask all interrupt)
uint8_t si:1; // Use for software cause interrupt
uint8_t simb:6; // Specific interrupt mask bit
}u;
}scb_cmd;
/* SCB general pointer */
union
{
uint32_t val;
struct
{
uint32_t scb_ptr;
}u;
}scb_pointer;
/* Port interface */
union
{
uint32_t val;
struct
{
uint8_t opcode:4; // Op code for function selection
uint32_t ptr:28; // Result pointer
}u;
}port;
uint16_t rs1; // Reserved
/* EEPROM control register */
union
{
uint16_t val;
struct
{
uint8_t eesk:1; // Serial clock
uint8_t eecs:1; // Chip select
uint8_t eedi:1; // Serial data in
uint8_t eedo:1; // Serial data out
uint8_t rs1:4; // Reserved
uint8_t data;
}u;
}eeprom_ctrl;
/* MDI control register */
union
{
uint32_t val;
struct
{
uint16_t data; // Data
uint8_t regaddr:5; // PHY register address
uint8_t phyaddr:5; // PHY address
uint8_t opcode:2; // Opcode
uint8_t r:1; // Ready
uint8_t ie:1; // Interrup enable
uint8_t rs1:2; // Reserved
}u;
} mdi_ctrl;
/* Receive byte counter register */
uint32_t rx_byte_counter;
/* Early receive interrupt register */
uint8_t early_interrupt;
/* Flow control register */
union
{
uint16_t val;
}flow_ctrl;
/* Power management driver register */
union
{
uint8_t val;
struct
{
uint8_t pme_s:1; // PME status
uint8_t tco_r:1; // TCO request
uint8_t f_tco_i:1; // Force TCO indication
uint8_t tco_re:1; // TCO ready
uint8_t rs1:1; // Reserved
uint8_t isp:1; // Intersting packet
uint8_t mg:1; // Magic packet
uint8_t lsci:1; // Link status change indication
}u;
}pm_reg;
/* General control register */
uint8_t gen_ctrl;
/* General status register */
uint8_t gen_status;
/* These are reserved or we don't support register */
uint8_t others[30];
} __attribute__ ((packed)) csr_t;
typedef struct
{
uint8_t byte_count;
uint8_t rx_fifo_limit:4;
uint8_t tx_fifo_limit:4;
uint8_t adpt_inf_spacing;
uint8_t rs1;
uint8_t rx_dma_max_bytes;
uint8_t tx_dma_max_bytes:7;
uint8_t dmbc_en:1;
uint8_t late_scb:1,
rs2:1,
tno_intr:1,
ci_intr:1,
rs3:1,
rs4:1,
dis_overrun_rx:1,
save_bad_frame:1;
uint8_t dis_short_rx:1,
underrun_retry:2,
rs5:5;
uint8_t mii:1,
rs6:7;
uint8_t rs7;
uint8_t rs8:3,
nsai:1,
preamble_len:2,
loopback:2;
uint8_t linear_prio:3,
rs9:5;
uint8_t pri_mode:1,
rs10:3,
interframe_spacing:4;
uint16_t rs11;
uint8_t promiscuous:1,
broadcast_dis:1,
rs12:5,
crs_cdt:1;
uint16_t rs13;
uint8_t strip:1,
padding:1,
rx_crc:1,
rs14:5;
uint8_t rs15:6,
force_fdx:1,
fdx_en:1;
uint8_t rs16:6,
mul_ia:2;
uint8_t rs17:3,
mul_all:1,
rs18:4;
} __attribute__ ((packed)) i82557_cfg_t;
typedef struct {
VLANClientState *vc;
PCIDevice *pci_dev;
int mmio_index;
uint8_t scb_stat; /* SCB stat/ack byte */
uint32_t region_base_addr[REGION_NUM]; /* PCI region addresses */
uint8_t macaddr[6];
uint16_t mdimem[32];
eeprom_t eeprom;
uint32_t device; /* device variant */
uint8_t mult_list[8]; /* Multicast address list */
int is_multcast_enable;
/* (cu_base + cu_offset) address the next command block in the command block list. */
uint32_t cu_base; /* CU base address */
uint32_t cu_offset; /* CU address offset */
uint32_t cu_next; /* Point to next command when CU go to suspend */
/* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
uint32_t ru_base; /* RU base address */
uint32_t ru_offset; /* RU address offset */
uint32_t statsaddr; /* pointer to e100_stats_t */
e100_stats_t statistics; /* statistical counters */
/* Configuration bytes. */
i82557_cfg_t config;
/* FIFO buffer of card. The packet that need to be sent buffered in it */
uint8_t pkt_buf[MAX_ETH_FRAME_SIZE+4];
/* Data length in FIFO buffer */
int pkt_buf_len;
#ifdef USE_BUFFER_TCP
int buffer_tcp_enable;
int continuous_tcp_frame;
int unflush_tcp_num;
#endif
/* Data in mem is always in the byte order of the controller (le). */
union
{
csr_t csr;
uint8_t mem[PCI_MEM_SIZE];
}pci_mem;
} E100State;
/* CB structure, filled by device driver
* This is a common structure of CB. In some
* special case such as TRANSMIT command, the
* reserved field will be used.
*/
struct control_block
{
uint16_t rs1:13; /* reserved */
uint8_t ok:1; /* 1:command executed without error, otherwise 0 */
uint8_t rs2:1;
uint8_t c:1; /* execution status. set by device, clean by software */
uint8_t cmd:3; /* command */
uint16_t rs3:10; /* most time equal to 0 */
uint8_t i:1; /* whether trigger interrupt after execution. 1:yes; 0:no */
uint8_t s:1; /* suspend */
uint8_t el:1; /* end flag */
uint32_t link_addr;
} __attribute__ ((packed));
typedef struct
{
uint32_t tx_desc_addr; /* transmit buffer decsriptor array address. */
uint16_t tcb_bytes:14; /* transmit command block byte count (in lower 14 bits)*/
uint8_t rs1:1;
uint8_t eof:1;
uint8_t tx_threshold; /* transmit threshold */
uint8_t tbd_num; /* TBD number */
} __attribute__ ((packed)) tbd_t;
/* Receive frame descriptore structure */
typedef struct
{
uint16_t status:13; // Result of receive opration
uint8_t ok:1; // 1:receive without error, otherwise 0
uint8_t rs1:1;
uint8_t c:1; // 1:receive complete
uint8_t rs2:3;
uint8_t sf:1; // 0:simplified mode
uint8_t h:1; // 1:header RFD
uint16_t rs3:9;
uint8_t s:1; // 1:go to suspend
uint8_t el:1; // 1:last RFD
uint32_t link_addr; // Add on RU base point to next RFD
uint32_t rs4;
uint16_t count:14; // Number of bytes written into data area
uint8_t f:1; // Set by device when count field update
uint8_t eof:1; // Set by device when placing data into data area complete
uint16_t size:14; // Buffer size (even number)
uint8_t rs5:2;
} __attribute__ ((packed)) rfd_t;
enum
{
RX_COLLISION = BIT(0), // 1:Receive collision detected
RX_IA_MATCH = BIT(1), // 0:Receive frame match individual address
RX_NO_MATCH = BIT(2), // 1:Receive frame match no address
RX_ERR = BIT(4), // 1:Receive frame error
RX_TYPE = BIT(5), // 1:Receive frame is a type frame
RX_SHORT = BIT(7), // 1:Receive frame is too short
RX_DMA_ERR = BIT(8),
RX_LARGE = BIT(9), // 1:Receive frame is too large
RX_CRC_ERR = BIT(10),
} RFD_STATUS;
typedef struct PCIE100State {
PCIDevice dev;
E100State e100;
} PCIE100State;
/* Default values for MDI (PHY) registers */
static const uint16_t e100_mdi_default[] = {
/* MDI Registers 0 - 6, 7 */
0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
/* MDI Registers 8 - 15 */
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* MDI Registers 16 - 31 */
0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
};
static const uint8_t broadcast_macaddr[6] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/* Debugging codes */
#ifdef DEBUG_E100
static void e100_dump(char *comment, uint8_t *info, int len)
{
int i;
if ( !comment || !info )
return;
fprintf(stderr, "EE100\t%-24s%s", __func__, comment);
for ( i=0; i<len; i++ )
fprintf(stderr, "%x ", info[i]);
fprintf(stderr, "\n");
}
static const char *regname[] =
{
[0] = "SCB Status", [1] = "SCB Ack",
[2] = "SCB Cmd", [3] = "SCB Interrupt Mask",
[4] = "SCB Pointer", [8] = "SCB Port",
[0xc] = "SCB Flash", [0xe] = "SCB Eeprom",
[0x10] = "SCB Ctrl MDI", [0x14] = "SCB Early RX",
};
#define SCBNAME(x) \
( (x) < (sizeof(regname) / sizeof(regname[0])) ? regname[(x)] : "Unknown SCB Register" )
static const char *cb_cmd_name[] =
{
[CBL_NOP] = "NOP", [CBL_IASETUP] = "Individual address setup",
[CBL_CONFIGURE] = "Configure", [CBL_MULTCAST_ADDR_SETUP] = "Set Multcast address list",
[CBL_TRANSMIT] = "Transmit", [CBL_LOAD_MICROCODE] = "Load microcode",
[CBL_DUMP] = "Dump", [CBL_DIAGNOSE] = "Diagnose",
};
#define CB_CMD_NAME(x) \
( (x) < (sizeof(cb_cmd_name) / sizeof(cb_cmd_name[0])) ? cb_cmd_name[(x)] : "Unknown CB command" )
static const char *eeprom_opcode_name[] =
{
[0] = "Unknow", [EEPROM_WRITE] = "Write",
[EEPROM_READ] = "Read", [EEPROM_ERASE] = "Erase",
};
#define EEPROM_OPCODE_NAME(x) \
( (x) < (sizeof(eeprom_opcode_name) / sizeof(eeprom_opcode_name[0])) ? \
eeprom_opcode_name[(x)] : "Unknown" )
static struct eeprom_trace_data
{
uint8_t eedo[256];
uint8_t di[256];
int op;
int i;
uint32_t data;
}etd = {.op = NOP};
static void eeprom_trace(int eedo, int di, int dir, int next_op, int clr)
{
int i;
if ( clr )
{
char *opname = NULL;
switch ( etd.op )
{
case NOP:
break;
case OPCODE:
opname = "opcode";
break;
case ADDR:
opname = "address";
break;
case DATA:
opname = "data transfer";
break;
default:
opname = "Unknown";
}
if ( opname )
{
logout("EEPROM trace:\n");
fprintf(stderr, "\toperation: %s\n", opname);
fprintf(stderr, "\tDI track:");
for ( i=0; i<etd.i; i++ )
fprintf(stderr, "%x ", etd.di[i]);
fprintf(stderr, "\n\tDO track:");
for ( i=0; i<etd.i; i++ )
fprintf(stderr, "%x ", etd.eedo[i]);
fprintf(stderr, "\n\tData:%#x\n", etd.data);
}
memset(&etd, 0x0, sizeof(etd));
etd.op = next_op;
return;
}
etd.eedo[etd.i] = eedo;
etd.di[etd.i] = di;
etd.i ++;
if ( dir == EEPROM_READ && etd.op == DATA )
etd.data = (etd.data << 1) | eedo;
else
etd.data = (etd.data << 1) | di;
}
#define INT_NAME(x) \
({ \
char *name = NULL; \
switch (x) \
{ \
case INT_FCP: \
name = "FCP"; \
break; \
case INT_SWI: \
name = "SWI"; \
break; \
case INT_MDI: \
name = "MDI"; \
break; \
case INT_RNR: \
name = "RNR"; \
break; \
case INT_CNA: \
name = "CNA"; \
break; \
case INT_FR: \
name = "FR"; \
break; \
case INT_CX_TNO: \
name ="CX/TNO"; \
break; \
default: \
name ="Unknown"; \
} \
name; \
})
#else
static void e100_dump(char *comment, uint8_t *info, int len) {}
static void eeprom_trace(int eedo, int di, int dir, int next_op, int clr) {}
#endif
static void pci_reset(E100State * s)
{
uint8_t *pci_conf = s->pci_dev->config;
memcpy(pci_conf, &e100_pci_configure[0], sizeof(e100_pci_configure));
logout("%p\n", s);
/* I82557 */
PCI_CONFIG_8(E100_PCI_REVISION_ID, 0x01);
PCI_CONFIG_8(0x3c, 0x0);
}
static void e100_selective_reset(E100State * s)
{
memset(s->pci_mem.mem, 0x0, sizeof(s->pci_mem.mem));
// Set RU/CU to idle, maintain the register mentioned in spec,
SET_CU_STATE(CU_IDLE);
SET_RU_STATE(RU_IDLE);
logout("CU and RU go to idle\n");
s->ru_offset = 0;
s->cu_offset = 0;
s->cu_next = 0;
// For 82557, special interrupt bits are all 1
CSR(CSR_CMD, simb) = 0x3f;
// Set PHY to 1
CSR_VAL(CSR_MDI) |= BIT(21);
/* Initialize EEDO bit to 1. Due to driver would detect dummy 0 at
* EEDO bit, so initialize it to 1 is safety a way.
*/
CSR(CSR_EEPROM, eedo) = 1;
// no pending interrupts
s->scb_stat = 0;
return;
}
static void e100_software_reset(E100State *s)
{
memset(s->pci_mem.mem, 0x0, sizeof(s->pci_mem.mem));
// Clear multicast list
memset(s->mult_list, 0x0, sizeof(s->mult_list));
// Set MDI register to default value
memcpy(&s->mdimem[0], &e100_mdi_default[0], sizeof(s->mdimem));
s->is_multcast_enable = 1;
/* Clean FIFO buffer */
memset(s->pkt_buf, 0x0, sizeof(s->pkt_buf));
s->pkt_buf_len = 0;
memset(&s->statistics, 0x0, sizeof(s->statistics));
e100_selective_reset(s);
return;
}
static void e100_reset(void *opaque)
{
E100State *s = (E100State *) opaque;
logout("%p\n", s);
e100_software_reset(s);
}
static void e100_save(QEMUFile * f, void *opaque)
{
//TODO
return;
}
static int e100_load(QEMUFile * f, void *opaque, int version_id)
{
//TODO
return 0;
}
/* Interrupt functions */
static void e100_interrupt(E100State *s, uint16_t int_type)
{
//TODO: Add another i8255x card supported mask bit
if ( !CSR(CSR_CMD,m) )
{
//Set bit in stat/ack, so driver can no what interrupt happen
CSR_VAL(CSR_STATUS) |= int_type;
s->scb_stat = CSR(CSR_STATUS, stat_ack);
/* SCB maske and SCB Bit M do not disable interrupt. */
logout("Trigger an interrupt(type = %s(%#x), SCB Status = %#x)\n",
INT_NAME(int_type), int_type, CSR_VAL(CSR_STATUS));
pci_set_irq(s->pci_dev, 0, 1);
}
}
static void e100_interrupt_ack(E100State * s, uint8_t ack)
{
/* Ignore acknowledege if driver write 0 to ack or
* according interrupt bit is not set
*/
if ( !ack || !(s->scb_stat & ack) )
{
logout("Illegal interrupt ack(ack=%#x, SCB Stat/Ack=%#x), ignore it\n",
ack, s->scb_stat);
// Due to we do write operation before e100_execute(), so
// we must restore value of ack field here
CSR(CSR_STATUS, stat_ack) = s->scb_stat;
return;
}
s->scb_stat &= ~ack;
CSR(CSR_STATUS, stat_ack) = s->scb_stat;
logout("Interrupt ack(name=%s,val=%#x)\n", INT_NAME(({uint16_t bit = ack<<8;bit;})),ack);
if ( !s->scb_stat )
{
logout("All interrupts are acknowledeged, de-assert interrupt line\n");
pci_set_irq(s->pci_dev, 0, 0);
}
}
static void e100_self_test(uint32_t res_addr)
{
struct
{
uint32_t st_sign; /* Self Test Signature */
uint32_t st_result; /* Self Test Results */
} test_res;
test_res.st_sign = (uint32_t)-1;
test_res.st_result = 0; // Our self test always success
cpu_physical_memory_write(res_addr, (uint8_t *)&test_res, sizeof(test_res));
logout("Write self test result to %#x\n", res_addr);
}
static void scb_port_func(E100State *s, uint32_t val, int dir)
{
#define PORT_SELECTION_MASK 0xfU
uint32_t sel = val & PORT_SELECTION_MASK;
switch ( sel )
{
case PORT_SOFTWARE_RESET:
logout("do PORT_SOFTWARE_RESET!\n");
e100_software_reset(s);
break;
case PORT_SELF_TEST:
e100_self_test(val & ~PORT_SELECTION_MASK);
logout("do PORT_SELF_TEST!\n");
break;
case PORT_SELECTIVE_RESET:
logout("do PORT_SELECTIVE_RESET!\n");
e100_selective_reset(s);
break;
case PORT_DUMP:
logout("do PORT_SOFTWARE_RESET!\n");
break;
case PORT_DUMP_WAKE_UP:
logout("do PORT_SOFTWARE_RESET!\n");
break;
default:
logout("Unkonw SCB port command(selection function = %#x)\n", sel);
}
}
static void e100_write_mdi(E100State *s, uint32_t val)
{
uint32_t ie = (val & 0x20000000) >> 29;
uint32_t opcode = (val & 0x0c000000) >> 26;
uint32_t phyaddr = (val & 0x03e00000) >> 21;
uint32_t regaddr = (val & 0x001f0000) >> 16;
uint32_t data = val & 0x0000ffff;
logout("Write MDI:\n"
"\topcode:%#x\n"
"\tphy address:%#x\n"
"\treg address:%#x\n"
"\tie:%#x\n"
"\tdata:%#x\n",
opcode, phyaddr, regaddr, ie, data);
/* We use default value --- PHY1
* If driver operate on other PHYs, do nothing and
* deceive it that the operation is finished
*/
if ( phyaddr != 1 )
{
logout("Unsupport PHY address(phy = %#x)\n", phyaddr);
goto done;
}
// 1: MDI write
// 2: MDI read
if ( opcode != MDI_WRITE && opcode != MDI_READ )
{
logout("Invalid Opcode(opcode = %#x)\n", opcode);
return;
}
// Current only support MDI generic registers.
if ( regaddr > 6 )
{
logout("Invalid phy register index( phy register addr = %#x)\n", regaddr);
}
if ( opcode == MDI_WRITE )
{
// MDI write
switch ( regaddr )
{
case 0: // Control Register
if ( data & 0x8000 ) // Reset
{
/* Reset status and control registers to default. */
s->mdimem[0] = e100_mdi_default[0];
s->mdimem[1] = e100_mdi_default[1];
data = s->mdimem[regaddr];
}
else
{
/* Restart Auto Configuration = Normal Operation */
data &= ~0x0200;
}
break;
case 1: // Status Register
logout("Invalid write on readonly register(opcode = %#x)\n", opcode);
data = s->mdimem[regaddr];
break;
case 2:
case 3:
case 4:
case 5:
case 6:
break;
}
s->mdimem[regaddr] = data;
logout("MDI WRITE: reg = %#x, data = %#x\n", regaddr, data);
}
else if ( opcode == MDI_READ )
{
// MDI read
switch ( regaddr )
{
case 0: // Control Register
if ( data & 0x8000 ) // Reset
{
/* Reset status and control registers to default. */
s->mdimem[0] = e100_mdi_default[0];
s->mdimem[1] = e100_mdi_default[1];
}
break;
case 1: // Status Register
// Auto Negotiation complete, set sticky bit to 1
s->mdimem[regaddr] |= 0x0026;
break;
case 2: // PHY Identification Register (Word 1)
case 3: // PHY Identification Register (Word 2)
break;
case 5: // Auto-Negotiation Link Partner Ability Register
s->mdimem[regaddr] = 0x41fe;
break;
case 6: // Auto-Negotiation Expansion Register
s->mdimem[regaddr] = 0x0001;
break;
}
data = s->mdimem[regaddr];
logout("MDI READ: reg = %#x, data = %#x\n", regaddr, data);
}
/* Emulation takes no time to finish MDI transaction.
* Set MDI bit in SCB status register. */
done:
val |= BIT(28);
val = (val & 0xffff0000) + data;
CSR_WRITE(SCB_MDI, val, uint32_t);
if ( ie )
e100_interrupt(s, (uint16_t)INT_MDI);
}
static void scb_mdi_func(E100State *s, uint32_t val, int dir)
{
if ( dir == OP_READ )
// Do nothing, just tell driver we are ready
CSR_VAL(CSR_MDI) |= BIT(28);
else if ( dir == OP_WRITE )
e100_write_mdi(s, val);
else
logout("Invalid operation direction(dir=%x)\n", dir);
}
static void eeprom_reset(E100State *s, int type)
{
eeprom_t *e = &s->eeprom;
if ( type == EEPROM_RESET_ALL )
{
memset(e, 0x0, sizeof(eeprom_t));
e->val_type = NOP;
logout("EEPROM reset all\n");
return;
}
CSR(CSR_EEPROM, eedo) = 1;
e->start_bit = 0;
e->opcode = 0;
e->address = 0;
e->data = 0;
e->val = 0;
e->val_len = 0;
e->val_type = NOP;
e->cs = 0;
e->sk = 0;
logout("EEPROM select reset\n");
}
static void do_eeprom_op(E100State *s, eeprom_t *e, int cs, int sk, int di, int dir)
{
int assert_cs = (cs == 1 && e->cs == 0);
int de_assert_cs = (cs == 0 && e->cs == 1);
int de_assert_sk = (sk == 0 && e->sk == 1);
// Chip select is not be enabled
if ( cs == 0 && e->cs == 0 )
{
logout("Invalid EECS signal\n");
return;
}
// update state
e->cs = cs;
e->sk = sk;
// Do nothing
if ( assert_cs )
{
logout("EECS assert\n");
return;
}
// Complete one command
if ( de_assert_cs )
{
if ( e->val_type == DATA && e->opcode == EEPROM_WRITE )
{
e->data = e->val;
memcpy((void *)((unsigned long)e->contents + e->address),
&e->data, sizeof(e->data));
logout("EEPROM write complete(data=%#x)\n", e->data);
}
eeprom_trace(0,0,0,NOP,1);
eeprom_reset(s, EEPROM_SELECT_RESET);
logout("EECS de-asserted\n");
return;
}
// Chip is selected and serial clock is change, so the operation is vaild
if ( cs == 1 && de_assert_sk == 1)
{
// Set start bit
if ( e->start_bit == 0 && di == 1 )
{
e->start_bit = di;
e->val_len = 0;
e->val = 0;
e->val_type = OPCODE;
eeprom_trace(0,0,0,OPCODE,1);
logout("EEPROM start bit set\n");
return;
}
// Data in DI is vaild
else if ( e->start_bit == 1 )
{
// If current operation is eeprom read, ignore DI
if ( !(e->val_type == DATA && e->opcode == EEPROM_READ) )
{
e->val = (e->val << 1) | di;
e->val_len ++;
}
switch ( e->val_type )
{
// Get the opcode.
case OPCODE:
eeprom_trace(CSR(CSR_EEPROM, eedo), di, e->opcode, 0, 0);
if ( e->val_len == 2 )
{
e->opcode = e->val;
e->val = 0;
e->val_len = 0;
e->val_type = ADDR;
eeprom_trace(0,0,0,ADDR,1);
logout("EEPROM get opcode(opcode name=%s,opcode=%#x )\n",
EEPROM_OPCODE_NAME(e->opcode), e->opcode);
}
break;
// Get address
case ADDR:
eeprom_trace(CSR(CSR_EEPROM, eedo), di, e->opcode, 0, 0);
if ( e->val_len == e->addr_len )
{
e->address = e->val;
e->val = 0;
e->val_len = 0;
e->val_type = DATA;
// We prepare data eary for later read operation
if ( e->opcode == EEPROM_READ )
{
memcpy(&e->data, (void *)(e->contents + e->address),
sizeof(e->data));
logout("EEPROM prepare data to read(addr=%#x,data=%#x)\n",
e->address, e->data);
}
// Write dummy 0 to response to driver the address is written complete
CSR(CSR_EEPROM, eedo) = 0;
eeprom_trace(0,0,0,DATA,1);
logout("EEPROM get address(addr=%#x)\n", e->address);
}
break;
// Only do data out operation
case DATA:
if ( e->opcode == EEPROM_READ )
{
// Start from the most significant bit
//uint16_t t = ((e->data & (1<<(sizeof(e->data)*8 - e->val_len - 1))) != 0);
uint16_t t = !!(e->data & (0x8000U >> e->val_len));
CSR(CSR_EEPROM, eedo) = t;
logout("EEPROM read(reg address=%#x, reg val=%#x, do=%#x, len=%#x)\n",
e->address, e->data, t, e->val_len);
if ( e->val_len > sizeof(e->data)*8 )
{
/* Driver may do more write op to de-assert EESK,
* So we let EEPROM go to idle after a register be
* read complete
*/
e->val_type = NOP;
logout("Read complete\n");
break;
}
e->val_len ++;
}
eeprom_trace(CSR(CSR_EEPROM, eedo), di, e->opcode, 0, 0);
// Do eerpom write when CS de-assert
break;
default:
break;
}
}
}
return;
}
static void scb_eeprom_func(E100State *s, uint32_t val, int dir)
{
int eecs = ((val & EEPROM_CS) != 0);
int eesk = ((val & EEPROM_SK) != 0);
int eedi = ((val & EEPROM_DI) != 0);
logout("EEPROM: Old(cs=%#x, sk=%#x), New(cs=%#x, sk=%#x, di=%#x)\n",
s->eeprom.cs, s->eeprom.sk, eecs, eesk, eedi);
do_eeprom_op(s, &s->eeprom, eecs, eesk, eedi, dir);
return;
}
static void e100_ru_command(E100State *s, uint8_t val)
{
switch ( val )
{
case RU_NOP:
/* Will not be here */
break;
case RU_START:
/* RU start */
SET_RU_STATE(RU_READY);
logout("RU is set to ready\n");
s->ru_offset = CSR_VAL(CSR_POINTER);
logout("RFD offset is at %#x\n", s->ru_offset);
break;
case RU_RESUME:
/* RU Resume */
if ( GET_RU_STATE == RU_SUSPENDED )
SET_RU_STATE(RU_READY);
logout("RU resume to ready\n");
break;
case RU_ADDR_LOAD:
/* Load RU base */
s->ru_base = CSR_VAL(CSR_POINTER);
logout("Load RU base address at %#x\n", s->ru_base);
break;
case RU_DMA_REDIRECT:
logout("RU DMA redirect not implemented\n");
break;
case RU_ABORT:
e100_interrupt(s, INT_RNR);
SET_RU_STATE(RU_IDLE);
logout("RU abort, go to idle\n");
break;
case RU_LOAD_HDS:
logout("RU load header data size(HDS) not implemented\n");
default:
break;
}
}
// This function will change CU's state, so CU start and
// CU resume must set CU's state before it
static void e100_execute_cb_list(E100State *s, int is_resume)
{
struct control_block cb = {0};
uint32_t cb_addr;
if ( !is_resume )
s->cu_offset = CSR_VAL(CSR_POINTER);
/* If call from CU resume, cu_offset has been set */
while (1)
{
cb_addr = s->cu_base + s->cu_offset;
cpu_physical_memory_read(cb_addr, (uint8_t *)&cb, sizeof(cb));
switch ( cb.cmd )
{
case CBL_NOP:
/* Do nothing */
break;
case CBL_IASETUP:
cpu_physical_memory_read(cb_addr + 8, &s->macaddr[0], sizeof(s->macaddr));
e100_dump("Setup Individual Address:", &s->macaddr[0], 6);
break;
case CBL_CONFIGURE:
{
i82557_cfg_t *cfg = &s->config;
assert(sizeof(s->config) == 22);
cpu_physical_memory_read(cb_addr + 8, (uint8_t *)cfg, sizeof(s->config));
logout("Setup card configuration:"
"\tbyte count:%d\n"
"\tRx FIFO limit:%d\n"
"\tTx FIFO limit:%d\n"
"\tAdaptive interframe spacing:%d\n"
"\tRx DMA max:%d\n"
"\tTX DMA max:%d\n"
"\tDMBC enable:%d\n"
"\tLate SCB:%d\n"
"\tTNO:%d\n"
"\tCI:%d\n"
"\tDiscard overrun RX:%d\n"
"\tSave bad frame:%d\n"
"\tDiscard short RX:%d\n"
"\tunderrun retry:%d\n"
"\tMII:%d\n"
"\tNSAI:%d\n"
"\tPreamble len:%d\n"
"\tloopback:%d\n"
"\tliner pro:%d\n"
"\tPRI mode:%d\n"
"\tinterframe spacing:%d\n"
"\tpromiscuous:%d\n"
"\tbroadcast dis:%d\n"
"\tCRS CDT:%d\n"
"\tstripping:%d\n"
"\tpadding:%d\n"
"\tRX crc:%d\n"
"\tforce fdx:%d\n"
"\tfdx enable:%d\n"
"\tmultiple IA:%d\n"
"\tmulticast all:%d\n",
cfg->byte_count, cfg->rx_fifo_limit, cfg->tx_fifo_limit,
cfg->adpt_inf_spacing, cfg->rx_dma_max_bytes, cfg->tx_dma_max_bytes,
cfg->dmbc_en, cfg->late_scb, cfg->tno_intr, cfg->ci_intr,
cfg->dis_overrun_rx, cfg->save_bad_frame, cfg->dis_short_rx,
cfg->underrun_retry, cfg->mii, cfg->nsai, cfg->preamble_len,
cfg->loopback, cfg->linear_prio, cfg->pri_mode, cfg->interframe_spacing,
cfg->promiscuous, cfg->broadcast_dis, cfg->crs_cdt, cfg->strip,
cfg->padding, cfg->rx_crc, cfg->force_fdx, cfg->fdx_en,
cfg->mul_ia, cfg->mul_all);
}
break;
case CBL_MULTCAST_ADDR_SETUP:
{
uint16_t mult_list_count = 0;
uint16_t size = 0;
cpu_physical_memory_read(cb_addr + 8, (uint8_t *)&mult_list_count, 2);
mult_list_count = (mult_list_count << 2) >> 2;
if ( !mult_list_count )
{
logout("Multcast disabled(multicast count=0)\n");
s->is_multcast_enable = 0;
memset(s->mult_list, 0x0, sizeof(s->mult_list));
break;
}
size = mult_list_count > sizeof(s->mult_list) ?
sizeof(s->mult_list) : mult_list_count;
cpu_physical_memory_read(cb_addr + 12, &s->mult_list[0], size);
e100_dump("Setup Multicast list: ", &s->mult_list[0], size);
break;
}
case CBL_TRANSMIT:
{
struct
{
struct control_block cb;
tbd_t tbd;
} __attribute__ ((packed)) tx;
struct
{
uint32_t addr;
uint16_t size;
uint16_t is_el_set;
} tx_buf = {0};
uint32_t tbd_array;
uint16_t tcb_bytes;
uint8_t sf;
int len = s->pkt_buf_len;
assert( len < sizeof(s->pkt_buf));
cpu_physical_memory_read(cb_addr, (uint8_t *)&tx, sizeof(tx));
tbd_array = le32_to_cpu(tx.tbd.tx_desc_addr);
tcb_bytes = le16_to_cpu(tx.tbd.tcb_bytes);
// Indicate use what mode to transmit(simple or flexible)
sf = tx.cb.rs3 & 0x1;
logout("Get a TBD:\n"
"\tTBD array address:%#x\n"
"\tTCB byte count:%#x\n"
"\tEOF:%#x\n"
"\tTransmit Threshold:%#x\n"
"\tTBD number:%#x\n"
"\tUse %s mode to send frame\n",
tbd_array, tcb_bytes, tx.tbd.eof,
tx.tbd.tx_threshold, tx.tbd.tbd_num,
sf ? "Flexible" : "Simple");
if ( !sf || tbd_array == (uint32_t)-1 )
{
/* Simple mode */
/* For simple mode, TCB bytes should not be zero.
* But we still check here for safety
*/
if ( !tcb_bytes || tcb_bytes > sizeof(s->pkt_buf) )
break;
cpu_physical_memory_read(cb_addr+16, &s->pkt_buf[0], tcb_bytes);
len = tcb_bytes;
logout("simple mode(size=%d)\n", len);
}
else
{
/* Flexible mode */
/* For flexible mode, TBD num should not be zero.
* But we still check here for safety
*/
if ( !tx.tbd.tbd_num )
break;
// I82557 don't support extend TCB
if ( s->device == i82557C || s->device == i82557B )
{
/* Standard TCB mode */
int i;
for ( i=0; i<tx.tbd.tbd_num; i++ )
{
cpu_physical_memory_read(tbd_array, (uint8_t *)&tx_buf,
sizeof(tx_buf));
tx_buf.is_el_set &= 0x1;
tx_buf.size &= 0x7fff;
tbd_array += 8;
if ( tx_buf.size > sizeof(s->pkt_buf) - len )
{
logout("Warning: Get a too big TBD, ignore it"
"(buf addr %#x, size %d, el:%#x)\n",
tx_buf.addr, tx_buf.size, tx_buf.is_el_set);
continue;
}
cpu_physical_memory_read(tx_buf.addr, &s->pkt_buf[len],
tx_buf.size);
logout("TBD (standard mode): buf addr %#x, size %d, el:%#x\n",
tx_buf.addr, tx_buf.size, tx_buf.is_el_set);
len += tx_buf.size;
if ( tx_buf.is_el_set )
break;
}
}
//FIXME: Extend mode is not be tested
else
{
/* Extend TCB mode */
/* A strandard TCB followed by two TBDs */
uint32_t tbd_addr = cb_addr+16;
int i = 0;
for ( ; i<2 && i<tx.tbd.tbd_num; i++ )
{
cpu_physical_memory_read(tbd_array, (uint8_t *)&tx_buf,
sizeof(tx_buf));
tx_buf.is_el_set &= 0x1;
tbd_addr += 8;
/* From Intel's spec, size of TBD equal to zero
* has same effect with EL bit set
*/
if ( tx_buf.size == 0 )
{
tx_buf.is_el_set = 1;
break;
}
if ( tx_buf.size + len > sizeof(s->pkt_buf) )
{
logout("TX frame is too large, discarding it"
"(buf addr=%#x, size=%#x)\n", tx_buf.addr,
tx_buf.size);
//continue;
break;
}
logout("TBD (extended mode): buf addr %#08x, size %#04x, el:%#x\n",
tx_buf.addr, tx_buf.size, tx_buf.is_el_set);
cpu_physical_memory_read(tx_buf.addr, &s->pkt_buf[len],
tx_buf.size);
len += tx_buf.size;
if ( tx_buf.is_el_set )
break;
}
/* In extend TCB mode, TDB array point to the thrid TBD
* if it is not NULL(0xffffffff) and EL bit of before
* two TBDs is not set
*/
if ( tbd_array != (uint32_t)-1 && !tx_buf.is_el_set )
{
tbd_addr = tbd_array;
/* TBD number includes first two TBDs, so don't
* initialize i here
*/
for ( ; i<tx.tbd.tbd_num; i++ )
{
cpu_physical_memory_read(tbd_addr, (uint8_t *)&tx_buf,
sizeof(tx_buf));
tx_buf.is_el_set &= 0x1;
tbd_addr += 8;
cpu_physical_memory_read(tx_buf.addr, &s->pkt_buf[len],
tx_buf.size);
logout("TBD (extended mode): buf addr 0x%#08x, size 0x%#04x\n",
tx_buf.addr, tx_buf.size);
len += tx_buf.size;
if ( tx_buf.is_el_set )
break;
}
}
}
}
s->pkt_buf_len = len;
/* Below codes are used for Threshold. But with these logic, network of guest
* getting bad performance. So I comment it and leave codes here to hope anyone
* fix it
*/
#if 0
/* If threshold is set, only send packet when threshold
* bytes are read
*/
if ( tx.tbd.tx_threshold && s->pkt_buf_len < tx.tbd.tx_threshold * 8 )
{
logout("Current data length in FIFO buffer:%d\n", s->pkt_buf_len);
break;
}
#endif
if ( s->pkt_buf_len )
{
qemu_send_packet(s->vc, s->pkt_buf, s->pkt_buf_len);
s->statistics.tx_good_frames ++;
logout("Send out frame successful(size=%d,"
"already sent %d frames)\n", s->pkt_buf_len,
s->statistics.tx_good_frames);
s->pkt_buf_len = 0;
}
e100_dump("Dest addr:", (uint8_t *)s->pkt_buf, 6);
e100_dump("Src addr:", (uint8_t *)(s->pkt_buf+6), 6);
e100_dump("type:", (uint8_t *)(s->pkt_buf+8), 2);
break;
}
case CBL_LOAD_MICROCODE:
#ifdef DEBUG_E100
{
/* Don't support load marco code, just dump it */
#define MICRO_CODE_LEN 256
uint8_t micro_code[MICRO_CODE_LEN] = {0};
cpu_physical_memory_read(cb_addr+8, micro_code, MICRO_CODE_LEN);
e100_dump("Load micro code:", micro_code, MICRO_CODE_LEN);
}
#endif
break;
case CBL_DUMP:
logout("Control block dump\n");
break;
case CBL_DIAGNOSE:
logout("Control block diagnose\n");
break;
default:
logout("Unknown Control block command(val=%#x)\n", cb.cmd);
break;
}
/* Now, we finished executing a command, update status of CB.
* We always success
*/
cb.c = 1;
cb.ok = 1;
// Only update C bit and OK bit field in TCB
cpu_physical_memory_write(cb_addr, (uint8_t *)&cb, 2);
logout("Finished a command from CB list:\n"
"\tok:%d\n"
"\tc:%d\n"
"\tcommand name:%s(cmd=%#x)\n"
"\ti:%d\n"
"\ts:%d\n"
"\tel:%d\n"
"\tlink address:%#x\n",
cb.ok, cb.c, CB_CMD_NAME(cb.cmd), cb.cmd,
cb.i, cb.s, cb.el, cb.link_addr);
if ( cb.i )
e100_interrupt(s, (uint16_t)INT_CX_TNO);
// Suspend CU
if ( cb.s )
{
logout("CU go to suspend\n");
SET_CU_STATE(CU_SUSPENDED);
s->cu_next = cb.link_addr; // Save it for go on executing when resume
// Trigger CNA interrupt only when CNA mode is configured
if ( !(s->config.ci_intr) && cb.i )
e100_interrupt(s, (uint16_t)INT_CNA);
return;
}
// This is last command in CB list, CU go back to IDLE
if ( cb.el )
{
logout("Command block list is empty, CU go to idle\n");
SET_CU_STATE(CU_IDLE);
/* Either in CNA mode or CI mode, interrupt need be triggered
* when CU go to idle.
*/
if ( cb.i )
e100_interrupt(s, (uint16_t)INT_CNA);
return;
}
s->cu_offset = le32_to_cpu(cb.link_addr); // get next CB offset
}
}
static void dump_statistics(E100State * s, uint32_t complete_word)
{
/* Dump statistical data. Most data is never changed by the emulation
* and always 0.
*/
s->statistics.complete_word = complete_word;
cpu_physical_memory_write(s->statsaddr, (uint8_t *)&s->statistics, sizeof(s->statistics));
}
static void e100_cu_command(E100State *s, uint8_t val)
{
switch ( val )
{
case CU_NOP:
/* Will not be here */
break;
case CU_START:
/* This strictly follow Intel's spec */
if ( GET_CU_STATE != CU_IDLE && GET_CU_STATE != CU_SUSPENDED )
{
logout("Illegal CU start command. Device is not idle or suspend\n");
return;
}
SET_CU_STATE(CU_LPQ_ACTIVE);
logout("CU start\n");
e100_execute_cb_list(s, 0);
break;
case CU_RESUME:
{
uint32_t previous_cb = s->cu_base + s->cu_offset;
struct control_block cb;
/* Resume from suspend */
/* FIXME:From Intel's spec, CU resume from idle is
* forbidden, but e100 drive in linux
* indeed do this.
*/
if ( GET_CU_STATE == CU_IDLE )
{
logout("Illegal resume form IDLE\n");
}
cpu_physical_memory_read(previous_cb, (uint8_t *)&cb,
sizeof(cb));
//FIXME: Need any speical handle when CU is active ?
/* Driver must clean S bit in previous CB when
* it issue CU resume command
*/
if ( cb.s )
{
logout("CU still in suspend\n");
break;
}
SET_CU_STATE(CU_LPQ_ACTIVE);
if ( cb.el )
{
logout("CB list is empty, CU just go to active\n");
break;
}
// Continue next command
s->cu_offset = s->cu_next;
e100_execute_cb_list(s, 1);
logout("CU resume\n");
}
break;
case CU_STATSADDR:
/* Load dump counters address */
s->statsaddr = CSR_VAL(CSR_POINTER);
logout("Load Stats address at %#x\n", s->statsaddr);
break;
case CU_SHOWSTATS:
/* Dump statistical counters */
dump_statistics(s, 0xa005);
logout("Execute dump statistics\n");
break;
case CU_CMD_BASE:
/* Load CU base */
s->cu_base = CSR_VAL(CSR_POINTER);
logout("Load CU base at %x\n", s->cu_base);
break;
case CU_DUMPSTATS:
/* Dump statistical counters and reset counters. */
dump_statistics(s, 0xa007);
memset(&s->statistics, 0x0, sizeof(s->statistics));
logout("Execute dump and reset statistics\n");
break;
case CU_S_RESUME:
/* CU static resume */
logout("CU static resume is not implemented\n");
break;
default:
logout("Unknown CU command(val=%#x)\n", val);
break;
}
}
static void scb_cmd_func(E100State *s, uint16_t val, int dir)
{
/* ignore NOP operation */
if ( val & 0x0f )
{
e100_ru_command(s, val & 0x0f);
CSR(CSR_CMD, ru_cmd) = 0;
}
else if ( val & 0xf0 )
{
e100_cu_command(s, val & 0xf0);
CSR(CSR_CMD, cu_cmd) = 0;
}
}
enum
{
WRITEB,
WRITEW,
WRITEL,
OP_IS_READ,
} WRITE_BYTES;
/* Driver may issue a command by writting one 32bit-entry,
* two 16bit-entries or four 8bit-entries. In late two case, we
* must wait until driver finish writting to the highest byte. The parameter
* 'bytes' means write action of driver(writeb, wirtew, wirtel)
*/
static void e100_execute(E100State *s, uint32_t addr_offset,
uint32_t val, int dir, int bytes)
{
switch ( addr_offset )
{
case SCB_STATUS:
if ( bytes == WRITEB )
break;
case SCB_ACK:
if ( dir == OP_WRITE )
{
uint8_t _val = 0;
if ( bytes == WRITEB )
_val = (uint8_t)val;
else if ( bytes == WRITEW )
_val = ((uint16_t)val) >> 8;
else if ( bytes == WRITEL)
{
// This should not be happen
_val = ((uint16_t)val) >> 8;
logout("WARNNING: Drvier write 4 bytes to CSR register at offset %d,"
"emulator may do things wrong!!!\n", addr_offset);
}
e100_interrupt_ack(s, _val);
}
break;
case SCB_CMD:
if ( dir == OP_WRITE )
scb_cmd_func(s, val, dir);
/* I don't know whether there is any driver writes command words and
* interrupt mask at same time by two bytes. This is not a regular operation.
* but if we meet the case, below codes could copy with it. As far
* as I know. windows's and linux's driver don't do this thing.
*/
#if 0
if ( bytes == WRITEW && (val&0xff00) != 0 )
;
else
break;
#endif
break;
case SCB_INTERRUPT_MASK:
if ( dir == OP_WRITE )
{
uint8_t _val = 0;
if ( bytes == WRITEB )
_val = (uint8_t)val;
else if ( bytes == WRITEW )
_val = (val & 0xff00) >> 8;
else
logout("WARNNING: Drvier write 4 bytes to CSR register at offset %d,"
"emulator may do things wrong!!!\n", addr_offset);
// Driver generates a software interrupt
if ( _val & BIT(1) )
e100_interrupt(s, INT_SWI);
}
break;
case SCB_PORT ... SCB_PORT + 3:
if ( dir == OP_WRITE )
{
// Waitting for driver write to the highest byte
if ( (bytes == WRITEB && addr_offset != SCB_PORT + 3) ||
(bytes == WRITEW && addr_offset != SCB_PORT + 2) )
break;
scb_port_func(s, CSR_VAL(CSR_PORT), dir);
}
break;
case SCB_MDI ... SCB_MDI + 3:
if ( dir == OP_WRITE )
{
// Waitting for driver write to the highest byte
if ( (bytes == WRITEB && addr_offset != SCB_MDI + 3) ||
(bytes == WRITEW && addr_offset != SCB_MDI + 2) )
break;
}
scb_mdi_func(s, CSR_VAL(CSR_MDI), dir);
break;
case SCB_EEPROM:
if ( dir == OP_WRITE )
scb_eeprom_func(s, val, dir);
// Nothing need do when driver read EEPROM registers of CSR
break;
case SCB_POINTER:
break;
default:
logout("Driver operate on CSR reg(offset=%#x,dir=%s,val=%#x)\n",
addr_offset, dir==OP_WRITE?"write":"read", val);
}
}
/* MMIO access functions */
static uint8_t e100_read1(E100State * s, uint32_t addr_offset)
{
uint8_t val = -1;
if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) )
{
logout("Invaild read, beyond memory boundary(addr:%#x)\n", addr_offset
+ s->region_base_addr[CSR_MEMORY_BASE]);
return val;
}
e100_execute(s, addr_offset, val, OP_READ, OP_IS_READ);
val = CSR_READ(addr_offset, uint8_t);
logout("READ1: Register name = %s, addr_offset = %#x, val=%#x\n", SCBNAME(addr_offset), addr_offset, val);
return val;
}
static uint16_t e100_read2(E100State * s, uint32_t addr_offset)
{
uint16_t val = -1;
if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) )
{
logout("Invaild read, beyond memory boundary(addr:%#x)\n", addr_offset
+ s->region_base_addr[CSR_MEMORY_BASE]);
return val;
}
e100_execute(s, addr_offset, val, OP_READ, OP_IS_READ);
val = CSR_READ(addr_offset, uint16_t);
logout("READ2: Register name = %s, addr_offset = %#x, val=%#x\n", SCBNAME(addr_offset), addr_offset, val);
return val;
}
static uint32_t e100_read4(E100State * s, uint32_t addr_offset)
{
uint32_t val = -1;
if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) )
{
logout("Invaild read, beyond memory boundary(addr:%#x)\n", addr_offset
+ s->region_base_addr[CSR_MEMORY_BASE]);
return val;
}
e100_execute(s, addr_offset, val, OP_READ, OP_IS_READ);
val = CSR_READ(addr_offset, uint32_t);
logout("READ4: Register name = %s, addr_offset = %#x, val=%#x\n", SCBNAME(addr_offset), addr_offset, val);
return val;
}
static uint32_t pci_mmio_readb(void *opaque, target_phys_addr_t addr)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_MEMORY_BASE];
return e100_read1(s, addr);
}
static uint32_t pci_mmio_readw(void *opaque, target_phys_addr_t addr)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_MEMORY_BASE];
return e100_read2(s, addr);
}
static uint32_t pci_mmio_readl(void *opaque, target_phys_addr_t addr)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_MEMORY_BASE];
return e100_read4(s, addr);
}
static CPUReadMemoryFunc *pci_mmio_read[] = {
pci_mmio_readb,
pci_mmio_readw,
pci_mmio_readl
};
static void e100_write1(E100State * s, uint32_t addr_offset, uint8_t val)
{
if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) )
{
logout("Invaild write, beyond memory boundary(addr = %#x, val = %#x\n", addr_offset
+ s->region_base_addr[CSR_MEMORY_BASE], val);
return;
}
// SCB stauts is read-only word, can not be directly write
if ( addr_offset == SCB_STATUS )
{
return;
}
// EEDO bit of eeprom register is read-only, can not be written;
else if ( addr_offset == SCB_EEPROM )
{
int eedo = BIT(3) & CSR_VAL(CSR_EEPROM);
CSR_WRITE(addr_offset, val, uint8_t);
CSR(CSR_EEPROM, eedo) = !!(eedo & EEPROM_DO);
logout("WRITE1: Register name = %s, addr_offset = %#x, val = %#x\n", SCBNAME(addr_offset),addr_offset, (uint8_t)CSR_VAL(CSR_EEPROM));
return;
}
else
{
CSR_WRITE(addr_offset, val, uint8_t);
}
logout("WRITE1: Register name = %s, addr_offset = %#x, val = %#x\n", SCBNAME(addr_offset),addr_offset, val);
return;
}
static void e100_write2(E100State * s, uint32_t addr_offset, uint16_t val)
{
if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) )
{
logout("Invaild write, beyond memory boundary(addr = %#x, val = %#x\n", addr_offset
+ s->region_base_addr[CSR_MEMORY_BASE], val);
return;
}
// SCB stauts is readonly word, can not be directly write
if ( addr_offset == SCB_STATUS )
{
uint8_t __val = val >> 8;
CSR_WRITE(addr_offset+1, __val, uint8_t);
}
// EEDO bit of eeprom register is read-only, can not be written;
else if ( addr_offset == SCB_EEPROM )
{
int eedo = BIT(3) & CSR_VAL(CSR_EEPROM);
CSR_WRITE(addr_offset, val, uint16_t);
CSR(CSR_EEPROM, eedo) = !!(eedo & EEPROM_DO);
logout("WRITE1: Register name = %s, addr_offset = %#x, val = %#x\n", SCBNAME(addr_offset),addr_offset, CSR_VAL(CSR_EEPROM));
return;
}
else
{
CSR_WRITE(addr_offset, val, uint16_t);
}
logout("WRITE2: Register name = %s, addr_offset = %#x, val = %#x\n", SCBNAME(addr_offset),addr_offset, val);
return;
}
static void e100_write4(E100State * s, uint32_t addr_offset, uint32_t val)
{
if ( addr_offset + sizeof(val) >= sizeof(s->pci_mem.mem) )
{
logout("Invaild write, beyond memory boundary(addr = %#x, val = %#x\n", addr_offset
+ s->region_base_addr[CSR_MEMORY_BASE], val);
return;
}
// SCB stauts is readonly word, can not be directly write
if ( addr_offset == SCB_STATUS )
{
uint8_t __val[4] = {0};
//FIXME: any un-aligned reference ?
*(uint32_t *)&__val = val;
CSR_WRITE(addr_offset+1, __val[1], uint8_t);
CSR_WRITE(addr_offset+2, __val[2], uint8_t);
CSR_WRITE(addr_offset+3, __val[3], uint8_t);
}
/* No write4 opertaion on EEPROM register */
else
{
CSR_WRITE(addr_offset, val, uint32_t);
}
logout("WRITE4: Register name = %s, addr_offset = %#x, val = %#x\n", SCBNAME(addr_offset),addr_offset, val);
return;
}
static void pci_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_MEMORY_BASE];
e100_write1(s, addr, val);
e100_execute(s, addr, val, OP_WRITE, WRITEB);
}
static void pci_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_MEMORY_BASE];
e100_write2(s, addr, val);
e100_execute(s, addr, val, OP_WRITE, WRITEW);
}
static void pci_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_MEMORY_BASE];
e100_write4(s, addr, val);
(void)e100_execute(s, addr, val, OP_WRITE, WRITEL);
}
static CPUWriteMemoryFunc *pci_mmio_write[] = {
pci_mmio_writeb,
pci_mmio_writew,
pci_mmio_writel
};
static void pci_mmio_map(PCIDevice * pci_dev, int region_num,
uint32_t addr, uint32_t size, int type)
{
PCIE100State *d = (PCIE100State *) pci_dev;
logout("region %d, addr=0x%08x, size=0x%08x, type=%d\n",
region_num, addr, size, type);
if ( region_num == CSR_MEMORY_BASE ) {
/* Map control / status registers. */
cpu_register_physical_memory(addr, size, d->e100.mmio_index);
d->e100.region_base_addr[region_num] = addr;
}
}
/* IO access functions */
static void ioport_write1(void *opaque, uint32_t addr, uint32_t val)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_IO_BASE];
e100_write1(s, addr, val);
(void)e100_execute(s, addr, (uint32_t)val, OP_WRITE, WRITEB);
}
static void ioport_write2(void *opaque, uint32_t addr, uint32_t val)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_IO_BASE];
e100_write2(s, addr, val);
(void)e100_execute(s, addr, (uint32_t)val, OP_WRITE, WRITEW);
}
static void ioport_write4(void *opaque, uint32_t addr, uint32_t val)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_IO_BASE];
e100_write4(s, addr, val);
(void)e100_execute(s, addr, (uint32_t)val, OP_WRITE, WRITEL);
}
static uint32_t ioport_read1(void *opaque, uint32_t addr)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_IO_BASE];
return e100_read1(s, addr);
}
static uint32_t ioport_read2(void *opaque, uint32_t addr)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_IO_BASE];
return e100_read2(s, addr);
}
static uint32_t ioport_read4(void *opaque, uint32_t addr)
{
E100State *s = opaque;
addr -= s->region_base_addr[CSR_IO_BASE];
return e100_read4(s, addr);
}
static void pci_ioport_map(PCIDevice * pci_dev, int region_num,
uint32_t addr, uint32_t size, int type)
{
PCIE100State *d = (PCIE100State *) pci_dev;
E100State *s = &d->e100;
logout("region %d, addr=0x%08x, size=0x%08x, type=%d\n",
region_num, addr, size, type);
if ( region_num != 1 )
{
logout("Invaid region number!\n");
return;
}
register_ioport_write(addr, size, 1, ioport_write1, s);
register_ioport_read(addr, size, 1, ioport_read1, s);
register_ioport_write(addr, size, 2, ioport_write2, s);
register_ioport_read(addr, size, 2, ioport_read2, s);
register_ioport_write(addr, size, 4, ioport_write4, s);
register_ioport_read(addr, size, 4, ioport_read4, s);
s->region_base_addr[region_num] = addr;
}
/* From FreeBSD */
#define POLYNOMIAL 0x04c11db6
static int compute_mcast_idx(const uint8_t *ep)
{
uint32_t crc;
int carry, i, j;
uint8_t b;
crc = 0xffffffff;
for (i = 0; i < 6; i++) {
b = *ep++;
for (j = 0; j < 8; j++) {
carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
crc <<= 1;
b >>= 1;
if (carry)
crc = ((crc ^ POLYNOMIAL) | carry);
}
}
return (crc >> 26);
}
#ifdef USE_BUFFER_TCP
#define BUFFER_TCP_ENABLE_VALVE 3
#define BUFFER_TCP_FLUSH_VALVE 5
static int buffer_tcp(E100State *s, const uint8_t *pkt)
{
uint16_t eth_type = 0;
uint8_t ip_type = 0;
if ( !pkt )
return 0;
//FIXME: any un-aligned reference ?
eth_type = ntohs(*(uint16_t *)(pkt+12));
if ( eth_type == 0x0800 )
{
/* Get an IP frame */
ip_type = *(uint8_t *)(pkt+23);
if ( ip_type == 0x6 )
{
/* Get a TCP frame */
if ( !s->buffer_tcp_enable &&
(++ s->continuous_tcp_frame > BUFFER_TCP_ENABLE_VALVE) )
s->buffer_tcp_enable = 1;
if ( !s->buffer_tcp_enable )
goto do_flush_tcp_frame;
if ( ++ s->unflush_tcp_num >= BUFFER_TCP_FLUSH_VALVE )
goto do_flush_tcp_frame;
else
return 0;
}
else
{
s->continuous_tcp_frame = 0;
s->buffer_tcp_enable = 0;
}
}
else
{
s->continuous_tcp_frame = 0;
s->buffer_tcp_enable = 0;
}
do_flush_tcp_frame:
s->unflush_tcp_num = 0;
return 1;
}
#else
static inline int buffer_tcp(E100State *s, const uint8_t *pkt)
{
return 1;
}
#endif
/* Eerpro100 receive functions */
static int e100_can_receive(void *opaque)
{
E100State *s = opaque;
int is_ready = (GET_RU_STATE == RU_READY);
logout("%s\n", is_ready ? "EEPro100 receiver is ready"
: "EEPro100 receiver is not ready");
return is_ready;
}
static void e100_receive(void *opaque, const uint8_t * buf, int size)
{
E100State *s = opaque;
uint32_t rfd_addr = 0;
rfd_t rfd = {0};
if ( GET_RU_STATE != RU_READY )
{
//logout("RU is not ready. Begin discarding frame(state=%x)\n", GET_RU_STATE);
return;
}
rfd_addr = s->ru_base + s->ru_offset;
cpu_physical_memory_read(rfd_addr, (uint8_t *)&rfd, sizeof(rfd_t));
if ( size > MAX_ETH_FRAME_SIZE+4 )
{
/* Long frame and configuration byte 18/3 (long receive ok) not set:
* Long frames are discarded. */
logout("Discard long frame(size=%d)\n", size);
return;
}
else if ( !memcmp(buf, s->macaddr, sizeof(s->macaddr)) )
{
/* The frame is for me */
logout("Receive a frame for me(size=%d)\n", size);
e100_dump("FRAME:", (uint8_t *)buf, size);
}
else if ( !memcmp(buf, broadcast_macaddr, sizeof(broadcast_macaddr)) )
{
if ( s->config.broadcast_dis && !s->config.promiscuous )
{
logout("Discard a broadcast frame\n");
return;
}
/* Broadcast frame */
rfd.status |= RX_IA_MATCH;
logout("Receive a broadcast frame(size=%d)\n", size);
}
else if ( s->is_multcast_enable && (buf[0] & 0x1) )
{
int mcast_idx = compute_mcast_idx(buf);
if ( !(s->mult_list[mcast_idx >> 3] & (1 << (mcast_idx & 7))) )
{
logout("Multicast address mismatch, discard\n");
return;
}
logout("Receive a multicast frame(size=%d)\n", size);
}
else if ( size < 64 && (s->config.dis_short_rx) )
{
/* From Intel's spec, short frame should be discarded
* when configuration byte 7/0 (discard short receive) set.
* But this will cause frame lossing such as ICMP frame, ARP frame.
* So we check is the frame for me before discarding short frame
*/
/* Save Bad Frame bit */
if ( s->config.save_bad_frame )
{
rfd.status |= RX_SHORT;
s->statistics.rx_short_frame_errors ++;
}
logout("Receive a short frame(size=%d), discard it\n", size);
return;
}
else if ( s->config.promiscuous )
{
/* Promiscuous: receive all. No address match */
logout("Received frame in promiscuous mode(size=%d)\n", size);
rfd.status |= RX_NO_MATCH;
}
else
{
e100_dump("Unknown frame, MAC = ", (uint8_t *)buf, 6);
return;
}
e100_dump("Get frame, MAC = ", (uint8_t *)buf, 6);
rfd.c = 1;
rfd.ok = 1;
rfd.f = 1;
rfd.eof = 1;
rfd.status &= ~RX_COLLISION;
rfd.count = size;
logout("Get a RFD configure:\n"
"\tstatus:%#x\n"
"\tok:%#x\n" "\tc:%#x\n" "\tsf:%#x\n"
"\th:%#x\n" "\ts:%#x\n" "\tel:%#x\n"
"\tlink add:%#x\n" "\tactual count:%#x\n"
"\tf:%#x\n" "\teof:%#x\n" "\tsize:%#x\n",
rfd.status, rfd.ok, rfd.c, rfd.sf, rfd.h,
rfd.s, rfd.el, rfd.link_addr, rfd.count,
rfd.f, rfd.eof, rfd.size);
cpu_physical_memory_write(rfd_addr, (uint8_t *)&rfd, sizeof(rfd));
cpu_physical_memory_write(rfd_addr + sizeof(rfd_t), buf, size);
s->statistics.rx_good_frames ++;
s->ru_offset = le32_to_cpu(rfd.link_addr);
if ( buffer_tcp(s, buf) )
e100_interrupt(s, INT_FR);
if ( rfd.el || rfd.s )
{
/* Go to suspend */
SET_RU_STATE(RU_SUSPENDED);
e100_interrupt(s, INT_RNR);
logout("RFD met S or EL bit set, RU go to suspend\n");
return;
}
logout("Complete a frame receive(size = %d)\n", size);
return;
}
static void eeprom_init(E100State *s)
{
int i;
int chksum = 0;
/* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
* i82559 and later support 64 or 256 word EEPROM. */
eeprom_reset(s, EEPROM_RESET_ALL);
s->eeprom.addr_len = EEPROM_I82557_ADDRBIT;
memcpy(s->eeprom.contents, eeprom_i82557, sizeof(eeprom_i82557));
/* Dirver is going to get MAC from eeprom*/
memcpy((uint8_t *)s->eeprom.contents, s->macaddr, sizeof(s->macaddr));
/* The last word in eeprom saving checksum value.
* After we update MAC in eeprom, the checksum need be re-calculate
* and saved at the end of eeprom
*/
for ( i=0; i<(1<<s->eeprom.addr_len)-1; i++ )
chksum += s->eeprom.contents[i];
s->eeprom.contents[i] = 0xBABA - chksum;
}
static void e100_init(PCIBus * bus, NICInfo * nd,
const char *name, uint32_t device)
{
PCIE100State *d;
E100State *s;
logout("\n");
d = (PCIE100State *) pci_register_device(bus, name,
sizeof(PCIE100State), -1,
NULL, NULL);
s = &d->e100;
s->device = device;
s->pci_dev = &d->dev;
pci_reset(s);
/* Handler for memory-mapped I/O */
d->e100.mmio_index =
cpu_register_io_memory(0, pci_mmio_read, pci_mmio_write, s);
//CSR Memory mapped base
pci_register_io_region(&d->dev, 0, PCI_MEM_SIZE,
PCI_ADDRESS_SPACE_MEM | PCI_ADDRESS_SPACE_MEM_PREFETCH,
pci_mmio_map);
//CSR I/O mapped base
pci_register_io_region(&d->dev, 1, PCI_IO_SIZE, PCI_ADDRESS_SPACE_IO,
pci_ioport_map);
//Flash memory mapped base
pci_register_io_region(&d->dev, 2, PCI_FLASH_SIZE, PCI_ADDRESS_SPACE_MEM,
pci_mmio_map);
memcpy(s->macaddr, nd->macaddr, 6);
e100_dump("MAC ADDR", (uint8_t *)&s->macaddr[0], 6);
eeprom_init(s);
e100_reset(s);
s->vc = qemu_new_vlan_client(nd->vlan, e100_receive, e100_can_receive, s);
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
"e100 pci macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
s->macaddr[0],
s->macaddr[1],
s->macaddr[2], s->macaddr[3], s->macaddr[4], s->macaddr[5]);
qemu_register_reset(e100_reset, s);
register_savevm(name, 0, 3, e100_save, e100_load, s);
}
void pci_e100_init(PCIBus * bus, NICInfo * nd)
{
e100_init(bus, nd, "e100", i82557C);
}
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [Qemu-devel] [PATCH][RFC] E100 emulator
2007-11-22 9:41 [Qemu-devel] [PATCH][RFC] E100 emulator Zhang, Xing Z
@ 2007-11-22 21:16 ` Stefan Weil
0 siblings, 0 replies; 2+ messages in thread
From: Stefan Weil @ 2007-11-22 21:16 UTC (permalink / raw)
To: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 902 bytes --]
Zhang, Xing Z schrieb:
> Hi All:
> I know there is an eepro100 implementation in QEMU upstream. But it
> only supports x86 linux guest.
> Base on the old version, I re-wrote a new E100 emulator. It should
> support linux/windows guest on x86, x86_64 and IA64 platform.
> I tested it in linux/windows guest of IA64/XEN and got 30% faster than
> rtl8139. It also works fine in windows guest of QEMU on x86_64 box(I
> don't test it on x86 box, but it should be ok).
>
>
>
>
> Good good study,day day up ! ^_^
> -Wing(zhang xin)
>
> OTC,Intel Corporation
Hi,
could you update your implementation so that it compiles with latest
QEMU from CVS?
There were some changes in header files and QEMU interfaces in the last
weeks.
Perhaps you can also try / merge the latest version of eepro100.c which
I send with
this mail. It works with Linux guest on x86 and mips / mipsel malta
host, too.
Regards
Stefan
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: eepro100.c --]
[-- Type: text/x-csrc; name="eepro100.c", Size: 64663 bytes --]
/*
* QEMU i8255x (PRO100) emulation
*
* Copyright (C) 2006-2007 Stefan Weil
*
* Portions of the code are copies from grub / etherboot eepro100.c
* and linux e100.c.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Tested features (i82559):
* PXE boot (i386) ok
* Linux networking (i386) ok
* Linux networking e100 driver (mips, mipsel) ok
* Linux networking eepro100 driver (mipsel) not ok
*
* Untested:
* non-i386 platforms
* Windows networking
*
* References:
*
* Intel 8255x 10/100 Mbps Ethernet Controller Family
* Open Source Software Developer Manual
*/
#include <assert.h>
#include <stddef.h> /* offsetof */
#include "hw.h"
#include "pci.h"
#include "net.h"
#include "eeprom93xx.h"
/* Common declarations for all PCI devices. */
#define PCI_VENDOR_ID 0x00 /* 16 bits */
#define PCI_DEVICE_ID 0x02 /* 16 bits */
#define PCI_COMMAND 0x04 /* 16 bits */
#define PCI_STATUS 0x06 /* 16 bits */
#define PCI_REVISION_ID 0x08 /* 8 bits */
#define PCI_CLASS_CODE 0x0b /* 8 bits */
#define PCI_SUBCLASS_CODE 0x0a /* 8 bits */
#define PCI_HEADER_TYPE 0x0e /* 8 bits */
#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */
#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits */
#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits */
#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */
#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */
#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */
#define PCI_CONFIG_8(offset, value) \
(pci_conf[offset] = (value))
#define PCI_CONFIG_16(offset, value) \
(*(uint16_t *)&pci_conf[offset] = cpu_to_le16(value))
#define PCI_CONFIG_32(offset, value) \
(*(uint32_t *)&pci_conf[offset] = cpu_to_le32(value))
#define KiB 1024
/* debug EEPRO100 card */
//~ #define DEBUG_EEPRO100
#ifdef DEBUG_EEPRO100
#define logout(fmt, args...) fprintf(stderr, "EE100\t%-24s" fmt, __func__, ##args)
#else
#define logout(fmt, args...) ((void)0)
#endif
/* Set flags to 0 to disable debug output. */
#define INT 1 /* interrupt related actions */
#define MDI 1 /* mdi related actions */
#define OTHER 1
#define RXTX 1
#define TRACE(flag, command) ((flag) ? (command) : (void)0)
#define UNEXPECTED() logout("%s:%u unexpected\n", __FILE__, __LINE__)
#define missing(text) assert(!"feature is missing in this emulation: " text)
#define MAX_ETH_FRAME_SIZE 1514
/* This driver supports several different devices which are declared here. */
#define i82551 0x82551
#define i82557B 0x82557b
#define i82557C 0x82557c
#define i82558B 0x82558b
#define i82559C 0x82559c
#define i82559ER 0x82559e
#define i82562 0x82562
/* Use 64 word EEPROM. */
#define EEPROM_SIZE 64
#define PCI_MEM_SIZE (4 * KiB)
#define PCI_IO_SIZE 64
#define PCI_FLASH_SIZE (128 * KiB)
#define BIT(n) (1 << (n))
#define BITS(n, m) (((0xffffffffU << (31 - n)) >> (31 - n + m)) << m)
/* The SCB accepts the following controls for the Tx and Rx units: */
#define CU_NOP 0x0000 /* No operation. */
#define CU_START 0x0010 /* CU start. */
#define CU_RESUME 0x0020 /* CU resume. */
#define CU_STATSADDR 0x0040 /* Load dump counters address. */
#define CU_SHOWSTATS 0x0050 /* Dump statistical counters. */
#define CU_CMD_BASE 0x0060 /* Load CU base address. */
#define CU_DUMPSTATS 0x0070 /* Dump and reset statistical counters. */
#define CU_SRESUME 0x00a0 /* CU static resume. */
#define RU_NOP 0x0000
#define RX_START 0x0001
#define RX_RESUME 0x0002
#define RX_ABORT 0x0004
#define RX_ADDR_LOAD 0x0006
#define RX_RESUMENR 0x0007
#define INT_MASK 0x0100
#define DRVR_INT 0x0200 /* Driver generated interrupt. */
typedef unsigned char bool;
/* Offsets to the various registers.
All accesses need not be longword aligned. */
typedef enum {
SCBStatus = 0,
SCBAck = 1,
SCBCmd = 2, /* Rx/Command Unit command and status. */
SCBIntmask = 3,
SCBPointer = 4, /* General purpose pointer. */
SCBPort = 8, /* Misc. commands and operands. */
SCBflash = 12, /* Flash memory control. */
SCBeeprom = 14, /* EEPROM control. */
SCBCtrlMDI = 16, /* MDI interface control. */
SCBEarlyRx = 20, /* Early receive byte count. */
SCBFlow = 24, /* Flow Control. */
SCBpmdr = 27, /* Power Management Driver. */
SCBgctrl = 28, /* General Control. */
SCBgstat = 29, /* General Status. */
} speedo_offset_t;
/* A speedo3 transmit buffer descriptor with two buffers... */
typedef struct {
uint16_t status;
uint16_t command;
uint32_t link; /* void * */
uint32_t tbd_array_addr; /* transmit buffer descriptor array address. */
uint16_t tcb_bytes; /* transmit command block byte count (in lower 14 bits */
uint8_t tx_threshold; /* transmit threshold */
uint8_t tbd_count; /* TBD number */
//~ /* This constitutes two "TBD" entries: hdr and data */
//~ uint32_t tx_buf_addr0; /* void *, header of frame to be transmitted. */
//~ int32_t tx_buf_size0; /* Length of Tx hdr. */
//~ uint32_t tx_buf_addr1; /* void *, data to be transmitted. */
//~ int32_t tx_buf_size1; /* Length of Tx data. */
} eepro100_tx_t;
/* Receive frame descriptor. */
typedef struct {
int16_t status;
uint16_t command;
uint32_t link; /* struct RxFD * */
uint32_t rx_buf_addr; /* void * */
uint16_t count;
uint16_t size;
char packet[MAX_ETH_FRAME_SIZE + 4];
} eepro100_rx_t;
typedef enum {
COMMAND_EL = BIT(15),
COMMAND_S = BIT(14),
COMMAND_I = BIT(13),
COMMAND_NC = BIT(4),
COMMAND_SF = BIT(3),
COMMAND_CMD = BITS(2, 0),
} cb_command_bit_t;
typedef enum {
STATUS_C = BIT(15),
STATUS_OK = BIT(13),
} cb_status_bit_t;
typedef struct {
uint32_t tx_good_frames, tx_max_collisions, tx_late_collisions,
tx_underruns, tx_lost_crs, tx_deferred, tx_single_collisions,
tx_multiple_collisions, tx_total_collisions;
uint32_t rx_good_frames, rx_crc_errors, rx_alignment_errors,
rx_resource_errors, rx_overrun_errors, rx_cdt_errors,
rx_short_frame_errors;
uint32_t fc_xmt_pause, fc_rcv_pause, fc_rcv_unsupported;
uint16_t xmt_tco_frames, rcv_tco_frames;
uint32_t complete;
} eepro100_stats_t;
typedef enum {
cu_idle = 0,
cu_suspended = 1,
cu_active = 2,
cu_lpq_active = 2,
cu_hqp_active = 3
} cu_state_t;
typedef enum {
ru_idle = 0,
ru_suspended = 1,
ru_no_resources = 2,
ru_ready = 4
} ru_state_t;
typedef struct {
#if 1 // überflüssige Einträge entfernen, save / load prüfen!!!
uint8_t cmd;
uint32_t start;
uint32_t stop;
uint8_t boundary;
uint8_t tsr;
uint8_t tpsr;
uint16_t tcnt;
uint16_t rcnt;
uint32_t rsar;
uint8_t rsr;
uint8_t rxcr;
uint8_t isr;
uint8_t dcfg;
uint8_t imr;
uint8_t phys[6]; /* mac address */
uint8_t curpag;
uint8_t mult[8]; /* multicast mask array */
int mmio_index;
PCIDevice *pci_dev;
VLANClientState *vc;
#endif
uint8_t scb_stat; /* SCB stat/ack byte */
uint8_t int_stat; /* PCI interrupt status */
uint32_t region[3]; /* PCI region addresses */
uint8_t macaddr[6];
uint32_t statcounter[19];
uint16_t mdimem[32];
eeprom_t *eeprom;
uint32_t device; /* device variant */
uint32_t pointer;
/* (cu_base + cu_offset) address the next command block in the command block list. */
uint32_t cu_base; /* CU base address */
uint32_t cu_offset; /* CU address offset */
/* (ru_base + ru_offset) address the RFD in the Receive Frame Area. */
uint32_t ru_base; /* RU base address */
uint32_t ru_offset; /* RU address offset */
uint32_t statsaddr; /* pointer to eepro100_stats_t */
cu_state_t cu_state;
ru_state_t ru_state;
/* Temporary data. */
eepro100_tx_t tx;
uint32_t cb_address;
/* Statistical counters. */
eepro100_stats_t statistics;
/* Configuration bytes. */
uint8_t configuration[22];
/* Data in mem is always in the byte order of the controller (le). */
uint8_t mem[PCI_MEM_SIZE];
} EEPRO100State;
typedef enum {
eeprom_cnfg_mdix = 0x03,
eeprom_phy_id = 0x06,
eeprom_id = 0x0a,
eeprom_config_asf = 0x0d,
eeprom_smbus_addr = 0x90,
} eeprom_offset_t;
/* Bit values for EEPROM ID word (offset 0x0a). */
typedef enum {
eeprom_id_mdm = BIT(0), /* Modem */
eeprom_id_stb = BIT(1), /* Standby Enable */
eeprom_id_wmr = BIT(2), /* ??? */
eeprom_id_wol = BIT(5), /* Wake on LAN */
eeprom_id_dpd = BIT(6), /* Deep Power Down */
eeprom_id_alt = BIT(7), /* */
/* BITS(10, 8) device revision */
eeprom_id_bd = BIT(11), /* boot disable */
eeprom_id_id = BIT(13), /* id bit */
/* BITS(15, 14) signature */
eeprom_id_valid = BIT(14), /* signature for valid eeprom */
} eeprom_id_t;
/* Default values for MDI (PHY) registers */
static const uint16_t eepro100_mdi_default[] = {
/* MDI Registers 0 - 6, 7 */
0x3000, 0x780d, 0x02a8, 0x0154, 0x05e1, 0x0000, 0x0000, 0x0000,
/* MDI Registers 8 - 15 */
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
/* MDI Registers 16 - 31 */
0x0003, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
};
/* Readonly mask for MDI (PHY) registers */
static const uint16_t eepro100_mdi_mask[] = {
0x0000, 0xffff, 0xffff, 0xffff, 0xc01f, 0xffff, 0xffff, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
0x0fff, 0x0000, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff, 0xffff,
0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
};
/* XXX: optimize */
uint32_t lduw_le_phys(target_phys_addr_t addr)
{
/* Load 16 bit (little endian) word from emulated hardware. */
uint16_t val;
cpu_physical_memory_read(addr, (uint8_t *)&val, sizeof(val));
return le16_to_cpu(val);
}
/* XXX: optimize */
uint32_t ldl_le_phys(target_phys_addr_t addr)
{
/* Load 32 bit (little endian) word from emulated hardware. */
uint32_t val;
cpu_physical_memory_read(addr, (uint8_t *)&val, sizeof(val));
return le32_to_cpu(val);
}
/* XXX: optimize */
void stw_le_phys(target_phys_addr_t addr, uint16_t val)
{
val = cpu_to_le16(val);
cpu_physical_memory_write(addr, (const uint8_t *)&val, sizeof(val));
}
/* XXX: optimize */
void stl_le_phys(target_phys_addr_t addr, uint32_t val)
{
val = cpu_to_le32(val);
cpu_physical_memory_write(addr, (const uint8_t *)&val, sizeof(val));
}
#define POLYNOMIAL 0x04c11db6
/* From FreeBSD */
/* XXX: optimize */
static unsigned compute_mcast_idx(const uint8_t * ep)
{
uint32_t crc;
int carry, i, j;
uint8_t b;
crc = 0xffffffff;
for (i = 0; i < 6; i++) {
b = *ep++;
for (j = 0; j < 8; j++) {
carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01);
crc <<= 1;
b >>= 1;
if (carry)
crc = ((crc ^ POLYNOMIAL) | carry);
}
}
return (crc & BITS(7, 2)) >> 2;
}
#if defined(DEBUG_EEPRO100)
static const char *nic_dump(const uint8_t * buf, unsigned size)
{
static char dump[3 * 16 + 1];
char *p = &dump[0];
if (size > 16)
size = 16;
while (size-- > 0) {
p += sprintf(p, " %02x", *buf++);
}
return dump;
}
#endif /* DEBUG_EEPRO100 */
#if 0
enum scb_stat_ack {
stat_ack_not_ours = 0x00,
stat_ack_sw_gen = 0x04,
stat_ack_rnr = 0x10,
stat_ack_cu_idle = 0x20,
stat_ack_frame_rx = 0x40,
stat_ack_cu_cmd_done = 0x80,
stat_ack_not_present = 0xFF,
stat_ack_rx = (stat_ack_sw_gen | stat_ack_rnr | stat_ack_frame_rx),
stat_ack_tx = (stat_ack_cu_idle | stat_ack_cu_cmd_done),
};
#endif
static void disable_interrupt(EEPRO100State * s)
{
if (s->int_stat) {
TRACE(INT, logout("interrupt disabled\n"));
qemu_irq_lower(s->pci_dev->irq[0]);
s->int_stat = 0;
}
}
static void enable_interrupt(EEPRO100State * s)
{
if (!s->int_stat) {
TRACE(INT, logout("interrupt enabled\n"));
qemu_irq_raise(s->pci_dev->irq[0]);
s->int_stat = 1;
}
}
static void eepro100_acknowledge(EEPRO100State * s)
{
s->scb_stat &= ~s->mem[SCBAck];
s->mem[SCBAck] = s->scb_stat;
if (s->scb_stat == 0) {
disable_interrupt(s);
}
}
static void eepro100_interrupt(EEPRO100State * s, uint8_t status)
{
uint8_t mask = ~s->mem[SCBIntmask];
s->mem[SCBAck] |= status;
status = s->scb_stat = s->mem[SCBAck];
status &= (mask | 0x0f);
//~ status &= (~s->mem[SCBIntmask] | 0x0xf);
if (status && (mask & 0x01)) {
/* SCB mask and SCB Bit M do not disable interrupt. */
enable_interrupt(s);
} else if (s->int_stat) {
disable_interrupt(s);
}
}
static void eepro100_cx_interrupt(EEPRO100State * s)
{
/* CU completed action command. */
/* Transmit not ok (82557 only, not in emulation). */
eepro100_interrupt(s, 0x80);
}
static void eepro100_cna_interrupt(EEPRO100State * s)
{
/* CU left the active state. */
eepro100_interrupt(s, 0x20);
}
static void eepro100_fr_interrupt(EEPRO100State * s)
{
/* RU received a complete frame. */
eepro100_interrupt(s, 0x40);
}
#if 0
static void eepro100_rnr_interrupt(EEPRO100State * s)
{
/* RU is not ready. */
eepro100_interrupt(s, 0x10);
}
#endif
static void eepro100_mdi_interrupt(EEPRO100State * s)
{
/* MDI completed read or write cycle. */
eepro100_interrupt(s, 0x08);
}
static void eepro100_swi_interrupt(EEPRO100State * s)
{
/* Software has requested an interrupt. */
eepro100_interrupt(s, 0x04);
}
#if 0
static void eepro100_fcp_interrupt(EEPRO100State * s)
{
/* Flow control pause interrupt (82558 and later). */
eepro100_interrupt(s, 0x01);
}
#endif
static void pci_reset(EEPRO100State * s)
{
uint32_t device = s->device;
uint8_t *pci_conf = s->pci_dev->config;
TRACE(OTHER, logout("%p\n", s));
/* PCI Vendor ID */
PCI_CONFIG_16(PCI_VENDOR_ID, 0x8086);
/* PCI Device ID */
PCI_CONFIG_16(PCI_DEVICE_ID, 0x1209);
/* PCI Command */
PCI_CONFIG_16(PCI_COMMAND, 0x0000);
/* PCI Status */
PCI_CONFIG_16(PCI_STATUS, 0x2800);
/* PCI Revision ID */
PCI_CONFIG_8(PCI_REVISION_ID, 0x08);
/* PCI Class Code */
PCI_CONFIG_8(0x09, 0x00);
PCI_CONFIG_8(PCI_SUBCLASS_CODE, 0x00); // ethernet network controller
PCI_CONFIG_8(PCI_CLASS_CODE, 0x02); // network controller
/* PCI Cache Line Size */
/* check cache line size!!! */
//~ PCI_CONFIG_8(0x0c, 0x00);
/* PCI Latency Timer */
PCI_CONFIG_8(0x0d, 0x20); // latency timer = 32 clocks
PCI_CONFIG_8(PCI_HEADER_TYPE, 0x00);
/* BIST (built-in self test) */
#if defined(TARGET_I386)
// !!! workaround for buggy bios
//~ #define PCI_ADDRESS_SPACE_MEM_PREFETCH 0
#endif
#if 0
/* PCI Base Address Registers */
/* CSR Memory Mapped Base Address */
PCI_CONFIG_32(PCI_BASE_ADDRESS_0,
PCI_ADDRESS_SPACE_MEM | PCI_ADDRESS_SPACE_MEM_PREFETCH);
/* CSR I/O Mapped Base Address */
PCI_CONFIG_32(PCI_BASE_ADDRESS_1, PCI_ADDRESS_SPACE_IO);
#if 0
/* Flash Memory Mapped Base Address */
PCI_CONFIG_32(PCI_BASE_ADDRESS_2, 0xfffe0000 | PCI_ADDRESS_SPACE_MEM);
#endif
#endif
/* Expansion ROM Base Address (depends on boot disable!!!) */
PCI_CONFIG_32(0x30, 0x00000000);
/* Capability Pointer */
PCI_CONFIG_8(0x34, 0xdc);
/* Interrupt Pin */
PCI_CONFIG_8(0x3d, 1); // interrupt pin 0
/* Minimum Grant */
PCI_CONFIG_8(0x3e, 0x08);
/* Maximum Latency */
PCI_CONFIG_8(0x3f, 0x18);
/* Power Management Capabilities / Next Item Pointer / Capability ID */
PCI_CONFIG_32(0xdc, 0x7e210001);
switch (device) {
case i82551:
//~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1209);
PCI_CONFIG_8(PCI_REVISION_ID, 0x0f);
break;
case i82557B:
PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229);
PCI_CONFIG_8(PCI_REVISION_ID, 0x02);
break;
case i82557C:
PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229);
PCI_CONFIG_8(PCI_REVISION_ID, 0x03);
break;
case i82558B:
PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229);
PCI_CONFIG_16(PCI_STATUS, 0x2810);
PCI_CONFIG_8(PCI_REVISION_ID, 0x05);
break;
case i82559C:
PCI_CONFIG_16(PCI_DEVICE_ID, 0x1229);
PCI_CONFIG_16(PCI_STATUS, 0x2810);
//~ PCI_CONFIG_8(PCI_REVISION_ID, 0x08);
break;
case i82559ER:
//~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1209);
PCI_CONFIG_16(PCI_STATUS, 0x2810);
PCI_CONFIG_8(PCI_REVISION_ID, 0x09);
break;
//~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1029);
//~ PCI_CONFIG_16(PCI_DEVICE_ID, 0x1030); /* 82559 InBusiness 10/100 */
default:
logout("Device %X is undefined!\n", device);
}
if (device == i82557C || device == i82558B || device == i82559C) {
logout("Get device id and revision from EEPROM!!!\n");
}
}
static void nic_selective_reset(EEPRO100State * s)
{
size_t i;
uint16_t *eeprom_contents = eeprom93xx_data(s->eeprom);
//~ eeprom93xx_reset(s->eeprom);
memcpy(eeprom_contents, s->macaddr, 6);
for (i = 0; i < 3; i++) {
eeprom_contents[i] = le16_to_cpu(eeprom_contents[i]);
}
eeprom_contents[eeprom_phy_id] = 1;
/* TODO: eeprom_id_alt for i82559 */
eeprom_contents[eeprom_id] = eeprom_id_valid;
uint16_t sum = 0;
for (i = 0; i < EEPROM_SIZE - 1; i++) {
sum += eeprom_contents[i];
}
eeprom_contents[EEPROM_SIZE - 1] = (0xbaba - sum);
memset(s->mem, 0, sizeof(s->mem));
uint32_t val = cpu_to_le32(BIT(21));
memcpy(&s->mem[SCBCtrlMDI], &val, sizeof(val));
assert(sizeof(s->mdimem) == sizeof(eepro100_mdi_default));
memcpy(&s->mdimem[0], &eepro100_mdi_default[0], sizeof(s->mdimem));
}
static void nic_reset(void *opaque)
{
static int first;
EEPRO100State *s = (EEPRO100State *) opaque;
TRACE(OTHER, logout("%p\n", s));
if (!first) {
first = 1;
}
nic_selective_reset(s);
}
#if defined(DEBUG_EEPRO100)
static const char *reg[PCI_IO_SIZE / 4] = {
"Command/Status",
"General Pointer",
"Port",
"EEPROM/Flash Control",
"MDI Control",
"Receive DMA Byte Count",
"Flow Control",
"General Status/Control"
};
static char *regname(uint32_t addr)
{
static char buf[16];
if (addr < PCI_IO_SIZE) {
const char *r = reg[addr / 4];
if (r != 0) {
sprintf(buf, "%s+%u", r, addr % 4);
} else {
sprintf(buf, "0x%02x", addr);
}
} else {
sprintf(buf, "??? 0x%08x", addr);
}
return buf;
}
#endif /* DEBUG_EEPRO100 */
#if 0
static uint16_t eepro100_read_status(EEPRO100State * s)
{
uint16_t val = s->status;
TRACE(OTHER, logout("val=0x%04x\n", val));
return val;
}
static void eepro100_write_status(EEPRO100State * s, uint16_t val)
{
TRACE(OTHER, logout("val=0x%04x\n", val));
s->status = val;
}
#endif
/*****************************************************************************
*
* Command emulation.
*
****************************************************************************/
#if 0
static uint16_t eepro100_read_command(EEPRO100State * s)
{
uint16_t val = 0xffff;
//~ TRACE(OTHER, logout("val=0x%04x\n", val));
return val;
}
#endif
/* Commands that can be put in a command list entry. */
enum commands {
CmdNOp = 0,
CmdIASetup = 1,
CmdConfigure = 2,
CmdMulticastList = 3,
CmdTx = 4,
CmdTDR = 5, /* load microcode */
CmdDump = 6,
CmdDiagnose = 7,
/* And some extra flags: */
CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */
};
static cu_state_t get_cu_state(EEPRO100State * s)
{
return s->cu_state;
//~ return ((s->mem[SCBStatus] >> 6) & 0x03);
}
static void set_cu_state(EEPRO100State * s, cu_state_t state)
{
s->cu_state = state;
s->mem[SCBStatus] = (s->mem[SCBStatus] & 0x3f) + (state << 6);
}
static ru_state_t get_ru_state(EEPRO100State * s)
{
return s->ru_state;
//~ return ((s->mem[SCBStatus] >> 2) & 0x0f);
}
static void set_ru_state(EEPRO100State * s, ru_state_t state)
{
s->ru_state = state;
s->mem[SCBStatus] = (s->mem[SCBStatus] & 0xc3) + (state << 2);
}
static void dump_statistics(EEPRO100State * s)
{
/* Dump statistical data. Most data is never changed by the emulation
* and always 0, so we first just copy the whole block and then those
* values which really matter.
* Number of data should check configuration!!!
*/
cpu_physical_memory_write(s->statsaddr, (uint8_t *) & s->statistics, 64);
stl_le_phys(s->statsaddr + 0, s->statistics.tx_good_frames);
stl_le_phys(s->statsaddr + 36, s->statistics.rx_good_frames);
stl_le_phys(s->statsaddr + 48, s->statistics.rx_resource_errors);
stl_le_phys(s->statsaddr + 60, s->statistics.rx_short_frame_errors);
//~ stw_le_phys(s->statsaddr + 76, s->statistics.xmt_tco_frames);
//~ stw_le_phys(s->statsaddr + 78, s->statistics.rcv_tco_frames);
//~ missing("CU dump statistical counters");
}
static void read_cb(EEPRO100State *s)
{
cpu_physical_memory_read(s->cb_address, (uint8_t *) &s->tx, sizeof(s->tx));
s->tx.status = le16_to_cpu(s->tx.status);
s->tx.command = le16_to_cpu(s->tx.command);
s->tx.link = le32_to_cpu(s->tx.link);
s->tx.tbd_array_addr = le32_to_cpu(s->tx.tbd_array_addr);
s->tx.tcb_bytes = le16_to_cpu(s->tx.tcb_bytes);
}
static void tx_command(EEPRO100State *s)
{
uint32_t tbd_array = s->tx.tbd_array_addr;
uint16_t tcb_bytes = (s->tx.tcb_bytes & 0x3fff);
uint8_t buf[2600];
uint16_t size = 0;
uint32_t tbd_address = s->cb_address + 0x10;
TRACE(RXTX, logout
("transmit, TBD array address 0x%08x, TCB byte count 0x%04x, TBD count %u\n",
tbd_array, tcb_bytes, s->tx.tbd_count));
assert(!(s->tx.command & COMMAND_NC));
assert(tcb_bytes <= sizeof(buf));
if (!((tcb_bytes > 0) || (tbd_array != 0xffffffff))) {
logout
("illegal values of TBD array address and TCB byte count!\n");
}
for (size = 0; size < tcb_bytes; ) {
uint32_t tx_buffer_address = ldl_le_phys(tbd_address);
uint16_t tx_buffer_size = lduw_le_phys(tbd_address + 4);
//~ uint16_t tx_buffer_el = lduw_le_phys(tbd_address + 6);
tbd_address += 8;
TRACE(RXTX, logout
("TBD (simplified mode): buffer address 0x%08x, size 0x%04x\n",
tx_buffer_address, tx_buffer_size));
assert(size + tx_buffer_size <= sizeof(buf));
cpu_physical_memory_read(tx_buffer_address, &buf[size],
tx_buffer_size);
size += tx_buffer_size;
}
if (!(s->tx.command & COMMAND_SF)) {
/* Simplified mode. Was already handled by code above. */
if (tbd_array != 0xffffffff) {
UNEXPECTED();
}
} else {
/* Flexible mode. */
uint8_t tbd_count = 0;
if (!(s->configuration[6] & BIT(4)) && s->device != i82557C) {
/* Extended TCB (not for 82557). */
assert(tcb_bytes == 0);
for (; tbd_count < 2 && tbd_count < s->tx.tbd_count; tbd_count++) {
uint32_t tx_buffer_address = ldl_le_phys(tbd_address);
uint16_t tx_buffer_size = lduw_le_phys(tbd_address + 4);
uint16_t tx_buffer_el = lduw_le_phys(tbd_address + 6);
tbd_address += 8;
TRACE(RXTX, logout
("TBD (extended mode): buffer address 0x%08x, size 0x%04x\n",
tx_buffer_address, tx_buffer_size));
if (size + tx_buffer_size > sizeof(buf)) {
logout("bad extended TCB with size 0x%04x\n", tx_buffer_size);
} else if (tx_buffer_size > 0) {
assert(tx_buffer_address != 0);
cpu_physical_memory_read(tx_buffer_address, &buf[size],
tx_buffer_size);
size += tx_buffer_size;
}
if (tx_buffer_el & 1) {
break;
}
}
}
tbd_address = tbd_array;
for (; tbd_count < s->tx.tbd_count; tbd_count++) {
uint32_t tx_buffer_address = ldl_le_phys(tbd_address);
uint16_t tx_buffer_size = lduw_le_phys(tbd_address + 4);
uint16_t tx_buffer_el = lduw_le_phys(tbd_address + 6);
tbd_address += 8;
TRACE(RXTX, logout
("TBD (flexible mode): buffer address 0x%08x, size 0x%04x\n",
tx_buffer_address, tx_buffer_size));
if (size + tx_buffer_size > sizeof(buf)) {
logout("bad flexible TCB with size 0x%04x\n", tx_buffer_size);
} else {
cpu_physical_memory_read(tx_buffer_address, &buf[size],
tx_buffer_size);
size += tx_buffer_size;
}
if (tx_buffer_el & 1) {
break;
}
}
}
TRACE(RXTX, logout("%p sending frame, len=%d,%s\n", s, size, nic_dump(buf, size)));
assert(size <= sizeof(buf));
qemu_send_packet(s->vc, buf, size);
s->statistics.tx_good_frames++;
/* Transmit with bad status would raise an CX/TNO interrupt.
* (82557 only). Emulation never has bad status. */
//~ eepro100_cx_interrupt(s);
}
static void set_multicast_list(EEPRO100State *s, uint16_t multicast_count)
{
uint16_t i;
memset(&s->mult[0], 0, sizeof(s->mult));
TRACE(OTHER, logout("multicast list, %u entries\n", multicast_count));
for (i = 0; i < multicast_count; i++) {
uint8_t multicast_addr[6];
cpu_physical_memory_read(s->cb_address + 10 + 6 * i, multicast_addr, 6);
TRACE(OTHER, logout("multicast entry %s\n", nic_dump(multicast_addr, 6)));
unsigned mcast_idx = compute_mcast_idx(multicast_addr);
assert(mcast_idx < 64);
s->mult[mcast_idx >> 3] |= (1 << (mcast_idx & 7));
}
}
static void action_command(EEPRO100State *s)
{
for (;;) {
s->cb_address = s->cu_base + s->cu_offset;
read_cb(s);
bool bit_el = ((s->tx.command & COMMAND_EL) != 0);
bool bit_s = ((s->tx.command & COMMAND_S) != 0);
s->cu_offset = s->tx.link;
TRACE(OTHER, logout
("val=(cu start), status=0x%04x, command=0x%04x, link=0x%08x\n",
s->tx.status, s->tx.command, s->cu_offset));
switch (s->tx.command & COMMAND_CMD) {
case CmdNOp:
/* Do nothing. */
break;
case CmdIASetup:
cpu_physical_memory_read(s->cb_address + 8, &s->macaddr[0], 6);
TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->macaddr[0], 6)));
// !!! missing
break;
case CmdConfigure:
cpu_physical_memory_read(s->cb_address + 8, &s->configuration[0],
sizeof(s->configuration));
TRACE(OTHER, logout("configuration: %s\n", nic_dump(&s->configuration[0], 16)));
break;
case CmdMulticastList:
set_multicast_list(s, s->tx.tbd_array_addr & BITS(13, 0));
break;
case CmdTx:
tx_command(s);
break;
case CmdTDR:
TRACE(OTHER, logout("load microcode\n"));
/* Starting with offset 8, the command contains
* 64 dwords microcode which we just ignore here. */
break;
default:
missing("undefined command");
}
/* Write new status (success). */
stw_le_phys(s->cb_address, s->tx.status | STATUS_C | STATUS_OK);
if (s->tx.command & COMMAND_I) {
/* CU completed action. */
eepro100_cx_interrupt(s);
}
if (bit_el) {
/* CU becomes idle. Terminate command loop. */
set_cu_state(s, cu_idle);
eepro100_cna_interrupt(s);
break;
} else if (bit_s) {
/* CU becomes suspended. Terminate command loop. */
set_cu_state(s, cu_suspended);
eepro100_cna_interrupt(s);
break;
} else {
/* More entries in list. */
TRACE(OTHER, logout("CU list with at least one more entry\n"));
}
}
TRACE(OTHER, logout("CU list empty\n"));
/* List is empty. Now CU is idle or suspended. */
}
static void eepro100_cu_command(EEPRO100State * s, uint8_t val)
{
cu_state_t cu_state;
switch (val) {
case CU_NOP:
/* No operation. */
break;
case CU_START:
cu_state = get_cu_state(s);
if (cu_state != cu_idle && cu_state != cu_suspended) {
/* Intel documentation says that CU must be idle or suspended
* for the CU start command. */
logout("unexpected CU state is %u\n", cu_state);
}
set_cu_state(s, cu_active);
s->cu_offset = s->pointer;
action_command(s);
break;
case CU_RESUME:
if (get_cu_state(s) != cu_suspended) {
logout("bad CU resume from CU state %u\n", get_cu_state(s));
/* Workaround for bad Linux eepro100 driver which resumes
* from idle state. */
//~ missing("cu resume");
set_cu_state(s, cu_suspended);
}
if (get_cu_state(s) == cu_suspended) {
TRACE(OTHER, logout("CU resuming\n"));
set_cu_state(s, cu_active);
action_command(s);
}
break;
case CU_STATSADDR:
/* Load dump counters address. */
s->statsaddr = s->pointer;
TRACE(OTHER, logout("val=0x%02x (status address)\n", val));
break;
case CU_SHOWSTATS:
/* Dump statistical counters. */
dump_statistics(s);
break;
case CU_CMD_BASE:
/* Load CU base. */
TRACE(OTHER, logout("val=0x%02x (CU base address)\n", val));
s->cu_base = s->pointer;
break;
case CU_DUMPSTATS:
/* Dump and reset statistical counters. */
dump_statistics(s);
memset(&s->statistics, 0, sizeof(s->statistics));
break;
case CU_SRESUME:
/* CU static resume. */
missing("CU static resume");
break;
default:
missing("Undefined CU command");
}
}
static void eepro100_ru_command(EEPRO100State * s, uint8_t val)
{
switch (val) {
case RU_NOP:
/* No operation. */
break;
case RX_START:
/* RU start. */
if (get_ru_state(s) != ru_idle) {
logout("RU state is %u, should be %u\n", get_ru_state(s), ru_idle);
//~ assert(!"wrong RU state");
}
set_ru_state(s, ru_ready);
s->ru_offset = s->pointer;
TRACE(OTHER, logout("val=0x%02x (rx start)\n", val));
break;
case RX_RESUME:
/* Restart RU. */
if (get_ru_state(s) != ru_suspended) {
logout("RU state is %u, should be %u\n", get_ru_state(s),
ru_suspended);
//~ assert(!"wrong RU state");
}
set_ru_state(s, ru_ready);
break;
case RX_ADDR_LOAD:
/* Load RU base. */
TRACE(OTHER, logout("val=0x%02x (RU base address)\n", val));
s->ru_base = s->pointer;
break;
default:
logout("val=0x%02x (undefined RU command)\n", val);
missing("Undefined SU command");
}
}
static void eepro100_write_command(EEPRO100State * s, uint8_t val)
{
eepro100_ru_command(s, val & 0x0f);
eepro100_cu_command(s, val & 0xf0);
if ((val) == 0) {
TRACE(OTHER, logout("val=0x%02x\n", val));
}
/* Clear command byte after command was accepted. */
s->mem[SCBCmd] = 0;
}
/*****************************************************************************
*
* EEPROM emulation.
*
****************************************************************************/
#define EEPROM_CS 0x02
#define EEPROM_SK 0x01
#define EEPROM_DI 0x04
#define EEPROM_DO 0x08
static uint16_t eepro100_read_eeprom(EEPRO100State * s)
{
uint16_t val;
memcpy(&val, &s->mem[SCBeeprom], sizeof(val));
val = le16_to_cpu(val);
if (eeprom93xx_read(s->eeprom)) {
val |= EEPROM_DO;
} else {
val &= ~EEPROM_DO;
}
val = cpu_to_le16(val);
TRACE(OTHER, logout("val=0x%04x\n", val));
return val;
}
static void eepro100_write_eeprom(eeprom_t * eeprom, uint8_t val)
{
TRACE(OTHER, logout("val=0x%02x\n", val));
/* mask unwriteable bits */
//~ val = SET_MASKED(val, 0x31, eeprom->value);
int eecs = ((val & EEPROM_CS) != 0);
int eesk = ((val & EEPROM_SK) != 0);
int eedi = ((val & EEPROM_DI) != 0);
eeprom93xx_write(eeprom, eecs, eesk, eedi);
}
static void eepro100_write_pointer(EEPRO100State * s, uint32_t val)
{
s->pointer = val;
TRACE(OTHER, logout("val=0x%08x\n", val));
}
/*****************************************************************************
*
* MDI emulation.
*
****************************************************************************/
#if defined(DEBUG_EEPRO100)
static const char *mdi_op_name[] = {
"opcode 0",
"write",
"read",
"opcode 3"
};
static const char *mdi_reg_name[] = {
"Control",
"Status",
"PHY Identification (Word 1)",
"PHY Identification (Word 2)",
"Auto-Negotiation Advertisement",
"Auto-Negotiation Link Partner Ability",
"Auto-Negotiation Expansion"
};
static const char *reg2name(uint8_t reg)
{
static char buffer[10];
const char *p = buffer;
if (reg < sizeof(mdi_reg_name) / sizeof(*mdi_reg_name)) {
p = mdi_reg_name[reg];
} else {
sprintf(buffer, "reg=0x%02x", reg);
}
return p;
}
#endif /* DEBUG_EEPRO100 */
static uint32_t eepro100_read_mdi(EEPRO100State * s)
{
uint32_t val;
memcpy(&val, &s->mem[0x10], sizeof(val));
val = le32_to_cpu(val);
#ifdef DEBUG_EEPRO100
uint8_t raiseint = (val & BIT(29)) >> 29;
uint8_t opcode = (val & BITS(27, 26)) >> 26;
uint8_t phy = (val & BITS(25, 21)) >> 21;
uint8_t reg = (val & BITS(20, 16)) >> 16;
uint16_t data = (val & BITS(15, 0));
#endif
/* Emulation takes no time to finish MDI transaction. */
val |= BIT(28);
TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
val, raiseint, mdi_op_name[opcode], phy,
reg2name(reg), data));
return val;
}
//~ #define BITS(val, upper, lower) (val & ???)
static void eepro100_write_mdi(EEPRO100State * s, uint32_t val)
{
uint8_t raiseint = (val & BIT(29)) >> 29;
uint8_t opcode = (val & BITS(27, 26)) >> 26;
uint8_t phy = (val & BITS(25, 21)) >> 21;
uint8_t reg = (val & BITS(20, 16)) >> 16;
uint16_t data = (val & BITS(15, 0));
TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
val, raiseint, mdi_op_name[opcode], phy,
reg2name(reg), data));
if (phy != 1) {
/* Unsupported PHY address. */
//~ logout("phy must be 1 but is %u\n", phy);
data = 0;
} else if (opcode != 1 && opcode != 2) {
/* Unsupported opcode. */
logout("opcode must be 1 or 2 but is %u\n", opcode);
data = 0;
} else if (reg > 6) {
/* Unsupported register. */
logout("register must be 0...6 but is %u\n", reg);
data = 0;
} else {
TRACE(MDI, logout("val=0x%08x (int=%u, %s, phy=%u, %s, data=0x%04x\n",
val, raiseint, mdi_op_name[opcode], phy,
reg2name(reg), data));
if (opcode == 1) {
/* MDI write */
switch (reg) {
case 0: /* Control Register */
if (data & 0x8000) {
/* Reset status and control registers to default. */
s->mdimem[0] = eepro100_mdi_default[0];
s->mdimem[1] = eepro100_mdi_default[1];
data = s->mdimem[reg];
} else {
/* Restart Auto Configuration = Normal Operation */
data &= ~0x0200;
}
break;
case 1: /* Status Register */
missing("not writable");
data = s->mdimem[reg];
break;
case 2: /* PHY Identification Register (Word 1) */
case 3: /* PHY Identification Register (Word 2) */
missing("not implemented");
break;
case 4: /* Auto-Negotiation Advertisement Register */
case 5: /* Auto-Negotiation Link Partner Ability Register */
break;
case 6: /* Auto-Negotiation Expansion Register */
default:
missing("not implemented");
}
s->mdimem[reg] = data;
} else if (opcode == 2) {
/* MDI read */
switch (reg) {
case 0: /* Control Register */
if (data & 0x8000) {
/* Reset status and control registers to default. */
s->mdimem[0] = eepro100_mdi_default[0];
s->mdimem[1] = eepro100_mdi_default[1];
}
break;
case 1: /* Status Register */
s->mdimem[reg] |= 0x0020;
break;
case 2: /* PHY Identification Register (Word 1) */
case 3: /* PHY Identification Register (Word 2) */
case 4: /* Auto-Negotiation Advertisement Register */
break;
case 5: /* Auto-Negotiation Link Partner Ability Register */
s->mdimem[reg] = 0x41fe;
break;
case 6: /* Auto-Negotiation Expansion Register */
s->mdimem[reg] = 0x0001;
break;
}
data = s->mdimem[reg];
}
/* Emulation takes no time to finish MDI transaction.
* Set MDI bit in SCB status register. */
s->mem[SCBAck] |= 0x08;
val |= BIT(28);
if (raiseint) {
eepro100_mdi_interrupt(s);
}
}
val = (val & 0xffff0000) + data;
val = cpu_to_le32(val);
memcpy(&s->mem[0x10], &val, sizeof(val));
}
/*****************************************************************************
*
* Port emulation.
*
****************************************************************************/
#define PORT_SOFTWARE_RESET 0
#define PORT_SELFTEST 1
#define PORT_SELECTIVE_RESET 2
#define PORT_DUMP 3
#define PORT_SELECTION_MASK 3
typedef struct {
uint32_t st_sign; /* Self Test Signature */
uint32_t st_result; /* Self Test Results */
} eepro100_selftest_t;
static uint32_t eepro100_read_port(EEPRO100State * s)
{
return 0;
}
static void eepro100_write_port(EEPRO100State * s, uint32_t val)
{
uint32_t address = (val & ~PORT_SELECTION_MASK);
uint8_t selection = (val & PORT_SELECTION_MASK);
switch (selection) {
case PORT_SOFTWARE_RESET:
nic_reset(s);
break;
case PORT_SELFTEST:
TRACE(OTHER, logout("selftest address=0x%08x\n", address));
eepro100_selftest_t data;
cpu_physical_memory_read(address, (uint8_t *) & data, sizeof(data));
data.st_sign = cpu_to_le32(0xffffffff);
data.st_result = cpu_to_le32(0);
cpu_physical_memory_write(address, (uint8_t *) & data, sizeof(data));
break;
case PORT_SELECTIVE_RESET:
TRACE(OTHER, logout("selective reset, selftest address=0x%08x\n", address));
nic_selective_reset(s);
break;
default:
logout("val=0x%08x\n", val);
missing("unknown port selection");
}
}
/*****************************************************************************
*
* General hardware emulation.
*
****************************************************************************/
static uint8_t eepro100_read1(EEPRO100State * s, uint32_t addr)
{
uint8_t val;
if (addr <= sizeof(s->mem) - sizeof(val)) {
memcpy(&val, &s->mem[addr], sizeof(val));
}
switch (addr) {
case SCBStatus:
//~ val = eepro100_read_status(s);
TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
break;
case SCBAck:
//~ val = eepro100_read_status(s);
TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
break;
case SCBCmd:
TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
//~ val = eepro100_read_command(s);
break;
case SCBIntmask:
TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
break;
case SCBPort + 3:
TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
break;
case SCBeeprom:
val = le16_to_cpu(eepro100_read_eeprom(s));
break;
case SCBpmdr: /* Power Management Driver Register */
val = 0;
TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
break;
case SCBgstat: /* General Status Register */
/* 100 Mbps full duplex, valid link */
val = 0x07;
TRACE(OTHER, logout("addr=General Status val=%02x\n", val));
break;
default:
logout("addr=%s val=0x%02x\n", regname(addr), val);
missing("unknown byte read");
}
return val;
}
static uint16_t eepro100_read2(EEPRO100State * s, uint32_t addr)
{
uint16_t val;
if (addr <= sizeof(s->mem) - sizeof(val)) {
memcpy(&val, &s->mem[addr], sizeof(val));
}
val = le16_to_cpu(val);
TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
switch (addr) {
case SCBStatus:
//~ val = eepro100_read_status(s);
break;
case SCBeeprom:
val = eepro100_read_eeprom(s);
break;
default:
logout("addr=%s val=0x%04x\n", regname(addr), val);
missing("unknown word read");
}
val = cpu_to_le16(val);
#if defined(TARGET_WORDS_BIGENDIAN)
val = bswap16(val);
#endif
return val;
}
static uint32_t eepro100_read4(EEPRO100State * s, uint32_t addr)
{
uint32_t val = 0;
if (addr <= sizeof(s->mem) - sizeof(val)) {
memcpy(&val, &s->mem[addr], sizeof(val));
}
val = le32_to_cpu(val);
switch (addr) {
case SCBStatus:
//~ val = eepro100_read_status(s);
TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
break;
case SCBPointer:
//~ val = eepro100_read_pointer(s);
TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
break;
case SCBPort:
val = eepro100_read_port(s);
TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
break;
case SCBCtrlMDI:
val = le32_to_cpu(eepro100_read_mdi(s));
break;
default:
logout("addr=%s val=0x%08x\n", regname(addr), val);
missing("unknown longword read");
}
val = cpu_to_le32(val);
#if defined(TARGET_WORDS_BIGENDIAN)
val = bswap32(val);
#endif
return val;
}
static void eepro100_write1(EEPRO100State * s, uint32_t addr, uint8_t val)
{
if (addr <= sizeof(s->mem) - sizeof(val)) {
memcpy(&s->mem[addr], &val, sizeof(val));
}
TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
switch (addr) {
case SCBStatus:
//~ eepro100_write_status(s, val);
break;
case SCBAck:
eepro100_acknowledge(s);
break;
case SCBCmd:
eepro100_write_command(s, val);
break;
case SCBIntmask:
if (val & BIT(1)) {
eepro100_swi_interrupt(s);
}
eepro100_interrupt(s, 0);
break;
case SCBPort + 3:
case SCBFlow: /* does not exist on 82557 */
case SCBFlow + 1:
case SCBFlow + 2:
case SCBpmdr: /* does not exist on 82557 */
TRACE(OTHER, logout("addr=%s val=0x%02x\n", regname(addr), val));
break;
case SCBeeprom:
eepro100_write_eeprom(s->eeprom, val);
break;
default:
logout("addr=%s val=0x%02x\n", regname(addr), val);
missing("unknown byte write");
}
}
static void eepro100_write2(EEPRO100State * s, uint32_t addr, uint16_t val)
{
#if defined(TARGET_WORDS_BIGENDIAN)
val = bswap16(val);
#endif
if (addr <= sizeof(s->mem) - sizeof(val)) {
memcpy(&s->mem[addr], &val, sizeof(val));
}
val = le16_to_cpu(val);
TRACE(OTHER, logout("addr=%s val=0x%04x\n", regname(addr), val));
switch (addr) {
case SCBStatus:
//~ eepro100_write_status(s, val);
eepro100_acknowledge(s);
break;
case SCBCmd:
eepro100_write_command(s, val);
eepro100_write1(s, SCBIntmask, val >> 8);
break;
case SCBeeprom:
eepro100_write_eeprom(s->eeprom, val);
break;
default:
logout("addr=%s val=0x%04x\n", regname(addr), val);
missing("unknown word write");
}
}
static void eepro100_write4(EEPRO100State * s, uint32_t addr, uint32_t val)
{
#if defined(TARGET_WORDS_BIGENDIAN)
val = bswap32(val);
#endif
if (addr <= sizeof(s->mem) - sizeof(val)) {
memcpy(&s->mem[addr], &val, sizeof(val));
}
//~ val = le32_to_cpu(val);
switch (addr) {
case SCBPointer:
eepro100_write_pointer(s, val);
break;
case SCBPort:
TRACE(OTHER, logout("addr=%s val=0x%08x\n", regname(addr), val));
eepro100_write_port(s, val);
break;
case SCBCtrlMDI:
eepro100_write_mdi(s, val);
break;
default:
logout("addr=%s val=0x%08x\n", regname(addr), val);
missing("unknown longword write");
}
}
/*****************************************************************************
*
* Port mapped I/O.
*
****************************************************************************/
static uint32_t ioport_read1(void *opaque, uint32_t addr)
{
EEPRO100State *s = opaque;
//~ logout("addr=%s\n", regname(addr));
return eepro100_read1(s, addr - s->region[1]);
}
static uint32_t ioport_read2(void *opaque, uint32_t addr)
{
EEPRO100State *s = opaque;
return eepro100_read2(s, addr - s->region[1]);
}
static uint32_t ioport_read4(void *opaque, uint32_t addr)
{
EEPRO100State *s = opaque;
return eepro100_read4(s, addr - s->region[1]);
}
static void ioport_write1(void *opaque, uint32_t addr, uint32_t val)
{
EEPRO100State *s = opaque;
//~ logout("addr=%s val=0x%02x\n", regname(addr), val);
eepro100_write1(s, addr - s->region[1], val);
}
static void ioport_write2(void *opaque, uint32_t addr, uint32_t val)
{
EEPRO100State *s = opaque;
eepro100_write2(s, addr - s->region[1], val);
}
static void ioport_write4(void *opaque, uint32_t addr, uint32_t val)
{
EEPRO100State *s = opaque;
eepro100_write4(s, addr - s->region[1], val);
}
/***********************************************************/
/* PCI EEPRO100 definitions */
typedef struct {
PCIDevice dev;
EEPRO100State eepro100;
} PCIEEPRO100State;
static void pci_map(PCIDevice * pci_dev, int region_num,
uint32_t addr, uint32_t size, int type)
{
PCIEEPRO100State *d = (PCIEEPRO100State *) pci_dev;
EEPRO100State *s = &d->eepro100;
TRACE(OTHER, logout("region %d, addr=0x%08x, size=0x%08x, type=%d\n",
region_num, addr, size, type));
assert(region_num == 1);
register_ioport_write(addr, size, 1, ioport_write1, s);
register_ioport_read(addr, size, 1, ioport_read1, s);
register_ioport_write(addr, size, 2, ioport_write2, s);
register_ioport_read(addr, size, 2, ioport_read2, s);
register_ioport_write(addr, size, 4, ioport_write4, s);
register_ioport_read(addr, size, 4, ioport_read4, s);
s->region[region_num] = addr;
}
/*****************************************************************************
*
* Memory mapped I/O.
*
****************************************************************************/
static void pci_mmio_writeb(void *opaque, target_phys_addr_t addr, uint32_t val)
{
EEPRO100State *s = opaque;
addr -= s->region[0];
//~ logout("addr=%s val=0x%02x\n", regname(addr), val);
eepro100_write1(s, addr, val);
}
static void pci_mmio_writew(void *opaque, target_phys_addr_t addr, uint32_t val)
{
EEPRO100State *s = opaque;
addr -= s->region[0];
//~ logout("addr=%s val=0x%02x\n", regname(addr), val);
eepro100_write2(s, addr, val);
}
static void pci_mmio_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
{
EEPRO100State *s = opaque;
addr -= s->region[0];
//~ logout("addr=%s val=0x%02x\n", regname(addr), val);
eepro100_write4(s, addr, val);
}
static uint32_t pci_mmio_readb(void *opaque, target_phys_addr_t addr)
{
EEPRO100State *s = opaque;
addr -= s->region[0];
//~ logout("addr=%s\n", regname(addr));
return eepro100_read1(s, addr);
}
static uint32_t pci_mmio_readw(void *opaque, target_phys_addr_t addr)
{
EEPRO100State *s = opaque;
addr -= s->region[0];
//~ logout("addr=%s\n", regname(addr));
return eepro100_read2(s, addr);
}
static uint32_t pci_mmio_readl(void *opaque, target_phys_addr_t addr)
{
EEPRO100State *s = opaque;
addr -= s->region[0];
//~ logout("addr=%s\n", regname(addr));
return eepro100_read4(s, addr);
}
static CPUWriteMemoryFunc *pci_mmio_write[] = {
pci_mmio_writeb,
pci_mmio_writew,
pci_mmio_writel
};
static CPUReadMemoryFunc *pci_mmio_read[] = {
pci_mmio_readb,
pci_mmio_readw,
pci_mmio_readl
};
static void pci_mmio_map(PCIDevice * pci_dev, int region_num,
uint32_t addr, uint32_t size, int type)
{
PCIEEPRO100State *d = (PCIEEPRO100State *) pci_dev;
TRACE(OTHER, logout("region %d, addr=0x%08x, size=0x%08x, type=%d\n",
region_num, addr, size, type));
if (region_num == 0) {
/* Map control / status registers. */
cpu_register_physical_memory(addr, size, d->eepro100.mmio_index);
d->eepro100.region[region_num] = addr;
}
}
static int nic_can_receive(void *opaque)
{
EEPRO100State *s = opaque;
TRACE(RXTX, logout("%p\n", s));
return get_ru_state(s) == ru_ready;
//~ return !eepro100_buffer_full(s);
}
#define MIN_BUF_SIZE 60
static void nic_receive(void *opaque, const uint8_t * buf, int size)
{
/* TODO:
* - Magic packets should set bit 30 in power management driver register.
* - Interesting packets should set bit 29 in power management driver register.
*/
EEPRO100State *s = opaque;
uint16_t rfd_status = 0xa000;
static const uint8_t broadcast_macaddr[6] =
{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
/* TODO: check multiple IA bit. */
assert(!(s->configuration[20] & BIT(6)));
if (s->configuration[8] & 0x80) {
/* CSMA is disabled. */
logout("%p received while CSMA is disabled\n", s);
return;
} else if (size < 64 && (s->configuration[7] & 1)) {
/* Short frame and configuration byte 7/0 (discard short receive) set:
* Short frame is discarded */
logout("%p received short frame (%d byte)\n", s, size);
s->statistics.rx_short_frame_errors++;
//~ return;
} else if ((size > MAX_ETH_FRAME_SIZE + 4) && !(s->configuration[18] & 8)) {
/* Long frame and configuration byte 18/3 (long receive ok) not set:
* Long frames are discarded. */
logout("%p received long frame (%d byte), ignored\n", s, size);
return;
} else if (memcmp(buf, s->macaddr, 6) == 0) { // !!!
/* Frame matches individual address. */
/* TODO: check configuration byte 15/4 (ignore U/L). */
TRACE(RXTX, logout("%p received frame for me, len=%d\n", s, size));
} else if (memcmp(buf, broadcast_macaddr, 6) == 0) {
/* Broadcast frame. */
TRACE(RXTX, logout("%p received broadcast, len=%d\n", s, size));
rfd_status |= 0x0002;
} else if (buf[0] & 0x01) { // !!!
/* Multicast frame. */
TRACE(RXTX, logout("%p received multicast, len=%d,%s\n", s, size, nic_dump(buf, size)));
if (s->configuration[21] & BIT(3)) {
/* Multicast all bit is set, receive all frames. */
} else {
unsigned mcast_idx = compute_mcast_idx(buf);
assert(mcast_idx < 64);
if (!(s->mult[mcast_idx >> 3] & (1 << (mcast_idx & 7)))) {
TRACE(RXTX, logout("%p multicast ignored\n", s));
return;
}
}
rfd_status |= 0x0002;
} else if (s->configuration[15] & 1) {
/* Promiscuous: receive all. */
TRACE(RXTX, logout("%p received frame in promiscuous mode, len=%d\n", s, size));
rfd_status |= 0x0004;
} else {
TRACE(RXTX, logout("%p received frame, ignored, len=%d,%s\n", s, size,
nic_dump(buf, size)));
return;
}
if (get_ru_state(s) != ru_ready) {
/* No resources available. */
logout("no resources, state=%u\n", get_ru_state(s));
s->statistics.rx_resource_errors++;
//~ assert(!"no resources");
return;
}
//~ !!!
//~ $3 = {status = 0x0, command = 0xc000, link = 0x2d220, rx_buf_addr = 0x207dc, count = 0x0, size = 0x5f8, packet = {0x0 <repeats 1518 times>}}
eepro100_rx_t rx;
cpu_physical_memory_read(s->ru_base + s->ru_offset, (uint8_t *) & rx,
offsetof(eepro100_rx_t, packet));
// !!!
uint16_t rfd_command = le16_to_cpu(rx.command);
uint16_t rfd_size = le16_to_cpu(rx.size);
assert(size <= rfd_size);
if (size < 64) {
rfd_status |= 0x0080;
}
TRACE(OTHER, logout("command 0x%04x, link 0x%08x, addr 0x%08x, size %u\n",
rfd_command, rx.link, rx.rx_buf_addr, rfd_size));
stw_le_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, status),
rfd_status);
stw_le_phys(s->ru_base + s->ru_offset + offsetof(eepro100_rx_t, count), size);
/* Early receive interrupt not supported. */
//~ eepro100_er_interrupt(s);
/* Receive CRC Transfer not supported. */
assert(!(s->configuration[18] & 4));
/* TODO: check stripping enable bit. */
//~ assert(!(s->configuration[17] & 1));
cpu_physical_memory_write(s->ru_base + s->ru_offset +
offsetof(eepro100_rx_t, packet), buf, size);
s->statistics.rx_good_frames++;
eepro100_fr_interrupt(s);
s->ru_offset = le32_to_cpu(rx.link);
if (rfd_command & 0x8000) {
/* EL bit is set, so this was the last frame. */
assert(0);
}
if (rfd_command & 0x4000) {
/* S bit is set. */
set_ru_state(s, ru_suspended);
}
}
static int nic_load(QEMUFile * f, void *opaque, int version_id)
{
EEPRO100State *s = (EEPRO100State *) opaque;
int i;
int ret;
if (version_id > 3)
return -EINVAL;
if (s->pci_dev && version_id >= 3) {
ret = pci_device_load(s->pci_dev, f);
if (ret < 0)
return ret;
}
if (version_id >= 2) {
qemu_get_8s(f, &s->rxcr);
} else {
s->rxcr = 0x0c;
}
qemu_get_8s(f, &s->cmd);
qemu_get_be32s(f, &s->start);
qemu_get_be32s(f, &s->stop);
qemu_get_8s(f, &s->boundary);
qemu_get_8s(f, &s->tsr);
qemu_get_8s(f, &s->tpsr);
qemu_get_be16s(f, &s->tcnt);
qemu_get_be16s(f, &s->rcnt);
qemu_get_be32s(f, &s->rsar);
qemu_get_8s(f, &s->rsr);
qemu_get_8s(f, &s->isr);
qemu_get_8s(f, &s->dcfg);
qemu_get_8s(f, &s->imr);
qemu_get_buffer(f, s->phys, 6);
qemu_get_8s(f, &s->curpag);
qemu_get_buffer(f, s->mult, 8);
qemu_get_buffer(f, s->mem, sizeof(s->mem));
/* Restore all members of struct between scv_stat and mem */
qemu_get_8s(f, &s->scb_stat);
qemu_get_8s(f, &s->int_stat);
for (i = 0; i < 3; i++)
qemu_get_be32s(f, &s->region[i]);
qemu_get_buffer(f, s->macaddr, 6);
for (i = 0; i < 19; i++)
qemu_get_be32s(f, &s->statcounter[i]);
for (i = 0; i < 32; i++)
qemu_get_be16s(f, &s->mdimem[i]);
/* The eeprom should be saved and restored by its own routines */
qemu_get_be32s(f, &s->device);
qemu_get_be32s(f, &s->pointer);
qemu_get_be32s(f, &s->cu_base);
qemu_get_be32s(f, &s->cu_offset);
qemu_get_be32s(f, &s->ru_base);
qemu_get_be32s(f, &s->ru_offset);
qemu_get_be32s(f, &s->statsaddr);
/* Restore epro100_stats_t statistics */
qemu_get_be32s(f, &s->statistics.tx_good_frames);
qemu_get_be32s(f, &s->statistics.tx_max_collisions);
qemu_get_be32s(f, &s->statistics.tx_late_collisions);
qemu_get_be32s(f, &s->statistics.tx_underruns);
qemu_get_be32s(f, &s->statistics.tx_lost_crs);
qemu_get_be32s(f, &s->statistics.tx_deferred);
qemu_get_be32s(f, &s->statistics.tx_single_collisions);
qemu_get_be32s(f, &s->statistics.tx_multiple_collisions);
qemu_get_be32s(f, &s->statistics.tx_total_collisions);
qemu_get_be32s(f, &s->statistics.rx_good_frames);
qemu_get_be32s(f, &s->statistics.rx_crc_errors);
qemu_get_be32s(f, &s->statistics.rx_alignment_errors);
qemu_get_be32s(f, &s->statistics.rx_resource_errors);
qemu_get_be32s(f, &s->statistics.rx_overrun_errors);
qemu_get_be32s(f, &s->statistics.rx_cdt_errors);
qemu_get_be32s(f, &s->statistics.rx_short_frame_errors);
qemu_get_be32s(f, &s->statistics.fc_xmt_pause);
qemu_get_be32s(f, &s->statistics.fc_rcv_pause);
qemu_get_be32s(f, &s->statistics.fc_rcv_unsupported);
qemu_get_be16s(f, &s->statistics.xmt_tco_frames);
qemu_get_be16s(f, &s->statistics.rcv_tco_frames);
qemu_get_be32s(f, &s->statistics.complete);
#if 0
qemu_get_be16s(f, &s->status);
#endif
/* Configuration bytes. */
qemu_get_buffer(f, s->configuration, sizeof(s->configuration));
return 0;
}
static void nic_save(QEMUFile * f, void *opaque)
{
EEPRO100State *s = (EEPRO100State *) opaque;
int i;
if (s->pci_dev)
pci_device_save(s->pci_dev, f);
qemu_put_8s(f, &s->rxcr);
qemu_put_8s(f, &s->cmd);
qemu_put_be32s(f, &s->start);
qemu_put_be32s(f, &s->stop);
qemu_put_8s(f, &s->boundary);
qemu_put_8s(f, &s->tsr);
qemu_put_8s(f, &s->tpsr);
qemu_put_be16s(f, &s->tcnt);
qemu_put_be16s(f, &s->rcnt);
qemu_put_be32s(f, &s->rsar);
qemu_put_8s(f, &s->rsr);
qemu_put_8s(f, &s->isr);
qemu_put_8s(f, &s->dcfg);
qemu_put_8s(f, &s->imr);
qemu_put_buffer(f, s->phys, 6);
qemu_put_8s(f, &s->curpag);
qemu_put_buffer(f, s->mult, 8);
qemu_put_buffer(f, s->mem, sizeof(s->mem));
/* Save all members of struct between scv_stat and mem */
qemu_put_8s(f, &s->scb_stat);
qemu_put_8s(f, &s->int_stat);
for (i = 0; i < 3; i++)
qemu_put_be32s(f, &s->region[i]);
qemu_put_buffer(f, s->macaddr, 6);
for (i = 0; i < 19; i++)
qemu_put_be32s(f, &s->statcounter[i]);
for (i = 0; i < 32; i++)
qemu_put_be16s(f, &s->mdimem[i]);
/* The eeprom should be saved and restored by its own routines */
qemu_put_be32s(f, &s->device);
qemu_put_be32s(f, &s->pointer);
qemu_put_be32s(f, &s->cu_base);
qemu_put_be32s(f, &s->cu_offset);
qemu_put_be32s(f, &s->ru_base);
qemu_put_be32s(f, &s->ru_offset);
qemu_put_be32s(f, &s->statsaddr);
/* Save epro100_stats_t statistics */
qemu_put_be32s(f, &s->statistics.tx_good_frames);
qemu_put_be32s(f, &s->statistics.tx_max_collisions);
qemu_put_be32s(f, &s->statistics.tx_late_collisions);
qemu_put_be32s(f, &s->statistics.tx_underruns);
qemu_put_be32s(f, &s->statistics.tx_lost_crs);
qemu_put_be32s(f, &s->statistics.tx_deferred);
qemu_put_be32s(f, &s->statistics.tx_single_collisions);
qemu_put_be32s(f, &s->statistics.tx_multiple_collisions);
qemu_put_be32s(f, &s->statistics.tx_total_collisions);
qemu_put_be32s(f, &s->statistics.rx_good_frames);
qemu_put_be32s(f, &s->statistics.rx_crc_errors);
qemu_put_be32s(f, &s->statistics.rx_alignment_errors);
qemu_put_be32s(f, &s->statistics.rx_resource_errors);
qemu_put_be32s(f, &s->statistics.rx_overrun_errors);
qemu_put_be32s(f, &s->statistics.rx_cdt_errors);
qemu_put_be32s(f, &s->statistics.rx_short_frame_errors);
qemu_put_be32s(f, &s->statistics.fc_xmt_pause);
qemu_put_be32s(f, &s->statistics.fc_rcv_pause);
qemu_put_be32s(f, &s->statistics.fc_rcv_unsupported);
qemu_put_be16s(f, &s->statistics.xmt_tco_frames);
qemu_put_be16s(f, &s->statistics.rcv_tco_frames);
qemu_put_be32s(f, &s->statistics.complete);
#if 0
qemu_put_be16s(f, &s->status);
#endif
/* Configuration bytes. */
qemu_put_buffer(f, s->configuration, sizeof(s->configuration));
}
static void nic_init(PCIBus * bus, NICInfo * nd,
const char *name, uint32_t device)
{
PCIEEPRO100State *d;
EEPRO100State *s;
TRACE(OTHER, logout("\n"));
d = (PCIEEPRO100State *) pci_register_device(bus, name,
sizeof(PCIEEPRO100State), -1,
NULL, NULL);
s = &d->eepro100;
s->device = device;
s->pci_dev = &d->dev;
pci_reset(s);
/* Add 64 * 2 EEPROM. i82557 and i82558 support a 64 word EEPROM,
* i82559 and later support 64 or 256 word EEPROM. */
s->eeprom = eeprom93xx_new(EEPROM_SIZE);
/* Handler for memory-mapped I/O */
d->eepro100.mmio_index =
cpu_register_io_memory(0, pci_mmio_read, pci_mmio_write, s);
pci_register_io_region(&d->dev, 0, PCI_MEM_SIZE,
PCI_ADDRESS_SPACE_MEM |
PCI_ADDRESS_SPACE_MEM_PREFETCH, pci_mmio_map);
pci_register_io_region(&d->dev, 1, PCI_IO_SIZE, PCI_ADDRESS_SPACE_IO,
pci_map);
pci_register_io_region(&d->dev, 2, PCI_FLASH_SIZE, PCI_ADDRESS_SPACE_MEM,
pci_mmio_map);
memcpy(s->macaddr, nd->macaddr, 6);
TRACE(OTHER, logout("macaddr: %s\n", nic_dump(&s->macaddr[0], 6)));
assert(s->region[1] == 0);
nic_reset(s);
s->vc = qemu_new_vlan_client(nd->vlan, nic_receive, nic_can_receive, s);
snprintf(s->vc->info_str, sizeof(s->vc->info_str),
"eepro100 pci macaddr=%02x:%02x:%02x:%02x:%02x:%02x",
s->macaddr[0], s->macaddr[1], s->macaddr[2],
s->macaddr[3], s->macaddr[4], s->macaddr[5]);
qemu_register_reset(nic_reset, s);
/* XXX: instance number ? */
register_savevm(name, 0, 3, nic_save, nic_load, s);
}
void pci_i82551_init(PCIBus * bus, NICInfo * nd, int devfn)
{
nic_init(bus, nd, "i82551", i82551);
//~ uint8_t *pci_conf = d->dev.config;
}
void pci_i82557b_init(PCIBus * bus, NICInfo * nd, int devfn)
{
nic_init(bus, nd, "i82557b", i82557B);
}
void pci_i82559er_init(PCIBus * bus, NICInfo * nd, int devfn)
{
nic_init(bus, nd, "i82559er", i82559ER);
}
/* eof */
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2007-11-22 21:16 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-11-22 9:41 [Qemu-devel] [PATCH][RFC] E100 emulator Zhang, Xing Z
2007-11-22 21:16 ` Stefan Weil
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).