enet: Implement driver

This commit is contained in:
Jan Schär 2022-05-27 19:11:22 +02:00
parent 4dab497576
commit 0c79d7048d
25 changed files with 1676 additions and 116 deletions

View File

@ -99,7 +99,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" {
/****************************************************************************
* 14.6.5.3/3638
***************************************************************************/
register rdar rw addr(base, 0x0010) "Receive Descriptor Active Register ring0" {
_ 24 rsvd;
rdar 1 "Receive Descriptor Active";
@ -109,7 +109,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" {
/****************************************************************************
* 14.6.5.3/3638
***************************************************************************/
register tdar rw addr(base, 0x0014) "Transmit Descriptor Active Register ring0" {
_ 24 rsvd;
tdar 1 "Transmit Descriptor Active";
@ -119,7 +119,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" {
/****************************************************************************
* 14.6.5.5/3640 Ethernet control register
***************************************************************************/
register ecr rw addr(base, 0x0024) "Control register" {
reset 1 "Ethernet MAC Reset";
etheren 1 "Ethernet enable";
@ -142,7 +142,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" {
/****************************************************************************
* 14.6.5.6/3643 MII Management Frame Register
***************************************************************************/
register mmfr rw addr(base, 0x0040) "MII Management Frame Register" {
data 16 "Data written to or read from PHY register";
ta 2 "Turn Around: needs to be programmed to 10 to be valid";
@ -155,7 +155,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" {
/****************************************************************************
* 14.6.5.6/3643 MII Speed Control Register
***************************************************************************/
register mscr rw addr(base, 0x0044) "MII Speed Control Register" {
_ 1 rsvd;
mii_speed 6 "MII Speed";
@ -167,7 +167,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" {
/****************************************************************************
* 14.6.5.9/3646 Receive Control Register
***************************************************************************/
register rcr rw addr(base, 0x0084) "Receive Control Register" {
loop 1 "Internal Loopback";
drt 1 "Disable Receive on Transmit";
@ -224,7 +224,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" {
typ 16 "Always contains 0x8808";
paddr2 16 "Pause Address";
};
/****************************************************************************
* 14.6.5.12/3651 Opcode/Pause Duration Register
***************************************************************************/
@ -338,6 +338,19 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" {
_ 22 rsvd;
};
/****************************************************************************
* 14.6.5.40/3745
***************************************************************************/
register racc rw addr(base, 0x001C4) "Receive Accelerator Function Configuration" {
padrem 1 "";
ipdis 1 "";
prodis 1 "";
_ 3 rsvd;
linedis 1 "Enable Discard Of Frames With MAC Layer Errors";
shift16 1 "";
_ 24 rsvd;
};
/****************************************************************************
* 14.6.5.49/3679 Tx Packet Count Statistic Register
@ -458,7 +471,7 @@ device enet lsbfirst ( addr base ) "Imx8x enet controller" {
addr 32 "Buffer address";
esc 32 "";
prot 32 "";
ts 32 "";
ts 32 "";
res0 64 "";
};
*/

View File

@ -379,6 +379,14 @@ errors libaos LIB_ERR_ {
failure UMP_GET_GLOBAL "Failure in get_global()",
failure UMP_REGISTER "Failure in ump_register()",
// UMP net
failure NET_ALREADY_INIT "Already initialized",
failure NET_PORT_IN_USE "Can't listen on this port, someone else is already listening",
failure NET_ALLOC_PORT "Failed to allocate ephemeral port",
failure NET_NOT_LISTENING "You are not listening on this port",
failure NET_PACKET_TOO_BIG "Can't send packet because it is too big",
failure NET_ARP_MISS "The packet was dropped and an ARP request was sent instead. Try again later.",
// IDC binding/export and Monitor client interface
failure MONITOR_CLIENT_BIND "Error in monitor_client_lmp_bind()",
failure MONITOR_CLIENT_ACCEPT "Error in monitor_client_lmp_accept()",

View File

@ -5,8 +5,8 @@
bootdriver /armv8/sbin/boot_armv8_generic
cpudriver /armv8/sbin/cpu_imx8x
module /armv8/sbin/init
module /armv8/sbin/enet
module /armv8/sbin/hello echoclient
module /armv8/sbin/enet 192.168.2.2 24
module /armv8/sbin/hello hi
# module /armv8/sbin/memeater
module /armv8/sbin/mallocator
module /armv8/sbin/stackoverflow

43
include/aos/simpleslab.h Normal file
View File

@ -0,0 +1,43 @@
/**
* \file
* \brief Very simple slab allocator
*/
/*
* Copyright (c) 2008, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
*/
#ifndef LIBBARRELFISH_SIMPLESLAB_H
#define LIBBARRELFISH_SIMPLESLAB_H
#include <sys/cdefs.h>
__BEGIN_DECLS
// forward declarations
struct simpleslab_allocator;
struct simpleblock_head;
struct simpleslab_allocator {
void *start, *end;
uint32_t total, free;
struct simpleblock_head *blocks;
};
void simpleslab_init(struct simpleslab_allocator *slabs, size_t blocksize, void *buf, size_t buflen);
void *simpleslab_alloc(struct simpleslab_allocator *slabs);
void simpleslab_free(struct simpleslab_allocator *slabs, void *block);
// size of block header
#define SIMPLESLAB_BLOCK_HDRSIZE (sizeof(void *))
// should be able to fit the header into the block
__END_DECLS
#endif // LIBBARRELFISH_SIMPLESLAB_H

View File

@ -3,10 +3,11 @@
#include <aos/aos.h>
#define UMP_FRAME_SIZE BASE_PAGE_SIZE
#define UMP_FRAME_SIZE (2 * BASE_PAGE_SIZE)
enum ump_server_id {
UMP_SERVER_ECHO,
UMP_SERVER_NET,
UMP_SERVER_COUNT // How many servers exist
};

View File

@ -21,7 +21,7 @@ typedef void (*ump_send_callback_fn_t)(void *arg, struct ump_send_queue_entry *e
/**
* @brief Specifies which role an endpoint would like to take in the UMP protocol
*
*
* This is just used to agree on which part of the shared frame should be used
* to send data and which to receive data
*/
@ -51,7 +51,7 @@ struct ump_ring_state {
/**
* @brief State of a send queue entry
*
*
* Used to keep track of which part of the message we are
* currently sending
*/
@ -64,12 +64,12 @@ enum ump_send_entry_state {
/**
* @brief An entry in the sending queue of a channel.
*
*
* Contains all metadata information needed to send this message
*/
struct ump_send_queue_entry {
enum ump_send_entry_state state;
size_t header_size;
const void *header;
@ -103,7 +103,7 @@ struct ump_send_chan {
/**
* @brief Used to keep track of which part of a message was received or is being received
*
*
*/
enum recv_state {
UMP_RECV_STATE_IDLE,
@ -116,7 +116,7 @@ enum recv_state {
/**
* @brief Metadata for the receiving side of the channel
*
*
*/
struct ump_recv_chan {
struct ump_ring_state ring_state;
@ -175,18 +175,18 @@ struct ump_start_message {
// in sizes on the two endpoints
size_t header_size;
};
STATIC_ASSERT(sizeof(struct ump_start_message) <= sizeof(struct ump_ring_buf_entry), "struct ump_start_message is too big");
STATIC_ASSERT(sizeof(struct ump_start_message) <= UMP_RING_BUF_DATA_SIZE, "struct ump_start_message is too big");
/**
* @brief Initialize the send and receive objects for a channel.
*
*
* @param role Specifies if this side should be the server or client in the connection
* @param send_chan send channel object to be allocated and initialized
* @param recv_chan receive channel object to be allocated and initialized
* @param recv_header_size size of the headers that will be received
* @param shared_frame the frame capability shared between the client and server
* @param run_on_waitset the waitset on which the async tasks should run. if it is NULL a thread will be started to handle the tasks
* @return errval_t
* @return errval_t
*/
errval_t ump_chan_init(
enum ump_role role,
@ -199,7 +199,7 @@ errval_t ump_chan_init(
/**
* @brief Send a message on the channel
*
*
* @param chan channel to send on
* @param entry an allocated object to carry the send information
* @param header_size size of the header to be sent
@ -214,7 +214,7 @@ void ump_send (
struct ump_send_queue_entry *entry,
size_t header_size,
const void *header,
size_t payload_size,
size_t payload_size,
const void *payload,
ump_send_callback_fn_t callback,
void *callback_arg
@ -222,7 +222,7 @@ void ump_send (
/**
* @brief Receive the header of the next message. Must only be called on a fresh channel or after the receive payload completed
*
*
* @param chan channel to listen on
* @param callback function to call with the header and the size of the payload
* @param callback_arg argument to pass to the callback function
@ -235,7 +235,7 @@ void ump_recv_header (
/**
* @brief Receive the payload of the next message. Must only be called after the receive header completed
*
*
* @param chan channel to listen on
* @param payload buffer to store the payload in. must be at least as large as the payload_size argument in the corresponding callback from receive header. if this is NULL then the payload will be dropped
* @param callback function to call with payload

59
include/aos/ump_net.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef _LIB_BARRELFISH_UMP_NET_H
#define _LIB_BARRELFISH_UMP_NET_H
#include <aos/aos.h>
enum ump_net_op {
// calls (out: call, in: return)
UMP_NET_OP_UDP_SEND,
UMP_NET_OP_UDP_LISTEN,
UMP_NET_OP_UDP_LISTEN_STOP,
// events (in only)
UMP_NET_EV_UDP_RECV,
};
struct ump_net_op_udp_send {
uint32_t dest_ip;
uint16_t src_port;
uint16_t dest_port;
};
struct ump_net_op_udp_listen {
uint16_t dest_port; // If 0, allocate a port
};
struct ump_net_ret_udp_listen {
uint16_t dest_port;
};
struct ump_net_op_udp_listen_stop {
uint16_t dest_port;
};
struct ump_net_ev_udp_recv {
uint32_t src_ip;
uint32_t dest_ip;
uint16_t src_port;
uint16_t dest_port;
};
struct ump_net_out_header {
enum ump_net_op op;
union ump_net_out_d {
struct ump_net_op_udp_send udp_send;
struct ump_net_op_udp_listen udp_listen;
struct ump_net_op_udp_listen_stop udp_listen_stop;
} d;
};
struct ump_net_in_header {
enum ump_net_op op;
errval_t ret_err;
union ump_net_in_d {
struct ump_net_ret_udp_listen udp_listen;
struct ump_net_ev_udp_recv udp_recv;
} d;
};
#endif // _LIB_BARRELFISH_UMP_NET_H

View File

@ -0,0 +1,87 @@
#ifndef _LIB_BARRELFISH_UMP_NET_CLIENT_H
#define _LIB_BARRELFISH_UMP_NET_CLIENT_H
#include <aos/aos.h>
#include <aos/ump_chan.h>
#include <aos/ump_net.h>
// This is the client library for the UMP net server.
/**
* @brief Initialize networking in this process.
*
* @param ws waitset on which networking runs
*/
errval_t ump_net_init (struct waitset *ws);
/**
* @brief Internal data structure. You need to allocate this space when
* making a call, and can release it in the callback.
*/
struct ump_net_call {
struct ump_send_queue_entry ump_entry;
struct ump_net_out_header out_header;
struct ump_net_in_header in_header;
bool half_done;
void *cb;
void *cb_arg;
struct ump_net_call *next;
};
typedef void (*ump_net_udp_send_callback_t)(void *arg, errval_t err);
/**
* @brief Send an UDP packet.
* @param call You need to allocate this space until cb is called.
* @param cb This will be called when the operation is complete, with cb_arg and an error value.
*/
void ump_net_udp_send(
struct ump_net_call *call,
uint32_t dest_ip,
uint16_t src_port,
uint16_t dest_port,
size_t payload_size, const void *payload,
ump_net_udp_send_callback_t cb, void *cb_arg
);
typedef void (*ump_net_udp_recv_handler_t)(void *arg, struct ump_net_ev_udp_recv *udp_recv, size_t payload_size);
typedef void (*ump_net_udp_listen_callback_t)(void *arg, errval_t err, uint16_t dest_port);
/**
* @brief Listen for UDP packets.
* @param call You need to allocate this space until cb is called.
* @param dest_port UDP port to listen on. If 0, an ephemeral port is allocated, and passed to the callback.
* @param recv_handler This will be called when a packet arrives.
* When the callback is called, you *must* call ump_net_recv_payload.
* @param cb This will be called when the operation is complete, with cb_arg, an error value, and the port.
*/
void ump_net_udp_listen (
struct ump_net_call *call,
uint16_t dest_port,
ump_net_udp_recv_handler_t recv_handler, void *recv_handler_arg,
ump_net_udp_listen_callback_t cb, void *cb_arg
);
typedef void (*ump_net_udp_listen_stop_callback_t)(void *arg, errval_t err);
/**
* @brief Stop listening for UDP packets.
* @param call You need to allocate this space until cb is called.
* @param cb This will be called when the operation is complete, with cb_arg and an error value.
*/
void ump_net_udp_listen_stop (
struct ump_net_call *call,
uint16_t dest_port,
ump_net_udp_listen_stop_callback_t cb, void *cb_arg
);
/**
* @brief Receive payload.
* @param payload Buffer to write data into.
* Must have size payload_size (argument of recv_handler).
* Can be NULL to drop the payload.
* @param callback Will be called when all data has been received.
* Can be NULL when payload is NULL.
*/
void ump_net_recv_payload (
void *payload,
ump_recv_payload_callback_fn_t callback, void *callback_arg
);
#endif // _LIB_BARRELFISH_UMP_NET_CLIENT_H

View File

@ -6,13 +6,15 @@
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
*/
#ifndef ENET_DEVIF_H
#define ENET_DEVIF_H
struct enet_queue;
struct enet_t;
struct region_entry* enet_get_region(struct enet_queue* q, regionid_t rid);
errval_t enet_rx_queue_create(struct enet_queue ** q, struct enet_t* dev);
errval_t enet_tx_queue_create(struct enet_queue ** q, struct enet_t* dev);

View File

@ -4,6 +4,7 @@
#include <stdint.h>
#include <stddef.h>
#include <aos/aos.h>
#include <netutil/types.h>
//#define ETHARP_DEBUG_OPTION 1
@ -24,14 +25,10 @@
#define ETH_ADDR_LEN 6
struct eth_addr {
uint8_t addr[6];
} __attribute__((__packed__));
struct eth_hdr {
struct eth_addr dst;
struct eth_addr src;
uint16_t type;
struct eth_addr_net dst;
struct eth_addr_net src;
struct uint16_net type;
} __attribute__((__packed__));
#define ARP_HW_TYPE_ETH 0x1
@ -41,17 +38,16 @@ struct eth_hdr {
#define ARP_HLEN 28
struct arp_hdr {
uint16_t hwtype;
uint16_t proto;
struct uint16_net hwtype;
struct uint16_net proto;
uint8_t hwlen;
uint8_t protolen;
uint16_t opcode;
struct eth_addr eth_src;
uint32_t ip_src;
struct eth_addr eth_dst;
uint32_t ip_dst;
struct uint16_net opcode;
struct eth_addr_net eth_src;
struct uint32_net ip_src;
struct eth_addr_net eth_dst;
struct uint32_net ip_dst;
} __attribute__((__packed__));
#endif

View File

@ -4,6 +4,7 @@
#include <stddef.h>
#include <stdint.h>
#include <aos/aos.h>
#include <netutil/types.h>
//#define ICMP_DEBUG_OPTION 1
@ -46,14 +47,14 @@ enum icmp_te_type {
* This header is also used for other ICMP types that do not
* use the data part.
*/
#define ICMP_HLEN 8
#define ICMP_HLEN 8
struct icmp_echo_hdr {
uint8_t type;
uint8_t code;
uint16_t chksum;
uint16_t id;
uint16_t seqno;
struct uint16_net chksum;
struct uint16_net id;
struct uint16_net seqno;
} __attribute__((__packed__)) ;
#define ICMPH_TYPE(hdr) ((hdr)->type)

View File

@ -4,6 +4,7 @@
#include <aos/aos.h>
#include <stdint.h>
#include <stddef.h>
#include <netutil/types.h>
//#define IP_DEBUG_OPTION 1
@ -17,6 +18,7 @@
#define IP_DF 0x4000U /* dont fragment flag */
#define IP_MF 0x2000U /* more fragments flag */
#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */
#define IP_ADDR_LEN 4
#define IP_HLEN 20 /* Default size for ip header */
#define IP_PROTO_ICMP 1
#define IP_PROTO_IGMP 2
@ -24,7 +26,6 @@
#define IP_PROTO_UDPLITE 136
#define IP_PROTO_TCP 6
typedef uint32_t ip_addr_t;
#define MK_IP(a,b,c,d) (((a)<<24)|((b)<<16)|((c)<<8)|(d))
#define IPH_V(hdr) ((hdr)->v_hl >> 4)
@ -37,20 +38,20 @@ struct ip_hdr {
/* type of service */
uint8_t tos;
/* total length */
uint16_t len;
struct uint16_net len;
/* identification */
uint16_t id;
struct uint16_net id;
/* fragment offset field */
uint16_t offset;
struct uint16_net offset;
/* time to live */
uint8_t ttl;
/* protocol*/
uint8_t proto;
/* checksum */
uint16_t chksum;
struct uint16_net chksum;
/* source and destination IP addresses */
ip_addr_t src;
ip_addr_t dest;
ip_addr_net_t src;
ip_addr_net_t dest;
} __attribute__((__packed__));
/**

82
include/netutil/types.h Normal file
View File

@ -0,0 +1,82 @@
#ifndef _NETUTIL_TYPES_H_
#define _NETUTIL_TYPES_H_
#include <stdint.h>
#include <stddef.h>
struct uint16_net {
uint8_t val[2];
} __attribute__((__packed__));
static inline struct uint16_net uint16_wr(uint16_t val)
{
return (struct uint16_net){{
(val >> 8) & 0xff,
val & 0xff
}};
}
static inline uint16_t uint16_rd(struct uint16_net val_net)
{
return (
((uint16_t)val_net.val[0] << 8) |
(uint16_t)val_net.val[1]
);
}
struct uint32_net {
uint8_t val[4];
} __attribute__((__packed__));
static inline struct uint32_net uint32_wr(uint32_t val)
{
return (struct uint32_net){{
(val >> 24) & 0xff,
(val >> 16) & 0xff,
(val >> 8) & 0xff,
val & 0xff
}};
}
static inline uint32_t uint32_rd(struct uint32_net val_net)
{
return (
((uint32_t)val_net.val[0] << 24) |
((uint32_t)val_net.val[1] << 16) |
((uint32_t)val_net.val[2] << 8) |
(uint32_t)val_net.val[3]
);
}
struct eth_addr_net {
uint8_t addr[6];
} __attribute__((__packed__));
static inline struct eth_addr_net eth_addr_wr(uint64_t addr)
{
return (struct eth_addr_net){{
(addr >> 40) & 0xff,
(addr >> 32) & 0xff,
(addr >> 24) & 0xff,
(addr >> 16) & 0xff,
(addr >> 8) & 0xff,
addr & 0xff
}};
}
static inline uint64_t eth_addr_rd(struct eth_addr_net addr_net)
{
return (
((uint64_t)addr_net.addr[0] << 40) |
((uint64_t)addr_net.addr[1] << 32) |
((uint64_t)addr_net.addr[2] << 24) |
((uint64_t)addr_net.addr[3] << 16) |
((uint64_t)addr_net.addr[4] << 8) |
(uint64_t)addr_net.addr[5]
);
}
typedef uint32_t ip_addr_t;
typedef struct uint32_net ip_addr_net_t;
#endif

View File

@ -2,6 +2,7 @@
#define _UDP_H_
#include <netutil/ip.h>
#include <netutil/types.h>
//#define UDP_DEBUG_OPTION 1
@ -17,10 +18,10 @@
*/
#define UDP_HLEN 8
struct udp_hdr {
uint16_t src;
uint16_t dest; /* src/dest UDP ports */
uint16_t len;
uint16_t chksum;
struct uint16_net src;
struct uint16_net dest; /* src/dest UDP ports */
struct uint16_net len;
struct uint16_net chksum;
} __attribute__((__packed__));

View File

@ -27,6 +27,7 @@
"aos_urpc.c",
"ump_binding.c",
"ump_chan.c",
"ump_net_client.c",
"performance.c",
"capabilities.c",
"coreset.c",
@ -48,6 +49,7 @@
"paging.c",
"ram_alloc.c",
"slab.c",
"simpleslab.c",
"sys_debug.c",
"syscalls.c",
"systime.c",

94
lib/aos/simpleslab.c Normal file
View File

@ -0,0 +1,94 @@
/**
* \file
* \brief Very simple slab allocator.
*
* This file implements a simple slab allocator. It allocates blocks of a fixed
* size from one contiguous memory region.
*/
/*
* Copyright (c) 2008, 2009, 2010, ETH Zurich.
* All rights reserved.
*
* This file is distributed under the terms in the attached LICENSE file.
* If you do not find this file, copies can be found by writing to:
* ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
*/
#include <aos/aos.h>
#include <aos/simpleslab.h>
#include <aos/static_assert.h>
struct simpleblock_head {
struct simpleblock_head *next;///< Pointer to next block in free list
};
STATIC_ASSERT_SIZEOF(struct simpleblock_head, SIMPLESLAB_BLOCK_HDRSIZE);
/**
* \brief Initialise a new slab allocator
*
* \param slabs Pointer to slab allocator instance, to be filled-in
* \param blocksize Size of blocks to be allocated by this allocator
* \param buf Buffer from which to allocate blocks
* \param buflen Size of buf
*/
void simpleslab_init(struct simpleslab_allocator *slabs, size_t blocksize, void *buf, size_t buflen)
{
assert(blocksize >= sizeof(struct simpleblock_head));
/* calculate number of blocks in buffer */
assert(buflen / blocksize <= UINT32_MAX);
slabs->free = slabs->total = buflen / blocksize;
assert(slabs->total > 0);
assert(buf != NULL);
slabs->start = buf;
slabs->end = buf + buflen;
/* enqueue blocks in freelist */
struct simpleblock_head *bh = slabs->blocks = buf;
for (uint32_t i = slabs->total; i > 1; i--) {
buf = (char *)buf + blocksize;
bh->next = buf;
bh = buf;
}
bh->next = NULL;
}
/**
* \brief Allocate a new block from the slab allocator
*
* \param slabs Pointer to slab allocator instance
*
* \returns Pointer to block on success, NULL on error (out of blocks)
*/
void *simpleslab_alloc(struct simpleslab_allocator *slabs)
{
struct simpleblock_head *bh = slabs->blocks;
if (bh == NULL) return NULL;
slabs->blocks = bh->next;
slabs->free--;
return bh;
}
/**
* \brief Free a block to the slab allocator
*
* \param slabs Pointer to slab allocator instance
* \param block Pointer to block previously returned by #slab_alloc
*/
void simpleslab_free(struct simpleslab_allocator *slabs, void *block)
{
assert(block != NULL);
assert(slabs->start <= block && block < slabs->end);
struct simpleblock_head *bh = (struct simpleblock_head *)block;
/* re-enqueue in slab's free list */
bh->next = slabs->blocks;
slabs->blocks = bh;
slabs->free++;
assert(slabs->free <= slabs->total);
}

207
lib/aos/ump_net_client.c Normal file
View File

@ -0,0 +1,207 @@
#include <aos/aos.h>
#include <aos/deferred.h>
#include <aos/aos_rpc.h>
#include <aos/ump_binding.h>
#include <aos/ump_chan.h>
#include <aos/ump_net.h>
#include <aos/ump_net_client.h>
#include <collections/hash_table.h>
struct udp_listen_entry {
ump_net_udp_recv_handler_t recv_handler;
void *recv_handler_arg;
};
static bool net_initialized = false;
static struct ump_send_chan *net_send_chan;
static struct ump_recv_chan *net_recv_chan;
static struct ump_net_call *call_queue_head = NULL;
static struct ump_net_call *call_queue_tail = NULL;
static collections_hash_table* udp_listen_table;
static void net_recv_next (void);
static void handle_net_header_recv (void *arg, size_t header_size, void *header_raw, size_t payload_size);
errval_t ump_net_init (struct waitset *ws) {
if (net_initialized) return LIB_ERR_NET_ALREADY_INIT;
net_initialized = true;
errval_t err;
struct aos_rpc *rpc = aos_rpc_get_init_channel();
struct capref net_cap;
for (int attempts = 0; attempts < 60; attempts++) {
err = aos_rpc_ump_connect(rpc, UMP_SERVER_NET, &net_cap);
if (err != LIB_ERR_UMP_NOT_REGISTERED) break;
barrelfish_usleep(500000);
}
if (err_is_fail(err)) return err;
// Create the two uni-directional channels
err = ump_chan_init(UMP_ROLE_CLIENT, &net_send_chan, &net_recv_chan,
sizeof(struct ump_net_in_header), net_cap, ws);
if (err_is_fail(err)) return err;
net_recv_next();
collections_hash_create_with_buckets(&udp_listen_table, 100, free);
return SYS_ERR_OK;
}
static void net_recv_next (void) {
ump_recv_header(net_recv_chan, handle_net_header_recv, NULL);
}
static void net_recv_ignore_payload (void *arg, size_t payload_size, void *payload) {
net_recv_next();
}
// This is called twice, once from the send callback, once from the receive
// handler. We do it this way because we don't know in which order these
// two events happen.
static void ump_net_op_callback (void *arg, struct ump_send_queue_entry *entry) {
struct ump_net_call *call = arg;
if (call->half_done == false) {
call->half_done = true;
return;
}
if (call->in_header.op == UMP_NET_OP_UDP_SEND) {
((ump_net_udp_send_callback_t)call->cb)(
call->cb_arg, call->in_header.ret_err
);
} else if (call->in_header.op == UMP_NET_OP_UDP_LISTEN) {
((ump_net_udp_listen_callback_t)call->cb)(
call->cb_arg, call->in_header.ret_err,
call->in_header.d.udp_listen.dest_port
);
} else if (call->in_header.op == UMP_NET_OP_UDP_LISTEN_STOP) {
((ump_net_udp_listen_stop_callback_t)call->cb)(
call->cb_arg, call->in_header.ret_err
);
}
}
static void handle_net_header_recv (void *arg, size_t header_size, void *header_raw, size_t payload_size) {
struct ump_net_in_header *header = header_raw;
if (header->op == UMP_NET_EV_UDP_RECV) {
struct udp_listen_entry *entry = collections_hash_find(udp_listen_table, header->d.udp_recv.dest_port);
if (entry != NULL) {
entry->recv_handler(entry->recv_handler_arg, &header->d.udp_recv, payload_size);
return;
}
} else {
assert(call_queue_head != NULL);
assert(call_queue_head->out_header.op == header->op);
call_queue_head->in_header = *header;
ump_net_op_callback(call_queue_head, NULL);
call_queue_head = call_queue_head->next;
if (call_queue_head == NULL) call_queue_tail = NULL;
}
ump_recv_payload(net_recv_chan, NULL, net_recv_ignore_payload, NULL);
}
static void ump_net_send_call (
struct ump_net_call *call,
size_t payload_size, const void *payload,
void *cb, void *cb_arg
) {
call->half_done = false;
call->cb = cb;
call->cb_arg = cb_arg;
if (call_queue_head == NULL) {
call_queue_head = call;
} else {
call_queue_tail->next = call;
}
call_queue_tail = call;
ump_send(net_send_chan, &call->ump_entry,
sizeof(struct ump_net_out_header), &call->out_header,
payload_size, payload,
ump_net_op_callback, call);
}
void ump_net_udp_send (
struct ump_net_call *call,
uint32_t dest_ip,
uint16_t src_port,
uint16_t dest_port,
size_t payload_size, const void *payload,
ump_net_udp_send_callback_t cb, void *cb_arg
) {
call->out_header = (struct ump_net_out_header){
.op = UMP_NET_OP_UDP_SEND,
.d = { .udp_send = {
.dest_ip = dest_ip,
.src_port = src_port,
.dest_port = dest_port,
} }
};
ump_net_send_call(call, payload_size, payload, cb, cb_arg);
}
void ump_net_udp_listen (
struct ump_net_call *call,
uint16_t dest_port,
ump_net_udp_recv_handler_t recv_handler, void *recv_handler_arg,
ump_net_udp_listen_callback_t cb, void *cb_arg
) {
if (collections_hash_find(udp_listen_table, dest_port) != NULL) {
cb(cb_arg, LIB_ERR_NET_PORT_IN_USE, 0);
return;
}
struct udp_listen_entry *entry = malloc(sizeof(struct udp_listen_entry));
if (entry == NULL) {
cb(cb_arg, LIB_ERR_MALLOC_FAIL, 0);
return;
}
entry->recv_handler = recv_handler;
entry->recv_handler_arg = recv_handler_arg;
collections_hash_insert(udp_listen_table, dest_port, entry);
call->out_header = (struct ump_net_out_header){
.op = UMP_NET_OP_UDP_LISTEN,
.d = { .udp_listen = { .dest_port = dest_port } }
};
ump_net_send_call(call, 0, NULL, cb, cb_arg);
}
void ump_net_udp_listen_stop (
struct ump_net_call *call,
uint16_t dest_port,
ump_net_udp_listen_stop_callback_t cb, void *cb_arg
) {
if (collections_hash_find(udp_listen_table, dest_port) == NULL) {
cb(cb_arg, LIB_ERR_NET_NOT_LISTENING);
return;
}
collections_hash_delete(udp_listen_table, dest_port);
call->out_header = (struct ump_net_out_header){
.op = UMP_NET_OP_UDP_LISTEN_STOP,
.d = { .udp_listen_stop = { .dest_port = dest_port } }
};
ump_net_send_call(call, 0, NULL, cb, cb_arg);
}
static ump_recv_payload_callback_fn_t recv_payload_callback;
static void *recv_payload_callback_arg;
static void recv_payload_callback_wrap (void *arg, size_t payload_size, void *payload) {
if (recv_payload_callback != NULL) {
recv_payload_callback(recv_payload_callback_arg, payload_size, payload);
}
net_recv_next();
}
void ump_net_recv_payload (
void *payload,
ump_recv_payload_callback_fn_t callback, void *callback_arg
) {
recv_payload_callback = callback;
recv_payload_callback_arg = callback_arg;
ump_recv_payload(net_recv_chan, payload, recv_payload_callback_wrap, NULL);
}

View File

@ -33,10 +33,7 @@ lwip_standard_chksum(void *dataptr, uint16_t len)
if ((acc & 0xffff0000UL) != 0) {
acc = (acc >> 16) + (acc & 0x0000ffffUL);
}
/* This maybe a little confusing: reorder sum using htons()
instead of ntohs() since it has a little less call overhead.
The caller must invert bits for Internet sum ! */
return htons((uint16_t)acc);
return (uint16_t)acc;
};
/**

View File

@ -23,8 +23,9 @@
build application {
target = "enet",
cFiles = [
"enet_module.c"
cFiles = [
"enet_module.c",
"enet_proto.c"
],
mackerelDevices = ["imx8x/enet"],
addLibraries = libDeps ["devif_backend_enet", "netutil"],

View File

@ -10,8 +10,20 @@
#ifndef ENET_H_
#define ENET_H_
#include <aos/simpleslab.h>
#include <aos/ump_chan.h>
#include <aos/ump_net.h>
#include <collections/hash_table.h>
#include <netutil/types.h>
#include <netutil/etharp.h>
#include <netutil/ip.h>
#include <netutil/udp.h>
// enable verbose debug logs
//#define ENET_DEBUG_OPTION 1
// enable warnings when packets are dropped, and other unusual events happen
#define ENET_WARN_OPTION 1
#if defined(ENET_DEBUG_OPTION)
#define ENET_DEBUG(x...) debug_printf("[enet] " x);
@ -19,6 +31,12 @@
#define ENET_DEBUG(fmt, ...) ((void)0)
#endif
#if defined(ENET_WARN_OPTION)
#define ENET_WARN(x...) debug_printf("[enet] WARN: " x);
#else
#define ENET_WARN(fmt, ...) ((void)0)
#endif
#define ENET_PROMISC
@ -62,7 +80,7 @@ struct enet_queue {
size_t size;
// stop and wake threashold
uint16_t stop_th;
uint16_t stop_th;
uint16_t wake_th;
char* tso_hdr;
@ -85,6 +103,83 @@ struct enet_queue {
struct region_entry* regions;
};
typedef void (*tx_alloc_callback_t)(void *cb_arg, struct devq_buf *buf, void *vaddr);
struct tx_alloc_queue_entry {
tx_alloc_callback_t cb;
void *cb_arg;
struct tx_alloc_queue_entry *next;
};
#define ARP_TX_QUEUE_LEN 64
STATIC_ASSERT((ARP_TX_QUEUE_LEN & (ARP_TX_QUEUE_LEN - 1)) == 0, "Must be power of 2");
struct arp_tx_queue_entry {
struct tx_alloc_queue_entry tx_entry;
uint64_t eth_dst;
uint32_t ip_dst;
};
struct arp_state {
struct arp_tx_queue_entry tx_queue[ARP_TX_QUEUE_LEN];
size_t tx_queue_len;
size_t tx_queue_tail;
collections_hash_table* table;
};
struct ip_state {
uint16_t next_id;
};
struct udp_state {
collections_hash_table* listen_table;
uint16_t next_ephemeral_port;
};
struct ump_client;
struct ump_reply_buf {
struct ump_send_queue_entry ump_entry;
struct ump_net_in_header ump_header;
struct ump_client *client;
};
struct ump_client {
struct ump_send_chan *send_chan;
struct ump_recv_chan *recv_chan;
struct tx_alloc_queue_entry tx_entry;
struct devq_buf tx_buf;
struct ip_hdr *tx_ip_hdr;
// If ump_reply_slab becomes empty, this is set to true and
// UMP receiving is blocked.
bool blocked_on_reply_slab;
struct simpleslab_allocator ump_reply_slab;
uint8_t ump_reply_slab_buf[sizeof(struct ump_reply_buf) * 128];
};
struct icmp_echo_reply_meta {
struct devq_buf rx_buf;
struct tx_alloc_queue_entry tx_entry;
struct icmp_echo_hdr *icmp_echo_hdr;
struct eth_hdr *eth_hdr;
struct ip_hdr *ip_hdr;
};
struct udp_recv {
struct devq_buf rx_buf;
struct ump_send_queue_entry ump_entry;
struct ump_net_in_header ump_header;
};
// union to obtain max required size
union rx_meta_slab_data {
struct icmp_echo_reply_meta icmp_echo_reply_meta;
struct udp_recv udp_recv;
};
struct enet_driver_state {
struct bfdriver_instance *bfi;
struct capref regs;
@ -99,9 +194,30 @@ struct enet_driver_state {
struct capref rx_mem;
struct capref tx_mem;
regionid_t tx_rid;
int tx_init_i;
ip_addr_t my_ip;
int net_bits;
ip_addr_t net_mask;
struct tx_alloc_queue_entry *tx_alloc_queue_head;
struct tx_alloc_queue_entry *tx_alloc_queue_tail;
// There are exactly as many of these blocks as there are RX buffers.
// You can allocate one for as long as you hold an RX buffer.
struct simpleslab_allocator rx_meta_slab;
uint8_t rx_meta_slab_buf[sizeof(union rx_meta_slab_data) * RX_RING_SIZE];
struct arp_state arp;
struct ip_state ip;
struct udp_state udp;
};
#define ENET_HASH_BITS 6
#define ENET_CRC32_POLY 0xEDB88320
void enet_loop (void);
#endif // ndef ENET_H_

View File

@ -25,7 +25,7 @@
#include "enet.h"
static struct region_entry* get_region(struct enet_queue* q, regionid_t rid)
struct region_entry* enet_get_region(struct enet_queue* q, regionid_t rid)
{
struct region_entry* entry = q->regions;
while (entry != NULL) {
@ -47,7 +47,7 @@ static errval_t enet_register(struct devq* q, struct capref cap, regionid_t rid)
assert(entry);
entry->rid = rid;
entry->next = NULL;
struct frame_identity id;
err = frame_identify(cap, &id);
if (err_is_fail(err)) {
@ -65,7 +65,7 @@ static errval_t enet_register(struct devq* q, struct capref cap, regionid_t rid)
entry->mem.vbase = (lvaddr_t) va;
entry->mem.mem = cap;
entry->mem.size = id.bytes;
ENET_DEBUG("register region id %d base=%lx \n", rid, entry->mem.devaddr);
// linked list of regions
struct region_entry* cur = queue->regions;
@ -77,10 +77,10 @@ static errval_t enet_register(struct devq* q, struct capref cap, regionid_t rid)
while (cur->next != NULL) {
cur = cur->next;
}
cur->next = entry;
ENET_DEBUG("registerd region id %d base=%p len=%ld \n", rid,
ENET_DEBUG("registerd region id %d base=%p len=%ld \n", rid,
(void*) entry->mem.vbase, entry->mem.size);
return SYS_ERR_OK;
}
@ -102,13 +102,13 @@ static inline size_t enet_full_slots(struct enet_queue* q)
static void enet_activate_tx_ring(enet_t * d)
{
// bit is always set to 1 only when ring is empty then it is set to 0
enet_tdar_tdar_wrf(d, 1);
enet_tdar_tdar_wrf(d, 1);
}
static void enet_activate_rx_ring(enet_t* d)
{
// bit is always set to 1 only when ring is empty then it is set to 0
enet_rdar_rdar_wrf(d, 1);
enet_rdar_rdar_wrf(d, 1);
}
static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid,
@ -118,7 +118,7 @@ static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid,
genoffset_t* valid_length,
uint64_t* flags)
{
struct enet_queue* q = (struct enet_queue*) que;
struct enet_queue* q = (struct enet_queue*) que;
enet_bufdesc_t desc = q->ring[q->head];
struct devq_buf* buf = &q->ring_bufs[q->head];
@ -131,7 +131,7 @@ static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid,
}
/*
ENET_DEBUG("Try dequeue %d RADR %d ENABLED %d STATUS %lx \n", q->head,
ENET_DEBUG("Try dequeue %d RADR %d ENABLED %d STATUS %lx \n", q->head,
enet_rdar_rdar_rdf(q->d), enet_ecr_etheren_rdf(q->d), status);
*/
if (!(status & ENET_RX_EMPTY)) {
@ -153,15 +153,15 @@ static errval_t enet_rx_dequeue(struct devq* que, regionid_t* rid,
status &= ~ENET_RX_STATS;
// remove chached stuff in buffer
struct region_entry *entry = get_region(q, *rid);
assert(entry);
struct region_entry *entry = enet_get_region(q, *rid);
assert(entry);
lvaddr_t vaddr = (lvaddr_t) entry->mem.vbase + *offset + *valid_data;
cpu_dcache_wb_range(vaddr, *valid_length);
cpu_dcache_wb_range(vaddr, *valid_length);
dmb();
enet_bufdesc_sc_insert(desc, status);
q->head = (q->head+1) & (q->size -1);
@ -175,18 +175,18 @@ static errval_t enet_tx_dequeue(struct devq* que, regionid_t* rid,
genoffset_t* valid_length,
uint64_t* flags)
{
struct enet_queue* q = (struct enet_queue*) que;
struct enet_queue* q = (struct enet_queue*) que;
if (enet_full_slots(q)) {
enet_bufdesc_t desc = q->ring[q->head];
dmb();
cpu_dcache_wb_range((lvaddr_t) &q->ring[q->head],
cpu_dcache_wb_range((lvaddr_t) &q->ring[q->head],
sizeof(enet_bufdesc_t));
desc = q->ring[q->head];
struct devq_buf* buf= &q->ring_bufs[q->head];
if (!(enet_bufdesc_sc_extract(desc) & ENET_TX_READY)) {
ENET_DEBUG("We sent something!! \n");
// ENET_DEBUG("We sent something!! \n");
*valid_length = buf->valid_length;
*offset = buf->offset;
*length = buf->length;
@ -210,8 +210,8 @@ static errval_t enet_tx_enqueue(struct devq* que, regionid_t rid, genoffset_t of
genoffset_t length, genoffset_t valid_data,
genoffset_t valid_length, uint64_t flags)
{
struct enet_queue* q = (struct enet_queue*) que;
struct enet_queue* q = (struct enet_queue*) que;
assert(valid_length > 0 && valid_length < ENET_MAX_PKT_SIZE);
@ -221,11 +221,11 @@ static errval_t enet_tx_enqueue(struct devq* que, regionid_t rid, genoffset_t of
lpaddr_t addr = 0;
lvaddr_t vaddr = 0;
struct region_entry *entry = get_region(q, rid);
assert(entry);
struct region_entry *entry = enet_get_region(q, rid);
assert(entry);
addr = (lpaddr_t) entry->mem.devaddr + offset + valid_data;
vaddr = (lvaddr_t) entry->mem.vbase + offset + valid_data;
struct devq_buf* buf= &q->ring_bufs[q->tail];
buf->offset = offset;
buf->length = length;
@ -233,9 +233,9 @@ static errval_t enet_tx_enqueue(struct devq* que, regionid_t rid, genoffset_t of
buf->valid_data = valid_data;
buf->rid = rid;
buf->flags = flags;
// TODO alignment
enet_bufdesc_t desc = q->ring[q->tail];
enet_bufdesc_addr_insert(desc, addr);
enet_bufdesc_len_insert(desc, valid_length);
@ -244,7 +244,7 @@ static errval_t enet_tx_enqueue(struct devq* que, regionid_t rid, genoffset_t of
dmb();
if (q->tail == (q->size -1)) {
enet_bufdesc_sc_insert(desc, ENET_TX_READY | ENET_TX_CRC |
enet_bufdesc_sc_insert(desc, ENET_TX_READY | ENET_TX_CRC |
ENET_TX_LAST | ENET_TX_WRAP);
} else {
enet_bufdesc_sc_insert(desc, ENET_TX_READY | ENET_TX_CRC | ENET_TX_LAST);
@ -277,20 +277,20 @@ static errval_t enet_rx_enqueue(struct devq* que, regionid_t rid, genoffset_t of
genoffset_t length, genoffset_t valid_data,
genoffset_t valid_length, uint64_t flags)
{
struct enet_queue* q = (struct enet_queue*) que;
struct enet_queue* q = (struct enet_queue*) que;
//enet_bufdesc_addr_insert(desc, );
struct region_entry *entry = get_region(q, rid);
assert(entry);
struct region_entry *entry = enet_get_region(q, rid);
assert(entry);
assert(valid_length > 0 && length <= ENET_MAX_BUF_SIZE);
if (enet_full_slots(q) == q->size) {
return DEVQ_ERR_QUEUE_FULL;
}
lpaddr_t addr = 0;
addr = (lpaddr_t) entry->mem.devaddr + offset;
struct devq_buf* buf= &q->ring_bufs[q->tail];
buf->offset = offset;
buf->length = length;
@ -298,7 +298,7 @@ static errval_t enet_rx_enqueue(struct devq* que, regionid_t rid, genoffset_t of
buf->valid_data = valid_data;
buf->rid = rid;
buf->flags = flags;
enet_bufdesc_t desc = q->ring[q->tail];
enet_bufdesc_addr_insert(desc, addr);
enet_bufdesc_len_insert(desc, 0);
@ -311,7 +311,7 @@ static errval_t enet_rx_enqueue(struct devq* que, regionid_t rid, genoffset_t of
}
arm64_dcache_wb_range((lvaddr_t) &q->ring[q->tail], sizeof(enet_bufdesc_t));
/*ENET_DEBUG("enqueue ring_buf[%d]=%p phys=%lx offset=%lx length=%zu\n", q->tail,
/*ENET_DEBUG("enqueue ring_buf[%d]=%p phys=%lx offset=%lx length=%zu\n", q->tail,
q->ring[q->tail], addr, offset, length);
*/
// activate RX (This is only needed if ring is empty)
@ -330,7 +330,7 @@ errval_t enet_rx_queue_create(struct enet_queue ** q, enet_t *dev)
assert(rxq);
rxq->size = RX_RING_SIZE;
/* Initialize Mackerel binding */
rxq->d = dev;
@ -413,7 +413,7 @@ errval_t enet_tx_queue_create(struct enet_queue ** q, struct enet_t* dev)
struct enet_queue* txq;
txq = calloc(1, sizeof(struct enet_queue));
txq->size = TX_RING_SIZE;
/* Initialize Mackerel binding */
txq->d = dev;
assert(txq->d);

View File

@ -24,6 +24,7 @@
#include <dev/imx8x/enet_dev.h>
#include <maps/imx8x_map.h>
#include <netutil/etharp.h>
#include <netutil/types.h>
#include "enet.h"
@ -446,8 +447,15 @@ static void enet_reg_setup(struct enet_driver_state* st)
reg = enet_rcr_mii_mode_insert(reg, 0x1);
reg = enet_rcr_fce_insert(reg, 0x1);
reg = enet_rcr_max_fl_insert(reg, 1522);
reg = enet_rcr_crcfwd_insert(reg, 1); // strip received Ethernet CRC
//reg = enet_rcr_prom_insert(reg, 1);
enet_rcr_wr(st->d, reg);
enet_racc_t racc = 0;
racc = enet_racc_linedis_insert(racc, 1); // discard received packets with invalid Ethernet CRC
enet_racc_wr(st->d, racc);
enet_tcr_crcfwd_wrf(st->d, 0); // add Ethernet CRC when transmitting
}
static errval_t enet_open(struct enet_driver_state *st)
@ -563,13 +571,13 @@ static errval_t enet_probe(struct enet_driver_state* st)
return SYS_ERR_OK;
}
struct enet_driver_state *st;
int main(int argc, char *argv[]) {
errval_t err;
debug_printf("Enet driver started \n");
struct enet_driver_state * st = (struct enet_driver_state*)
calloc(1, sizeof(struct enet_driver_state));
st = (struct enet_driver_state*)calloc(1, sizeof(struct enet_driver_state));
assert(st != NULL);
/* Net Project: get the capability to the register region
@ -653,17 +661,47 @@ int main(int argc, char *argv[]) {
if (err_is_fail(err)) {
return err;
}
struct devq_buf buf;
while(true) {
err = devq_dequeue((struct devq*) st->rxq, &buf.rid, &buf.offset,
&buf.length, &buf.valid_data, &buf.valid_length,
&buf.flags);
if (err_is_ok(err)) {
debug_printf("Received Packet of size %lu \n", buf.valid_length);
err = devq_enqueue((struct devq*) st->rxq, buf.rid, buf.offset,
buf.length, buf.valid_data, buf.valid_length,
buf.flags);
assert(err_is_ok(err));
}
st->tx_rid = rid;
st->tx_init_i = 0;
st->tx_alloc_queue_head = NULL;
st->tx_alloc_queue_tail = NULL;
if (argc < 3) {
printf("ENET: Error: missing address arguments\n");
return 1;
}
int a1, a2, a3, a4;
int parsed = sscanf(argv[1], "%d.%d.%d.%d.", &a1, &a2, &a3, &a4);
if (parsed != 4) {
printf("ENET: Error: failed to parse IP address\n");
return 1;
}
st->my_ip = MK_IP(a1, a2, a3, a4);
int net_bits = strtol(argv[2], NULL, 10);
if (!(0 <= net_bits && net_bits <= 32)) {
printf("ENET: Error: network bit count out of range\n");
return 1;
}
st->net_bits = net_bits;
if (net_bits == 0) st->net_mask = 0; // shift by 32 bits is undefined behavior
else st->net_mask = 0xffffffffU << (32 - net_bits);
struct eth_addr_net mac = eth_addr_wr(st->mac);
debug_printf("my MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
mac.addr[0], mac.addr[1], mac.addr[2], mac.addr[3], mac.addr[4], mac.addr[5]
);
struct uint32_net ip = uint32_wr(st->my_ip);
debug_printf("my IP: %d.%d.%d.%d\n",
ip.val[0], ip.val[1], ip.val[2], ip.val[3]
);
struct uint32_net net = uint32_wr(st->my_ip & st->net_mask);
debug_printf("net: %d.%d.%d.%d/%d\n",
net.val[0], net.val[1], net.val[2], net.val[3],
st->net_bits
);
enet_loop();
}

View File

@ -0,0 +1,706 @@
#include <devif/queue_interface_backend.h>
#include <devif/backends/net/enet_devif.h>
#include <aos/aos.h>
#include <driverkit/driverkit.h>
#include <dev/imx8x/enet_dev.h>
#include <aos/ump_binding.h>
#include <aos/ump_chan.h>
#include <aos/ump_net.h>
#include <netutil/etharp.h>
#include <netutil/ip.h>
#include <netutil/icmp.h>
#include <netutil/udp.h>
#include <netutil/checksum.h>
#include <netutil/types.h>
#include "enet.h"
// https://datatracker.ietf.org/doc/html/rfc6335#section-6
#define EPHEMERAL_PORT_START 49152
#define EPHEMERAL_PORT_END 65535
extern struct enet_driver_state *st;
static void write_eth_ip_header (
struct devq_buf *buf, void *vaddr,
uint64_t eth_dst, uint32_t ip_dest, uint8_t proto, size_t payload_len
);
static void tx_alloc (struct tx_alloc_queue_entry *entry, tx_alloc_callback_t cb, void *cb_arg) {
errval_t err = DEVQ_ERR_QUEUE_EMPTY;
struct devq_buf buf;
// If the queue is not empty, always append to the queue, so that FIFO order is maintained
if (st->tx_alloc_queue_head == NULL) {
err = devq_dequeue((struct devq*) st->txq, &buf.rid, &buf.offset,
&buf.length, &buf.valid_data, &buf.valid_length,
&buf.flags);
}
if (err_is_fail(err)) {
if (st->tx_init_i < st->txq->size) {
buf.rid = st->tx_rid;
buf.offset = st->tx_init_i * 2048;
buf.length = 2048;
buf.valid_data = 0;
buf.valid_length = 2048;
buf.flags = 0;
st->tx_init_i++;
} else {
entry->cb = cb;
entry->cb_arg = cb_arg;
entry->next = NULL;
if (st->tx_alloc_queue_tail == NULL) {
st->tx_alloc_queue_head = entry;
} else {
st->tx_alloc_queue_tail->next = entry;
}
st->tx_alloc_queue_tail = entry;
return;
}
}
struct region_entry *region_entry = enet_get_region(st->txq, buf.rid);
assert(region_entry != NULL);
void *vaddr = (void*)region_entry->mem.vbase + buf.offset;
cb(cb_arg, &buf, vaddr);
}
static void tx_send (struct devq_buf *buf) {
errval_t err;
err = devq_enqueue((struct devq*) st->txq, buf->rid, buf->offset,
buf->length, buf->valid_data, buf->valid_length,
buf->flags);
assert(err_is_ok(err));
}
static void rx_release (struct devq_buf *buf) {
errval_t err;
err = devq_enqueue((struct devq*) st->rxq, buf->rid, buf->offset,
buf->length, buf->valid_data, buf->valid_length,
buf->flags);
assert(err_is_ok(err));
}
// ARP: https://datatracker.ietf.org/doc/html/rfc826
static void arp_send (
struct devq_buf *buf, void *vaddr,
uint64_t eth_dst, uint32_t ip_dest,
uint16_t op
) {
buf->valid_data = 0;
buf->valid_length = ETH_HLEN + sizeof(struct arp_hdr);
struct eth_hdr *eth_hdr = vaddr;
eth_hdr->dst = eth_addr_wr(eth_dst);
eth_hdr->src = eth_addr_wr(st->mac);
eth_hdr->type = uint16_wr(ETH_TYPE_ARP);
struct arp_hdr *arp_hdr = vaddr + ETH_HLEN;
arp_hdr->hwtype = uint16_wr(ARP_HW_TYPE_ETH);
arp_hdr->proto = uint16_wr(ETH_TYPE_IP);
arp_hdr->hwlen = ETH_ADDR_LEN;
arp_hdr->protolen = IP_ADDR_LEN;
arp_hdr->opcode = uint16_wr(op);
arp_hdr->eth_src = eth_addr_wr(st->mac);
arp_hdr->ip_src = uint32_wr(st->my_ip);
arp_hdr->eth_dst = eth_addr_wr(eth_dst);
arp_hdr->ip_dst = uint32_wr(ip_dest);
tx_send(buf);
}
static void arp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) {
struct arp_tx_queue_entry *entry = &st->arp.tx_queue[st->arp.tx_queue_tail];
assert(cb_arg == entry);
arp_send(buf, vaddr, entry->eth_dst, entry->ip_dst, ARP_OP_REP);
st->arp.tx_queue_tail = (st->arp.tx_queue_tail + 1) & (ARP_TX_QUEUE_LEN - 1);
st->arp.tx_queue_len--;
}
static void arp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr) {
ENET_DEBUG("Received ARP packet\n");
if (buf->valid_length < sizeof(struct arp_hdr)) {
ENET_WARN("Received ARP packet too small\n");
rx_release(buf);
return;
}
struct arp_hdr *arp_hdr = vaddr + buf->valid_data;
uint16_t opcode = uint16_rd(arp_hdr->opcode);
if (
uint16_rd(arp_hdr->hwtype) != ARP_HW_TYPE_ETH ||
uint16_rd(arp_hdr->proto) != ETH_TYPE_IP ||
arp_hdr->hwlen != ETH_ADDR_LEN ||
arp_hdr->protolen != IP_ADDR_LEN ||
(opcode != ARP_OP_REQ && opcode != ARP_OP_REP)
) {
ENET_WARN("Unknown ARP packet type\n");
rx_release(buf);
return;
}
uint32_t ip_src = uint32_rd(arp_hdr->ip_src);
if ((st->my_ip & st->net_mask) != (ip_src & st->net_mask)) {
ENET_WARN("Received ARP packet for address outside my network\n");
rx_release(buf);
return;
}
uint64_t eth_src = eth_addr_rd(arp_hdr->eth_src);
uint64_t eth_old = (uintptr_t)collections_hash_find(st->arp.table, ip_src);
// check if sender protocol address is in translation table, if so update entry
if (eth_old != 0 && eth_src != eth_old) {
ENET_WARN("MAC address of %d.%d.%d.%d has changed\n",
arp_hdr->ip_src.val[0],
arp_hdr->ip_src.val[1],
arp_hdr->ip_src.val[2],
arp_hdr->ip_src.val[3]
);
collections_hash_delete(st->arp.table, ip_src);
collections_hash_insert(st->arp.table, ip_src, (void*)eth_src);
}
if (uint32_rd(arp_hdr->ip_dst) == st->my_ip) {
ENET_DEBUG("Received ARP packet addressed to me\n");
// if sender protocol address was not in translation table, add entry
if (eth_old == 0) {
collections_hash_insert(st->arp.table, ip_src, (void*)eth_src);
}
if (opcode == ARP_OP_REQ) {
if (st->arp.tx_queue_len >= ARP_TX_QUEUE_LEN) {
ENET_WARN("Too many queued ARP replies, not sending reply\n");
} else {
struct arp_tx_queue_entry *entry = &st->arp.tx_queue[(st->arp.tx_queue_tail + st->arp.tx_queue_len) & (ARP_TX_QUEUE_LEN - 1)];
st->arp.tx_queue_len++;
entry->eth_dst = eth_src;
entry->ip_dst = ip_src;
tx_alloc(&entry->tx_entry, arp_reply, entry);
}
}
}
rx_release(buf);
}
// ICMP: https://datatracker.ietf.org/doc/html/rfc792
static void icmp_reply (void *cb_arg, struct devq_buf *buf, void *vaddr) {
struct icmp_echo_reply_meta *entry = cb_arg;
write_eth_ip_header(
buf, vaddr,
eth_addr_rd(entry->eth_hdr->src),
uint32_rd(entry->ip_hdr->src),
IP_PROTO_ICMP,
entry->rx_buf.valid_length
);
struct icmp_echo_hdr *icmp_echo_hdr = vaddr + ETH_HLEN + sizeof(struct ip_hdr);
memcpy(icmp_echo_hdr, entry->icmp_echo_hdr, entry->rx_buf.valid_length);
icmp_echo_hdr->type = ICMP_ER;
// Incremental update: https://datatracker.ietf.org/doc/html/rfc1071
uint32_t chksum = uint16_rd(icmp_echo_hdr->chksum);
chksum += (ICMP_ECHO << 8) + ((ICMP_ER << 8) ^ 0x0000ffffUL);
chksum = (chksum >> 16) + (chksum & 0x0000ffffUL);
icmp_echo_hdr->chksum = uint16_wr(chksum);
rx_release(&entry->rx_buf);
simpleslab_free(&st->rx_meta_slab, entry);
tx_send(buf);
}
static void icmp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr, struct ip_hdr *ip_hdr) {
if (buf->valid_length < 4) {
ENET_WARN("Received ICMP packet too small\n");
rx_release(buf);
return;
}
if (inet_checksum(vaddr + buf->valid_data, buf->valid_length) != 0) {
ENET_WARN("Received ICMP packet with bad checksum\n");
rx_release(buf);
return;
}
uint8_t type = *(uint8_t*)(vaddr + buf->valid_data);
if (type == ICMP_ECHO) {
if (buf->valid_length < sizeof(struct icmp_echo_hdr)) {
ENET_WARN("Received ICMP packet too small\n");
rx_release(buf);
return;
}
struct icmp_echo_hdr *icmp_echo_hdr = vaddr + buf->valid_data;
ENET_DEBUG("Received ICMP echo, seq=%d\n", uint16_rd(icmp_echo_hdr->seqno));
struct icmp_echo_reply_meta *meta = simpleslab_alloc(&st->rx_meta_slab);
meta->rx_buf = *buf;
meta->eth_hdr = eth_hdr;
meta->ip_hdr = ip_hdr;
meta->icmp_echo_hdr = icmp_echo_hdr;
tx_alloc(&meta->tx_entry, icmp_reply, meta);
return;
} else {
ENET_WARN("Received ICMP packet with unknown type %d\n", type);
}
rx_release(buf);
}
// UDP: https://datatracker.ietf.org/doc/html/rfc768
static void udp_recv_ump_callback (void *arg, struct ump_send_queue_entry *entry) {
struct udp_recv *meta = arg;
rx_release(&meta->rx_buf);
simpleslab_free(&st->rx_meta_slab, meta);
}
static uint16_t udp_checksum (struct ip_hdr *ip_hdr, struct udp_hdr *udp_hdr) {
uint16_t udp_len = uint16_rd(udp_hdr->len);
uint32_t chksum = inet_checksum(udp_hdr, udp_len) ^ 0x0000ffffUL;
// add pseudo header
chksum += (ip_hdr->src.val[0] << 8) | ip_hdr->src.val[1];
chksum += (ip_hdr->src.val[2] << 8) | ip_hdr->src.val[3];
chksum += (ip_hdr->dest.val[0] << 8) | ip_hdr->dest.val[1];
chksum += (ip_hdr->dest.val[2] << 8) | ip_hdr->dest.val[3];
chksum += IP_PROTO_UDP;
chksum += udp_len;
chksum = (chksum >> 16) + (chksum & 0x0000ffffUL);
chksum = (chksum >> 16) + (chksum & 0x0000ffffUL);
return ~(uint16_t)chksum;
}
static void udp_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr, struct ip_hdr *ip_hdr) {
if (buf->valid_length < UDP_HLEN) {
ENET_WARN("Received UDP packet too small\n");
rx_release(buf);
return;
}
struct udp_hdr *udp_hdr = vaddr + buf->valid_data;
uint16_t udp_len = uint16_rd(udp_hdr->len);
if (udp_len < UDP_HLEN || buf->valid_length < udp_len) {
ENET_WARN("Received UDP packet too small\n");
rx_release(buf);
return;
}
if (
uint16_rd(udp_hdr->chksum) != 0 && // 0 means no checksum
udp_checksum(ip_hdr, udp_hdr) != 0
) {
ENET_WARN("Received UDP packet with bad checksum\n");
rx_release(buf);
return;
}
uint16_t src_port = uint16_rd(udp_hdr->src);
uint16_t dest_port = uint16_rd(udp_hdr->dest);
void *payload = (void*)udp_hdr + UDP_HLEN;
uint16_t payload_len = udp_len - UDP_HLEN;
ENET_DEBUG("Received UDP packet from %d to %d, len %d\n", src_port, dest_port, payload_len);
struct ump_client *client = collections_hash_find(st->udp.listen_table, dest_port);
if (client == NULL) {
// TODO: send ICMP unreachable
rx_release(buf);
} else {
struct udp_recv *meta = simpleslab_alloc(&st->rx_meta_slab);
meta->rx_buf = *buf;
meta->ump_header = (struct ump_net_in_header){
.op = UMP_NET_EV_UDP_RECV,
.d = { .udp_recv = {
.src_ip = uint32_rd(ip_hdr->src),
.dest_ip = uint32_rd(ip_hdr->dest),
.src_port = src_port,
.dest_port = dest_port,
} }
};
ump_send(client->send_chan, &meta->ump_entry,
sizeof(struct ump_net_in_header), &meta->ump_header,
payload_len, payload,
udp_recv_ump_callback, meta);
return;
}
}
// IP: https://datatracker.ietf.org/doc/html/rfc791#section-3.1
static void ip_handle (struct devq_buf *buf, void *vaddr, struct eth_hdr *eth_hdr) {
if (buf->valid_length < sizeof(struct ip_hdr)) {
ENET_WARN("Received IP packet too small\n");
rx_release(buf);
return;
}
struct ip_hdr *ip_hdr = vaddr + buf->valid_data;
uint16_t ip_len = uint16_rd(ip_hdr->len);
uint16_t header_len = IPH_HL(ip_hdr) * 4;
if (
IPH_V(ip_hdr) != 4 ||
header_len < 20 ||
header_len > ip_len ||
ip_len > buf->valid_length
) {
ENET_WARN("Received bad IP packet\n");
rx_release(buf);
return;
}
if (inet_checksum(ip_hdr, header_len) != 0) {
ENET_WARN("Received IP packet with bad checksum\n");
rx_release(buf);
return;
}
uint16_t offset_flags = uint16_rd(ip_hdr->offset);
if (
(offset_flags & IP_MF) != 0 ||
(offset_flags & IP_OFFMASK) != 0
) {
ENET_WARN("Received IP fragment, dropping\n");
rx_release(buf);
return;
}
ENET_DEBUG("Received IP packet from %d.%d.%d.%d to %d.%d.%d.%d, proto %d\n",
ip_hdr->src.val[0],
ip_hdr->src.val[1],
ip_hdr->src.val[2],
ip_hdr->src.val[3],
ip_hdr->dest.val[0],
ip_hdr->dest.val[1],
ip_hdr->dest.val[2],
ip_hdr->dest.val[3],
ip_hdr->proto
);
buf->valid_data += header_len;
buf->valid_length = ip_len - header_len;
if (uint32_rd(ip_hdr->dest) == st->my_ip) {
if (ip_hdr->proto == IP_PROTO_ICMP) {
icmp_handle(buf, vaddr, eth_hdr, ip_hdr);
} else if (ip_hdr->proto == IP_PROTO_UDP) {
udp_handle(buf, vaddr, eth_hdr, ip_hdr);
} else {
ENET_WARN("Received IP packet with unknown protocol %d\n", ip_hdr->proto);
rx_release(buf);
}
} else {
ENET_WARN("Received IP packet for someone else\n");
rx_release(buf);
}
}
static void write_eth_ip_header (
struct devq_buf *buf, void *vaddr,
uint64_t eth_dst, uint32_t ip_dest, uint8_t proto, size_t payload_len
) {
buf->valid_data = 0;
buf->valid_length = ETH_HLEN + sizeof(struct ip_hdr) + payload_len;
assert(buf->valid_length <= ETH_HLEN + 1500);
struct eth_hdr *eth_hdr = vaddr;
eth_hdr->dst = eth_addr_wr(eth_dst);
eth_hdr->src = eth_addr_wr(st->mac);
eth_hdr->type = uint16_wr(ETH_TYPE_IP);
struct ip_hdr *ip_hdr = vaddr + ETH_HLEN;
IPH_VHL_SET(ip_hdr, 4, 5);
ip_hdr->tos = 0;
ip_hdr->len = uint16_wr(sizeof(struct ip_hdr) + payload_len);
ip_hdr->id = uint16_wr(st->ip.next_id++);
ip_hdr->offset = uint16_wr(IP_DF);
ip_hdr->ttl = 64;
ip_hdr->proto = proto;
ip_hdr->chksum = uint16_wr(0);
ip_hdr->src = uint32_wr(st->my_ip);
ip_hdr->dest = uint32_wr(ip_dest);
ip_hdr->chksum = uint16_wr(inet_checksum(ip_hdr, 20));
}
// Ethernet
static void rx_handle (struct devq_buf *buf) {
struct region_entry *entry = enet_get_region(st->rxq, buf->rid);
assert(entry != NULL);
void *vaddr = (void*)entry->mem.vbase + buf->offset;
void *eth_vaddr = vaddr + buf->valid_data;
#if defined(ENET_DEBUG_OPTION)
debug_printf("Received Packet of size %lu:", buf->valid_length);
for (size_t i = 0; i < buf->valid_length; i++) {
printf(" %02x", ((uint8_t*)eth_vaddr)[i]);
}
printf("\n");
#endif
if (buf->valid_length < ETH_HLEN) {
ENET_WARN("Ethernet packet too small\n");
rx_release(buf);
return;
}
struct eth_hdr *eth_hdr = eth_vaddr;
uint16_t type = uint16_rd(eth_hdr->type);
buf->valid_data += ETH_HLEN;
buf->valid_length -= ETH_HLEN;
if (type == ETH_TYPE_ARP) {
arp_handle(buf, vaddr, eth_hdr);
} else if (type == ETH_TYPE_IP) {
ip_handle(buf, vaddr, eth_hdr);
} else {
ENET_WARN("Received packet of unknown type: %04x\n", type);
rx_release(buf);
}
}
// UMP
static void netump_header_handle (void *arg, size_t header_size, void *header_raw, size_t payload_size);
static void netump_next (struct ump_client *client) {
if (client->ump_reply_slab.free != 0) {
ump_recv_header(client->recv_chan, netump_header_handle, client);
} else {
client->blocked_on_reply_slab = true;
}
}
static void netump_reply_callback (void *arg, struct ump_send_queue_entry *entry) {
struct ump_reply_buf *reply = arg;
struct ump_client *client = reply->client;
simpleslab_free(&client->ump_reply_slab, reply);
if (client->blocked_on_reply_slab) {
client->blocked_on_reply_slab = false;
netump_next(client);
}
}
static void netump_payload_ignore (void *arg, size_t payload_size, void *payload) {
netump_next(arg);
}
static void netump_send_reply (struct ump_reply_buf *reply, errval_t err) {
reply->ump_header.ret_err = err;
ump_send(reply->client->send_chan, &reply->ump_entry,
sizeof(struct ump_net_in_header), &reply->ump_header,
0, NULL,
netump_reply_callback, reply);
}
static void netump_udp_tx_payload (void *arg, size_t payload_size, void *payload) {
struct ump_client *client = arg;
struct udp_hdr *udp_hdr = (void*)client->tx_ip_hdr + sizeof(struct ip_hdr);
uint16_t checksum = udp_checksum(client->tx_ip_hdr, udp_hdr);
if (checksum == 0) checksum = 0xffff;
udp_hdr->chksum = uint16_wr(checksum);
tx_send(&client->tx_buf);
netump_next(client);
}
static void netump_udp_tx_allocated (void *arg, struct devq_buf *buf, void *vaddr) {
struct ump_reply_buf *reply = arg;
struct ump_client *client = reply->client;
struct ump_net_out_header *header = client->recv_chan->header;
size_t payload_size = client->recv_chan->next_payload_size;
uint64_t eth_dest = (uintptr_t)collections_hash_find(st->arp.table, header->d.udp_send.dest_ip);
if (eth_dest == 0) {
ENET_WARN("IP not in ARP table, dropping packet and sending ARP request\n");
arp_send(buf, vaddr, 0xffffffffffff,
header->d.udp_send.dest_ip, ARP_OP_REQ);
netump_send_reply(reply, LIB_ERR_NET_ARP_MISS);
ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client);
return;
}
write_eth_ip_header(
buf, vaddr,
eth_dest,
header->d.udp_send.dest_ip,
IP_PROTO_UDP,
UDP_HLEN + payload_size
);
struct udp_hdr *udp_hdr = vaddr + ETH_HLEN + sizeof(struct ip_hdr);
udp_hdr->src = uint16_wr(header->d.udp_send.src_port);
udp_hdr->dest = uint16_wr(header->d.udp_send.dest_port);
udp_hdr->len = uint16_wr(UDP_HLEN + payload_size);
udp_hdr->chksum = uint16_wr(0);
void *payload = (void*)udp_hdr + UDP_HLEN;
client->tx_buf = *buf;
client->tx_ip_hdr = vaddr + ETH_HLEN;
netump_send_reply(reply, SYS_ERR_OK);
ump_recv_payload(client->recv_chan, payload, netump_udp_tx_payload, client);
}
static void netump_header_handle (void *arg, size_t header_size, void *header_raw, size_t payload_size) {
struct ump_client *client = arg;
struct ump_net_out_header *header = header_raw;
errval_t err;
struct ump_reply_buf *reply = simpleslab_alloc(&client->ump_reply_slab);
assert(reply != NULL);
reply->client = client;
reply->ump_header.op = header->op;
if (header->op == UMP_NET_OP_UDP_SEND) {
if (payload_size > 1500 - sizeof(struct ip_hdr) - UDP_HLEN) {
err = LIB_ERR_NET_PACKET_TOO_BIG;
} else {
tx_alloc(&client->tx_entry, netump_udp_tx_allocated, reply);
return;
}
} else if (header->op == UMP_NET_OP_UDP_LISTEN) {
uint16_t port = header->d.udp_listen.dest_port;
if (port == 0) {
// allocate an ephemeral port
uint16_t first = st->udp.next_ephemeral_port;
port = first;
while (true) {
uint16_t next_port;
if (port == EPHEMERAL_PORT_END) {
next_port = EPHEMERAL_PORT_START;
} else {
next_port = port + 1;
}
if (collections_hash_find(st->udp.listen_table, port) == NULL) {
st->udp.next_ephemeral_port = next_port;
break;
}
port = next_port;
if (port == first) {
port = 0;
break;
}
}
}
if (port == 0) {
err = LIB_ERR_NET_ALLOC_PORT;
} else if (collections_hash_find(st->udp.listen_table, port) != NULL) {
err = LIB_ERR_NET_PORT_IN_USE;
} else {
ENET_DEBUG("Listening on UDP port %d\n", port);
reply->ump_header.d.udp_listen.dest_port = port;
collections_hash_insert(st->udp.listen_table, port, client);
err = SYS_ERR_OK;
}
} else if (header->op == UMP_NET_OP_UDP_LISTEN_STOP) {
uint16_t port = header->d.udp_listen_stop.dest_port;
if (collections_hash_find(st->udp.listen_table, port) != client) {
err = LIB_ERR_NET_NOT_LISTENING;
} else {
ENET_DEBUG("Stopped listening on UDP port %d\n", port);
collections_hash_delete(st->udp.listen_table, port);
err = SYS_ERR_OK;
}
} else {
debug_printf("Error: unknown UMP op\n");
simpleslab_free(&client->ump_reply_slab, reply);
ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client);
return;
}
netump_send_reply(reply, err);
ump_recv_payload(client->recv_chan, NULL, netump_payload_ignore, client);
}
static errval_t netump_connect (void *arg, struct capref cap) {
errval_t err;
ENET_DEBUG("Incoming UMP connection\n");
struct ump_client *client = malloc(sizeof(struct ump_client));
if (client == NULL) return LIB_ERR_MALLOC_FAIL;
err = ump_chan_init(UMP_ROLE_SERVER, &client->send_chan, &client->recv_chan,
sizeof(struct ump_net_out_header), cap, get_default_waitset());
if (err_is_fail(err)) return err;
simpleslab_init(&client->ump_reply_slab, sizeof(struct ump_reply_buf),
&client->ump_reply_slab_buf, sizeof(client->ump_reply_slab_buf));
client->blocked_on_reply_slab = false;
netump_next(client);
return SYS_ERR_OK;
}
void enet_loop (void) {
errval_t err;
st->arp.tx_queue_len = 0;
st->arp.tx_queue_tail = 0;
collections_hash_create(&st->arp.table, NULL);
st->ip.next_id = 0;
collections_hash_create_with_buckets(&st->udp.listen_table, 100, NULL);
st->udp.next_ephemeral_port = EPHEMERAL_PORT_START;
simpleslab_init(&st->rx_meta_slab, sizeof(union rx_meta_slab_data),
&st->rx_meta_slab_buf, sizeof(st->rx_meta_slab_buf));
struct ump_binding_server server;
err = ump_binding_register(&server, UMP_SERVER_NET, netump_connect, NULL);
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to register UMP server");
struct devq_buf buf;
struct waitset *default_ws = get_default_waitset();
while (true) {
bool made_progress = false;
err = event_dispatch_non_block(default_ws);
if (err_is_fail(err) && err != LIB_ERR_NO_EVENT) {
DEBUG_ERR(err, "in event_dispatch");
abort();
}
// TODO: set made_progress if we handled an event. Currently,
// UMP always registers a callback, so we would always see an event.
// See TODO in poll_channels_disabled.
err = devq_dequeue((struct devq*) st->rxq, &buf.rid, &buf.offset,
&buf.length, &buf.valid_data, &buf.valid_length,
&buf.flags);
if (err_is_ok(err)) {
made_progress = true;
rx_handle(&buf);
}
if (st->tx_alloc_queue_head != NULL) {
err = devq_dequeue((struct devq*) st->txq, &buf.rid, &buf.offset,
&buf.length, &buf.valid_data, &buf.valid_length,
&buf.flags);
if (err_is_ok(err)) {
made_progress = true;
struct tx_alloc_queue_entry *entry = st->tx_alloc_queue_head;
st->tx_alloc_queue_head = entry->next;
if (entry->next == NULL) {
st->tx_alloc_queue_tail = NULL;
}
struct region_entry *region_entry = enet_get_region(st->txq, buf.rid);
assert(region_entry != NULL);
void *vaddr = (void*)region_entry->mem.vbase + buf.offset;
entry->cb(entry->cb_arg, &buf, vaddr);
}
}
if (!made_progress) {
thread_yield();
}
}
}

View File

@ -1,9 +1,16 @@
#include <stdio.h>
#include <stdlib.h>
#include <aos/aos.h>
#include <aos/ump_binding.h>
#include <aos/simpleslab.h>
#include <aos/deferred.h>
#include <aos/aos_rpc.h>
#include <aos/ump_binding.h>
#include <aos/ump_chan.h>
#include <aos/ump_net.h>
#include <aos/ump_net_client.h>
// Echo Protocol: https://datatracker.ietf.org/doc/html/rfc862
#define PORT_ECHO 7
#define ECHO_TEST_MESSAGE "Hello Client! Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
@ -48,18 +55,108 @@ static errval_t connect_server (void *arg, struct capref cap) {
return SYS_ERR_OK;
}
// net echo server
#define BUF_SIZE 1500
struct echo_buf {
uint32_t src_ip;
uint16_t src_port;
struct ump_net_call call;
uint8_t payload[BUF_SIZE];
};
static struct simpleslab_allocator slabs;
static uint8_t slab_buf[sizeof(struct echo_buf) * 128];
static void net_listen_callback (void *arg, errval_t err, uint16_t src_port) {
if (err_is_fail(err)) {
DEBUG_ERR(err, "failed to listen on UDP");
return;
}
printf("Echo server: Listening on UDP port %d.\n", src_port);
}
static void net_send_callback (void *arg, errval_t err) {
if (err_is_fail(err)) {
DEBUG_ERR(err, "failed to send packet");
}
}
static void net_reply_callback (void *arg, errval_t err) {
struct echo_buf *echo_buf = arg;
simpleslab_free(&slabs, echo_buf);
if (err_is_fail(err)) {
DEBUG_ERR(err, "failed to send packet");
}
}
static struct ump_net_ev_udp_recv net_udp_recv;
static void net_recv_packet (void *arg, size_t payload_size, void *payload) {
struct echo_buf *echo_buf = arg;
//printf("Echo server: Received payload: '%.*s'\n", payload_size, &echo_buf->payload);
// Send reply
ump_net_udp_send(
&echo_buf->call,
net_udp_recv.src_ip,
PORT_ECHO, net_udp_recv.src_port,
payload_size, &echo_buf->payload,
net_reply_callback, echo_buf
);
}
static void handle_udp_recv (void *arg, struct ump_net_ev_udp_recv *udp_recv, size_t payload_size) {
if (payload_size > BUF_SIZE) {
printf("Echo server: ERROR: Packet too big\n");
} else {
struct echo_buf *echo_buf = simpleslab_alloc(&slabs);
if (echo_buf == NULL) {
printf("Echo server: WARN: All buffers full, dropping packet\n");
} else {
net_udp_recv = *udp_recv;
ump_net_recv_payload(&echo_buf->payload, net_recv_packet, echo_buf);
return;
}
}
ump_net_recv_payload(NULL, NULL, NULL);
}
static void send_again (void *arg) {
static struct ump_net_call call;
ump_net_udp_send(&call, 0xc0a80204, PORT_ECHO, 7000, 12, "hello again\n", net_send_callback, NULL);
}
int main (int argc, char *argv[]) {
errval_t err;
struct ump_binding_server server;
struct waitset *default_ws = get_default_waitset();
printf("Echo server: registering\n");
struct ump_binding_server server;
err = ump_binding_register(&server, UMP_SERVER_ECHO, connect_server, NULL);
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to register UMP server");
printf("Echo server: listening\n");
struct waitset *default_ws = get_default_waitset();
simpleslab_init(&slabs, sizeof(struct echo_buf), slab_buf, sizeof(slab_buf));
err = ump_net_init(default_ws);
if (err_is_fail(err)) USER_PANIC_ERR(err, "Failed to connect to net server");
struct ump_net_call call;
ump_net_udp_listen(&call, PORT_ECHO, handle_udp_recv, NULL, net_listen_callback, NULL);
struct ump_net_call call2;
ump_net_udp_send(&call2, 0xc0a80204, PORT_ECHO, 7000, 6, "hello\n", net_send_callback, NULL);
struct deferred_event dev;
deferred_event_init(&dev);
err = deferred_event_register(&dev, default_ws, 1000000,
MKCLOSURE(send_again, NULL));
if (err_is_fail(err)) return err;
while (true) {
err = event_dispatch(default_ws);
if (err_is_fail(err)) {

View File

@ -148,6 +148,14 @@ bsp_main(int argc, char *argv[]) {
thread_create(urpc_client_loop, &urpc_to_app_ws);
// Spawn enet driver
struct spawninfo enet_si;
domainid_t enet_pid;
err = spawn_load_by_name("enet", &enet_si, &enet_pid);
if (err_is_fail(err)) {
DEBUG_ERR(err, "when spawning enet");
}
// Grading
grading_test_late();