Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_e82545.c
Show First 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | |||||
#include "e1000_regs.h" | #include "e1000_regs.h" | ||||
#include "e1000_defines.h" | #include "e1000_defines.h" | ||||
#include "mii.h" | #include "mii.h" | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
#include "pci_emul.h" | #include "pci_emul.h" | ||||
#include "mevent.h" | #include "mevent.h" | ||||
#include "net_utils.h" | #include "net_utils.h" | ||||
#include "net_backends.h" | |||||
/* Hardware/register definitions XXX: move some to common code. */ | /* Hardware/register definitions XXX: move some to common code. */ | ||||
#define E82545_VENDOR_ID_INTEL 0x8086 | #define E82545_VENDOR_ID_INTEL 0x8086 | ||||
#define E82545_DEV_ID_82545EM_COPPER 0x100F | #define E82545_DEV_ID_82545EM_COPPER 0x100F | ||||
#define E82545_SUBDEV_ID 0x1008 | #define E82545_SUBDEV_ID 0x1008 | ||||
#define E82545_REVISION_4 4 | #define E82545_REVISION_4 4 | ||||
▲ Show 20 Lines • Show All 163 Lines • ▼ Show 20 Lines | |||||
int eu_addrsel; | int eu_addrsel; | ||||
struct ether_addr eu_eth; | struct ether_addr eu_eth; | ||||
}; | }; | ||||
struct e82545_softc { | struct e82545_softc { | ||||
struct pci_devinst *esc_pi; | struct pci_devinst *esc_pi; | ||||
struct vmctx *esc_ctx; | struct vmctx *esc_ctx; | ||||
struct mevent *esc_mevp; | |||||
struct mevent *esc_mevpitr; | struct mevent *esc_mevpitr; | ||||
pthread_mutex_t esc_mtx; | pthread_mutex_t esc_mtx; | ||||
struct ether_addr esc_mac; | struct ether_addr esc_mac; | ||||
int esc_tapfd; | net_backend_t *esc_be; | ||||
/* General */ | /* General */ | ||||
uint32_t esc_CTRL; /* x0000 device ctl */ | uint32_t esc_CTRL; /* x0000 device ctl */ | ||||
uint32_t esc_FCAL; /* x0028 flow ctl addr lo */ | uint32_t esc_FCAL; /* x0028 flow ctl addr lo */ | ||||
uint32_t esc_FCAH; /* x002C flow ctl addr hi */ | uint32_t esc_FCAH; /* x002C flow ctl addr hi */ | ||||
uint32_t esc_FCT; /* x0030 flow ctl type */ | uint32_t esc_FCT; /* x0030 flow ctl type */ | ||||
uint32_t esc_VET; /* x0038 VLAN eth type */ | uint32_t esc_VET; /* x0038 VLAN eth type */ | ||||
uint32_t esc_FCTTV; /* x0170 flow ctl tx timer */ | uint32_t esc_FCTTV; /* x0170 flow ctl tx timer */ | ||||
▲ Show 20 Lines • Show All 89 Lines • ▼ Show 20 Lines | |||||
#define E82545_NVM_MODE_DATAOUT 0x2 | #define E82545_NVM_MODE_DATAOUT 0x2 | ||||
/* EEPROM data */ | /* EEPROM data */ | ||||
uint16_t eeprom_data[E82545_NVM_EEPROM_SIZE]; | uint16_t eeprom_data[E82545_NVM_EEPROM_SIZE]; | ||||
}; | }; | ||||
static void e82545_reset(struct e82545_softc *sc, int dev); | static void e82545_reset(struct e82545_softc *sc, int dev); | ||||
static void e82545_rx_enable(struct e82545_softc *sc); | static void e82545_rx_enable(struct e82545_softc *sc); | ||||
static void e82545_rx_disable(struct e82545_softc *sc); | static void e82545_rx_disable(struct e82545_softc *sc); | ||||
static void e82545_tap_callback(int fd, enum ev_type type, void *param); | static void e82545_rx_callback(int fd, enum ev_type type, void *param); | ||||
static void e82545_tx_start(struct e82545_softc *sc); | static void e82545_tx_start(struct e82545_softc *sc); | ||||
static void e82545_tx_enable(struct e82545_softc *sc); | static void e82545_tx_enable(struct e82545_softc *sc); | ||||
static void e82545_tx_disable(struct e82545_softc *sc); | static void e82545_tx_disable(struct e82545_softc *sc); | ||||
static inline int | static inline int | ||||
e82545_size_stat_index(uint32_t size) | e82545_size_stat_index(uint32_t size) | ||||
{ | { | ||||
if (size <= 64) { | if (size <= 64) { | ||||
▲ Show 20 Lines • Show All 452 Lines • ▼ Show 20 Lines | |||||
case (E1000_RCTL_SZ_256): return (256); | case (E1000_RCTL_SZ_256): return (256); | ||||
case (E1000_RCTL_BSEX|E1000_RCTL_SZ_16384): return (16384); | case (E1000_RCTL_BSEX|E1000_RCTL_SZ_16384): return (16384); | ||||
case (E1000_RCTL_BSEX|E1000_RCTL_SZ_8192): return (8192); | case (E1000_RCTL_BSEX|E1000_RCTL_SZ_8192): return (8192); | ||||
case (E1000_RCTL_BSEX|E1000_RCTL_SZ_4096): return (4096); | case (E1000_RCTL_BSEX|E1000_RCTL_SZ_4096): return (4096); | ||||
} | } | ||||
return (256); /* Forbidden value. */ | return (256); /* Forbidden value. */ | ||||
} | } | ||||
static uint8_t dummybuf[2048]; | |||||
/* XXX one packet at a time until this is debugged */ | /* XXX one packet at a time until this is debugged */ | ||||
static void | static void | ||||
e82545_tap_callback(int fd, enum ev_type type, void *param) | e82545_rx_callback(int fd, enum ev_type type, void *param) | ||||
{ | { | ||||
struct e82545_softc *sc = param; | struct e82545_softc *sc = param; | ||||
struct e1000_rx_desc *rxd; | struct e1000_rx_desc *rxd; | ||||
struct iovec vec[64]; | struct iovec vec[64]; | ||||
int left, len, lim, maxpktsz, maxpktdesc, bufsz, i, n, size; | int left, len, lim, maxpktsz, maxpktdesc, bufsz, i, n, size; | ||||
uint32_t cause = 0; | uint32_t cause = 0; | ||||
uint16_t *tp, tag, head; | uint16_t *tp, tag, head; | ||||
pthread_mutex_lock(&sc->esc_mtx); | pthread_mutex_lock(&sc->esc_mtx); | ||||
DPRINTF("rx_run: head %x, tail %x\r\n", sc->esc_RDH, sc->esc_RDT); | DPRINTF("rx_run: head %x, tail %x\r\n", sc->esc_RDH, sc->esc_RDT); | ||||
if (!sc->esc_rx_enabled || sc->esc_rx_loopback) { | if (!sc->esc_rx_enabled || sc->esc_rx_loopback) { | ||||
DPRINTF("rx disabled (!%d || %d) -- packet(s) dropped\r\n", | DPRINTF("rx disabled (!%d || %d) -- packet(s) dropped\r\n", | ||||
sc->esc_rx_enabled, sc->esc_rx_loopback); | sc->esc_rx_enabled, sc->esc_rx_loopback); | ||||
while (read(sc->esc_tapfd, dummybuf, sizeof(dummybuf)) > 0) { | while (netbe_rx_discard(sc->esc_be) > 0) { | ||||
} | } | ||||
goto done1; | goto done1; | ||||
} | } | ||||
bufsz = e82545_bufsz(sc->esc_RCTL); | bufsz = e82545_bufsz(sc->esc_RCTL); | ||||
maxpktsz = (sc->esc_RCTL & E1000_RCTL_LPE) ? 16384 : 1522; | maxpktsz = (sc->esc_RCTL & E1000_RCTL_LPE) ? 16384 : 1522; | ||||
maxpktdesc = (maxpktsz + bufsz - 1) / bufsz; | maxpktdesc = (maxpktsz + bufsz - 1) / bufsz; | ||||
size = sc->esc_RDLEN / 16; | size = sc->esc_RDLEN / 16; | ||||
head = sc->esc_RDH; | head = sc->esc_RDH; | ||||
left = (size + sc->esc_RDT - head) % size; | left = (size + sc->esc_RDT - head) % size; | ||||
if (left < maxpktdesc) { | if (left < maxpktdesc) { | ||||
DPRINTF("rx overflow (%d < %d) -- packet(s) dropped\r\n", | DPRINTF("rx overflow (%d < %d) -- packet(s) dropped\r\n", | ||||
left, maxpktdesc); | left, maxpktdesc); | ||||
while (read(sc->esc_tapfd, dummybuf, sizeof(dummybuf)) > 0) { | while (netbe_rx_discard(sc->esc_be) > 0) { | ||||
} | } | ||||
goto done1; | goto done1; | ||||
} | } | ||||
sc->esc_rx_active = 1; | sc->esc_rx_active = 1; | ||||
pthread_mutex_unlock(&sc->esc_mtx); | pthread_mutex_unlock(&sc->esc_mtx); | ||||
for (lim = size / 4; lim > 0 && left >= maxpktdesc; lim -= n) { | for (lim = size / 4; lim > 0 && left >= maxpktdesc; lim -= n) { | ||||
/* Grab rx descriptor pointed to by the head pointer */ | /* Grab rx descriptor pointed to by the head pointer */ | ||||
for (i = 0; i < maxpktdesc; i++) { | for (i = 0; i < maxpktdesc; i++) { | ||||
rxd = &sc->esc_rxdesc[(head + i) % size]; | rxd = &sc->esc_rxdesc[(head + i) % size]; | ||||
vec[i].iov_base = paddr_guest2host(sc->esc_ctx, | vec[i].iov_base = paddr_guest2host(sc->esc_ctx, | ||||
rxd->buffer_addr, bufsz); | rxd->buffer_addr, bufsz); | ||||
vec[i].iov_len = bufsz; | vec[i].iov_len = bufsz; | ||||
} | } | ||||
len = readv(sc->esc_tapfd, vec, maxpktdesc); | len = netbe_recv(sc->esc_be, vec, maxpktdesc); | ||||
if (len <= 0) { | if (len <= 0) { | ||||
DPRINTF("tap: readv() returned %d\n", len); | DPRINTF("netbe_recv() returned %d\n", len); | ||||
goto done; | goto done; | ||||
} | } | ||||
/* | /* | ||||
* Adjust the packet length based on whether the CRC needs | * Adjust the packet length based on whether the CRC needs | ||||
* to be stripped or if the packet is less than the minimum | * to be stripped or if the packet is less than the minimum | ||||
* eth packet size. | * eth packet size. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | |||||
DPRINTF("tx cksum: iovcnt/s/off/len %d/%d/%d/%d\r\n", | DPRINTF("tx cksum: iovcnt/s/off/len %d/%d/%d/%d\r\n", | ||||
iovcnt, ck->ck_start, ck->ck_off, ck->ck_len); | iovcnt, ck->ck_start, ck->ck_off, ck->ck_len); | ||||
cklen = ck->ck_len ? ck->ck_len - ck->ck_start + 1 : INT_MAX; | cklen = ck->ck_len ? ck->ck_len - ck->ck_start + 1 : INT_MAX; | ||||
cksum = e82545_iov_checksum(iov, iovcnt, ck->ck_start, cklen); | cksum = e82545_iov_checksum(iov, iovcnt, ck->ck_start, cklen); | ||||
*(uint16_t *)((uint8_t *)iov[0].iov_base + ck->ck_off) = ~cksum; | *(uint16_t *)((uint8_t *)iov[0].iov_base + ck->ck_off) = ~cksum; | ||||
} | } | ||||
static void | static inline void | ||||
jhb: Does 'inline' actually make a difference? I recently found in another patch that clang ignored… | |||||
vmaffioneAuthorUnsubmitted Done Inline ActionsFor sure it doesn't for e1000, so in this case it's useless. vmaffione: For sure it doesn't for e1000, so in this case it's useless. | |||||
e82545_transmit_backend(struct e82545_softc *sc, struct iovec *iov, int iovcnt) | e82545_transmit_backend(struct e82545_softc *sc, struct iovec *iov, int iovcnt) | ||||
{ | { | ||||
if (sc->esc_tapfd == -1) | if (sc->esc_be == NULL) | ||||
return; | return; | ||||
(void) writev(sc->esc_tapfd, iov, iovcnt); | (void) netbe_send(sc->esc_be, iov, iovcnt); | ||||
} | } | ||||
static void | static void | ||||
e82545_transmit_done(struct e82545_softc *sc, uint16_t head, uint16_t tail, | e82545_transmit_done(struct e82545_softc *sc, uint16_t head, uint16_t tail, | ||||
uint16_t dsize, int *tdwb) | uint16_t dsize, int *tdwb) | ||||
{ | { | ||||
union e1000_tx_udesc *dsc; | union e1000_tx_udesc *dsc; | ||||
▲ Show 20 Lines • Show All 584 Lines • ▼ Show 20 Lines | |||||
sc->esc_TXCW = 0; | sc->esc_TXCW = 0; | ||||
sc->esc_TCTL = 0; | sc->esc_TCTL = 0; | ||||
sc->esc_TDLEN = 0; | sc->esc_TDLEN = 0; | ||||
sc->esc_TDT = 0; | sc->esc_TDT = 0; | ||||
sc->esc_TDHr = sc->esc_TDH = 0; | sc->esc_TDHr = sc->esc_TDH = 0; | ||||
sc->esc_TXDCTL = 0; | sc->esc_TXDCTL = 0; | ||||
} | } | ||||
static void | |||||
e82545_open_tap(struct e82545_softc *sc, char *opts) | |||||
{ | |||||
char tbuf[80]; | |||||
#ifndef WITHOUT_CAPSICUM | |||||
cap_rights_t rights; | |||||
#endif | |||||
if (opts == NULL) { | |||||
sc->esc_tapfd = -1; | |||||
return; | |||||
} | |||||
strcpy(tbuf, "/dev/"); | |||||
strlcat(tbuf, opts, sizeof(tbuf)); | |||||
sc->esc_tapfd = open(tbuf, O_RDWR); | |||||
if (sc->esc_tapfd == -1) { | |||||
DPRINTF("unable to open tap device %s\n", opts); | |||||
exit(4); | |||||
} | |||||
/* | |||||
* Set non-blocking and register for read | |||||
* notifications with the event loop | |||||
*/ | |||||
int opt = 1; | |||||
if (ioctl(sc->esc_tapfd, FIONBIO, &opt) < 0) { | |||||
WPRINTF("tap device O_NONBLOCK failed: %d\n", errno); | |||||
close(sc->esc_tapfd); | |||||
sc->esc_tapfd = -1; | |||||
} | |||||
#ifndef WITHOUT_CAPSICUM | |||||
cap_rights_init(&rights, CAP_EVENT, CAP_READ, CAP_WRITE); | |||||
if (caph_rights_limit(sc->esc_tapfd, &rights) == -1) | |||||
errx(EX_OSERR, "Unable to apply rights for sandbox"); | |||||
#endif | |||||
sc->esc_mevp = mevent_add(sc->esc_tapfd, | |||||
EVF_READ, | |||||
e82545_tap_callback, | |||||
sc); | |||||
if (sc->esc_mevp == NULL) { | |||||
DPRINTF("Could not register mevent %d\n", EVF_READ); | |||||
close(sc->esc_tapfd); | |||||
sc->esc_tapfd = -1; | |||||
} | |||||
} | |||||
static int | static int | ||||
e82545_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | e82545_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | ||||
{ | { | ||||
char nstr[80]; | char nstr[80]; | ||||
struct e82545_softc *sc; | struct e82545_softc *sc; | ||||
char *devname; | char *devname; | ||||
char *vtopts; | char *vtopts; | ||||
int mac_provided; | int mac_provided; | ||||
Show All 32 Lines | |||||
pci_emul_alloc_bar(pi, E82545_BAR_REGISTER, PCIBAR_MEM32, | pci_emul_alloc_bar(pi, E82545_BAR_REGISTER, PCIBAR_MEM32, | ||||
E82545_BAR_REGISTER_LEN); | E82545_BAR_REGISTER_LEN); | ||||
pci_emul_alloc_bar(pi, E82545_BAR_FLASH, PCIBAR_MEM32, | pci_emul_alloc_bar(pi, E82545_BAR_FLASH, PCIBAR_MEM32, | ||||
E82545_BAR_FLASH_LEN); | E82545_BAR_FLASH_LEN); | ||||
pci_emul_alloc_bar(pi, E82545_BAR_IO, PCIBAR_IO, | pci_emul_alloc_bar(pi, E82545_BAR_IO, PCIBAR_IO, | ||||
E82545_BAR_IO_LEN); | E82545_BAR_IO_LEN); | ||||
/* | /* | ||||
* Attempt to open the tap device and read the MAC address | * Attempt to open the net backend and read the MAC address | ||||
* if specified. Copied from virtio-net, slightly modified. | * if specified. Copied from virtio-net, slightly modified. | ||||
*/ | */ | ||||
mac_provided = 0; | mac_provided = 0; | ||||
sc->esc_tapfd = -1; | sc->esc_be = NULL; | ||||
if (opts != NULL) { | if (opts != NULL) { | ||||
int err; | int err; | ||||
devname = vtopts = strdup(opts); | devname = vtopts = strdup(opts); | ||||
(void) strsep(&vtopts, ","); | (void) strsep(&vtopts, ","); | ||||
if (vtopts != NULL) { | if (vtopts != NULL) { | ||||
err = net_parsemac(vtopts, sc->esc_mac.octet); | err = net_parsemac(vtopts, sc->esc_mac.octet); | ||||
if (err != 0) { | if (err != 0) { | ||||
free(devname); | free(devname); | ||||
return (err); | return (err); | ||||
} | } | ||||
mac_provided = 1; | mac_provided = 1; | ||||
} | } | ||||
if (strncmp(devname, "tap", 3) == 0 || | err = netbe_init(&sc->esc_be, devname, e82545_rx_callback, sc); | ||||
strncmp(devname, "vmnet", 5) == 0) | |||||
e82545_open_tap(sc, devname); | |||||
free(devname); | free(devname); | ||||
if (err) | |||||
return (err); | |||||
} | } | ||||
if (!mac_provided) { | if (!mac_provided) { | ||||
net_genmac(pi, sc->esc_mac.octet); | net_genmac(pi, sc->esc_mac.octet); | ||||
} | } | ||||
/* H/w initiated reset */ | /* H/w initiated reset */ | ||||
e82545_reset(sc, 0); | e82545_reset(sc, 0); | ||||
Show All 12 Lines |
Does 'inline' actually make a difference? I recently found in another patch that clang ignored this and made up its own mind to inline or not.