1 |
dpavlin |
7 |
/* |
2 |
|
|
* Cisco router simulation platform. |
3 |
|
|
* Copyright (C) 2007 Christophe Fillot. All rights reserved. |
4 |
|
|
* |
5 |
dpavlin |
8 |
* Intel i8254x (Wiseman/Livengood) Ethernet chip emulation. |
6 |
dpavlin |
7 |
*/ |
7 |
|
|
|
8 |
|
|
#include <stdio.h> |
9 |
|
|
#include <stdlib.h> |
10 |
|
|
#include <string.h> |
11 |
|
|
#include <stdarg.h> |
12 |
|
|
#include <unistd.h> |
13 |
|
|
#include <time.h> |
14 |
|
|
#include <errno.h> |
15 |
|
|
#include <assert.h> |
16 |
|
|
|
17 |
|
|
#include "utils.h" |
18 |
|
|
#include "cpu.h" |
19 |
|
|
#include "vm.h" |
20 |
|
|
#include "dynamips.h" |
21 |
|
|
#include "memory.h" |
22 |
|
|
#include "device.h" |
23 |
|
|
#include "net.h" |
24 |
|
|
#include "net_io.h" |
25 |
|
|
#include "ptask.h" |
26 |
|
|
#include "dev_i8254x.h" |
27 |
|
|
|
28 |
|
|
/* Debugging flags */ |
29 |
|
|
#define DEBUG_MII_REGS 0 |
30 |
dpavlin |
8 |
#define DEBUG_ACCESS 0 |
31 |
dpavlin |
7 |
#define DEBUG_TRANSMIT 0 |
32 |
|
|
#define DEBUG_RECEIVE 0 |
33 |
dpavlin |
8 |
#define DEBUG_UNKNOWN 0 |
34 |
dpavlin |
7 |
|
35 |
|
|
/* Intel i8254x PCI vendor/product codes */ |
36 |
|
|
#define I8254X_PCI_VENDOR_ID 0x8086 |
37 |
|
|
#define I8254X_PCI_PRODUCT_ID 0x1001 |
38 |
|
|
|
39 |
|
|
/* Maximum packet size */ |
40 |
|
|
#define I8254X_MAX_PKT_SIZE 16384 |
41 |
|
|
|
42 |
|
|
/* Register list */ |
43 |
|
|
#define I8254X_REG_CTRL 0x0000 /* Control Register */ |
44 |
|
|
#define I8254X_REG_STATUS 0x0008 /* Device Status Register */ |
45 |
|
|
#define I8254X_REG_CTRLEXT 0x0018 /* Extended Control Register */ |
46 |
|
|
#define I8254X_REG_MDIC 0x0020 /* MDI Control Register */ |
47 |
|
|
#define I8254X_REG_FCAL 0x0028 /* Flow Control Address Low */ |
48 |
|
|
#define I8254X_REG_FCAH 0x002c /* Flow Control Address High */ |
49 |
|
|
#define I8254X_REG_FCT 0x0030 /* Flow Control Type */ |
50 |
|
|
#define I8254X_REG_VET 0x0038 /* VLAN Ether Type */ |
51 |
|
|
#define I8254X_REG_ICR 0x00c0 /* Interrupt Cause Read */ |
52 |
|
|
#define I8254X_REG_ITR 0x00c4 /* Interrupt Throttling Register */ |
53 |
|
|
#define I8254X_REG_ICS 0x00c8 /* Interrupt Cause Set Register */ |
54 |
|
|
#define I8254X_REG_IMS 0x00d0 /* Interrupt Mask Set/Read Register */ |
55 |
|
|
#define I8254X_REG_IMC 0x00d8 /* Interrupt Mask Clear Register */ |
56 |
|
|
#define I8254X_REG_RCTL 0x0100 /* Receive Control Register */ |
57 |
|
|
#define I8254X_REG_FCTTV 0x0170 /* Flow Control Transmit Timer Value */ |
58 |
|
|
#define I8254X_REG_TXCW 0x0178 /* Transmit Configuration Word */ |
59 |
|
|
#define I8254X_REG_RXCW 0x0180 /* Receive Configuration Word */ |
60 |
|
|
#define I8254X_REG_TCTL 0x0400 /* Transmit Control Register */ |
61 |
|
|
#define I8254X_REG_TIPG 0x0410 /* Transmit Inter Packet Gap */ |
62 |
|
|
|
63 |
|
|
#define I8254X_REG_LEDCTL 0x0E00 /* LED Control */ |
64 |
|
|
#define I8254X_REG_PBA 0x1000 /* Packet Buffer Allocation */ |
65 |
|
|
|
66 |
|
|
#define I8254X_REG_RDBAL 0x2800 /* RX Descriptor Base Address Low */ |
67 |
|
|
#define I8254X_REG_RDBAH 0x2804 /* RX Descriptor Base Address High */ |
68 |
|
|
#define I8254X_REG_RDLEN 0x2808 /* RX Descriptor Length */ |
69 |
|
|
#define I8254X_REG_RDH 0x2810 /* RX Descriptor Head */ |
70 |
|
|
#define I8254X_REG_RDT 0x2818 /* RX Descriptor Tail */ |
71 |
|
|
#define I8254X_REG_RDTR 0x2820 /* RX Delay Timer Register */ |
72 |
|
|
#define I8254X_REG_RXDCTL 0x3828 /* RX Descriptor Control */ |
73 |
|
|
#define I8254X_REG_RADV 0x282c /* RX Int. Absolute Delay Timer */ |
74 |
|
|
#define I8254X_REG_RSRPD 0x2c00 /* RX Small Packet Detect Interrupt */ |
75 |
|
|
|
76 |
|
|
#define I8254X_REG_TXDMAC 0x3000 /* TX DMA Control */ |
77 |
|
|
#define I8254X_REG_TDBAL 0x3800 /* TX Descriptor Base Address Low */ |
78 |
|
|
#define I8254X_REG_TDBAH 0x3804 /* TX Descriptor Base Address Low */ |
79 |
|
|
#define I8254X_REG_TDLEN 0x3808 /* TX Descriptor Length */ |
80 |
|
|
#define I8254X_REG_TDH 0x3810 /* TX Descriptor Head */ |
81 |
|
|
#define I8254X_REG_TDT 0x3818 /* TX Descriptor Tail */ |
82 |
|
|
#define I8254X_REG_TIDV 0x3820 /* TX Interrupt Delay Value */ |
83 |
|
|
#define I8254X_REG_TXDCTL 0x3828 /* TX Descriptor Control */ |
84 |
|
|
#define I8254X_REG_TADV 0x382c /* TX Absolute Interrupt Delay Value */ |
85 |
|
|
#define I8254X_REG_TSPMT 0x3830 /* TCP Segmentation Pad & Min Threshold */ |
86 |
|
|
|
87 |
|
|
#define I8254X_REG_RXCSUM 0x5000 /* RX Checksum Control */ |
88 |
|
|
|
89 |
dpavlin |
8 |
/* Register list for i8254x */ |
90 |
|
|
#define I82542_REG_RDTR 0x0108 /* RX Delay Timer Register */ |
91 |
|
|
#define I82542_REG_RDBAL 0x0110 /* RX Descriptor Base Address Low */ |
92 |
|
|
#define I82542_REG_RDBAH 0x0114 /* RX Descriptor Base Address High */ |
93 |
|
|
#define I82542_REG_RDLEN 0x0118 /* RX Descriptor Length */ |
94 |
|
|
#define I82542_REG_RDH 0x0120 /* RDH for i82542 */ |
95 |
|
|
#define I82542_REG_RDT 0x0128 /* RDT for i82542 */ |
96 |
|
|
#define I82542_REG_TDBAL 0x0420 /* TX Descriptor Base Address Low */ |
97 |
|
|
#define I82542_REG_TDBAH 0x0424 /* TX Descriptor Base Address Low */ |
98 |
|
|
#define I82542_REG_TDLEN 0x0428 /* TX Descriptor Length */ |
99 |
|
|
#define I82542_REG_TDH 0x0430 /* TDH for i82542 */ |
100 |
|
|
#define I82542_REG_TDT 0x0438 /* TDT for i82542 */ |
101 |
dpavlin |
7 |
|
102 |
|
|
/* CTRL - Control Register (0x0000) */ |
103 |
|
|
#define I8254X_CTRL_FD 0x00000001 /* Full Duplex */ |
104 |
|
|
#define I8254X_CTRL_LRST 0x00000008 /* Link Reset */ |
105 |
|
|
#define I8254X_CTRL_ASDE 0x00000020 /* Auto-speed detection */ |
106 |
|
|
#define I8254X_CTRL_SLU 0x00000040 /* Set Link Up */ |
107 |
|
|
#define I8254X_CTRL_ILOS 0x00000080 /* Invert Loss of Signal */ |
108 |
|
|
#define I8254X_CTRL_SPEED_MASK 0x00000300 /* Speed selection */ |
109 |
|
|
#define I8254X_CTRL_SPEED_SHIFT 8 |
110 |
|
|
#define I8254X_CTRL_FRCSPD 0x00000800 /* Force Speed */ |
111 |
|
|
#define I8254X_CTRL_FRCDPLX 0x00001000 /* Force Duplex */ |
112 |
|
|
#define I8254X_CTRL_SDP0_DATA 0x00040000 /* SDP0 data */ |
113 |
|
|
#define I8254X_CTRL_SDP1_DATA 0x00080000 /* SDP1 data */ |
114 |
|
|
#define I8254X_CTRL_SDP0_IODIR 0x00400000 /* SDP0 direction */ |
115 |
|
|
#define I8254X_CTRL_SDP1_IODIR 0x00800000 /* SDP1 direction */ |
116 |
|
|
#define I8254X_CTRL_RST 0x04000000 /* Device Reset */ |
117 |
|
|
#define I8254X_CTRL_RFCE 0x08000000 /* RX Flow Ctrl Enable */ |
118 |
|
|
#define I8254X_CTRL_TFCE 0x10000000 /* TX Flow Ctrl Enable */ |
119 |
|
|
#define I8254X_CTRL_VME 0x40000000 /* VLAN Mode Enable */ |
120 |
|
|
#define I8254X_CTRL_PHY_RST 0x80000000 /* PHY reset */ |
121 |
|
|
|
122 |
|
|
/* STATUS - Device Status Register (0x0008) */ |
123 |
|
|
#define I8254X_STATUS_FD 0x00000001 /* Full Duplex */ |
124 |
|
|
#define I8254X_STATUS_LU 0x00000002 /* Link Up */ |
125 |
|
|
#define I8254X_STATUS_TXOFF 0x00000010 /* Transmit paused */ |
126 |
|
|
#define I8254X_STATUS_TBIMODE 0x00000020 /* TBI Mode */ |
127 |
|
|
#define I8254X_STATUS_SPEED_MASK 0x000000C0 /* Link Speed setting */ |
128 |
|
|
#define I8254X_STATUS_SPEED_SHIFT 6 |
129 |
|
|
#define I8254X_STATUS_ASDV_MASK 0x00000300 /* Auto Speed Detection */ |
130 |
|
|
#define I8254X_STATUS_ASDV_SHIFT 8 |
131 |
|
|
#define I8254X_STATUS_PCI66 0x00000800 /* PCI bus speed */ |
132 |
|
|
#define I8254X_STATUS_BUS64 0x00001000 /* PCI bus width */ |
133 |
|
|
#define I8254X_STATUS_PCIX_MODE 0x00002000 /* PCI-X mode */ |
134 |
|
|
#define I8254X_STATUS_PCIXSPD_MASK 0x0000C000 /* PCI-X speed */ |
135 |
|
|
#define I8254X_STATUS_PCIXSPD_SHIFT 14 |
136 |
|
|
|
137 |
|
|
/* CTRL_EXT - Extended Device Control Register (0x0018) */ |
138 |
|
|
#define I8254X_CTRLEXT_PHY_INT 0x00000020 /* PHY interrupt */ |
139 |
|
|
#define I8254X_CTRLEXT_SDP6_DATA 0x00000040 /* SDP6 data */ |
140 |
|
|
#define I8254X_CTRLEXT_SDP7_DATA 0x00000080 /* SDP7 data */ |
141 |
|
|
#define I8254X_CTRLEXT_SDP6_IODIR 0x00000400 /* SDP6 direction */ |
142 |
|
|
#define I8254X_CTRLEXT_SDP7_IODIR 0x00000800 /* SDP7 direction */ |
143 |
|
|
#define I8254X_CTRLEXT_ASDCHK 0x00001000 /* Auto-Speed Detect Chk */ |
144 |
|
|
#define I8254X_CTRLEXT_EE_RST 0x00002000 /* EEPROM reset */ |
145 |
|
|
#define I8254X_CTRLEXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */ |
146 |
|
|
#define I8254X_CTRLEXT_RO_DIS 0x00020000 /* Relaxed Ordering Dis. */ |
147 |
|
|
#define I8254X_CTRLEXT_LNKMOD_MASK 0x00C00000 /* Link Mode */ |
148 |
|
|
#define I8254X_CTRLEXT_LNKMOD_SHIFT 22 |
149 |
|
|
|
150 |
|
|
/* MDIC - MDI Control Register (0x0020) */ |
151 |
|
|
#define I8254X_MDIC_DATA_MASK 0x0000FFFF /* Data */ |
152 |
|
|
#define I8254X_MDIC_REG_MASK 0x001F0000 /* PHY Register */ |
153 |
|
|
#define I8254X_MDIC_REG_SHIFT 16 |
154 |
|
|
#define I8254X_MDIC_PHY_MASK 0x03E00000 /* PHY Address */ |
155 |
|
|
#define I8254X_MDIC_PHY_SHIFT 21 |
156 |
|
|
#define I8254X_MDIC_OP_MASK 0x0C000000 /* Opcode */ |
157 |
|
|
#define I8254X_MDIC_OP_SHIFT 26 |
158 |
|
|
#define I8254X_MDIC_R 0x10000000 /* Ready */ |
159 |
|
|
#define I8254X_MDIC_I 0x20000000 /* Interrupt Enable */ |
160 |
|
|
#define I8254X_MDIC_E 0x40000000 /* Error */ |
161 |
|
|
|
162 |
|
|
/* ICR - Interrupt Cause Read (0x00c0) */ |
163 |
|
|
#define I8254X_ICR_TXDW 0x00000001 /* TX Desc Written back */ |
164 |
|
|
#define I8254X_ICR_TXQE 0x00000002 /* TX Queue Empty */ |
165 |
|
|
#define I8254X_ICR_LSC 0x00000004 /* Link Status Change */ |
166 |
|
|
#define I8254X_ICR_RXSEQ 0x00000008 /* RX Sequence Error */ |
167 |
|
|
#define I8254X_ICR_RXDMT0 0x00000010 /* RX Desc min threshold reached */ |
168 |
|
|
#define I8254X_ICR_RXO 0x00000040 /* RX Overrun */ |
169 |
|
|
#define I8254X_ICR_RXT0 0x00000080 /* RX Timer Interrupt */ |
170 |
|
|
#define I8254X_ICR_MDAC 0x00000200 /* MDIO Access Complete */ |
171 |
|
|
#define I8254X_ICR_RXCFG 0x00000400 |
172 |
|
|
#define I8254X_ICR_PHY_INT 0x00001000 /* PHY Interrupt */ |
173 |
|
|
#define I8254X_ICR_GPI_SDP6 0x00002000 /* GPI on SDP6 */ |
174 |
|
|
#define I8254X_ICR_GPI_SDP7 0x00004000 /* GPI on SDP7 */ |
175 |
|
|
#define I8254X_ICR_TXD_LOW 0x00008000 /* TX Desc low threshold hit */ |
176 |
|
|
#define I8254X_ICR_SRPD 0x00010000 /* Small RX packet detected */ |
177 |
|
|
|
178 |
|
|
/* RCTL - Receive Control Register (0x0100) */ |
179 |
|
|
#define I8254X_RCTL_EN 0x00000002 /* Receiver Enable */ |
180 |
|
|
#define I8254X_RCTL_SBP 0x00000004 /* Store Bad Packets */ |
181 |
|
|
#define I8254X_RCTL_UPE 0x00000008 /* Unicast Promiscuous Enabled */ |
182 |
|
|
#define I8254X_RCTL_MPE 0x00000010 /* Xcast Promiscuous Enabled */ |
183 |
|
|
#define I8254X_RCTL_LPE 0x00000020 /* Long Packet Reception Enable */ |
184 |
|
|
#define I8254X_RCTL_LBM_MASK 0x000000C0 /* Loopback Mode */ |
185 |
|
|
#define I8254X_RCTL_LBM_SHIFT 6 |
186 |
|
|
#define I8254X_RCTL_RDMTS_MASK 0x00000300 /* RX Desc Min Threshold Size */ |
187 |
|
|
#define I8254X_RCTL_RDMTS_SHIFT 8 |
188 |
|
|
#define I8254X_RCTL_MO_MASK 0x00003000 /* Multicast Offset */ |
189 |
|
|
#define I8254X_RCTL_MO_SHIFT 12 |
190 |
|
|
#define I8254X_RCTL_BAM 0x00008000 /* Broadcast Accept Mode */ |
191 |
|
|
#define I8254X_RCTL_BSIZE_MASK 0x00030000 /* RX Buffer Size */ |
192 |
|
|
#define I8254X_RCTL_BSIZE_SHIFT 16 |
193 |
|
|
#define I8254X_RCTL_VFE 0x00040000 /* VLAN Filter Enable */ |
194 |
|
|
#define I8254X_RCTL_CFIEN 0x00080000 /* CFI Enable */ |
195 |
|
|
#define I8254X_RCTL_CFI 0x00100000 /* Canonical Form Indicator Bit */ |
196 |
|
|
#define I8254X_RCTL_DPF 0x00400000 /* Discard Pause Frames */ |
197 |
|
|
#define I8254X_RCTL_PMCF 0x00800000 /* Pass MAC Control Frames */ |
198 |
|
|
#define I8254X_RCTL_BSEX 0x02000000 /* Buffer Size Extension */ |
199 |
|
|
#define I8254X_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */ |
200 |
|
|
|
201 |
|
|
/* TCTL - Transmit Control Register (0x0400) */ |
202 |
|
|
#define I8254X_TCTL_EN 0x00000002 /* Transmit Enable */ |
203 |
|
|
#define I8254X_TCTL_PSP 0x00000008 /* Pad short packets */ |
204 |
|
|
#define I8254X_TCTL_SWXOFF 0x00400000 /* Software XOFF Transmission */ |
205 |
|
|
|
206 |
|
|
/* PBA - Packet Buffer Allocation (0x1000) */ |
207 |
|
|
#define I8254X_PBA_RXA_MASK 0x0000FFFF /* RX Packet Buffer */ |
208 |
|
|
#define I8254X_PBA_RXA_SHIFT 0 |
209 |
|
|
#define I8254X_PBA_TXA_MASK 0xFFFF0000 /* TX Packet Buffer */ |
210 |
|
|
#define I8254X_PBA_TXA_SHIFT 16 |
211 |
|
|
|
212 |
|
|
/* Flow Control Type */ |
213 |
|
|
#define I8254X_FCT_TYPE_DEFAULT 0x8808 |
214 |
|
|
|
215 |
|
|
/* === TX Descriptor fields === */ |
216 |
|
|
|
217 |
|
|
/* TX Packet Length (word 2) */ |
218 |
|
|
#define I8254X_TXDESC_LEN_MASK 0x0000ffff |
219 |
|
|
|
220 |
|
|
/* TX Descriptor CMD field (word 2) */ |
221 |
|
|
#define I8254X_TXDESC_IDE 0x80000000 /* Interrupt Delay Enable */ |
222 |
|
|
#define I8254X_TXDESC_VLE 0x40000000 /* VLAN Packet Enable */ |
223 |
|
|
#define I8254X_TXDESC_DEXT 0x20000000 /* Extension */ |
224 |
|
|
#define I8254X_TXDESC_RPS 0x10000000 /* Report Packet Sent */ |
225 |
|
|
#define I8254X_TXDESC_RS 0x08000000 /* Report Status */ |
226 |
|
|
#define I8254X_TXDESC_IC 0x04000000 /* Insert Checksum */ |
227 |
|
|
#define I8254X_TXDESC_IFCS 0x02000000 /* Insert FCS */ |
228 |
|
|
#define I8254X_TXDESC_EOP 0x01000000 /* End Of Packet */ |
229 |
|
|
|
230 |
|
|
/* TX Descriptor STA field (word 3) */ |
231 |
|
|
#define I8254X_TXDESC_TU 0x00000008 /* Transmit Underrun */ |
232 |
|
|
#define I8254X_TXDESC_LC 0x00000004 /* Late Collision */ |
233 |
|
|
#define I8254X_TXDESC_EC 0x00000002 /* Excess Collisions */ |
234 |
|
|
#define I8254X_TXDESC_DD 0x00000001 /* Descriptor Done */ |
235 |
|
|
|
236 |
|
|
/* === RX Descriptor fields === */ |
237 |
|
|
|
238 |
|
|
/* RX Packet Length (word 2) */ |
239 |
|
|
#define I8254X_RXDESC_LEN_MASK 0x0000ffff |
240 |
|
|
|
241 |
|
|
/* RX Descriptor STA field (word 3) */ |
242 |
|
|
#define I8254X_RXDESC_PIF 0x00000080 /* Passed In-exact Filter */ |
243 |
|
|
#define I8254X_RXDESC_IPCS 0x00000040 /* IP cksum calculated */ |
244 |
|
|
#define I8254X_RXDESC_TCPCS 0x00000020 /* TCP cksum calculated */ |
245 |
|
|
#define I8254X_RXDESC_VP 0x00000008 /* Packet is 802.1Q */ |
246 |
|
|
#define I8254X_RXDESC_IXSM 0x00000004 /* Ignore cksum indication */ |
247 |
|
|
#define I8254X_RXDESC_EOP 0x00000002 /* End Of Packet */ |
248 |
|
|
#define I8254X_RXDESC_DD 0x00000001 /* Descriptor Done */ |
249 |
|
|
|
250 |
|
|
/* Intel i8254x private data */ |
251 |
|
|
struct i8254x_data { |
252 |
|
|
char *name; |
253 |
|
|
|
254 |
|
|
/* Lock test */ |
255 |
|
|
pthread_mutex_t lock; |
256 |
|
|
|
257 |
|
|
/* Physical (MAC) address */ |
258 |
|
|
n_eth_addr_t mac_addr; |
259 |
|
|
|
260 |
|
|
/* Device information */ |
261 |
|
|
struct vdevice *dev; |
262 |
|
|
|
263 |
|
|
/* PCI device information */ |
264 |
|
|
struct pci_device *pci_dev; |
265 |
|
|
|
266 |
|
|
/* Virtual machine */ |
267 |
|
|
vm_instance_t *vm; |
268 |
|
|
|
269 |
|
|
/* NetIO descriptor */ |
270 |
|
|
netio_desc_t *nio; |
271 |
|
|
|
272 |
|
|
/* TX ring scanner task id */ |
273 |
|
|
ptask_id_t tx_tid; |
274 |
|
|
|
275 |
|
|
/* Interrupt registers */ |
276 |
|
|
m_uint32_t icr,imr; |
277 |
|
|
|
278 |
|
|
/* Device Control Register */ |
279 |
|
|
m_uint32_t ctrl; |
280 |
|
|
|
281 |
|
|
/* Extended Control Register */ |
282 |
|
|
m_uint32_t ctrl_ext; |
283 |
|
|
|
284 |
dpavlin |
8 |
/* Flow Control registers */ |
285 |
|
|
m_uint32_t fcal,fcah,fct; |
286 |
|
|
|
287 |
|
|
/* RX Delay Timer */ |
288 |
|
|
m_uint32_t rdtr; |
289 |
|
|
|
290 |
dpavlin |
7 |
/* RX/TX Control Registers */ |
291 |
|
|
m_uint32_t rctl,tctl; |
292 |
|
|
|
293 |
|
|
/* RX buffer size (computed from RX control register */ |
294 |
|
|
m_uint32_t rx_buf_size; |
295 |
|
|
|
296 |
|
|
/* RX/TX ring base addresses */ |
297 |
|
|
m_uint64_t rx_addr,tx_addr; |
298 |
|
|
|
299 |
|
|
/* RX/TX descriptor length */ |
300 |
|
|
m_uint32_t rdlen,tdlen; |
301 |
|
|
|
302 |
|
|
/* RX/TX descriptor head and tail */ |
303 |
|
|
m_uint32_t rdh,rdt,tdh,tdt; |
304 |
|
|
|
305 |
|
|
/* TX packet buffer */ |
306 |
|
|
m_uint8_t tx_buffer[I8254X_MAX_PKT_SIZE]; |
307 |
|
|
|
308 |
|
|
/* RX IRQ count */ |
309 |
|
|
m_uint32_t rx_irq_cnt; |
310 |
|
|
|
311 |
|
|
/* MII/PHY handling */ |
312 |
|
|
u_int mii_state; |
313 |
|
|
u_int mii_bit; |
314 |
|
|
u_int mii_opcode; |
315 |
|
|
u_int mii_phy; |
316 |
|
|
u_int mii_reg; |
317 |
|
|
u_int mii_data_pos; |
318 |
|
|
u_int mii_data; |
319 |
|
|
u_int mii_regs[32][32]; |
320 |
|
|
}; |
321 |
|
|
|
322 |
|
|
/* TX descriptor */ |
323 |
|
|
struct tx_desc { |
324 |
|
|
m_uint32_t tdes[4]; |
325 |
|
|
}; |
326 |
|
|
|
327 |
|
|
/* RX descriptor */ |
328 |
|
|
struct rx_desc { |
329 |
|
|
m_uint32_t rdes[4]; |
330 |
|
|
}; |
331 |
|
|
|
332 |
|
|
#define LVG_LOCK(d) pthread_mutex_lock(&(d)->lock) |
333 |
|
|
#define LVG_UNLOCK(d) pthread_mutex_unlock(&(d)->lock) |
334 |
|
|
|
335 |
|
|
/* Log an message */ |
336 |
|
|
#define LVG_LOG(d,msg...) vm_log((d)->vm,(d)->name,msg) |
337 |
|
|
|
338 |
|
|
/* Read a MII register */ |
339 |
|
|
static m_uint16_t mii_reg_read(struct i8254x_data *d) |
340 |
|
|
{ |
341 |
|
|
#if DEBUG_MII_REGS |
342 |
|
|
LVG_LOG(d,"MII PHY read %d reg %d\n",d->mii_phy,d->mii_reg); |
343 |
|
|
#endif |
344 |
|
|
|
345 |
|
|
switch(d->mii_reg) { |
346 |
|
|
case 0x00: |
347 |
|
|
return((d->mii_regs[d->mii_phy][d->mii_reg] & ~0x8200) | 0x2000); |
348 |
|
|
case 0x01: |
349 |
|
|
return(0x782c); |
350 |
|
|
case 0x02: |
351 |
|
|
return(0x0013); |
352 |
|
|
case 0x03: |
353 |
|
|
return(0x61d4); |
354 |
|
|
case 0x05: |
355 |
|
|
return(0x41e1); |
356 |
|
|
case 0x06: |
357 |
dpavlin |
8 |
return(0x0001); |
358 |
dpavlin |
7 |
case 0x11: |
359 |
|
|
return(0x4700); |
360 |
|
|
default: |
361 |
|
|
return(d->mii_regs[d->mii_phy][d->mii_reg]); |
362 |
|
|
} |
363 |
|
|
} |
364 |
|
|
|
365 |
|
|
/* Write a MII register */ |
366 |
|
|
static void mii_reg_write(struct i8254x_data *d) |
367 |
|
|
{ |
368 |
|
|
#if DEBUG_MII_REGS |
369 |
|
|
LVG_LOG(d,"MII PHY write %d reg %d value %04x\n", |
370 |
|
|
d->mii_phy,d->mii_reg,d->mii_data); |
371 |
|
|
#endif |
372 |
|
|
assert(d->mii_phy < 32); |
373 |
|
|
assert(d->mii_reg < 32); |
374 |
|
|
d->mii_regs[d->mii_phy][d->mii_reg] = d->mii_data; |
375 |
|
|
} |
376 |
|
|
|
377 |
|
|
enum { |
378 |
|
|
MII_OPCODE_READ = 1, |
379 |
|
|
MII_OPCODE_WRITE, |
380 |
|
|
}; |
381 |
|
|
|
382 |
|
|
/* MII Finite State Machine */ |
383 |
|
|
static void mii_access(struct i8254x_data *d) |
384 |
|
|
{ |
385 |
|
|
switch(d->mii_state) { |
386 |
|
|
case 0: /* reset */ |
387 |
|
|
d->mii_phy = 0; |
388 |
|
|
d->mii_reg = 0; |
389 |
|
|
d->mii_data_pos = 15; |
390 |
|
|
d->mii_data = 0; |
391 |
|
|
|
392 |
|
|
case 1: /* idle */ |
393 |
|
|
if (!d->mii_bit) |
394 |
|
|
d->mii_state = 2; |
395 |
|
|
else |
396 |
|
|
d->mii_state = 1; |
397 |
|
|
break; |
398 |
|
|
|
399 |
|
|
case 2: /* start */ |
400 |
|
|
d->mii_state = d->mii_bit ? 3 : 0; |
401 |
|
|
break; |
402 |
|
|
|
403 |
|
|
case 3: /* opcode */ |
404 |
|
|
d->mii_state = d->mii_bit ? 4 : 5; |
405 |
|
|
break; |
406 |
|
|
|
407 |
|
|
case 4: /* read: opcode "10" */ |
408 |
|
|
if (!d->mii_bit) { |
409 |
|
|
d->mii_opcode = MII_OPCODE_READ; |
410 |
|
|
d->mii_state = 6; |
411 |
|
|
} else { |
412 |
|
|
d->mii_state = 0; |
413 |
|
|
} |
414 |
|
|
break; |
415 |
|
|
|
416 |
|
|
case 5: /* write: opcode "01" */ |
417 |
|
|
if (d->mii_bit) { |
418 |
|
|
d->mii_opcode = MII_OPCODE_WRITE; |
419 |
|
|
d->mii_state = 6; |
420 |
|
|
} else { |
421 |
|
|
d->mii_state = 0; |
422 |
|
|
} |
423 |
|
|
break; |
424 |
|
|
|
425 |
|
|
case 6 ... 10: /* phy */ |
426 |
|
|
d->mii_phy <<= 1; |
427 |
|
|
d->mii_phy |= d->mii_bit; |
428 |
|
|
d->mii_state++; |
429 |
|
|
break; |
430 |
|
|
|
431 |
|
|
case 11 ... 15: /* reg */ |
432 |
|
|
d->mii_reg <<= 1; |
433 |
|
|
d->mii_reg |= d->mii_bit; |
434 |
|
|
d->mii_state++; |
435 |
|
|
break; |
436 |
|
|
|
437 |
|
|
case 16 ... 17: /* ta */ |
438 |
|
|
if (d->mii_opcode == MII_OPCODE_READ) |
439 |
|
|
d->mii_state = 18; |
440 |
|
|
else |
441 |
|
|
d->mii_state++; |
442 |
|
|
break; |
443 |
|
|
|
444 |
|
|
case 18: |
445 |
|
|
if (d->mii_opcode == MII_OPCODE_READ) { |
446 |
|
|
d->mii_data = mii_reg_read(d); |
447 |
|
|
d->mii_state++; |
448 |
|
|
} |
449 |
|
|
|
450 |
|
|
case 19 ... 35: |
451 |
|
|
if (d->mii_opcode == MII_OPCODE_READ) { |
452 |
|
|
d->mii_bit = (d->mii_data >> d->mii_data_pos) & 0x1; |
453 |
|
|
} else { |
454 |
|
|
d->mii_data |= d->mii_bit << d->mii_data_pos; |
455 |
|
|
} |
456 |
|
|
|
457 |
|
|
if (!d->mii_data_pos) { |
458 |
|
|
if (d->mii_opcode == MII_OPCODE_WRITE) |
459 |
|
|
mii_reg_write(d); |
460 |
|
|
d->mii_state = 0; |
461 |
|
|
} else { |
462 |
|
|
d->mii_state++; |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
d->mii_data_pos--; |
466 |
|
|
break; |
467 |
|
|
|
468 |
|
|
default: |
469 |
|
|
printf("MII: impossible state %u!\n",d->mii_state); |
470 |
|
|
} |
471 |
|
|
} |
472 |
|
|
|
473 |
|
|
/* Update the interrupt status */ |
474 |
|
|
static inline void dev_i8254x_update_irq_status(struct i8254x_data *d) |
475 |
|
|
{ |
476 |
|
|
if (d->icr & d->imr) |
477 |
|
|
pci_dev_trigger_irq(d->vm,d->pci_dev); |
478 |
dpavlin |
11 |
else |
479 |
dpavlin |
8 |
pci_dev_clear_irq(d->vm,d->pci_dev); |
480 |
dpavlin |
7 |
} |
481 |
|
|
|
482 |
|
|
/* Compute RX buffer size */ |
483 |
|
|
static inline void dev_i8254x_set_rx_buf_size(struct i8254x_data *d) |
484 |
|
|
{ |
485 |
|
|
m_uint32_t bsize; |
486 |
|
|
|
487 |
|
|
bsize = (d->rctl & I8254X_RCTL_BSIZE_MASK) >> I8254X_RCTL_BSIZE_SHIFT; |
488 |
|
|
|
489 |
|
|
if (!(d->rctl & I8254X_RCTL_BSEX)) { |
490 |
|
|
/* Standard buffer sizes */ |
491 |
|
|
switch(bsize) { |
492 |
|
|
case 0: |
493 |
|
|
d->rx_buf_size = 2048; |
494 |
|
|
break; |
495 |
|
|
case 1: |
496 |
|
|
d->rx_buf_size = 1024; |
497 |
|
|
break; |
498 |
|
|
case 2: |
499 |
|
|
d->rx_buf_size = 512; |
500 |
|
|
break; |
501 |
|
|
case 3: |
502 |
|
|
d->rx_buf_size = 256; |
503 |
|
|
break; |
504 |
|
|
} |
505 |
|
|
} else { |
506 |
|
|
/* Extended buffer sizes */ |
507 |
|
|
switch(bsize) { |
508 |
|
|
case 0: |
509 |
|
|
d->rx_buf_size = 0; /* invalid */ |
510 |
|
|
break; |
511 |
|
|
case 1: |
512 |
|
|
d->rx_buf_size = 16384; |
513 |
|
|
break; |
514 |
|
|
case 2: |
515 |
|
|
d->rx_buf_size = 8192; |
516 |
|
|
break; |
517 |
|
|
case 3: |
518 |
|
|
d->rx_buf_size = 4096; |
519 |
|
|
break; |
520 |
|
|
} |
521 |
|
|
} |
522 |
|
|
} |
523 |
|
|
|
524 |
|
|
/* |
525 |
|
|
* dev_i8254x_access() |
526 |
|
|
*/ |
527 |
|
|
void *dev_i8254x_access(cpu_gen_t *cpu,struct vdevice *dev, |
528 |
|
|
m_uint32_t offset,u_int op_size,u_int op_type, |
529 |
|
|
m_uint64_t *data) |
530 |
|
|
{ |
531 |
|
|
struct i8254x_data *d = dev->priv_data; |
532 |
|
|
|
533 |
|
|
if (op_type == MTS_READ) |
534 |
|
|
*data = 0x0; |
535 |
|
|
|
536 |
|
|
#if DEBUG_ACCESS |
537 |
|
|
if (op_type == MTS_READ) { |
538 |
|
|
cpu_log(cpu,d->name,"read access to offset=0x%x, pc=0x%llx, size=%u\n", |
539 |
|
|
offset,cpu_get_pc(cpu),op_size); |
540 |
|
|
} else { |
541 |
|
|
cpu_log(cpu,d->name,"write access to offset=0x%x, pc=0x%llx, " |
542 |
|
|
"val=0x%llx, size=%u\n",offset,cpu_get_pc(cpu),*data,op_size); |
543 |
|
|
} |
544 |
|
|
#endif |
545 |
|
|
|
546 |
|
|
LVG_LOCK(d); |
547 |
|
|
|
548 |
|
|
switch(offset) { |
549 |
dpavlin |
8 |
#if 0 /* TODO */ |
550 |
|
|
case 0x180: |
551 |
|
|
if (op_type == MTS_READ) |
552 |
dpavlin |
11 |
*data = 0xFFFFFFFF; //0xDC004020; //1 << 31; |
553 |
dpavlin |
8 |
break; |
554 |
|
|
#endif |
555 |
|
|
|
556 |
dpavlin |
7 |
/* Link is Up and Full Duplex */ |
557 |
|
|
case I8254X_REG_STATUS: |
558 |
|
|
if (op_type == MTS_READ) |
559 |
dpavlin |
8 |
*data = I8254X_STATUS_LU | I8254X_STATUS_FD; |
560 |
dpavlin |
7 |
break; |
561 |
|
|
|
562 |
|
|
/* Device Control Register */ |
563 |
|
|
case I8254X_REG_CTRL: |
564 |
|
|
if (op_type == MTS_WRITE) |
565 |
|
|
d->ctrl = *data; |
566 |
|
|
else |
567 |
|
|
*data = d->ctrl; |
568 |
|
|
break; |
569 |
|
|
|
570 |
|
|
/* Extended Device Control Register */ |
571 |
|
|
case I8254X_REG_CTRLEXT: |
572 |
|
|
if (op_type == MTS_WRITE) { |
573 |
|
|
/* MDIO clock set ? */ |
574 |
|
|
if (!(d->ctrl_ext & I8254X_CTRLEXT_SDP6_DATA) && |
575 |
|
|
(*data & I8254X_CTRLEXT_SDP6_DATA)) |
576 |
|
|
{ |
577 |
|
|
if (*data & I8254X_CTRLEXT_SDP7_IODIR) |
578 |
|
|
d->mii_bit = (*data & I8254X_CTRLEXT_SDP7_DATA) ? 1 : 0; |
579 |
|
|
|
580 |
|
|
mii_access(d); |
581 |
|
|
} |
582 |
|
|
|
583 |
|
|
d->ctrl_ext = *data; |
584 |
|
|
} else { |
585 |
|
|
*data = d->ctrl_ext; |
586 |
|
|
|
587 |
|
|
if (!(d->ctrl_ext & I8254X_CTRLEXT_SDP7_IODIR)) { |
588 |
|
|
if (d->mii_bit) |
589 |
|
|
*data |= I8254X_CTRLEXT_SDP7_DATA; |
590 |
|
|
else |
591 |
|
|
*data &= ~I8254X_CTRLEXT_SDP7_DATA; |
592 |
|
|
} |
593 |
|
|
} |
594 |
|
|
break; |
595 |
|
|
|
596 |
|
|
/* XXX */ |
597 |
|
|
case I8254X_REG_MDIC: |
598 |
|
|
if (op_type == MTS_READ) |
599 |
|
|
*data = 1 << 28; |
600 |
|
|
break; |
601 |
|
|
|
602 |
|
|
/* |
603 |
|
|
* Interrupt Cause Read Register. |
604 |
|
|
* |
605 |
|
|
* Notice: a read clears all interrupt bits. |
606 |
|
|
*/ |
607 |
|
|
case I8254X_REG_ICR: |
608 |
|
|
if (op_type == MTS_READ) { |
609 |
|
|
*data = d->icr; |
610 |
|
|
d->icr = 0; |
611 |
|
|
|
612 |
|
|
if (d->rx_irq_cnt > 0) { |
613 |
|
|
d->icr |= I8254X_ICR_RXT0; |
614 |
|
|
d->rx_irq_cnt--; |
615 |
|
|
} |
616 |
|
|
|
617 |
|
|
dev_i8254x_update_irq_status(d); |
618 |
|
|
} |
619 |
|
|
break; |
620 |
|
|
|
621 |
|
|
/* Interrupt Cause Set Register */ |
622 |
|
|
case I8254X_REG_ICS: |
623 |
|
|
if (op_type == MTS_WRITE) { |
624 |
|
|
d->icr |= *data; |
625 |
|
|
dev_i8254x_update_irq_status(d); |
626 |
|
|
} |
627 |
|
|
break; |
628 |
|
|
|
629 |
|
|
/* Interrupt Mask Set/Read Register */ |
630 |
|
|
case I8254X_REG_IMS: |
631 |
|
|
if (op_type == MTS_WRITE) { |
632 |
|
|
d->imr |= *data; |
633 |
|
|
dev_i8254x_update_irq_status(d); |
634 |
|
|
} else { |
635 |
|
|
*data = d->imr; |
636 |
|
|
} |
637 |
|
|
break; |
638 |
|
|
|
639 |
|
|
/* Interrupt Mask Clear Register */ |
640 |
|
|
case I8254X_REG_IMC: |
641 |
|
|
if (op_type == MTS_WRITE) { |
642 |
|
|
d->imr &= ~(*data); |
643 |
|
|
dev_i8254x_update_irq_status(d); |
644 |
|
|
} |
645 |
|
|
break; |
646 |
|
|
|
647 |
|
|
/* Receive Control Register */ |
648 |
|
|
case I8254X_REG_RCTL: |
649 |
|
|
if (op_type == MTS_READ) { |
650 |
|
|
*data = d->rctl; |
651 |
|
|
} else { |
652 |
|
|
d->rctl = *data; |
653 |
|
|
dev_i8254x_set_rx_buf_size(d); |
654 |
|
|
} |
655 |
|
|
break; |
656 |
|
|
|
657 |
|
|
/* Transmit Control Register */ |
658 |
|
|
case I8254X_REG_TCTL: |
659 |
|
|
if (op_type == MTS_READ) |
660 |
|
|
*data = d->tctl; |
661 |
|
|
else |
662 |
|
|
d->tctl = *data; |
663 |
|
|
break; |
664 |
|
|
|
665 |
|
|
/* RX Descriptor Base Address Low */ |
666 |
|
|
case I8254X_REG_RDBAL: |
667 |
dpavlin |
8 |
case I82542_REG_RDBAL: |
668 |
dpavlin |
7 |
if (op_type == MTS_WRITE) { |
669 |
|
|
d->rx_addr &= 0xFFFFFFFF00000000ULL; |
670 |
|
|
d->rx_addr |= (m_uint32_t)(*data); |
671 |
|
|
} else { |
672 |
|
|
*data = (m_uint32_t)d->rx_addr; |
673 |
|
|
} |
674 |
|
|
break; |
675 |
|
|
|
676 |
|
|
/* RX Descriptor Base Address High */ |
677 |
|
|
case I8254X_REG_RDBAH: |
678 |
dpavlin |
8 |
case I82542_REG_RDBAH: |
679 |
dpavlin |
7 |
if (op_type == MTS_WRITE) { |
680 |
|
|
d->rx_addr &= 0x00000000FFFFFFFFULL; |
681 |
|
|
d->rx_addr |= *data << 32; |
682 |
|
|
} else { |
683 |
|
|
*data = d->rx_addr >> 32; |
684 |
|
|
} |
685 |
|
|
break; |
686 |
|
|
|
687 |
|
|
/* TX Descriptor Base Address Low */ |
688 |
|
|
case I8254X_REG_TDBAL: |
689 |
dpavlin |
8 |
case I82542_REG_TDBAL: |
690 |
dpavlin |
7 |
if (op_type == MTS_WRITE) { |
691 |
|
|
d->tx_addr &= 0xFFFFFFFF00000000ULL; |
692 |
|
|
d->tx_addr |= (m_uint32_t)(*data); |
693 |
|
|
} else { |
694 |
|
|
*data = (m_uint32_t)d->tx_addr; |
695 |
|
|
} |
696 |
|
|
break; |
697 |
|
|
|
698 |
|
|
/* TX Descriptor Base Address High */ |
699 |
|
|
case I8254X_REG_TDBAH: |
700 |
dpavlin |
8 |
case I82542_REG_TDBAH: |
701 |
dpavlin |
7 |
if (op_type == MTS_WRITE) { |
702 |
|
|
d->tx_addr &= 0x00000000FFFFFFFFULL; |
703 |
|
|
d->tx_addr |= *data << 32; |
704 |
|
|
} else { |
705 |
|
|
*data = d->tx_addr >> 32; |
706 |
|
|
} |
707 |
|
|
break; |
708 |
|
|
|
709 |
|
|
/* RX Descriptor Length */ |
710 |
|
|
case I8254X_REG_RDLEN: |
711 |
dpavlin |
8 |
case I82542_REG_RDLEN: |
712 |
dpavlin |
7 |
if (op_type == MTS_WRITE) |
713 |
|
|
d->rdlen = *data & 0xFFF80; |
714 |
|
|
else |
715 |
|
|
*data = d->rdlen; |
716 |
|
|
break; |
717 |
|
|
|
718 |
|
|
/* TX Descriptor Length */ |
719 |
|
|
case I8254X_REG_TDLEN: |
720 |
dpavlin |
8 |
case I82542_REG_TDLEN: |
721 |
dpavlin |
7 |
if (op_type == MTS_WRITE) |
722 |
|
|
d->tdlen = *data & 0xFFF80; |
723 |
|
|
else |
724 |
|
|
*data = d->tdlen; |
725 |
|
|
break; |
726 |
|
|
|
727 |
|
|
/* RX Descriptor Head */ |
728 |
|
|
case I82542_REG_RDH: |
729 |
|
|
case I8254X_REG_RDH: |
730 |
|
|
if (op_type == MTS_WRITE) |
731 |
|
|
d->rdh = *data & 0xFFFF; |
732 |
|
|
else |
733 |
|
|
*data = d->rdh; |
734 |
|
|
break; |
735 |
|
|
|
736 |
|
|
/* RX Descriptor Tail */ |
737 |
|
|
case I8254X_REG_RDT: |
738 |
|
|
case I82542_REG_RDT: |
739 |
|
|
if (op_type == MTS_WRITE) |
740 |
|
|
d->rdt = *data & 0xFFFF; |
741 |
|
|
else |
742 |
|
|
*data = d->rdt; |
743 |
|
|
break; |
744 |
|
|
|
745 |
|
|
/* TX Descriptor Head */ |
746 |
|
|
case I82542_REG_TDH: |
747 |
|
|
case I8254X_REG_TDH: |
748 |
|
|
if (op_type == MTS_WRITE) |
749 |
|
|
d->tdh = *data & 0xFFFF; |
750 |
|
|
else |
751 |
|
|
*data = d->tdh; |
752 |
|
|
break; |
753 |
|
|
|
754 |
|
|
/* TX Descriptor Tail */ |
755 |
|
|
case I82542_REG_TDT: |
756 |
|
|
case I8254X_REG_TDT: |
757 |
|
|
if (op_type == MTS_WRITE) |
758 |
|
|
d->tdt = *data & 0xFFFF; |
759 |
|
|
else |
760 |
|
|
*data = d->tdt; |
761 |
|
|
break; |
762 |
|
|
|
763 |
dpavlin |
8 |
/* Flow Control Address Low */ |
764 |
|
|
case I8254X_REG_FCAL: |
765 |
|
|
if (op_type == MTS_WRITE) |
766 |
|
|
d->fcal = *data; |
767 |
|
|
else |
768 |
|
|
*data = d->fcal; |
769 |
|
|
break; |
770 |
|
|
|
771 |
|
|
/* Flow Control Address High */ |
772 |
|
|
case I8254X_REG_FCAH: |
773 |
|
|
if (op_type == MTS_WRITE) |
774 |
|
|
d->fcah = *data & 0xFFFF; |
775 |
|
|
else |
776 |
|
|
*data = d->fcah; |
777 |
|
|
break; |
778 |
|
|
|
779 |
|
|
/* Flow Control Type */ |
780 |
|
|
case I8254X_REG_FCT: |
781 |
|
|
if (op_type == MTS_WRITE) |
782 |
|
|
d->fct = *data & 0xFFFF; |
783 |
|
|
else |
784 |
|
|
*data = d->fct; |
785 |
|
|
break; |
786 |
|
|
|
787 |
|
|
/* RX Delay Timer */ |
788 |
|
|
case I8254X_REG_RDTR: |
789 |
|
|
case I82542_REG_RDTR: |
790 |
|
|
if (op_type == MTS_WRITE) |
791 |
|
|
d->rdtr = *data & 0xFFFF; |
792 |
|
|
else |
793 |
|
|
*data = d->rdtr; |
794 |
|
|
break; |
795 |
|
|
|
796 |
dpavlin |
7 |
#if DEBUG_UNKNOWN |
797 |
|
|
default: |
798 |
|
|
if (op_type == MTS_READ) { |
799 |
|
|
cpu_log(cpu,d->name, |
800 |
|
|
"read access to unknown offset=0x%x, " |
801 |
|
|
"pc=0x%llx (size=%u)\n", |
802 |
|
|
offset,cpu_get_pc(cpu),op_size); |
803 |
|
|
} else { |
804 |
|
|
cpu_log(cpu,d->name, |
805 |
|
|
"write access to unknown offset=0x%x, pc=0x%llx, " |
806 |
|
|
"val=0x%llx (size=%u)\n", |
807 |
|
|
offset,cpu_get_pc(cpu),*data,op_size); |
808 |
|
|
} |
809 |
|
|
#endif |
810 |
|
|
} |
811 |
|
|
|
812 |
|
|
LVG_UNLOCK(d); |
813 |
|
|
return NULL; |
814 |
|
|
} |
815 |
|
|
|
816 |
|
|
/* Read a TX descriptor */ |
817 |
|
|
static void txdesc_read(struct i8254x_data *d,m_uint64_t txd_addr, |
818 |
|
|
struct tx_desc *txd) |
819 |
|
|
{ |
820 |
|
|
/* Get the descriptor from VM physical RAM */ |
821 |
|
|
physmem_copy_from_vm(d->vm,txd,txd_addr,sizeof(struct tx_desc)); |
822 |
|
|
|
823 |
|
|
/* byte-swapping */ |
824 |
|
|
txd->tdes[0] = vmtoh32(txd->tdes[0]); |
825 |
|
|
txd->tdes[1] = vmtoh32(txd->tdes[1]); |
826 |
|
|
txd->tdes[2] = vmtoh32(txd->tdes[2]); |
827 |
|
|
txd->tdes[3] = vmtoh32(txd->tdes[3]); |
828 |
|
|
} |
829 |
|
|
|
830 |
|
|
/* Handle the TX ring */ |
831 |
|
|
static int dev_i8254x_handle_txring(struct i8254x_data *d) |
832 |
|
|
{ |
833 |
|
|
m_uint64_t txd_addr,buf_addr; |
834 |
|
|
m_uint32_t buf_len,tot_len; |
835 |
|
|
m_uint32_t norm_len,icr; |
836 |
|
|
struct tx_desc txd; |
837 |
|
|
m_uint8_t *pkt_ptr; |
838 |
|
|
|
839 |
|
|
/* Transmit Enabled ? */ |
840 |
|
|
if (!(d->tctl & I8254X_TCTL_EN)) |
841 |
|
|
return(FALSE); |
842 |
|
|
|
843 |
|
|
/* If Head is at same position than Tail, the ring is empty */ |
844 |
|
|
if (d->tdh == d->tdt) |
845 |
|
|
return(FALSE); |
846 |
|
|
|
847 |
|
|
LVG_LOCK(d); |
848 |
dpavlin |
11 |
|
849 |
dpavlin |
7 |
/* Empty packet for now */ |
850 |
|
|
pkt_ptr = d->tx_buffer; |
851 |
|
|
tot_len = 0; |
852 |
|
|
icr = 0; |
853 |
|
|
|
854 |
|
|
while(d->tdh != d->tdt) { |
855 |
|
|
txd_addr = d->tx_addr + (d->tdh * sizeof(struct tx_desc)); |
856 |
|
|
txdesc_read(d,txd_addr,&txd); |
857 |
|
|
|
858 |
|
|
/* Copy the packet buffer */ |
859 |
|
|
buf_addr = ((m_uint64_t)txd.tdes[1] << 32) | txd.tdes[0]; |
860 |
|
|
buf_len = txd.tdes[2] & I8254X_TXDESC_LEN_MASK; |
861 |
|
|
|
862 |
dpavlin |
11 |
//printf("COPYING DATA FROM 0x%8.8llx\n",buf_addr); |
863 |
dpavlin |
7 |
norm_len = normalize_size(buf_len,4,0); |
864 |
|
|
physmem_copy_from_vm(d->vm,pkt_ptr,buf_addr,norm_len); |
865 |
|
|
mem_bswap32(pkt_ptr,norm_len); |
866 |
|
|
|
867 |
|
|
pkt_ptr += buf_len; |
868 |
|
|
tot_len += buf_len; |
869 |
|
|
|
870 |
|
|
/* Write the descriptor done bit if required */ |
871 |
|
|
if (txd.tdes[2] & I8254X_TXDESC_RS) { |
872 |
|
|
txd.tdes[3] |= I8254X_TXDESC_DD; |
873 |
|
|
icr |= I8254X_ICR_TXDW; |
874 |
|
|
physmem_copy_u32_to_vm(d->vm,txd_addr+0x0c,txd.tdes[3]); |
875 |
|
|
} |
876 |
|
|
|
877 |
|
|
/* Go to the next descriptor. Wrap ring if we are at end */ |
878 |
|
|
if (++d->tdh == (d->tdlen / sizeof(struct tx_desc))) |
879 |
|
|
d->tdh = 0; |
880 |
|
|
|
881 |
|
|
/* End of packet ? */ |
882 |
|
|
if (txd.tdes[2] & I8254X_TXDESC_EOP) { |
883 |
dpavlin |
11 |
#if DEBUG_TRANSMIT |
884 |
|
|
LVG_LOG(d,"sending packet of %u bytes\n",tot_len); |
885 |
|
|
mem_dump(log_file,d->tx_buffer,tot_len); |
886 |
|
|
#endif |
887 |
dpavlin |
7 |
netio_send(d->nio,d->tx_buffer,tot_len); |
888 |
|
|
break; |
889 |
|
|
} |
890 |
|
|
} |
891 |
|
|
|
892 |
|
|
if (d->tdh == d->tdt) |
893 |
|
|
icr |= I8254X_ICR_TXQE; |
894 |
|
|
|
895 |
|
|
/* Update the interrupt cause register and trigger IRQ if needed */ |
896 |
|
|
d->icr |= icr; |
897 |
|
|
dev_i8254x_update_irq_status(d); |
898 |
|
|
LVG_UNLOCK(d); |
899 |
|
|
return(TRUE); |
900 |
|
|
} |
901 |
|
|
|
902 |
|
|
/* Read a RX descriptor */ |
903 |
|
|
static void rxdesc_read(struct i8254x_data *d,m_uint64_t rxd_addr, |
904 |
|
|
struct rx_desc *rxd) |
905 |
|
|
{ |
906 |
|
|
/* Get the descriptor from VM physical RAM */ |
907 |
|
|
physmem_copy_from_vm(d->vm,rxd,rxd_addr,sizeof(struct rx_desc)); |
908 |
|
|
|
909 |
|
|
/* byte-swapping */ |
910 |
|
|
rxd->rdes[0] = vmtoh32(rxd->rdes[0]); |
911 |
|
|
rxd->rdes[1] = vmtoh32(rxd->rdes[1]); |
912 |
|
|
rxd->rdes[2] = vmtoh32(rxd->rdes[2]); |
913 |
|
|
rxd->rdes[3] = vmtoh32(rxd->rdes[3]); |
914 |
|
|
} |
915 |
|
|
|
916 |
|
|
/* |
917 |
|
|
* Put a packet in the RX ring. |
918 |
|
|
*/ |
919 |
|
|
static int dev_i8254x_receive_pkt(struct i8254x_data *d, |
920 |
|
|
u_char *pkt,ssize_t pkt_len) |
921 |
|
|
{ |
922 |
|
|
m_uint64_t rxd_addr,buf_addr; |
923 |
|
|
m_uint32_t cur_len,norm_len,tot_len; |
924 |
|
|
struct rx_desc rxd; |
925 |
|
|
m_uint32_t icr; |
926 |
|
|
u_char *pkt_ptr; |
927 |
|
|
|
928 |
|
|
if (!d->rx_buf_size) |
929 |
|
|
return(FALSE); |
930 |
|
|
|
931 |
|
|
LVG_LOCK(d); |
932 |
|
|
pkt_ptr = pkt; |
933 |
|
|
tot_len = pkt_len; |
934 |
|
|
icr = 0; |
935 |
|
|
|
936 |
|
|
while(tot_len > 0) { |
937 |
|
|
/* No descriptor available: RX overrun condition */ |
938 |
|
|
if (d->rdh == d->rdt) { |
939 |
|
|
icr |= I8254X_ICR_RXO; |
940 |
|
|
break; |
941 |
|
|
} |
942 |
|
|
|
943 |
|
|
rxd_addr = d->rx_addr + (d->rdh * sizeof(struct rx_desc)); |
944 |
|
|
rxdesc_read(d,rxd_addr,&rxd); |
945 |
|
|
|
946 |
|
|
cur_len = (tot_len > d->rx_buf_size) ? d->rx_buf_size : tot_len; |
947 |
|
|
|
948 |
|
|
/* Copy the packet data into the RX buffer */ |
949 |
|
|
buf_addr = ((m_uint64_t)rxd.rdes[1] << 32) | rxd.rdes[0]; |
950 |
|
|
|
951 |
|
|
norm_len = normalize_size(cur_len,4,0); |
952 |
|
|
mem_bswap32(pkt_ptr,norm_len); |
953 |
|
|
physmem_copy_to_vm(d->vm,pkt_ptr,buf_addr,norm_len); |
954 |
|
|
tot_len -= cur_len; |
955 |
|
|
pkt_ptr += cur_len; |
956 |
|
|
|
957 |
|
|
/* Set length field */ |
958 |
|
|
rxd.rdes[2] = cur_len; |
959 |
|
|
|
960 |
|
|
/* Set the status */ |
961 |
|
|
rxd.rdes[3] = I8254X_RXDESC_IXSM|I8254X_RXDESC_DD; |
962 |
|
|
|
963 |
|
|
if (!tot_len) { |
964 |
|
|
rxd.rdes[3] |= I8254X_RXDESC_EOP; |
965 |
|
|
icr |= I8254X_ICR_RXT0; |
966 |
|
|
d->rx_irq_cnt++; |
967 |
|
|
rxd.rdes[2] += 4; /* FCS */ |
968 |
|
|
} |
969 |
|
|
|
970 |
|
|
/* Write back updated descriptor */ |
971 |
|
|
physmem_copy_u32_to_vm(d->vm,rxd_addr+0x08,rxd.rdes[2]); |
972 |
|
|
physmem_copy_u32_to_vm(d->vm,rxd_addr+0x0c,rxd.rdes[3]); |
973 |
|
|
|
974 |
|
|
/* Goto to the next descriptor, and wrap if necessary */ |
975 |
|
|
if (++d->rdh == (d->rdlen / sizeof(struct rx_desc))) |
976 |
|
|
d->rdh = 0; |
977 |
|
|
} |
978 |
|
|
|
979 |
|
|
/* Update the interrupt cause register and trigger IRQ if needed */ |
980 |
|
|
d->icr |= icr; |
981 |
|
|
dev_i8254x_update_irq_status(d); |
982 |
|
|
LVG_UNLOCK(d); |
983 |
|
|
return(TRUE); |
984 |
|
|
} |
985 |
|
|
|
986 |
|
|
/* Handle the RX ring */ |
987 |
|
|
static int dev_i8254x_handle_rxring(netio_desc_t *nio, |
988 |
|
|
u_char *pkt,ssize_t pkt_len, |
989 |
|
|
struct i8254x_data *d) |
990 |
|
|
{ |
991 |
|
|
/* |
992 |
|
|
* Don't start receive if RX has not been enabled in RCTL register. |
993 |
|
|
*/ |
994 |
|
|
if (!(d->rctl & I8254X_RCTL_EN)) |
995 |
|
|
return(FALSE); |
996 |
|
|
|
997 |
|
|
#if DEBUG_RECEIVE |
998 |
|
|
LVG_LOG(d,"receiving a packet of %d bytes\n",pkt_len); |
999 |
|
|
mem_dump(log_file,pkt,pkt_len); |
1000 |
|
|
#endif |
1001 |
|
|
|
1002 |
|
|
/* |
1003 |
|
|
* Receive only multicast/broadcast trafic + unicast traffic |
1004 |
|
|
* for this virtual machine. |
1005 |
|
|
*/ |
1006 |
|
|
//if (dec21140_handle_mac_addr(d,pkt)) |
1007 |
|
|
return(dev_i8254x_receive_pkt(d,pkt,pkt_len)); |
1008 |
|
|
|
1009 |
|
|
return(FALSE); |
1010 |
|
|
} |
1011 |
|
|
|
1012 |
|
|
/* |
1013 |
|
|
* pci_i8254x_read() |
1014 |
|
|
* |
1015 |
|
|
* Read a PCI register. |
1016 |
|
|
*/ |
1017 |
|
|
static m_uint32_t pci_i8254x_read(cpu_gen_t *cpu,struct pci_device *dev, |
1018 |
dpavlin |
11 |
int reg) |
1019 |
dpavlin |
7 |
{ |
1020 |
|
|
struct i8254x_data *d = dev->priv_data; |
1021 |
|
|
|
1022 |
|
|
#if DEBUG_PCI_REGS |
1023 |
|
|
I8254X_LOG(d,"read PCI register 0x%x\n",reg); |
1024 |
|
|
#endif |
1025 |
|
|
|
1026 |
|
|
switch (reg) { |
1027 |
|
|
case 0x00: |
1028 |
|
|
return((I8254X_PCI_PRODUCT_ID << 16) | I8254X_PCI_VENDOR_ID); |
1029 |
|
|
case 0x08: |
1030 |
dpavlin |
8 |
return(0x02000003); |
1031 |
dpavlin |
7 |
case PCI_REG_BAR0: |
1032 |
|
|
return(d->dev->phys_addr); |
1033 |
|
|
default: |
1034 |
|
|
return(0); |
1035 |
|
|
} |
1036 |
|
|
} |
1037 |
|
|
|
1038 |
|
|
/* |
1039 |
|
|
* pci_i8254x_write() |
1040 |
|
|
* |
1041 |
|
|
* Write a PCI register. |
1042 |
|
|
*/ |
1043 |
|
|
static void pci_i8254x_write(cpu_gen_t *cpu,struct pci_device *dev, |
1044 |
dpavlin |
11 |
int reg,m_uint32_t value) |
1045 |
dpavlin |
7 |
{ |
1046 |
|
|
struct i8254x_data *d = dev->priv_data; |
1047 |
|
|
|
1048 |
|
|
#if DEBUG_PCI_REGS |
1049 |
|
|
LVG_LOG(d,"write PCI register 0x%x, value 0x%x\n",reg,value); |
1050 |
|
|
#endif |
1051 |
|
|
|
1052 |
|
|
switch(reg) { |
1053 |
|
|
case PCI_REG_BAR0: |
1054 |
|
|
vm_map_device(cpu->vm,d->dev,(m_uint64_t)value); |
1055 |
|
|
LVG_LOG(d,"registers are mapped at 0x%x\n",value); |
1056 |
|
|
break; |
1057 |
|
|
} |
1058 |
|
|
} |
1059 |
|
|
|
1060 |
|
|
/* |
1061 |
|
|
* dev_i8254x_init() |
1062 |
|
|
*/ |
1063 |
|
|
struct i8254x_data * |
1064 |
|
|
dev_i8254x_init(vm_instance_t *vm,char *name,int interface_type, |
1065 |
|
|
struct pci_bus *pci_bus,int pci_device,int irq) |
1066 |
|
|
{ |
1067 |
|
|
struct i8254x_data *d; |
1068 |
|
|
struct pci_device *pci_dev; |
1069 |
|
|
struct vdevice *dev; |
1070 |
|
|
|
1071 |
|
|
/* Allocate the private data structure for I8254X */ |
1072 |
|
|
if (!(d = malloc(sizeof(*d)))) { |
1073 |
|
|
fprintf(stderr,"%s (i8254x): out of memory\n",name); |
1074 |
|
|
return NULL; |
1075 |
|
|
} |
1076 |
|
|
|
1077 |
|
|
memset(d,0,sizeof(*d)); |
1078 |
|
|
pthread_mutex_init(&d->lock,NULL); |
1079 |
|
|
|
1080 |
|
|
/* Add as PCI device */ |
1081 |
|
|
pci_dev = pci_dev_add(pci_bus,name, |
1082 |
|
|
I8254X_PCI_VENDOR_ID,I8254X_PCI_PRODUCT_ID, |
1083 |
|
|
pci_device,0,irq, |
1084 |
|
|
d,NULL,pci_i8254x_read,pci_i8254x_write); |
1085 |
|
|
|
1086 |
|
|
if (!pci_dev) { |
1087 |
|
|
fprintf(stderr,"%s (i8254x): unable to create PCI device.\n",name); |
1088 |
|
|
goto err_pci_dev; |
1089 |
|
|
} |
1090 |
|
|
|
1091 |
|
|
/* Create the device itself */ |
1092 |
|
|
if (!(dev = dev_create(name))) { |
1093 |
|
|
fprintf(stderr,"%s (i8254x): unable to create device.\n",name); |
1094 |
|
|
goto err_dev; |
1095 |
|
|
} |
1096 |
|
|
|
1097 |
|
|
d->name = name; |
1098 |
|
|
d->vm = vm; |
1099 |
|
|
d->pci_dev = pci_dev; |
1100 |
|
|
d->dev = dev; |
1101 |
|
|
|
1102 |
|
|
dev->phys_addr = 0; |
1103 |
|
|
dev->phys_len = 0x10000; |
1104 |
|
|
dev->handler = dev_i8254x_access; |
1105 |
|
|
dev->priv_data = d; |
1106 |
|
|
return(d); |
1107 |
|
|
|
1108 |
|
|
err_dev: |
1109 |
|
|
pci_dev_remove(pci_dev); |
1110 |
|
|
err_pci_dev: |
1111 |
|
|
free(d); |
1112 |
|
|
return NULL; |
1113 |
|
|
} |
1114 |
|
|
|
1115 |
|
|
/* Remove an Intel i8254x device */ |
1116 |
|
|
void dev_i8254x_remove(struct i8254x_data *d) |
1117 |
|
|
{ |
1118 |
|
|
if (d != NULL) { |
1119 |
|
|
pci_dev_remove(d->pci_dev); |
1120 |
|
|
vm_unbind_device(d->vm,d->dev); |
1121 |
|
|
cpu_group_rebuild_mts(d->vm->cpu_group); |
1122 |
|
|
free(d->dev); |
1123 |
|
|
free(d); |
1124 |
|
|
} |
1125 |
|
|
} |
1126 |
|
|
|
1127 |
|
|
/* Bind a NIO to an Intel i8254x device */ |
1128 |
|
|
int dev_i8254x_set_nio(struct i8254x_data *d,netio_desc_t *nio) |
1129 |
|
|
{ |
1130 |
|
|
/* check that a NIO is not already bound */ |
1131 |
|
|
if (d->nio != NULL) |
1132 |
|
|
return(-1); |
1133 |
|
|
|
1134 |
|
|
d->nio = nio; |
1135 |
|
|
d->tx_tid = ptask_add((ptask_callback)dev_i8254x_handle_txring,d,NULL); |
1136 |
|
|
netio_rxl_add(nio,(netio_rx_handler_t)dev_i8254x_handle_rxring,d,NULL); |
1137 |
|
|
return(0); |
1138 |
|
|
} |
1139 |
|
|
|
1140 |
|
|
/* Unbind a NIO from an Intel i8254x device */ |
1141 |
|
|
void dev_i8254x_unset_nio(struct i8254x_data *d) |
1142 |
|
|
{ |
1143 |
|
|
if (d->nio != NULL) { |
1144 |
|
|
ptask_remove(d->tx_tid); |
1145 |
|
|
netio_rxl_remove(d->nio); |
1146 |
|
|
d->nio = NULL; |
1147 |
|
|
} |
1148 |
|
|
} |