diff --git a/examples/echo_server/board/imx8mm_evk/echo_server.system b/examples/echo_server/board/imx8mm_evk/echo_server.system index 264e4e867..f4c9dd681 100644 --- a/examples/echo_server/board/imx8mm_evk/echo_server.system +++ b/examples/echo_server/board/imx8mm_evk/echo_server.system @@ -164,7 +164,7 @@ - + @@ -181,7 +181,7 @@ - + diff --git a/examples/echo_server/board/maaxboard/echo_server.system b/examples/echo_server/board/maaxboard/echo_server.system index 05e1d7684..a26891f98 100644 --- a/examples/echo_server/board/maaxboard/echo_server.system +++ b/examples/echo_server/board/maaxboard/echo_server.system @@ -164,7 +164,7 @@ - + @@ -181,7 +181,7 @@ - + diff --git a/examples/echo_server/board/odroidc4/echo_server.system b/examples/echo_server/board/odroidc4/echo_server.system index 74998d981..001b4f9c8 100644 --- a/examples/echo_server/board/odroidc4/echo_server.system +++ b/examples/echo_server/board/odroidc4/echo_server.system @@ -164,7 +164,7 @@ - + @@ -181,7 +181,7 @@ - + diff --git a/examples/echo_server/board/qemu_virt_aarch64/echo_server.system b/examples/echo_server/board/qemu_virt_aarch64/echo_server.system index 1cacbb3c9..8a859f46a 100644 --- a/examples/echo_server/board/qemu_virt_aarch64/echo_server.system +++ b/examples/echo_server/board/qemu_virt_aarch64/echo_server.system @@ -163,7 +163,7 @@ - + @@ -180,7 +180,7 @@ - + diff --git a/examples/echo_server/echo.c b/examples/echo_server/echo.c new file mode 100644 index 000000000..6f9b3ffa0 --- /dev/null +++ b/examples/echo_server/echo.c @@ -0,0 +1,168 @@ +/* + * Copyright 2022, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lwip/pbuf.h" + +#include "echo.h" + +#define SERIAL_TX_CH 0 +#define TIMER_CH 1 +#define RX_CH 2 +#define TX_CH 3 + +char *serial_tx_data; +serial_queue_t *serial_tx_queue; +serial_queue_handle_t serial_tx_queue_handle; + +net_queue_t *rx_free; +net_queue_t *rx_active; +net_queue_t *tx_free; +net_queue_t *tx_active; +uintptr_t rx_buffer_data_region; +uintptr_t tx_buffer_data_region; + +net_queue_handle_t net_rx_handle; +net_queue_handle_t net_tx_handle; + +#define LWIP_TICK_MS 100 + +struct pbuf *head; +struct pbuf *tail; + +/** + * Netif status callback function that output's client's Microkit name and + * obtained IP address. + * + * @param ip_addr ip address of the client. + */ +void netif_status_callback(char *ip_addr) +{ + sddf_printf("DHCP request finished, IP address for netif %s is: %s\n", microkit_name, ip_addr); +} + +/** + * Sets a timeout for the next lwip tick. + */ +void set_timeout(void) +{ + sddf_timer_set_timeout(TIMER_CH, LWIP_TICK_MS * NS_IN_MS); +} + +/** + * Stores a pbuf to be transmitted upon available transmit buffers. + * + * @param p pbuf to be stored. + */ +net_sddf_err_t enqueue_pbufs(struct pbuf *p) +{ + /* Indicate to the tx virt that we wish to be notified about free tx buffers */ + net_request_signal_free(&net_tx_handle); + + if (head == NULL) { + head = p; + } else { + tail->next_chain = p; + } + tail = p; + + /* Increment reference count to ensure this pbuf is not freed by lwip */ + pbuf_ref(p); + + return SDDF_LWIP_ERR_OK; +} + +void transmit(void) +{ + bool reprocess = true; + while (reprocess) { + while (head != NULL && !net_queue_empty_free(&net_tx_handle)) { + net_sddf_err_t err = sddf_lwip_transmit_pbuf(head); + if (err == SDDF_LWIP_ERR_PBUF) { + sddf_dprintf("LWIP|ERROR: attempted to send a packet of size %u > BUFFER SIZE %u\n", head->tot_len, + NET_BUFFER_SIZE); + } else if (err != SDDF_LWIP_ERR_OK) { + sddf_dprintf("LWIP|ERROR: unkown error when trying to send pbuf %p\n", head); + } + + struct pbuf *temp = head; + head = temp->next_chain; + if (head == NULL) { + tail = NULL; + } + pbuf_free(temp); + } + + /* Only request a signal if there are more pending pbufs to send */ + if (head == NULL || !net_queue_empty_free(&net_tx_handle)) { + net_cancel_signal_free(&net_tx_handle); + } else { + net_request_signal_free(&net_tx_handle); + } + reprocess = false; + + if (head != NULL && !net_queue_empty_free(&net_tx_handle)) { + net_cancel_signal_free(&net_tx_handle); + reprocess = true; + } + } +} + +void init(void) +{ + serial_cli_queue_init_sys(microkit_name, NULL, NULL, NULL, &serial_tx_queue_handle, serial_tx_queue, + serial_tx_data); + serial_putchar_init(SERIAL_TX_CH, &serial_tx_queue_handle); + + size_t rx_size, tx_size; + net_cli_queue_size(microkit_name, &rx_size, &tx_size); + net_queue_init(&net_rx_handle, rx_free, rx_active, rx_size); + net_queue_init(&net_tx_handle, tx_free, tx_active, tx_size); + net_buffers_init(&net_tx_handle, 0); + + sddf_lwip_init(net_rx_handle, net_tx_handle, RX_CH, TX_CH, rx_buffer_data_region, tx_buffer_data_region, TIMER_CH, + net_cli_mac_addr(microkit_name), NULL, netif_status_callback, enqueue_pbufs); + set_timeout(); + + setup_udp_socket(); + setup_utilization_socket(); + setup_tcp_socket(); + + sddf_lwip_maybe_notify(); +} + +void notified(microkit_channel ch) +{ + switch (ch) { + case RX_CH: + sddf_lwip_process_rx(); + break; + case TIMER_CH: + sddf_lwip_process_timeout(); + set_timeout(); + break; + case TX_CH: + transmit(); + break; + case SERIAL_TX_CH: + break; + default: + sddf_dprintf("LWIP|LOG: received notification on unexpected channel: %u\n", ch); + break; + } + + sddf_lwip_maybe_notify(); +} diff --git a/examples/echo_server/echo.h b/examples/echo_server/echo.h index f514c979c..066e22365 100644 --- a/examples/echo_server/echo.h +++ b/examples/echo_server/echo.h @@ -9,9 +9,6 @@ #define UTILIZATION_PORT 1236 #define TCP_ECHO_PORT 1237 -#define LINK_SPEED 1000000000 // Gigabit -#define ETHER_MTU 1500 - int setup_udp_socket(void); int setup_utilization_socket(void); int setup_tcp_socket(void); diff --git a/examples/echo_server/echo.mk b/examples/echo_server/echo.mk index bf5e3a67c..7240089b8 100644 --- a/examples/echo_server/echo.mk +++ b/examples/echo_server/echo.mk @@ -7,17 +7,17 @@ QEMU := qemu-system-aarch64 MICROKIT_TOOL ?= $(MICROKIT_SDK)/bin/microkit -ECHO_SERVER:=${SDDF}/examples/echo_server -LWIPDIR:=network/ipstacks/lwip/src -BENCHMARK:=$(SDDF)/benchmark -UTIL:=$(SDDF)/util -ETHERNET_DRIVER:=$(SDDF)/drivers/network/$(DRIV_DIR) -ETHERNET_CONFIG_INCLUDE:=${ECHO_SERVER}/include/ethernet_config +ECHO_SERVER := ${SDDF}/examples/echo_server +LWIPDIR := network/ipstacks/lwip/src +BENCHMARK := $(SDDF)/benchmark +UTIL := $(SDDF)/util +ETHERNET_DRIVER := $(SDDF)/drivers/network/$(DRIV_DIR) +ETHERNET_CONFIG_INCLUDE := ${ECHO_SERVER}/include/ethernet_config SERIAL_COMPONENTS := $(SDDF)/serial/components UART_DRIVER := $(SDDF)/drivers/serial/$(UART_DRIV_DIR) -SERIAL_CONFIG_INCLUDE:=${ECHO_SERVER}/include/serial_config +SERIAL_CONFIG_INCLUDE := ${ECHO_SERVER}/include/serial_config TIMER_DRIVER:=$(SDDF)/drivers/timer/$(TIMER_DRV_DIR) -NETWORK_COMPONENTS:=$(SDDF)/network/components +NETWORK_COMPONENTS := $(SDDF)/network/components BOARD_DIR := $(MICROKIT_SDK)/board/$(MICROKIT_BOARD)/$(MICROKIT_CONFIG) SYSTEM_FILE := ${ECHO_SERVER}/board/$(MICROKIT_BOARD)/echo_server.system @@ -26,7 +26,7 @@ REPORT_FILE := report.txt vpath %.c ${SDDF} ${ECHO_SERVER} -IMAGES := eth_driver.elf lwip.elf benchmark.elf idle.elf network_virt_rx.elf\ +IMAGES := eth_driver.elf echo.elf benchmark.elf idle.elf network_virt_rx.elf\ network_virt_tx.elf copy.elf timer_driver.elf uart_driver.elf serial_virt_tx.elf CFLAGS := -mcpu=$(CPU) \ @@ -41,14 +41,13 @@ CFLAGS := -mcpu=$(CPU) \ -I${ETHERNET_CONFIG_INCLUDE} \ -I$(SERIAL_CONFIG_INCLUDE) \ -I${SDDF}/$(LWIPDIR)/include \ - -I${SDDF}/$(LWIPDIR)/include/ipv4 \ -MD \ -MP LDFLAGS := -L$(BOARD_DIR)/lib -L${LIBC} LIBS := --start-group -lmicrokit -Tmicrokit.ld -lc libsddf_util_debug.a --end-group -CHECK_FLAGS_BOARD_MD5:=.board_cflags-$(shell echo -- ${CFLAGS} ${BOARD} ${MICROKIT_CONFIG} | shasum | sed 's/ *-//') +CHECK_FLAGS_BOARD_MD5 := .board_cflags-$(shell echo -- ${CFLAGS} ${BOARD} ${MICROKIT_CONFIG} | shasum | sed 's/ *-//') ${CHECK_FLAGS_BOARD_MD5}: -rm -f .board_cflags-* @@ -64,21 +63,20 @@ include ${SDDF}/${LWIPDIR}/Filelists.mk NETIFFILES:=$(LWIPDIR)/netif/ethernet.c # LWIPFILES: All the above. -LWIPFILES=lwip.c $(COREFILES) $(CORE4FILES) $(NETIFFILES) -LWIP_OBJS := $(LWIPFILES:.c=.o) lwip.o utilization_socket.o \ +LWIPFILES := $(COREFILES) $(CORE4FILES) $(NETIFFILES) +ECHO_OBJS := $(LWIPFILES:.c=.o) echo.o utilization_socket.o \ udp_echo_socket.o tcp_echo_socket.o -OBJS := $(LWIP_OBJS) -DEPS := $(filter %.d,$(OBJS:.o=.d)) +DEPS := $(ECHO_OBJS:.o=.d) all: loader.img -${LWIP_OBJS}: ${CHECK_FLAGS_BOARD_MD5} -lwip.elf: $(LWIP_OBJS) libsddf_util.a +${ECHO_OBJS}: ${CHECK_FLAGS_BOARD_MD5} +echo.elf: $(ECHO_OBJS) lib_sddf_lwip.a libsddf_util.a $(LD) $(LDFLAGS) $^ $(LIBS) -o $@ LWIPDIRS := $(addprefix ${LWIPDIR}/, core/ipv4 netif api) -${LWIP_OBJS}: |${BUILD_DIR}/${LWIPDIRS} +${ECHO_OBJS}: |${BUILD_DIR}/${LWIPDIRS} ${BUILD_DIR}/${LWIPDIRS}: mkdir -p $@ @@ -92,6 +90,9 @@ ${IMAGE_FILE} $(REPORT_FILE): $(IMAGES) $(SYSTEM_FILE) include ${SDDF}/util/util.mk include ${SDDF}/network/components/network_components.mk +# Specify how many pbufs sDDF LWIP library requires for all clients +SDDF_LWIP_NUM_BUFS=512 +include ${SDDF}/network/lib_sddf_lwip/lib_sddf_lwip.mk include ${ETHERNET_DRIVER}/eth_driver.mk include ${BENCHMARK}/benchmark.mk include ${TIMER_DRIVER}/timer_driver.mk diff --git a/examples/echo_server/include/ethernet_config/ethernet_config.h b/examples/echo_server/include/ethernet_config/ethernet_config.h index 2d810c381..7a5913a80 100644 --- a/examples/echo_server/include/ethernet_config/ethernet_config.h +++ b/examples/echo_server/include/ethernet_config/ethernet_config.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #define NUM_NETWORK_CLIENTS 2 diff --git a/examples/echo_server/lwip.c b/examples/echo_server/lwip.c deleted file mode 100644 index 7f21022cd..000000000 --- a/examples/echo_server/lwip.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * Copyright 2022, UNSW - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "lwip/init.h" -#include "netif/etharp.h" -#include "lwip/pbuf.h" -#include "lwip/netif.h" -#include "lwip/stats.h" -#include "lwip/snmp.h" -#include "lwip/sys.h" -#include "lwip/timeouts.h" -#include "lwip/dhcp.h" - -#include "echo.h" - -#define SERIAL_TX_CH 0 -#define TIMER 1 -#define RX_CH 2 -#define TX_CH 3 - -char *serial_tx_data; -serial_queue_t *serial_tx_queue; -serial_queue_handle_t serial_tx_queue_handle; - -#define LWIP_TICK_MS 100 -#define NUM_PBUFFS NET_MAX_CLIENT_QUEUE_SIZE - -net_queue_t *rx_free; -net_queue_t *rx_active; -net_queue_t *tx_free; -net_queue_t *tx_active; -uintptr_t rx_buffer_data_region; -uintptr_t tx_buffer_data_region; - -/* Booleans to indicate whether packets have been enqueued during notification handling */ -static bool notify_tx; -static bool notify_rx; - -/* Wrapper over custom_pbuf structure to keep track of buffer offset */ -typedef struct pbuf_custom_offset { - struct pbuf_custom custom; - uint64_t offset; -} pbuf_custom_offset_t; - -LWIP_MEMPOOL_DECLARE( - RX_POOL, - NUM_PBUFFS * 2, - sizeof(struct pbuf_custom_offset), - "Zero-copy RX pool" -); - -typedef struct state { - struct netif netif; - uint8_t mac[ETH_HWADDR_LEN]; - net_queue_handle_t rx_queue; - net_queue_handle_t tx_queue; - struct pbuf *head; - struct pbuf *tail; -} state_t; - -state_t state; - -void set_timeout(void) -{ - sddf_timer_set_timeout(TIMER, LWIP_TICK_MS * NS_IN_MS); -} - -uint32_t sys_now(void) -{ - return sddf_timer_time_now(TIMER) / NS_IN_MS; -} - -/** - * Free a pbuf. This also returns the underlying buffer to the receive free ring. - * - * @param p pbuf to free. - */ -static void interface_free_buffer(struct pbuf *p) -{ - SYS_ARCH_DECL_PROTECT(old_level); - pbuf_custom_offset_t *custom_pbuf_offset = (pbuf_custom_offset_t *)p; - SYS_ARCH_PROTECT(old_level); - net_buff_desc_t buffer = {custom_pbuf_offset->offset, 0}; - int err = net_enqueue_free(&(state.rx_queue), buffer); - assert(!err); - notify_rx = true; - LWIP_MEMPOOL_FREE(RX_POOL, custom_pbuf_offset); - SYS_ARCH_UNPROTECT(old_level); -} - -/** - * Create a pbuf structure to pass to the network interface. - * - * @param state client state data. - * @param buffer shared buffer containing the data. - * @param length length of data. - * - * @return the newly created pbuf. Can be cast to pbuf_custom. - */ -static struct pbuf *create_interface_buffer(uint64_t offset, size_t length) -{ - pbuf_custom_offset_t *custom_pbuf_offset = (pbuf_custom_offset_t *) LWIP_MEMPOOL_ALLOC(RX_POOL); - custom_pbuf_offset->offset = offset; - custom_pbuf_offset->custom.custom_free_function = interface_free_buffer; - - return pbuf_alloced_custom( - PBUF_RAW, - length, - PBUF_REF, - &custom_pbuf_offset->custom, - (void *)(offset + rx_buffer_data_region), - NET_BUFFER_SIZE - ); -} - -/** - * Stores a pbuf to be transmitted upon available transmit buffers. - * - * @param p pbuf to be stored. - */ -void enqueue_pbufs(struct pbuf *p) -{ - /* Indicate to the multiplexer that we require transmit free buffers */ - net_request_signal_free(&state.tx_queue); - - if (state.head == NULL) { - state.head = p; - } else { - state.tail->next_chain = p; - } - state.tail = p; - - /* Increment refernce count to ensure this pbuf is not freed by lwip */ - pbuf_ref(p); -} - -/** - * Insert pbuf into transmit active queue. If no free buffers available or transmit active queue is full, - * stores pbuf to be sent upon buffers becoming available. - * */ -static err_t lwip_eth_send(struct netif *netif, struct pbuf *p) -{ - if (p->tot_len > NET_BUFFER_SIZE) { - sddf_dprintf("LWIP|ERROR: attempted to send a packet of size %u > BUFFER SIZE %u\n", p->tot_len, NET_BUFFER_SIZE); - return ERR_MEM; - } - - if (net_queue_empty_free(&state.tx_queue)) { - enqueue_pbufs(p); - return ERR_OK; - } - - net_buff_desc_t buffer; - int err = net_dequeue_free(&state.tx_queue, &buffer); - assert(!err); - - uintptr_t frame = buffer.io_or_offset + tx_buffer_data_region; - uint16_t copied = 0; - for (struct pbuf *curr = p; curr != NULL; curr = curr->next) { - memcpy((void *)(frame + copied), curr->payload, curr->len); - copied += curr->len; - } - - buffer.len = copied; - err = net_enqueue_active(&state.tx_queue, buffer); - assert(!err); - - notify_tx = true; - - return ERR_OK; -} - -void transmit(void) -{ - bool reprocess = true; - while (reprocess) { - while (state.head != NULL && !net_queue_empty_free(&state.tx_queue)) { - err_t err = lwip_eth_send(&state.netif, state.head); - if (err == ERR_MEM) { - sddf_dprintf("LWIP|ERROR: attempted to send a packet of size %u > BUFFER SIZE %u\n", state.head->tot_len, - NET_BUFFER_SIZE); - } else if (err != ERR_OK) { - sddf_dprintf("LWIP|ERROR: unkown error when trying to send pbuf %p\n", state.head); - } - - struct pbuf *temp = state.head; - state.head = temp->next_chain; - if (state.head == NULL) { - state.tail = NULL; - } - pbuf_free(temp); - } - - /* Only request a signal if no more pbufs enqueud to send */ - if (state.head == NULL || !net_queue_empty_free(&state.tx_queue)) { - net_cancel_signal_free(&state.tx_queue); - } else { - net_request_signal_free(&state.tx_queue); - } - reprocess = false; - - if (state.head != NULL && !net_queue_empty_free(&state.tx_queue)) { - net_cancel_signal_free(&state.tx_queue); - reprocess = true; - } - } -} - -void receive(void) -{ - bool reprocess = true; - while (reprocess) { - while (!net_queue_empty_active(&state.rx_queue)) { - net_buff_desc_t buffer; - int err = net_dequeue_active(&state.rx_queue, &buffer); - assert(!err); - - struct pbuf *p = create_interface_buffer(buffer.io_or_offset, buffer.len); - assert(p != NULL); - if (state.netif.input(p, &state.netif) != ERR_OK) { - sddf_dprintf("LWIP|ERROR: unkown error inputting pbuf into network stack\n"); - pbuf_free(p); - } - } - - net_request_signal_active(&state.rx_queue); - reprocess = false; - - if (!net_queue_empty_active(&state.rx_queue)) { - net_cancel_signal_active(&state.rx_queue); - reprocess = true; - } - } -} - -/** - * Initialise the network interface data structure. - * - * @param netif network interface data structuer. - */ -static err_t ethernet_init(struct netif *netif) -{ - if (netif->state == NULL) { - return ERR_ARG; - } - state_t *data = netif->state; - - netif->hwaddr[0] = data->mac[0]; - netif->hwaddr[1] = data->mac[1]; - netif->hwaddr[2] = data->mac[2]; - netif->hwaddr[3] = data->mac[3]; - netif->hwaddr[4] = data->mac[4]; - netif->hwaddr[5] = data->mac[5]; - netif->mtu = ETHER_MTU; - netif->hwaddr_len = ETHARP_HWADDR_LEN; - netif->output = etharp_output; - netif->linkoutput = lwip_eth_send; - NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED); - netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_IGMP; - return ERR_OK; -} - -/* Callback function that prints DHCP supplied IP address. */ -static void netif_status_callback(struct netif *netif) -{ - if (dhcp_supplied_address(netif)) { - sddf_printf("LWIP|NOTICE: DHCP request for %s returned IP address: %s\n", microkit_name, - ip4addr_ntoa(netif_ip4_addr(netif))); - } -} - -void init(void) -{ - serial_cli_queue_init_sys(microkit_name, NULL, NULL, NULL, &serial_tx_queue_handle, serial_tx_queue, serial_tx_data); - serial_putchar_init(SERIAL_TX_CH, &serial_tx_queue_handle); - - size_t rx_size, tx_size; - net_cli_queue_size(microkit_name, &rx_size, &tx_size); - net_queue_init(&state.rx_queue, rx_free, rx_active, rx_size); - net_queue_init(&state.tx_queue, tx_free, tx_active, tx_size); - net_buffers_init(&state.tx_queue, 0); - - lwip_init(); - set_timeout(); - - LWIP_MEMPOOL_INIT(RX_POOL); - - uint64_t mac_addr = net_cli_mac_addr(microkit_name); - net_set_mac_addr(state.mac, mac_addr); - - /* Set dummy IP configuration values to get lwIP bootstrapped */ - struct ip4_addr netmask, ipaddr, gw, multicast; - ipaddr_aton("0.0.0.0", &gw); - ipaddr_aton("0.0.0.0", &ipaddr); - ipaddr_aton("0.0.0.0", &multicast); - ipaddr_aton("255.255.255.0", &netmask); - - state.netif.name[0] = 'e'; - state.netif.name[1] = '0'; - - if (!netif_add(&(state.netif), &ipaddr, &netmask, &gw, (void *)&state, - ethernet_init, ethernet_input)) { - sddf_dprintf("LWIP|ERROR: Netif add returned NULL\n"); - } - - netif_set_default(&(state.netif)); - netif_set_status_callback(&(state.netif), netif_status_callback); - netif_set_up(&(state.netif)); - - if (dhcp_start(&(state.netif))) { - sddf_dprintf("LWIP|ERROR: failed to start DHCP negotiation\n"); - } - - setup_udp_socket(); - setup_utilization_socket(); - setup_tcp_socket(); - - if (notify_rx && net_require_signal_free(&state.rx_queue)) { - net_cancel_signal_free(&state.rx_queue); - notify_rx = false; - if (!microkit_have_signal) { - microkit_deferred_notify(RX_CH); - } else if (microkit_signal_cap != BASE_OUTPUT_NOTIFICATION_CAP + RX_CH) { - microkit_notify(RX_CH); - } - } - - if (notify_tx && net_require_signal_active(&state.tx_queue)) { - net_cancel_signal_active(&state.tx_queue); - notify_tx = false; - if (!microkit_have_signal) { - microkit_deferred_notify(TX_CH); - } else if (microkit_signal_cap != BASE_OUTPUT_NOTIFICATION_CAP + TX_CH) { - microkit_notify(TX_CH); - } - } -} - -void notified(microkit_channel ch) -{ - switch (ch) { - case RX_CH: - receive(); - break; - case TIMER: - sys_check_timeouts(); - set_timeout(); - break; - case TX_CH: - transmit(); - receive(); - break; - case SERIAL_TX_CH: - // Nothing to do - break; - default: - sddf_dprintf("LWIP|LOG: received notification on unexpected channel: %u\n", ch); - break; - } - - if (notify_rx && net_require_signal_free(&state.rx_queue)) { - net_cancel_signal_free(&state.rx_queue); - notify_rx = false; - if (!microkit_have_signal) { - microkit_deferred_notify(RX_CH); - } else if (microkit_signal_cap != BASE_OUTPUT_NOTIFICATION_CAP + RX_CH) { - microkit_notify(RX_CH); - } - } - - if (notify_tx && net_require_signal_active(&state.tx_queue)) { - net_cancel_signal_active(&state.tx_queue); - notify_tx = false; - if (!microkit_have_signal) { - microkit_deferred_notify(TX_CH); - } else if (microkit_signal_cap != BASE_OUTPUT_NOTIFICATION_CAP + TX_CH) { - microkit_notify(TX_CH); - } - } -} diff --git a/include/sddf/network/lib_sddf_lwip.h b/include/sddf/network/lib_sddf_lwip.h new file mode 100644 index 000000000..68e7a1b6c --- /dev/null +++ b/include/sddf/network/lib_sddf_lwip.h @@ -0,0 +1,111 @@ +/* + * Copyright 2022, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include "lwip/pbuf.h" + +/* Default gigabit link speed. */ +#define SDDF_LWIP_LINK_SPEED 1000000000 + +/* Default ethernet MTU. */ +#define SDDF_LWIP_ETHER_MTU 1500 + +/* Definitions for sDDF error constants. */ +typedef enum { + /* No error, everything OK. */ + SDDF_LWIP_ERR_OK = 0, + /* Pbuf too large for sDDF buffer. */ + SDDF_LWIP_ERR_PBUF = -1, + /* No buffers available. */ + SDDF_LWIP_ERR_NO_BUF = -2, + /* Pbuf successfully enqueued to be sent later. */ + SDDF_LWIP_ERR_ENQUEUED = -3, + /* Could not resolve error. */ + SDDF_LWIP_ERR_UNHANDLED = -4 +} net_sddf_err_t; + +/** + * Function type for output of sDDF LWIP errors. + */ +typedef int (*sddf_lwip_err_output_fn)(const char *format, ...) __attribute__((format(__printf__, 1, 2))); + +/** + * Function type for netif status callback. Invoked by LWIP upon + * successfully obtaining an IP address for the network interface. + */ +typedef void (*sddf_lwip_netif_status_callback_fn)(char *ip_addr); + +/** + * Function type for handling function which is optionally invoked + * when a pbuf is unable to be sent due to no available sDDF tx buffers. + * Can be used to store pbuf until more buffers are available. + */ +typedef net_sddf_err_t (*sddf_lwip_handle_empty_tx_free_fn)(struct pbuf *p); + +/** + * Checks LWIP system timeouts. Should be invoked after every LWIP tick. + */ +void sddf_lwip_process_timeout(void); + +/** + * Transmits the provided pbuf through the sddf network system. + * + * @param p pbuf to be transmitted. + * + * @return If the pbuf is sent successfully, SDDF_LWIP_ERR_OK is returned and the + * pbuf can safely be freed. If the pbuf is too large, SDDF_LWIP_ERR_PBUF is + * returned. If there are no free sDDF buffers available, + * handle_empty_tx_free will be called with the pbuf, and the return value + * will be returned. + */ +net_sddf_err_t sddf_lwip_transmit_pbuf(struct pbuf *p); + +/** + * Handles the passing of incoming packets in sDDF buffers to LWIP. Must be + * called to process the sDDF RX queue each time a notification is received + * from the network virtualiser. + */ +void sddf_lwip_process_rx(void); + +/** + * Handles the sending of notifications to the network RX and TX virtualisers. + * Must be invoked at the end of each event handling loop and initialisation + * to ensure outgoing buffers are processed by the virtualisers. + */ +void sddf_lwip_maybe_notify(void); + +/** + * Initialisation function for the sDDF LWIP library. Must be called prior + * to using any other library functions. + * + * @param rx_queue RX net queue handle data structure. Must be initialised + * prior to being passed to this function. + * @param tx_queue TX net queue handle data structure. Must be initialised + * prior to being passed to this function. + * @param rx_ch RX notification channel to the net RX virtualiser. + * @param tx_ch TX notification channel to the net TX virtualiser. + * @param rx_buffer_data_region virtual address of the start of the RX + * buffer region. + * @param tx_buffer_data_region virtual address of the start of the TX + * buffer region. + * @param timer_ch timer notification channel to the timer driver. + * @param mac mac address of the client. + * @param err_output function pointer to optional user provided error + * output function. Provide NULL to use default sddf_printf_. + * @param netif_callback function pointer to optional user provided netif + * status callback function. Provide NULL to use err_output to print client + * MAC address and obtained IP address. + * @param handle_empty_tx_free function pointer to optional user provided + * handling function for no available sDDF TX buffers during sending of LWIP + * pbuf. Provide NULL to leave unhandled. + */ +void sddf_lwip_init(net_queue_handle_t rx_queue, net_queue_handle_t tx_queue, microkit_channel rx_ch, + microkit_channel tx_ch, uintptr_t rx_buffer_data_region, uintptr_t tx_buffer_data_region, + microkit_channel timer_ch, uint64_t mac, sddf_lwip_err_output_fn err_output, + sddf_lwip_netif_status_callback_fn netif_callback, + sddf_lwip_handle_empty_tx_free_fn handle_empty_tx_free); diff --git a/include/sddf/network/util.h b/include/sddf/network/util.h index 916a56322..1c45f8d33 100644 --- a/include/sddf/network/util.h +++ b/include/sddf/network/util.h @@ -21,4 +21,4 @@ static void net_set_mac_addr(uint8_t *mac, uint64_t val) mac[3] = val >> 16 & 0xff; mac[4] = val >> 8 & 0xff; mac[5] = val & 0xff; -} \ No newline at end of file +} diff --git a/network/components/network_components.mk b/network/components/network_components.mk index 9ed9b3fd4..d681a79c7 100644 --- a/network/components/network_components.mk +++ b/network/components/network_components.mk @@ -11,7 +11,6 @@ # Generates network_virt_rx.elf network_virt_tx.elf arp.elf copy.elf # Requires ${SDDF}/util/util.mk to build the utility library for debug output -NETWORK_COMPONENTS_DIR := $(abspath $(dir $(lastword ${MAKEFILE_LIST}))) NETWORK_IMAGES:= network_virt_rx.elf network_virt_tx.elf arp.elf copy.elf network/components/%.o: ${SDDF}/network/components/%.c ${CC} ${CFLAGS} -c -o $@ $< @@ -40,10 +39,11 @@ network/components/network_virt_%.o: ${SDDF}/network/components/virt_%.c ${LD} ${LDFLAGS} -o $@ $< ${LIBS} clean:: - rm -f network_virt_[rt]x.[od] copy.[od] arp.[od] + ${RM} -f network_virt_[rt]x.[od] copy.[od] arp.[od] clobber:: - rm -f ${IMAGES} + ${RM} -f ${NETWORK_IMAGES} + rmdir network/components network/components: mkdir -p $@ diff --git a/network/lib_sddf_lwip/lib_sddf_lwip.c b/network/lib_sddf_lwip/lib_sddf_lwip.c new file mode 100644 index 000000000..349e3eea4 --- /dev/null +++ b/network/lib_sddf_lwip/lib_sddf_lwip.c @@ -0,0 +1,368 @@ +/* + * Copyright 2022, UNSW + * SPDX-License-Identifier: BSD-2-Clause + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "lwip/err.h" +#include "lwip/init.h" +#include "lwip/ip4_addr.h" +#include "netif/etharp.h" +#include "lwip/pbuf.h" +#include "lwip/netif.h" +#include "lwip/snmp.h" +#include "lwip/sys.h" +#include "lwip/timeouts.h" +#include "lwip/dhcp.h" + +typedef struct lwip_state { + /* LWIP network interface struct. */ + struct netif netif; + /* MAC address of client. */ + uint64_t mac; + /* Output function used to print error messages. */ + sddf_lwip_err_output_fn err_output; + /* Callback function to be invoked when ip address is obtained. */ + sddf_lwip_netif_status_callback_fn netif_callback; + /* Function that optionally handles when no free tx buffers available. */ + sddf_lwip_handle_empty_tx_free_fn handle_empty_tx_free; +} lwip_state_t; + +typedef struct sddf_state { + /* sddf net rx queue handle. */ + net_queue_handle_t rx_queue; + /* sddf net tx queue handle. */ + net_queue_handle_t tx_queue; + /* sddf channel for net rx virt. */ + microkit_channel rx_ch; + /* sddf channel for net tx virt. */ + microkit_channel tx_ch; + /* Base address of data region containing rx buffers. */ + uintptr_t rx_buffer_data_region; + /* Base address of data region containing tx buffers. */ + uintptr_t tx_buffer_data_region; + /* Boolean indicating whether buffers have been given to rx virt. */ + bool notify_rx; + /* Boolean indicating whether buffers have been given to tx virt. */ + bool notify_tx; + /* sddf channel for timer. */ + microkit_channel timer_ch; +} sddf_state_t; + +/* Wrapper over custom_pbuf structure to keep track of buffer's offset into data region. */ +typedef struct pbuf_custom_offset { + struct pbuf_custom custom; + uint64_t offset; +} pbuf_custom_offset_t; + +LWIP_MEMPOOL_DECLARE(RX_POOL, SDDF_LWIP_NUM_BUFS * 2, sizeof(struct pbuf_custom_offset), "Zero-copy RX pool"); + +lwip_state_t lwip_state; +sddf_state_t sddf_state; + +/** + * Helper function to convert sddf errors to lwip errors. + * + * @param sddf_err sddf error. + * + * @return Equivalent lwip error. + */ +static err_t sddf_err_to_lwip_err(net_sddf_err_t sddf_err) +{ + switch (sddf_err) { + case SDDF_LWIP_ERR_OK: + return ERR_OK; + case SDDF_LWIP_ERR_PBUF: + return ERR_BUF; + case SDDF_LWIP_ERR_NO_BUF: + return ERR_MEM; + case SDDF_LWIP_ERR_ENQUEUED: + return ERR_OK; + case SDDF_LWIP_ERR_UNHANDLED: + return ERR_MEM; + } + return ERR_ARG; +} + +/** + * Default netif status callback function. Prints client MAC address and + * obtained ip address. + * + * @param ip_addr Obtained ip address as a string. + */ +static void netif_status_callback_default(char *ip_addr) +{ + uint8_t *mac = lwip_state.netif.hwaddr; + lwip_state.err_output("LWIP|NOTICE: DHCP request for mac " + "%02x:%02x:%02x:%02x:%02x:%02x " + "returned ip address: %s\n", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], ip_addr); +} + +/** + * Default handling function to be called during transmission if tx free + * queue is empty. + * + * @param p pbuf that could not be sent due to queue being empty. + * + * @return Simply returns the sddf error indicating nothing was done. + */ +static net_sddf_err_t handle_empty_tx_free_default(struct pbuf *p) +{ + return SDDF_LWIP_ERR_UNHANDLED; +} + +/** + * Returns current time from the timer. + */ +uint32_t sys_now(void) +{ + return sddf_timer_time_now(sddf_state.timer_ch) / NS_IN_MS; +} + +void sddf_lwip_process_timeout() +{ + sys_check_timeouts(); +} + +/** + * Free a pbuf. This also returns the underlying sddf buffer to the receive free ring. + * + * @param p pbuf to free. + */ +static void interface_free_buffer(struct pbuf *p) +{ + SYS_ARCH_DECL_PROTECT(old_level); + pbuf_custom_offset_t *custom_pbuf_offset = (pbuf_custom_offset_t *)p; + SYS_ARCH_PROTECT(old_level); + net_buff_desc_t buffer = { custom_pbuf_offset->offset, 0 }; + int err = net_enqueue_free(&(sddf_state.rx_queue), buffer); + assert(!err); + sddf_state.notify_rx = true; + LWIP_MEMPOOL_FREE(RX_POOL, custom_pbuf_offset); + SYS_ARCH_UNPROTECT(old_level); +} + +/** + * Create a pbuf structure to pass to the network interface. + * + * @param offset offset into the data region of the buffer to be passed. + * @param length length of data. + * + * @return the newly created pbuf. Can be cast to pbuf_custom. + */ +static struct pbuf *create_interface_buffer(uint64_t offset, size_t length) +{ + pbuf_custom_offset_t *custom_pbuf_offset = (pbuf_custom_offset_t *)LWIP_MEMPOOL_ALLOC(RX_POOL); + custom_pbuf_offset->offset = offset; + custom_pbuf_offset->custom.custom_free_function = interface_free_buffer; + + return pbuf_alloced_custom(PBUF_RAW, length, PBUF_REF, &custom_pbuf_offset->custom, + (void *)(offset + sddf_state.rx_buffer_data_region), NET_BUFFER_SIZE); +} + +/** + * Copy a pbuf into an sddf buffer and insert it into the transmit active queue. + * + * @param netif lwip network interface state. + * @param p pbuf to be transmitted. + * + * @return If the pbuf is sent, ERR_OK is returned and the pbuf can safely be + * freed. If the pbuf is too large ERR_MEM is returned. If there are no free + * sddf buffers available, handle_empty_tx_free will be called with the pbuf, + * and the equivalent lwip error will be returned. + */ +static err_t lwip_eth_send(struct netif *netif, struct pbuf *p) +{ + if (p->tot_len > NET_BUFFER_SIZE) { + lwip_state.err_output("LWIP|ERROR: attempted to send a packet of size %u > BUFFER SIZE %u\n", p->tot_len, + NET_BUFFER_SIZE); + return ERR_MEM; + } + + if (net_queue_empty_free(&sddf_state.tx_queue)) { + return sddf_err_to_lwip_err(lwip_state.handle_empty_tx_free(p)); + } + + net_buff_desc_t buffer; + int err = net_dequeue_free(&sddf_state.tx_queue, &buffer); + assert(!err); + + uintptr_t frame = buffer.io_or_offset + sddf_state.tx_buffer_data_region; + uint16_t copied = 0; + for (struct pbuf *curr = p; curr != NULL; curr = curr->next) { + memcpy((void *)(frame + copied), curr->payload, curr->len); + copied += curr->len; + } + + buffer.len = copied; + err = net_enqueue_active(&sddf_state.tx_queue, buffer); + assert(!err); + + sddf_state.notify_tx = true; + + return ERR_OK; +} + +net_sddf_err_t sddf_lwip_transmit_pbuf(struct pbuf *p) +{ + if (p->tot_len > NET_BUFFER_SIZE) { + lwip_state.err_output("LWIP|ERROR: attempted to send a packet of size %u > BUFFER SIZE %u\n", p->tot_len, + NET_BUFFER_SIZE); + return SDDF_LWIP_ERR_PBUF; + } + + if (net_queue_empty_free(&sddf_state.tx_queue)) { + return lwip_state.handle_empty_tx_free(p); + } + + err_t err = lwip_eth_send(&lwip_state.netif, p); + assert(!err); + + return SDDF_LWIP_ERR_OK; +} + +void sddf_lwip_process_rx(void) +{ + bool reprocess = true; + while (reprocess) { + while (!net_queue_empty_active(&sddf_state.rx_queue)) { + net_buff_desc_t buffer; + int err = net_dequeue_active(&sddf_state.rx_queue, &buffer); + assert(!err); + + struct pbuf *p = create_interface_buffer(buffer.io_or_offset, buffer.len); + assert(p != NULL); + if (lwip_state.netif.input(p, &lwip_state.netif) != ERR_OK) { + lwip_state.err_output("LWIP|ERROR: unkown error inputting pbuf into network stack\n"); + pbuf_free(p); + } + } + + net_request_signal_active(&sddf_state.rx_queue); + reprocess = false; + + if (!net_queue_empty_active(&sddf_state.rx_queue)) { + net_cancel_signal_active(&sddf_state.rx_queue); + reprocess = true; + } + } +} + +/** + * Initialise the network interface data structure. + * + * @param netif network interface data structure. + */ +static err_t ethernet_init(struct netif *netif) +{ + if (netif->state == NULL) { + return ERR_ARG; + } + + net_set_mac_addr(netif->hwaddr, lwip_state.mac); + netif->mtu = SDDF_LWIP_ETHER_MTU; + netif->hwaddr_len = ETHARP_HWADDR_LEN; + netif->output = etharp_output; + netif->linkoutput = lwip_eth_send; + NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, SDDF_LWIP_LINK_SPEED); + netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_IGMP; + + return ERR_OK; +} + +/** + * Network interface callback function invoked when DHCP packets are received. + * If an ip address is successfully obtained, the provided netif_callback + * function will be invoked with the ip address as a string. + * + * @param netif network interface data structure. + */ +static void netif_status_callback(struct netif *netif) +{ + if (dhcp_supplied_address(netif)) { + char ip4_str[IP4ADDR_STRLEN_MAX]; + lwip_state.netif_callback(ip4addr_ntoa_r(netif_ip4_addr(netif), ip4_str, IP4ADDR_STRLEN_MAX)); + } +} + +void sddf_lwip_init(net_queue_handle_t rx_queue, net_queue_handle_t tx_queue, microkit_channel rx_ch, + microkit_channel tx_ch, uintptr_t rx_buffer_data_region, uintptr_t tx_buffer_data_region, + microkit_channel timer_ch, uint64_t mac, sddf_lwip_err_output_fn err_output, + sddf_lwip_netif_status_callback_fn netif_callback, + sddf_lwip_handle_empty_tx_free_fn handle_empty_tx_free) +{ + /* Initialise sddf state */ + sddf_state.rx_queue = rx_queue; + sddf_state.tx_queue = tx_queue; + sddf_state.rx_ch = rx_ch; + sddf_state.tx_ch = tx_ch; + sddf_state.rx_buffer_data_region = rx_buffer_data_region; + sddf_state.tx_buffer_data_region = tx_buffer_data_region; + sddf_state.timer_ch = timer_ch; + + /* Initialise lwip state */ + lwip_state.mac = mac; + lwip_state.err_output = (err_output == NULL) ? sddf_printf_ : err_output; + lwip_state.netif_callback = (netif_callback == NULL) ? netif_status_callback_default : netif_callback; + lwip_state.handle_empty_tx_free = (handle_empty_tx_free == NULL) ? handle_empty_tx_free_default + : handle_empty_tx_free; + + lwip_init(); + + LWIP_MEMPOOL_INIT(RX_POOL); + + /* Set dummy IP configuration values to get lwIP bootstrapped */ + struct ip4_addr netmask, ipaddr, gw, multicast; + ipaddr_aton("0.0.0.0", &gw); + ipaddr_aton("0.0.0.0", &ipaddr); + ipaddr_aton("0.0.0.0", &multicast); + ipaddr_aton("255.255.255.0", &netmask); + + lwip_state.netif.name[0] = 'e'; + lwip_state.netif.name[1] = '0'; + + if (!netif_add(&(lwip_state.netif), &ipaddr, &netmask, &gw, (void *)&lwip_state, ethernet_init, ethernet_input)) { + lwip_state.err_output("LWIP|ERROR: Netif add returned NULL\n"); + } + + netif_set_default(&(lwip_state.netif)); + netif_set_status_callback(&(lwip_state.netif), netif_status_callback); + netif_set_up(&(lwip_state.netif)); + + if (dhcp_start(&(lwip_state.netif))) { + lwip_state.err_output("LWIP|ERROR: failed to start DHCP negotiation\n"); + } +} + +void sddf_lwip_maybe_notify() +{ + if (sddf_state.notify_rx && net_require_signal_free(&sddf_state.rx_queue)) { + net_cancel_signal_free(&sddf_state.rx_queue); + sddf_state.notify_rx = false; + if (!microkit_have_signal) { + microkit_deferred_notify(sddf_state.rx_ch); + } else if (microkit_signal_cap != BASE_OUTPUT_NOTIFICATION_CAP + sddf_state.rx_ch) { + microkit_notify(sddf_state.rx_ch); + } + } + + if (sddf_state.notify_tx && net_require_signal_active(&sddf_state.tx_queue)) { + net_cancel_signal_active(&sddf_state.tx_queue); + sddf_state.notify_tx = false; + if (!microkit_have_signal) { + microkit_deferred_notify(sddf_state.tx_ch); + } else if (microkit_signal_cap != BASE_OUTPUT_NOTIFICATION_CAP + sddf_state.tx_ch) { + microkit_notify(sddf_state.tx_ch); + } + } +} diff --git a/network/lib_sddf_lwip/lib_sddf_lwip.mk b/network/lib_sddf_lwip/lib_sddf_lwip.mk new file mode 100644 index 000000000..17506e141 --- /dev/null +++ b/network/lib_sddf_lwip/lib_sddf_lwip.mk @@ -0,0 +1,32 @@ +# +# Copyright 2022, UNSW +# +# SPDX-License-Identifier: BSD-2-Clause +# + +ifeq ($(strip $(SDDF_LWIP_NUM_BUFS)),) +$(error SDDF_LWIP_NUM_BUFS must be specified) +endif + +LIB_SDDF_LWIP_DIR := $(dir $(lastword $(MAKEFILE_LIST))) +LIB_SDDF_LWIP_FILES := $(addprefix ${LIB_SDDF_LWIP_DIR}/, lib_sddf_lwip.c) +LIB_SDDF_LWIP_OBJS := $(LIB_SDDF_LWIP_FILES:.c=.o) + +${LIB_SDDF_LWIP_OBJS}: CFLAGS += -DSDDF_LWIP_NUM_BUFS=$(SDDF_LWIP_NUM_BUFS) + +${LIB_SDDF_LWIP_OBJS}: ${CHECK_FLAGS_BOARD_MD5} | ${LIB_SDDF_LWIP_DIR} +${LIB_SDDF_LWIP_DIR}: + mkdir -p $@ + +lib_sddf_lwip.a: ${LIB_SDDF_LWIP_OBJS} + ${AR} rv $@ $^ + ${RANLIB} $@ + +clean:: + ${RM} -f ${LIB_SDDF_LWIP_OBJS} ${LIB_SDDF_LWIP_OBJS:.o=.d} + +clobber:: clean + ${RM} -f lib_sddf_lwip.a + rmdir ${LIB_SDDF_LWIP_DIR} + +-include ${LIB_SDDF_LWIP_OBJS:.o=.d}