Before safecopies, the IO_ENDPT and DL_ENDPT message fields were needed to know which actual process to copy data from/to, as that process may not always be the caller. Now that we have full safecopy support, these fields have become useless for that purpose: the owner of the grant is *always* the caller. Allowing the caller to supply another endpoint is in fact dangerous, because the callee may then end up using a grant from a third party. One could call this a variant of the confused deputy problem. From now on, safecopy calls should always use the caller's endpoint as grant owner. This fully obsoletes the DL_ENDPT field in the inet/ethernet protocol. IO_ENDPT has other uses besides identifying the grant owner though. This patch renames IO_ENDPT to USER_ENDPT, not only because that is a more fitting name (it should never be used for I/O after all), but also in order to intentionally break any old system source code outside the base system. If this patch breaks your code, fixing it is fairly simple: - DL_ENDPT should be replaced with m_source; - IO_ENDPT should be replaced with m_source when used for safecopies; - IO_ENDPT should be replaced with USER_ENDPT for any other use, e.g. when setting REP_ENDPT, matching requests in CANCEL calls, getting DEV_SELECT flags, and retrieving of the real user process's endpoint in DEV_OPEN. The changes in this patch are binary backward compatible.
839 lines
19 KiB
C
839 lines
19 KiB
C
/*
|
|
inet/mnx_eth.c
|
|
|
|
Created: Jan 2, 1992 by Philip Homburg
|
|
|
|
Copyright 1995 Philip Homburg
|
|
*/
|
|
|
|
#include "inet.h"
|
|
#include <minix/safecopies.h>
|
|
#include "proto.h"
|
|
#include "osdep_eth.h"
|
|
#include "generic/type.h"
|
|
|
|
#include "generic/assert.h"
|
|
#include "generic/buf.h"
|
|
#include "generic/clock.h"
|
|
#include "generic/eth.h"
|
|
#include "generic/eth_int.h"
|
|
#include "generic/sr.h"
|
|
|
|
THIS_FILE
|
|
|
|
FORWARD _PROTOTYPE( void setup_read, (eth_port_t *eth_port) );
|
|
FORWARD _PROTOTYPE( void read_int, (eth_port_t *eth_port, int count) );
|
|
FORWARD _PROTOTYPE( void eth_issue_send, (eth_port_t *eth_port) );
|
|
FORWARD _PROTOTYPE( void write_int, (eth_port_t *eth_port) );
|
|
FORWARD _PROTOTYPE( void eth_restart, (eth_port_t *eth_port,
|
|
endpoint_t endpoint) );
|
|
FORWARD _PROTOTYPE( void send_getstat, (eth_port_t *eth_port) );
|
|
|
|
PUBLIC void osdep_eth_init()
|
|
{
|
|
int i, j, rport;
|
|
struct eth_conf *ecp;
|
|
eth_port_t *eth_port, *rep;
|
|
cp_grant_id_t gid;
|
|
|
|
/* First initialize normal ethernet interfaces */
|
|
for (i= 0, ecp= eth_conf, eth_port= eth_port_table;
|
|
i<eth_conf_nr; i++, ecp++, eth_port++)
|
|
{
|
|
/* Set all grants to invalid */
|
|
for (j= 0; j<IOVEC_NR; j++)
|
|
eth_port->etp_osdep.etp_wr_iovec[j].iov_grant= -1;
|
|
eth_port->etp_osdep.etp_wr_vec_grant= -1;
|
|
for (j= 0; j<RD_IOVEC; j++)
|
|
eth_port->etp_osdep.etp_rd_iovec[j].iov_grant= -1;
|
|
eth_port->etp_osdep.etp_rd_vec_grant= -1;
|
|
|
|
eth_port->etp_osdep.etp_state= OEPS_INIT;
|
|
eth_port->etp_osdep.etp_flags= OEPF_EMPTY;
|
|
eth_port->etp_osdep.etp_stat_gid= -1;
|
|
eth_port->etp_osdep.etp_stat_buf= NULL;
|
|
|
|
if (eth_is_vlan(ecp))
|
|
continue;
|
|
|
|
/* Allocate grants */
|
|
for (j= 0; j<IOVEC_NR; j++)
|
|
{
|
|
if (cpf_getgrants(&gid, 1) != 1)
|
|
{
|
|
ip_panic((
|
|
"osdep_eth_init: cpf_getgrants failed: %d\n",
|
|
errno));
|
|
}
|
|
eth_port->etp_osdep.etp_wr_iovec[j].iov_grant= gid;
|
|
}
|
|
if (cpf_getgrants(&gid, 1) != 1)
|
|
{
|
|
ip_panic((
|
|
"osdep_eth_init: cpf_getgrants failed: %d\n",
|
|
errno));
|
|
}
|
|
eth_port->etp_osdep.etp_wr_vec_grant= gid;
|
|
for (j= 0; j<RD_IOVEC; j++)
|
|
{
|
|
if (cpf_getgrants(&gid, 1) != 1)
|
|
{
|
|
ip_panic((
|
|
"osdep_eth_init: cpf_getgrants failed: %d\n",
|
|
errno));
|
|
}
|
|
eth_port->etp_osdep.etp_rd_iovec[j].iov_grant= gid;
|
|
}
|
|
if (cpf_getgrants(&gid, 1) != 1)
|
|
{
|
|
ip_panic((
|
|
"osdep_eth_init: cpf_getgrants failed: %d\n",
|
|
errno));
|
|
}
|
|
eth_port->etp_osdep.etp_rd_vec_grant= gid;
|
|
|
|
eth_port->etp_osdep.etp_task= NONE;
|
|
eth_port->etp_osdep.etp_recvconf= 0;
|
|
ev_init(ð_port->etp_osdep.etp_recvev);
|
|
|
|
sr_add_minor(if2minor(ecp->ec_ifno, ETH_DEV_OFF),
|
|
i, eth_open, eth_close, eth_read,
|
|
eth_write, eth_ioctl, eth_cancel, eth_select);
|
|
|
|
eth_port->etp_flags |= EPF_ENABLED;
|
|
eth_port->etp_vlan= 0;
|
|
eth_port->etp_vlan_port= NULL;
|
|
eth_port->etp_wr_pack= 0;
|
|
eth_port->etp_rd_pack= 0;
|
|
}
|
|
|
|
/* And now come the VLANs */
|
|
for (i= 0, ecp= eth_conf, eth_port= eth_port_table;
|
|
i<eth_conf_nr; i++, ecp++, eth_port++)
|
|
{
|
|
if (!eth_is_vlan(ecp))
|
|
continue;
|
|
|
|
eth_port->etp_osdep.etp_task= NONE;
|
|
ev_init(ð_port->etp_osdep.etp_recvev);
|
|
|
|
rport= ecp->ec_port;
|
|
assert(rport >= 0 && rport < eth_conf_nr);
|
|
rep= ð_port_table[rport];
|
|
if (!(rep->etp_flags & EPF_ENABLED))
|
|
{
|
|
printf(
|
|
"eth%d: underlying ethernet device %d not enabled",
|
|
i, rport);
|
|
continue;
|
|
}
|
|
if (rep->etp_vlan != 0)
|
|
{
|
|
printf(
|
|
"eth%d: underlying ethernet device %d is a VLAN",
|
|
i, rport);
|
|
continue;
|
|
}
|
|
|
|
if (rep->etp_flags & EPF_GOT_ADDR)
|
|
{
|
|
eth_port->etp_ethaddr= rep->etp_ethaddr;
|
|
printf("osdep_eth_init: setting EPF_GOT_ADDR\n");
|
|
eth_port->etp_flags |= EPF_GOT_ADDR;
|
|
}
|
|
|
|
sr_add_minor(if2minor(ecp->ec_ifno, ETH_DEV_OFF),
|
|
i, eth_open, eth_close, eth_read,
|
|
eth_write, eth_ioctl, eth_cancel, eth_select);
|
|
|
|
eth_port->etp_flags |= EPF_ENABLED;
|
|
eth_port->etp_vlan= ecp->ec_vlan;
|
|
eth_port->etp_vlan_port= rep;
|
|
assert(eth_port->etp_vlan != 0);
|
|
eth_port->etp_wr_pack= 0;
|
|
eth_port->etp_rd_pack= 0;
|
|
eth_reg_vlan(rep, eth_port);
|
|
}
|
|
}
|
|
|
|
PUBLIC void eth_write_port(eth_port, pack)
|
|
eth_port_t *eth_port;
|
|
acc_t *pack;
|
|
{
|
|
assert(!no_ethWritePort);
|
|
assert(!eth_port->etp_vlan);
|
|
|
|
assert(eth_port->etp_wr_pack == NULL);
|
|
eth_port->etp_wr_pack= pack;
|
|
|
|
if (eth_port->etp_osdep.etp_state != OEPS_IDLE)
|
|
{
|
|
eth_port->etp_osdep.etp_flags |= OEPF_NEED_SEND;
|
|
return;
|
|
}
|
|
|
|
|
|
eth_issue_send(eth_port);
|
|
}
|
|
|
|
PUBLIC void eth_rec(message *m)
|
|
{
|
|
int i, r, m_type, flags;
|
|
eth_port_t *loc_port, *vlan_port;
|
|
|
|
m_type= m->m_type;
|
|
|
|
assert(m_type == DL_CONF_REPLY || m_type == DL_TASK_REPLY ||
|
|
m_type == DL_STAT_REPLY);
|
|
|
|
for (i=0, loc_port= eth_port_table; i<eth_conf_nr; i++, loc_port++)
|
|
{
|
|
if (loc_port->etp_osdep.etp_task == m->m_source)
|
|
break;
|
|
}
|
|
if (i >= eth_conf_nr)
|
|
{
|
|
printf("eth_rec: message 0x%x from unknown driver %d\n",
|
|
m_type, m->m_source);
|
|
return;
|
|
}
|
|
|
|
if (loc_port->etp_osdep.etp_state == OEPS_CONF_SENT)
|
|
{
|
|
if (m_type == DL_TASK_REPLY)
|
|
{
|
|
flags= m->DL_FLAGS;
|
|
|
|
if (flags & DL_PACK_SEND)
|
|
write_int(loc_port);
|
|
if (flags & DL_PACK_RECV)
|
|
read_int(loc_port, m->DL_COUNT);
|
|
return;
|
|
}
|
|
|
|
if (m_type != DL_CONF_REPLY)
|
|
{
|
|
printf(
|
|
"eth_rec: got bad message type 0x%x from %d in CONF state\n",
|
|
m_type, m->m_source);
|
|
return;
|
|
}
|
|
|
|
r= m->DL_STAT;
|
|
if (r < 0)
|
|
{
|
|
ip_warning(("eth_rec: DL_CONF returned error %d\n",
|
|
r));
|
|
|
|
/* Just leave it in limbo. Nothing more we can do. */
|
|
return;
|
|
}
|
|
|
|
loc_port->etp_osdep.etp_flags &= ~OEPF_NEED_CONF;
|
|
loc_port->etp_osdep.etp_state= OEPS_IDLE;
|
|
loc_port->etp_flags |= EPF_ENABLED;
|
|
|
|
loc_port->etp_ethaddr= *(ether_addr_t *)m->DL_HWADDR;
|
|
if (!(loc_port->etp_flags & EPF_GOT_ADDR))
|
|
{
|
|
loc_port->etp_flags |= EPF_GOT_ADDR;
|
|
#if 0
|
|
printf("eth_rec: calling eth_restart_ioctl\n");
|
|
#endif
|
|
eth_restart_ioctl(loc_port);
|
|
|
|
/* Also update any VLANs on this device */
|
|
for (i=0, vlan_port= eth_port_table; i<eth_conf_nr;
|
|
i++, vlan_port++)
|
|
{
|
|
if (!(vlan_port->etp_flags & EPF_ENABLED))
|
|
continue;
|
|
if (vlan_port->etp_vlan_port != loc_port)
|
|
continue;
|
|
|
|
vlan_port->etp_ethaddr= loc_port->etp_ethaddr;
|
|
vlan_port->etp_flags |= EPF_GOT_ADDR;
|
|
eth_restart_ioctl(vlan_port);
|
|
}
|
|
}
|
|
if (!(loc_port->etp_flags & EPF_READ_IP))
|
|
setup_read (loc_port);
|
|
|
|
#if 0
|
|
if (loc_port->etp_osdep.etp_flags & OEPF_NEED_SEND)
|
|
{
|
|
printf("eth_rec(conf): OEPF_NEED_SEND is set\n");
|
|
}
|
|
if (loc_port->etp_osdep.etp_flags & OEPF_NEED_RECV)
|
|
{
|
|
printf("eth_rec(conf): OEPF_NEED_RECV is set\n");
|
|
}
|
|
if (loc_port->etp_osdep.etp_flags & OEPF_NEED_STAT)
|
|
{
|
|
printf("eth_rec(conf): OEPF_NEED_STAT is set\n");
|
|
}
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
if (loc_port->etp_osdep.etp_state == OEPS_GETSTAT_SENT)
|
|
{
|
|
if (m_type != DL_STAT_REPLY)
|
|
{
|
|
printf(
|
|
"eth_rec: got bad message type 0x%x from %d in GETSTAT state\n",
|
|
m_type, m->m_source);
|
|
return;
|
|
}
|
|
|
|
loc_port->etp_osdep.etp_state= OEPS_IDLE;
|
|
loc_port->etp_osdep.etp_flags &= ~OEPF_NEED_STAT;
|
|
|
|
assert(loc_port->etp_osdep.etp_stat_gid != -1);
|
|
cpf_revoke(loc_port->etp_osdep.etp_stat_gid);
|
|
loc_port->etp_osdep.etp_stat_gid= -1;
|
|
loc_port->etp_osdep.etp_stat_buf= NULL;
|
|
|
|
/* Finish ioctl */
|
|
assert(loc_port->etp_flags & EPF_GOT_ADDR);
|
|
eth_restart_ioctl(loc_port);
|
|
|
|
#if 0
|
|
if (loc_port->etp_osdep.etp_flags & OEPF_NEED_SEND)
|
|
{
|
|
printf("eth_rec(stat): OEPF_NEED_SEND is set\n");
|
|
}
|
|
if (loc_port->etp_osdep.etp_flags & OEPF_NEED_RECV)
|
|
{
|
|
printf("eth_rec(stat): OEPF_NEED_RECV is set\n");
|
|
}
|
|
if (loc_port->etp_osdep.etp_flags & OEPF_NEED_CONF)
|
|
{
|
|
printf("eth_rec(stat): OEPF_NEED_CONF is set\n");
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
if (loc_port->etp_osdep.etp_state == OEPS_IDLE &&
|
|
(loc_port->etp_osdep.etp_flags & OEPF_NEED_CONF))
|
|
{
|
|
eth_set_rec_conf(loc_port,
|
|
loc_port->etp_osdep.etp_recvconf);
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
assert(loc_port->etp_osdep.etp_state == OEPS_IDLE ||
|
|
loc_port->etp_osdep.etp_state == OEPS_RECV_SENT ||
|
|
loc_port->etp_osdep.etp_state == OEPS_SEND_SENT ||
|
|
(printf("etp_state = %d\n", loc_port->etp_osdep.etp_state), 0));
|
|
loc_port->etp_osdep.etp_state= OEPS_IDLE;
|
|
|
|
flags= m->DL_FLAGS;
|
|
|
|
if (flags & DL_PACK_SEND)
|
|
write_int(loc_port);
|
|
if (flags & DL_PACK_RECV)
|
|
read_int(loc_port, m->DL_COUNT);
|
|
|
|
if (loc_port->etp_osdep.etp_state == OEPS_IDLE &&
|
|
loc_port->etp_osdep.etp_flags & OEPF_NEED_SEND)
|
|
{
|
|
loc_port->etp_osdep.etp_flags &= ~OEPF_NEED_SEND;
|
|
if (loc_port->etp_wr_pack)
|
|
eth_issue_send(loc_port);
|
|
}
|
|
if (loc_port->etp_osdep.etp_state == OEPS_IDLE &&
|
|
(loc_port->etp_osdep.etp_flags & OEPF_NEED_RECV))
|
|
{
|
|
loc_port->etp_osdep.etp_flags &= ~OEPF_NEED_RECV;
|
|
if (!(loc_port->etp_flags & EPF_READ_IP))
|
|
setup_read (loc_port);
|
|
}
|
|
if (loc_port->etp_osdep.etp_flags & OEPF_NEED_CONF)
|
|
{
|
|
#if 0
|
|
printf("eth_rec: OEPF_NEED_CONF is set\n");
|
|
#endif
|
|
}
|
|
if (loc_port->etp_osdep.etp_state == OEPS_IDLE &&
|
|
(loc_port->etp_osdep.etp_flags & OEPF_NEED_STAT))
|
|
{
|
|
send_getstat(loc_port);
|
|
}
|
|
}
|
|
|
|
PUBLIC void eth_check_driver(char *label, endpoint_t endpoint)
|
|
{
|
|
int i;
|
|
eth_port_t *loc_port;
|
|
struct eth_conf *ecp;
|
|
|
|
/* Re-init ethernet interface */
|
|
for (i= 0, ecp= eth_conf, loc_port= eth_port_table;
|
|
i<eth_conf_nr; i++, ecp++, loc_port++)
|
|
{
|
|
if (eth_is_vlan(ecp))
|
|
continue;
|
|
|
|
if (strcmp(ecp->ec_label, label) != 0)
|
|
{
|
|
/* Wrong driver */
|
|
continue;
|
|
}
|
|
eth_restart(loc_port, endpoint);
|
|
}
|
|
}
|
|
|
|
PUBLIC int eth_get_stat(eth_port, eth_stat)
|
|
eth_port_t *eth_port;
|
|
eth_stat_t *eth_stat;
|
|
{
|
|
cp_grant_id_t gid;
|
|
|
|
assert(!eth_port->etp_vlan);
|
|
|
|
if (eth_port->etp_osdep.etp_flags & OEPF_NEED_STAT)
|
|
ip_panic(( "eth_get_stat: getstat already in progress" ));
|
|
|
|
gid= cpf_grant_direct(eth_port->etp_osdep.etp_task,
|
|
(vir_bytes)eth_stat, sizeof(*eth_stat), CPF_WRITE);
|
|
if (gid == -1)
|
|
{
|
|
ip_panic(( "eth_get_stat: cpf_grant_direct failed: %d\n",
|
|
errno));
|
|
}
|
|
assert(eth_port->etp_osdep.etp_stat_gid == -1);
|
|
eth_port->etp_osdep.etp_stat_gid= gid;
|
|
eth_port->etp_osdep.etp_stat_buf= eth_stat;
|
|
|
|
if (eth_port->etp_osdep.etp_state != OEPS_IDLE)
|
|
{
|
|
eth_port->etp_osdep.etp_flags |= OEPF_NEED_STAT;
|
|
return SUSPEND;
|
|
}
|
|
|
|
send_getstat(eth_port);
|
|
|
|
return SUSPEND;
|
|
}
|
|
|
|
PUBLIC void eth_set_rec_conf (eth_port, flags)
|
|
eth_port_t *eth_port;
|
|
u32_t flags;
|
|
{
|
|
int r;
|
|
unsigned dl_flags, mask;
|
|
message mess;
|
|
|
|
assert(!eth_port->etp_vlan);
|
|
|
|
if (!(eth_port->etp_flags & EPF_GOT_ADDR))
|
|
{
|
|
/* We have never seen the device. */
|
|
#if 0
|
|
printf("eth_set_rec_conf: waiting for device to appear\n");
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (eth_port->etp_osdep.etp_state != OEPS_IDLE)
|
|
{
|
|
#if 0
|
|
printf(
|
|
"eth_set_rec_conf: setting OEPF_NEED_CONF, state = %d\n",
|
|
eth_port->etp_osdep.etp_state);
|
|
#endif
|
|
eth_port->etp_osdep.etp_flags |= OEPF_NEED_CONF;
|
|
return;
|
|
}
|
|
|
|
mask = NWEO_EN_BROAD | NWEO_EN_MULTI | NWEO_EN_PROMISC;
|
|
if ((eth_port->etp_osdep.etp_recvconf & mask) == (flags & mask))
|
|
{
|
|
/* No change for the driver, so don't send an update */
|
|
return;
|
|
}
|
|
|
|
eth_port->etp_osdep.etp_recvconf= flags;
|
|
dl_flags= DL_NOMODE;
|
|
if (flags & NWEO_EN_BROAD)
|
|
dl_flags |= DL_BROAD_REQ;
|
|
if (flags & NWEO_EN_MULTI)
|
|
dl_flags |= DL_MULTI_REQ;
|
|
if (flags & NWEO_EN_PROMISC)
|
|
dl_flags |= DL_PROMISC_REQ;
|
|
|
|
mess.m_type= DL_CONF;
|
|
mess.DL_MODE= dl_flags;
|
|
|
|
assert(eth_port->etp_osdep.etp_state == OEPS_IDLE);
|
|
r= asynsend(eth_port->etp_osdep.etp_task, &mess);
|
|
eth_port->etp_osdep.etp_state= OEPS_CONF_SENT;
|
|
|
|
if (r < 0)
|
|
{
|
|
printf("eth_set_rec_conf: asynsend to %d failed: %d\n",
|
|
eth_port->etp_osdep.etp_task, r);
|
|
return;
|
|
}
|
|
}
|
|
|
|
PRIVATE void eth_issue_send(eth_port)
|
|
eth_port_t *eth_port;
|
|
{
|
|
int i, r, pack_size;
|
|
acc_t *pack, *pack_ptr;
|
|
iovec_s_t *iovec;
|
|
message m;
|
|
|
|
iovec= eth_port->etp_osdep.etp_wr_iovec;
|
|
pack= eth_port->etp_wr_pack;
|
|
pack_size= 0;
|
|
for (i=0, pack_ptr= pack; i<IOVEC_NR && pack_ptr; i++,
|
|
pack_ptr= pack_ptr->acc_next)
|
|
{
|
|
r= cpf_setgrant_direct(iovec[i].iov_grant,
|
|
eth_port->etp_osdep.etp_task,
|
|
(vir_bytes)ptr2acc_data(pack_ptr),
|
|
(vir_bytes)pack_ptr->acc_length,
|
|
CPF_READ);
|
|
if (r != 0)
|
|
{
|
|
ip_panic((
|
|
"eth_write_port: cpf_setgrant_direct failed: %d\n",
|
|
errno));
|
|
}
|
|
pack_size += iovec[i].iov_size= pack_ptr->acc_length;
|
|
}
|
|
if (i>= IOVEC_NR)
|
|
{
|
|
pack= bf_pack(pack); /* packet is too fragmented */
|
|
eth_port->etp_wr_pack= pack;
|
|
pack_size= 0;
|
|
for (i=0, pack_ptr= pack; i<IOVEC_NR && pack_ptr;
|
|
i++, pack_ptr= pack_ptr->acc_next)
|
|
{
|
|
r= cpf_setgrant_direct(iovec[i].iov_grant,
|
|
eth_port->etp_osdep.etp_task,
|
|
(vir_bytes)ptr2acc_data(pack_ptr),
|
|
(vir_bytes)pack_ptr->acc_length,
|
|
CPF_READ);
|
|
if (r != 0)
|
|
{
|
|
ip_panic((
|
|
"eth_write_port: cpf_setgrant_direct failed: %d\n",
|
|
errno));
|
|
}
|
|
pack_size += iovec[i].iov_size= pack_ptr->acc_length;
|
|
}
|
|
}
|
|
assert (i< IOVEC_NR);
|
|
assert (pack_size >= ETH_MIN_PACK_SIZE);
|
|
|
|
r= cpf_setgrant_direct(eth_port->etp_osdep.etp_wr_vec_grant,
|
|
eth_port->etp_osdep.etp_task,
|
|
(vir_bytes)iovec,
|
|
(vir_bytes)(i * sizeof(iovec[0])),
|
|
CPF_READ);
|
|
if (r != 0)
|
|
{
|
|
ip_panic((
|
|
"eth_write_port: cpf_setgrant_direct failed: %d\n",
|
|
errno));
|
|
}
|
|
m.m_type= DL_WRITEV_S;
|
|
m.DL_ENDPT_LEGACY= this_proc; /* FIXME: legacy support */
|
|
m.DL_COUNT= i;
|
|
m.DL_GRANT= eth_port->etp_osdep.etp_wr_vec_grant;
|
|
|
|
assert(eth_port->etp_osdep.etp_state == OEPS_IDLE);
|
|
r= asynsend(eth_port->etp_osdep.etp_task, &m);
|
|
|
|
if (r < 0)
|
|
{
|
|
printf("eth_issue_send: send to %d failed: %d\n",
|
|
eth_port->etp_osdep.etp_task, r);
|
|
return;
|
|
}
|
|
eth_port->etp_osdep.etp_state= OEPS_SEND_SENT;
|
|
}
|
|
|
|
PRIVATE void write_int(eth_port_t *eth_port)
|
|
{
|
|
acc_t *pack;
|
|
int multicast;
|
|
u8_t *eth_dst_ptr;
|
|
|
|
pack= eth_port->etp_wr_pack;
|
|
if (pack == NULL)
|
|
{
|
|
printf("write_int: strange no packet on eth port %d\n",
|
|
(int)(eth_port-eth_port_table));
|
|
eth_restart_write(eth_port);
|
|
return;
|
|
}
|
|
|
|
eth_port->etp_wr_pack= NULL;
|
|
|
|
eth_dst_ptr= (u8_t *)ptr2acc_data(pack);
|
|
multicast= (*eth_dst_ptr & 1); /* low order bit indicates multicast */
|
|
if (multicast || (eth_port->etp_osdep.etp_recvconf & NWEO_EN_PROMISC))
|
|
{
|
|
assert(!no_ethWritePort);
|
|
no_ethWritePort= 1;
|
|
eth_arrive(eth_port, pack, bf_bufsize(pack));
|
|
assert(no_ethWritePort);
|
|
no_ethWritePort= 0;
|
|
}
|
|
else
|
|
bf_afree(pack);
|
|
|
|
eth_restart_write(eth_port);
|
|
}
|
|
|
|
PRIVATE void read_int(eth_port, count)
|
|
eth_port_t *eth_port;
|
|
int count;
|
|
{
|
|
acc_t *pack, *pack_ptr, *cut_pack;
|
|
iovec_s_t *iovec;
|
|
int i, r;
|
|
|
|
/* A buggy driver might try to feed us a reply for a request we never
|
|
* sent. Don't let this cause a crash further up.
|
|
*/
|
|
if (!(eth_port->etp_flags & EPF_READ_IP))
|
|
{
|
|
printf("mnx_eth`read_int: read reply with no read going on\n");
|
|
return;
|
|
}
|
|
|
|
pack= eth_port->etp_rd_pack;
|
|
eth_port->etp_rd_pack= NULL;
|
|
|
|
/* Invalidate the grants first, so that the ethernet driver can no
|
|
* longer modify the contents of the packet.
|
|
*/
|
|
iovec= eth_port->etp_osdep.etp_rd_iovec;
|
|
for (i=0, pack_ptr= pack; i<RD_IOVEC && pack_ptr;
|
|
i++, pack_ptr= pack_ptr->acc_next)
|
|
{
|
|
r= cpf_setgrant_disable(iovec[i].iov_grant);
|
|
if (r != 0)
|
|
{
|
|
ip_panic((
|
|
"mnx_eth`read_int: cpf_setgrant_disable failed: %d\n",
|
|
errno));
|
|
}
|
|
}
|
|
|
|
if (count < ETH_MIN_PACK_SIZE)
|
|
{
|
|
printf("mnx_eth`read_int: packet size too small (%d)\n",
|
|
count);
|
|
bf_afree(pack);
|
|
}
|
|
else if (count > ETH_MAX_PACK_SIZE_TAGGED)
|
|
{
|
|
printf("mnx_eth`read_int: packet size too big (%d)\n",
|
|
count);
|
|
bf_afree(pack);
|
|
}
|
|
else
|
|
{
|
|
cut_pack= bf_cut(pack, 0, count);
|
|
bf_afree(pack);
|
|
|
|
assert(!no_ethWritePort);
|
|
no_ethWritePort= 1;
|
|
eth_arrive(eth_port, cut_pack, count);
|
|
assert(no_ethWritePort);
|
|
no_ethWritePort= 0;
|
|
}
|
|
|
|
eth_port->etp_flags &= ~(EPF_READ_IP|EPF_READ_SP);
|
|
setup_read(eth_port);
|
|
}
|
|
|
|
PRIVATE void setup_read(eth_port)
|
|
eth_port_t *eth_port;
|
|
{
|
|
acc_t *pack, *pack_ptr;
|
|
message mess;
|
|
iovec_s_t *iovec;
|
|
int i, r;
|
|
|
|
assert(!eth_port->etp_vlan);
|
|
assert(!(eth_port->etp_flags & (EPF_READ_IP|EPF_READ_SP)));
|
|
|
|
if (eth_port->etp_osdep.etp_state != OEPS_IDLE)
|
|
{
|
|
eth_port->etp_osdep.etp_flags |= OEPF_NEED_RECV;
|
|
|
|
return;
|
|
}
|
|
|
|
assert (!eth_port->etp_rd_pack);
|
|
|
|
iovec= eth_port->etp_osdep.etp_rd_iovec;
|
|
pack= bf_memreq (ETH_MAX_PACK_SIZE_TAGGED);
|
|
|
|
for (i=0, pack_ptr= pack; i<RD_IOVEC && pack_ptr;
|
|
i++, pack_ptr= pack_ptr->acc_next)
|
|
{
|
|
r= cpf_setgrant_direct(iovec[i].iov_grant,
|
|
eth_port->etp_osdep.etp_task,
|
|
(vir_bytes)ptr2acc_data(pack_ptr),
|
|
(vir_bytes)pack_ptr->acc_length,
|
|
CPF_WRITE);
|
|
if (r != 0)
|
|
{
|
|
ip_panic((
|
|
"mnx_eth`setup_read: cpf_setgrant_direct failed: %d\n",
|
|
errno));
|
|
}
|
|
iovec[i].iov_size= (vir_bytes)pack_ptr->acc_length;
|
|
}
|
|
assert (!pack_ptr);
|
|
|
|
r= cpf_setgrant_direct(eth_port->etp_osdep.etp_rd_vec_grant,
|
|
eth_port->etp_osdep.etp_task,
|
|
(vir_bytes)iovec,
|
|
(vir_bytes)(i * sizeof(iovec[0])),
|
|
CPF_READ);
|
|
if (r != 0)
|
|
{
|
|
ip_panic((
|
|
"mnx_eth`setup_read: cpf_setgrant_direct failed: %d\n",
|
|
errno));
|
|
}
|
|
|
|
mess.m_type= DL_READV_S;
|
|
mess.DL_ENDPT_LEGACY= this_proc; /* FIXME: legacy support */
|
|
mess.DL_COUNT= i;
|
|
mess.DL_GRANT= eth_port->etp_osdep.etp_rd_vec_grant;
|
|
|
|
assert(eth_port->etp_osdep.etp_state == OEPS_IDLE);
|
|
|
|
r= asynsend(eth_port->etp_osdep.etp_task, &mess);
|
|
eth_port->etp_osdep.etp_state= OEPS_RECV_SENT;
|
|
|
|
if (r < 0)
|
|
{
|
|
printf(
|
|
"mnx_eth`setup_read: asynsend to %d failed: %d\n",
|
|
eth_port->etp_osdep.etp_task, r);
|
|
}
|
|
eth_port->etp_rd_pack= pack;
|
|
eth_port->etp_flags |= EPF_READ_IP;
|
|
eth_port->etp_flags |= EPF_READ_SP;
|
|
}
|
|
|
|
static void eth_restart(eth_port_t *eth_port, endpoint_t endpoint)
|
|
{
|
|
int r;
|
|
unsigned flags, dl_flags;
|
|
cp_grant_id_t gid;
|
|
message mess;
|
|
|
|
eth_port->etp_osdep.etp_task= endpoint;
|
|
|
|
switch(eth_port->etp_osdep.etp_state)
|
|
{
|
|
case OEPS_INIT:
|
|
case OEPS_CONF_SENT:
|
|
case OEPS_RECV_SENT:
|
|
case OEPS_SEND_SENT:
|
|
/* We can safely ignore the pending CONF, RECV, and SEND
|
|
* requests. If this is the first time that we see this
|
|
* driver at all, that's fine too.
|
|
*/
|
|
eth_port->etp_osdep.etp_state= OEPS_IDLE;
|
|
break;
|
|
case OEPS_GETSTAT_SENT:
|
|
/* Set the OEPF_NEED_STAT to trigger a new request */
|
|
eth_port->etp_osdep.etp_flags |= OEPF_NEED_STAT;
|
|
eth_port->etp_osdep.etp_state= OEPS_IDLE;
|
|
break;
|
|
}
|
|
|
|
/* If there is a pending GETSTAT request then we have to create a
|
|
* new grant.
|
|
*/
|
|
if (eth_port->etp_osdep.etp_flags & OEPF_NEED_STAT)
|
|
{
|
|
assert(eth_port->etp_osdep.etp_stat_gid != -1);
|
|
cpf_revoke(eth_port->etp_osdep.etp_stat_gid);
|
|
|
|
gid= cpf_grant_direct(eth_port->etp_osdep.etp_task,
|
|
(vir_bytes)eth_port->etp_osdep.etp_stat_buf,
|
|
sizeof(*eth_port->etp_osdep.etp_stat_buf), CPF_WRITE);
|
|
if (gid == -1)
|
|
{
|
|
ip_panic((
|
|
"eth_restart: cpf_grant_direct failed: %d\n",
|
|
errno));
|
|
}
|
|
eth_port->etp_osdep.etp_stat_gid= gid;
|
|
}
|
|
|
|
flags= eth_port->etp_osdep.etp_recvconf;
|
|
dl_flags= DL_NOMODE;
|
|
if (flags & NWEO_EN_BROAD)
|
|
dl_flags |= DL_BROAD_REQ;
|
|
if (flags & NWEO_EN_MULTI)
|
|
dl_flags |= DL_MULTI_REQ;
|
|
if (flags & NWEO_EN_PROMISC)
|
|
dl_flags |= DL_PROMISC_REQ;
|
|
mess.m_type= DL_CONF;
|
|
mess.DL_MODE= dl_flags;
|
|
|
|
compare(eth_port->etp_osdep.etp_state, ==, OEPS_IDLE);
|
|
r= asynsend(eth_port->etp_osdep.etp_task, &mess);
|
|
if (r<0)
|
|
{
|
|
printf(
|
|
"eth_restart: send to ethernet task %d failed: %d\n",
|
|
eth_port->etp_osdep.etp_task, r);
|
|
return;
|
|
}
|
|
eth_port->etp_osdep.etp_state= OEPS_CONF_SENT;
|
|
|
|
if (eth_port->etp_wr_pack)
|
|
{
|
|
bf_afree(eth_port->etp_wr_pack);
|
|
eth_port->etp_wr_pack= NULL;
|
|
eth_restart_write(eth_port);
|
|
}
|
|
if (eth_port->etp_rd_pack)
|
|
{
|
|
bf_afree(eth_port->etp_rd_pack);
|
|
eth_port->etp_rd_pack= NULL;
|
|
eth_port->etp_flags &= ~(EPF_READ_IP|EPF_READ_SP);
|
|
}
|
|
|
|
}
|
|
|
|
PRIVATE void send_getstat(eth_port)
|
|
eth_port_t *eth_port;
|
|
{
|
|
int r;
|
|
message mess;
|
|
|
|
mess.m_type= DL_GETSTAT_S;
|
|
mess.DL_ENDPT_LEGACY= this_proc; /* FIXME: legacy support */
|
|
mess.DL_GRANT= eth_port->etp_osdep.etp_stat_gid;
|
|
|
|
assert(eth_port->etp_osdep.etp_state == OEPS_IDLE);
|
|
|
|
r= asynsend(eth_port->etp_osdep.etp_task, &mess);
|
|
eth_port->etp_osdep.etp_state= OEPS_GETSTAT_SENT;
|
|
|
|
if (r != OK)
|
|
ip_panic(( "eth_get_stat: asynsend failed: %d", r));
|
|
}
|
|
|
|
/*
|
|
* $PchId: mnx_eth.c,v 1.16 2005/06/28 14:24:37 philip Exp $
|
|
*/
|