diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.linux | 17 | ||||
-rw-r--r-- | src/drivers/linux/slirp.c | 552 | ||||
-rw-r--r-- | src/include/ipxe/errfile.h | 1 | ||||
-rw-r--r-- | src/include/ipxe/linux_api.h | 19 | ||||
-rw-r--r-- | src/include/ipxe/slirp.h | 155 | ||||
-rw-r--r-- | src/interface/linux/linux_api.c | 158 |
6 files changed, 900 insertions, 2 deletions
diff --git a/src/Makefile.linux b/src/Makefile.linux index 4a7837916..09b2b1577 100644 --- a/src/Makefile.linux +++ b/src/Makefile.linux @@ -26,9 +26,21 @@ NON_AUTO_MEDIA = linux # LINUX_CFLAGS += -Os -idirafter include -DSYMBOL_PREFIX=$(SYMBOL_PREFIX) +# Check for libslirp +# +LIBSLIRP_TEST = $(CC) $(LINUX_CFLAGS) -x c /dev/null -nostartfiles \ + -include slirp/libslirp.h -lslirp \ + -o /dev/null >/dev/null 2>&1 +WITH_LIBSLIRP := $(shell $(LIBSLIRP_TEST) && $(ECHO) yes) +ifneq ($(WITH_LIBSLIRP),) +LINUX_CFLAGS += -DHAVE_LIBSLIRP +LINUX_LDFLAGS += -lslirp +endif + # Host API wrapper # -$(BIN)/linux_api.o : interface/linux/linux_api.c $(MAKEDEPS) +$(BIN)/linux_api.o : interface/linux/linux_api.c include/ipxe/linux_api.h \ + include/ipxe/slirp.h $(MAKEDEPS) $(QM)$(ECHO) " [BUILD] $@" $(Q)$(CC) $(LINUX_CFLAGS) $(WORKAROUND_CFLAGS) -o $@ -c $< @@ -36,4 +48,5 @@ $(BIN)/linux_api.o : interface/linux/linux_api.c $(MAKEDEPS) # $(BIN)/%.linux : $(BIN)/%.linux.tmp $(BIN)/linux_api.o $(QM)$(ECHO) " [FINISH] $@" - $(Q)$(CC) $(LINUX_CFLAGS) $(WORKAROUND_CFLAGS) -o $@ $^ + $(Q)$(CC) $(LINUX_CFLAGS) $(WORKAROUND_CFLAGS) $(LINUX_LDFLAGS) \ + -o $@ $^ diff --git a/src/drivers/linux/slirp.c b/src/drivers/linux/slirp.c new file mode 100644 index 000000000..8341c9676 --- /dev/null +++ b/src/drivers/linux/slirp.c @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2021 Michael Brown <mbrown@fensystems.co.uk>. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <stdio.h> +#include <errno.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/if_ether.h> +#include <ipxe/in.h> +#include <ipxe/timer.h> +#include <ipxe/retry.h> +#include <ipxe/linux.h> +#include <ipxe/linux_api.h> +#include <ipxe/slirp.h> + +/** @file + * + * Linux Slirp network driver + * + */ + +/** Maximum number of open file descriptors */ +#define SLIRP_MAX_FDS 128 + +/** A Slirp network interface */ +struct slirp_nic { + /** The libslirp device object */ + struct Slirp *slirp; + /** Polling file descriptor list */ + struct pollfd pollfds[SLIRP_MAX_FDS]; + /** Number of file descriptors */ + unsigned int numfds; +}; + +/** A Slirp alarm timer */ +struct slirp_alarm { + /** Slirp network interface */ + struct slirp_nic *slirp; + /** Retry timer */ + struct retry_timer timer; + /** Callback function */ + void ( __asmcall * callback ) ( void *opaque ); + /** Opaque value for callback function */ + void *opaque; +}; + +/** Default MAC address */ +static const uint8_t slirp_default_mac[ETH_ALEN] = + { 0x52, 0x54, 0x00, 0x12, 0x34, 0x56 }; + +/****************************************************************************** + * + * Slirp interface + * + ****************************************************************************** + */ + +/** + * Send packet + * + * @v buf Data buffer + * @v len Length of data + * @v device Device opaque pointer + * @ret len Consumed length (or negative on error) + */ +static ssize_t __asmcall slirp_send_packet ( const void *buf, size_t len, + void *device ) { + struct net_device *netdev = device; + struct io_buffer *iobuf; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) + return -1; + + /* Populate I/O buffer */ + memcpy ( iob_put ( iobuf, len ), buf, len ); + + /* Hand off to network stack */ + netdev_rx ( netdev, iobuf ); + + return len; +} + +/** + * Print an error message + * + * @v msg Error message + * @v device Device opaque pointer + */ +static void __asmcall slirp_guest_error ( const char *msg, void *device ) { + struct net_device *netdev = device; + struct slirp_nic *slirp = netdev->priv; + + DBGC ( slirp, "SLIRP %p error: %s\n", slirp, msg ); +} + +/** + * Get virtual clock + * + * @v device Device opaque pointer + * @ret clock_ns Clock time in nanoseconds + */ +static int64_t __asmcall slirp_clock_get_ns ( void *device __unused ) { + int64_t time; + + time = currticks(); + return ( time * ( 1000000 / TICKS_PER_MS ) ); +} + +/** + * Handle timer expiry + * + * @v timer Retry timer + * @v over Failure indicator + */ +static void slirp_expired ( struct retry_timer *timer, int over __unused ) { + struct slirp_alarm *alarm = + container_of ( timer, struct slirp_alarm, timer ); + struct slirp_nic *slirp = alarm->slirp; + + /* Notify callback */ + DBGC ( slirp, "SLIRP %p timer fired\n", slirp ); + alarm->callback ( alarm->opaque ); +} + +/** + * Create a new timer + * + * @v callback Timer callback + * @v opaque Timer opaque pointer + * @v device Device opaque pointer + * @ret timer Timer + */ +static void * __asmcall +slirp_timer_new ( void ( __asmcall * callback ) ( void *opaque ), + void *opaque, void *device ) { + struct net_device *netdev = device; + struct slirp_nic *slirp = netdev->priv; + struct slirp_alarm *alarm; + + /* Allocate timer */ + alarm = malloc ( sizeof ( *alarm ) ); + if ( ! alarm ) { + DBGC ( slirp, "SLIRP %p could not allocate timer\n", slirp ); + return NULL; + } + + /* Initialise timer */ + memset ( alarm, 0, sizeof ( *alarm ) ); + alarm->slirp = slirp; + timer_init ( &alarm->timer, slirp_expired, NULL ); + alarm->callback = callback; + alarm->opaque = opaque; + DBGC ( slirp, "SLIRP %p timer %p has callback %p (%p)\n", + slirp, alarm, alarm->callback, alarm->opaque ); + + return alarm; +} + +/** + * Delete a timer + * + * @v timer Timer + * @v device Device opaque pointer + */ +static void __asmcall slirp_timer_free ( void *timer, void *device ) { + struct net_device *netdev = device; + struct slirp_nic *slirp = netdev->priv; + struct slirp_alarm *alarm = timer; + + /* Ignore timers that failed to allocate */ + if ( ! alarm ) + return; + + /* Stop timer */ + stop_timer ( &alarm->timer ); + + /* Free timer */ + free ( alarm ); + DBGC ( slirp, "SLIRP %p timer %p freed\n", slirp, alarm ); +} + +/** + * Set timer expiry time + * + * @v timer Timer + * @v expire Expiry time + * @v device Device opaque pointer + */ +static void __asmcall slirp_timer_mod ( void *timer, int64_t expire, + void *device ) { + struct net_device *netdev = device; + struct slirp_nic *slirp = netdev->priv; + struct slirp_alarm *alarm = timer; + int64_t timeout_ms; + unsigned long timeout; + + /* Ignore timers that failed to allocate */ + if ( ! alarm ) + return; + + /* (Re)start timer */ + timeout_ms = ( expire - ( currticks() / TICKS_PER_MS ) ); + if ( timeout_ms < 0 ) + timeout_ms = 0; + timeout = ( timeout_ms * TICKS_PER_MS ); + start_timer_fixed ( &alarm->timer, timeout ); + DBGC ( slirp, "SLIRP %p timer %p set for %ld ticks\n", + slirp, alarm, timeout ); +} + +/** + * Register file descriptor for polling + * + * @v fd File descriptor + * @v device Device opaque pointer + */ +static void __asmcall slirp_register_poll_fd ( int fd, void *device ) { + struct net_device *netdev = device; + struct slirp_nic *slirp = netdev->priv; + + DBGC ( slirp, "SLIRP %p registered FD %d\n", slirp, fd ); +} + +/** + * Unregister file descriptor + * + * @v fd File descriptor + * @v device Device opaque pointer + */ +static void __asmcall slirp_unregister_poll_fd ( int fd, void *device ) { + struct net_device *netdev = device; + struct slirp_nic *slirp = netdev->priv; + + DBGC ( slirp, "SLIRP %p unregistered FD %d\n", slirp, fd ); +} + +/** + * Notify that new events are ready + * + * @v device Device opaque pointer + */ +static void __asmcall slirp_notify ( void *device ) { + struct net_device *netdev = device; + struct slirp_nic *slirp = netdev->priv; + + DBGC2 ( slirp, "SLIRP %p notified\n", slirp ); +} + +/** Slirp callbacks */ +static struct slirp_callbacks slirp_callbacks = { + .send_packet = slirp_send_packet, + .guest_error = slirp_guest_error, + .clock_get_ns = slirp_clock_get_ns, + .timer_new = slirp_timer_new, + .timer_free = slirp_timer_free, + .timer_mod = slirp_timer_mod, + .register_poll_fd = slirp_register_poll_fd, + .unregister_poll_fd = slirp_unregister_poll_fd, + .notify = slirp_notify, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int slirp_open ( struct net_device *netdev ) { + struct slirp_nic *slirp = netdev->priv; + + /* Nothing to do */ + DBGC ( slirp, "SLIRP %p opened\n", slirp ); + + return 0; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void slirp_close ( struct net_device *netdev ) { + struct slirp_nic *slirp = netdev->priv; + + /* Nothing to do */ + DBGC ( slirp, "SLIRP %p closed\n", slirp ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int slirp_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct slirp_nic *slirp = netdev->priv; + + /* Transmit packet */ + linux_slirp_input ( slirp->slirp, iobuf->data, iob_len ( iobuf ) ); + netdev_tx_complete ( netdev, iobuf ); + + return 0; +} + +/** + * Add polling file descriptor + * + * @v fd File descriptor + * @v events Events of interest + * @v device Device opaque pointer + * @ret index File descriptor index + */ +static int __asmcall slirp_add_poll ( int fd, int events, void *device ) { + struct net_device *netdev = device; + struct slirp_nic *slirp = netdev->priv; + struct pollfd *pollfd; + unsigned int index; + + /* Fail if too many descriptors are registered */ + if ( slirp->numfds >= SLIRP_MAX_FDS ) { + DBGC ( slirp, "SLIRP %p too many file descriptors\n", slirp ); + return -1; + } + + /* Populate polling file descriptor */ + index = slirp->numfds++; + pollfd = &slirp->pollfds[index]; + pollfd->fd = fd; + pollfd->events = 0; + if ( events & SLIRP_EVENT_IN ) + pollfd->events |= POLLIN; + if ( events & SLIRP_EVENT_OUT ) + pollfd->events |= POLLOUT; + if ( events & SLIRP_EVENT_PRI ) + pollfd->events |= POLLPRI; + if ( events & SLIRP_EVENT_ERR ) + pollfd->events |= POLLERR; + if ( events & SLIRP_EVENT_HUP ) + pollfd->events |= ( POLLHUP | POLLRDHUP ); + DBGCP ( slirp, "SLIRP %p polling FD %d event mask %#04x(%#04x)\n", + slirp, fd, events, pollfd->events ); + + return index; +} + +/** + * Get returned events for a file descriptor + * + * @v index File descriptor index + * @v device Device opaque pointer + * @ret events Returned events + */ +static int __asmcall slirp_get_revents ( int index, void *device ) { + struct net_device *netdev = device; + struct slirp_nic *slirp = netdev->priv; + int revents; + int events; + + /* Ignore failed descriptors */ + if ( index < 0 ) + return 0; + + /* Collect events */ + revents = slirp->pollfds[index].revents; + events = 0; + if ( revents & POLLIN ) + events |= SLIRP_EVENT_IN; + if ( revents & POLLOUT ) + events |= SLIRP_EVENT_OUT; + if ( revents & POLLPRI ) + events |= SLIRP_EVENT_PRI; + if ( revents & POLLERR ) + events |= SLIRP_EVENT_ERR; + if ( revents & ( POLLHUP | POLLRDHUP ) ) + events |= SLIRP_EVENT_HUP; + if ( events ) { + DBGC2 ( slirp, "SLIRP %p polled FD %d events %#04x(%#04x)\n", + slirp, slirp->pollfds[index].fd, events, revents ); + } + + return events; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void slirp_poll ( struct net_device *netdev ) { + struct slirp_nic *slirp = netdev->priv; + uint32_t timeout = 0; + int ready; + int error; + + /* Rebuild polling file descriptor list */ + slirp->numfds = 0; + linux_slirp_pollfds_fill ( slirp->slirp, &timeout, + slirp_add_poll, netdev ); + + /* Poll descriptors */ + ready = linux_poll ( slirp->pollfds, slirp->numfds, 0 ); + error = ( ready == -1 ); + linux_slirp_pollfds_poll ( slirp->slirp, error, slirp_get_revents, + netdev ); + + /* Record polling errors */ + if ( error ) { + DBGC ( slirp, "SLIRP %p poll failed: %s\n", + slirp, linux_strerror ( linux_errno ) ); + netdev_rx_err ( netdev, NULL, -ELINUX ( linux_errno ) ); + } +} + +/** Network device operations */ +static struct net_device_operations slirp_operations = { + .open = slirp_open, + .close = slirp_close, + .transmit = slirp_transmit, + .poll = slirp_poll, +}; + +/****************************************************************************** + * + * Linux driver interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v linux Linux device + * @v request Device creation request + * @ret rc Return status code + */ +static int slirp_probe ( struct linux_device *linux, + struct linux_device_request *request ) { + struct net_device *netdev; + struct slirp_nic *slirp; + struct slirp_config config; + int rc; + + /* Allocate device */ + netdev = alloc_etherdev ( sizeof ( *slirp ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &slirp_operations ); + linux_set_drvdata ( linux, netdev ); + snprintf ( linux->dev.name, sizeof ( linux->dev.name ), "host" ); + netdev->dev = &linux->dev; + memcpy ( netdev->hw_addr, slirp_default_mac, ETH_ALEN ); + slirp = netdev->priv; + memset ( slirp, 0, sizeof ( *slirp ) ); + + /* Apply requested settings */ + linux_apply_settings ( &request->settings, + netdev_settings ( netdev ) ); + + /* Initialise default configuration (matching qemu) */ + memset ( &config, 0, sizeof ( config ) ); + config.version = 1; + config.in_enabled = true; + config.vnetwork.s_addr = htonl ( 0x0a000200 ); /* 10.0.2.0 */ + config.vnetmask.s_addr = htonl ( 0xffffff00 ); /* 255.255.255.0 */ + config.vhost.s_addr = htonl ( 0x0a000202 ); /* 10.0.2.2 */ + config.in6_enabled = true; + config.vdhcp_start.s_addr = htonl ( 0x0a00020f ); /* 10.0.2.15 */ + config.vnameserver.s_addr = htonl ( 0x0a000203 ); /* 10.0.2.3 */ + + /* Instantiate device */ + slirp->slirp = linux_slirp_new ( &config, &slirp_callbacks, netdev ); + if ( ! slirp->slirp ) { + DBGC ( slirp, "SLIRP could not instantiate\n" ); + rc = -ENODEV; + goto err_new; + } + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + /* Set link up since there is no concept of link state */ + netdev_link_up ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register: + linux_slirp_cleanup ( slirp->slirp ); + err_new: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v linux Linux device + */ +static void slirp_remove ( struct linux_device *linux ) { + struct net_device *netdev = linux_get_drvdata ( linux ); + struct slirp_nic *slirp = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Shut down device */ + linux_slirp_cleanup ( slirp->slirp ); + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** Slirp driver */ +struct linux_driver slirp_driver __linux_driver = { + .name = "slirp", + .probe = slirp_probe, + .remove = slirp_remove, + .can_probe = 1, +}; diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index e3fc8fa09..b5c5d185e 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -212,6 +212,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_intelxlvf ( ERRFILE_DRIVER | 0x00cd0000 ) #define ERRFILE_usbblk ( ERRFILE_DRIVER | 0x00ce0000 ) #define ERRFILE_iphone ( ERRFILE_DRIVER | 0x00cf0000 ) +#define ERRFILE_slirp ( ERRFILE_DRIVER | 0x00d00000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) diff --git a/src/include/ipxe/linux_api.h b/src/include/ipxe/linux_api.h index ea247a613..040b52f8c 100644 --- a/src/include/ipxe/linux_api.h +++ b/src/include/ipxe/linux_api.h @@ -50,6 +50,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #endif struct sockaddr; +struct slirp_config; +struct slirp_callbacks; +struct Slirp; extern int linux_errno; extern int linux_argc; @@ -82,5 +85,21 @@ extern ssize_t __asmcall linux_sendto ( int sockfd, const void *buf, const struct sockaddr *dest_addr, size_t addrlen ); extern const char * __asmcall linux_strerror ( int linux_errno ); +extern struct Slirp * __asmcall +linux_slirp_new ( const struct slirp_config *config, + const struct slirp_callbacks *callbacks, void *opaque ); +extern void __asmcall linux_slirp_cleanup ( struct Slirp *slirp ); +extern void __asmcall linux_slirp_input ( struct Slirp *slirp, + const uint8_t *pkt, int pkt_len ); +extern void __asmcall +linux_slirp_pollfds_fill ( struct Slirp *slirp, uint32_t *timeout, + int ( __asmcall * add_poll ) ( int fd, int events, + void *opaque ), + void *opaque ); +extern void __asmcall +linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error, + int ( __asmcall * get_revents ) ( int idx, + void *opaque ), + void *opaque ); #endif /* _IPXE_LINUX_API_H */ diff --git a/src/include/ipxe/slirp.h b/src/include/ipxe/slirp.h new file mode 100644 index 000000000..4fb13b934 --- /dev/null +++ b/src/include/ipxe/slirp.h @@ -0,0 +1,155 @@ +#ifndef _IPXE_SLIRP_H +#define _IPXE_SLIRP_H + +/** @file + * + * Linux Slirp network driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER ); + +#include <stdint.h> +#include <stdbool.h> + +/** Ready to be read */ +#define SLIRP_EVENT_IN 0x01 + +/** Ready to be written */ +#define SLIRP_EVENT_OUT 0x02 + +/** Exceptional condition */ +#define SLIRP_EVENT_PRI 0x04 + +/** Error condition */ +#define SLIRP_EVENT_ERR 0x08 + +/** Hang up */ +#define SLIRP_EVENT_HUP 0x10 + +/** Slirp device configuration */ +struct slirp_config { + /** Configuration version */ + uint32_t version; + /** Restrict to host loopback connections only */ + int restricted; + /** IPv4 is enabled */ + bool in_enabled; + /** IPv4 network */ + struct in_addr vnetwork; + /** IPv4 netmask */ + struct in_addr vnetmask; + /** IPv4 host server address */ + struct in_addr vhost; + /** IPv6 is enabled */ + bool in6_enabled; + /** IPv6 prefix */ + struct in6_addr vprefix_addr6; + /** IPv6 prefix length */ + uint8_t vprefix_len; + /** IPv6 host server address */ + struct in6_addr vhost6; + /** Client hostname */ + const char *vhostname; + /** TFTP server name */ + const char *tftp_server_name; + /** TFTP path prefix */ + const char *tftp_path; + /** Boot filename */ + const char *bootfile; + /** DHCPv4 start address */ + struct in_addr vdhcp_start; + /** DNS IPv4 address */ + struct in_addr vnameserver; + /** DNS IPv6 address */ + struct in_addr vnameserver6; + /** DNS search list */ + const char **vdnssearch; + /** Domain name */ + const char *vdomainname; + /** Interface MTU */ + size_t if_mtu; + /** Interface MRU */ + size_t if_mru; + /** Disable host loopback connections */ + bool disable_host_loopback; + /** Enable emulation (apparently unsafe) */ + bool enable_emu; +}; + +/** Slirp device callbacks */ +struct slirp_callbacks { + /** + * Send packet + * + * @v buf Data buffer + * @v len Length of data + * @v device Device opaque pointer + * @ret len Consumed length (or negative on error) + */ + ssize_t ( __asmcall * send_packet ) ( const void *buf, size_t len, + void *device ); + /** + * Print an error message + * + * @v msg Error message + * @v device Device opaque pointer + */ + void ( __asmcall * guest_error ) ( const char *msg, void *device ); + /** + * Get virtual clock + * + * @v device Device opaque pointer + * @ret clock_ns Clock time in nanoseconds + */ + int64_t ( __asmcall * clock_get_ns ) ( void *device ); + /** + * Create a new timer + * + * @v callback Timer callback + * @v opaque Timer opaque pointer + * @v device Device opaque pointer + * @ret timer Timer + */ + void * ( __asmcall * timer_new ) ( void ( __asmcall * callback ) + ( void *opaque ), + void *opaque, void *device ); + /** + * Delete a timer + * + * @v timer Timer + * @v device Device opaque pointer + */ + void ( __asmcall * timer_free ) ( void *timer, void *device ); + /** + * Set timer expiry time + * + * @v timer Timer + * @v expire Expiry time + * @v device Device opaque pointer + */ + void ( __asmcall * timer_mod ) ( void *timer, int64_t expire, + void *device ); + /** + * Register file descriptor for polling + * + * @v fd File descriptor + * @v device Device opaque pointer + */ + void ( __asmcall * register_poll_fd ) ( int fd, void *device ); + /** + * Unregister file descriptor + * + * @v fd File descriptor + * @v device Device opaque pointer + */ + void ( __asmcall * unregister_poll_fd ) ( int fd, void *device ); + /** + * Notify that new events are ready + * + * @v device Device opaque pointer + */ + void ( __asmcall * notify ) ( void *device ); +}; + +#endif /* _IPXE_SLIRP_H */ diff --git a/src/interface/linux/linux_api.c b/src/interface/linux/linux_api.c index 4ab3c6603..1f44b532b 100644 --- a/src/interface/linux/linux_api.c +++ b/src/interface/linux/linux_api.c @@ -24,6 +24,7 @@ #include <unistd.h> #include <string.h> #include <errno.h> +#include <assert.h> #include <fcntl.h> #include <time.h> #include <poll.h> @@ -31,7 +32,13 @@ #include <sys/mman.h> #include <sys/socket.h> #include <sys/time.h> +#include <netinet/in.h> #include <ipxe/linux_api.h> +#include <ipxe/slirp.h> + +#ifdef HAVE_LIBSLIRP +#include <slirp/libslirp.h> +#endif /** @file * @@ -347,6 +354,152 @@ const char * __asmcall linux_strerror ( int linux_errno ) { /****************************************************************************** * + * libslirp wrappers + * + ****************************************************************************** + */ + +#ifdef HAVE_LIBSLIRP + +/** + * Wrap slirp_new() + * + */ +struct Slirp * __asmcall +linux_slirp_new ( const struct slirp_config *config, + const struct slirp_callbacks *callbacks, void *opaque ) { + const union { + struct slirp_callbacks callbacks; + SlirpCb cb; + } *u = ( ( typeof ( u ) ) callbacks ); + SlirpConfig cfg; + Slirp *slirp; + + /* Translate configuration */ + memset ( &cfg, 0, sizeof ( cfg ) ); + cfg.version = config->version; + cfg.restricted = config->restricted; + cfg.in_enabled = config->in_enabled; + cfg.vnetwork = config->vnetwork; + cfg.vnetmask = config->vnetmask; + cfg.vhost = config->vhost; + cfg.in6_enabled = config->in6_enabled; + memcpy ( &cfg.vprefix_addr6, &config->vprefix_addr6, + sizeof ( cfg.vprefix_addr6 ) ); + cfg.vprefix_len = config->vprefix_len; + memcpy ( &cfg.vhost6, &config->vhost6, sizeof ( cfg.vhost6 ) ); + cfg.vhostname = config->vhostname; + cfg.tftp_server_name = config->tftp_server_name; + cfg.tftp_path = config->tftp_path; + cfg.bootfile = config->bootfile; + cfg.vdhcp_start = config->vdhcp_start; + cfg.vnameserver = config->vnameserver; + memcpy ( &cfg.vnameserver6, &config->vnameserver6, + sizeof ( cfg.vnameserver6 ) ); + cfg.vdnssearch = config->vdnssearch; + cfg.vdomainname = config->vdomainname; + cfg.if_mtu = config->if_mtu; + cfg.if_mru = config->if_mru; + cfg.disable_host_loopback = config->disable_host_loopback; + cfg.enable_emu = config->enable_emu; + + /* Validate callback structure */ + static_assert ( &u->cb.send_packet == &u->callbacks.send_packet ); + static_assert ( &u->cb.guest_error == &u->callbacks.guest_error ); + static_assert ( &u->cb.clock_get_ns == &u->callbacks.clock_get_ns ); + static_assert ( &u->cb.timer_new == &u->callbacks.timer_new ); + static_assert ( &u->cb.timer_free == &u->callbacks.timer_free ); + static_assert ( &u->cb.timer_mod == &u->callbacks.timer_mod ); + static_assert ( &u->cb.register_poll_fd == + &u->callbacks.register_poll_fd ); + static_assert ( &u->cb.unregister_poll_fd == + &u->callbacks.unregister_poll_fd ); + static_assert ( &u->cb.notify == &u->callbacks.notify ); + + /* Create device */ + slirp = slirp_new ( &cfg, &u->cb, opaque ); + + return slirp; +} + +/** + * Wrap slirp_cleanup() + * + */ +void __asmcall linux_slirp_cleanup ( struct Slirp *slirp ) { + + slirp_cleanup ( slirp ); +} + +/** + * Wrap slirp_input() + * + */ +void __asmcall linux_slirp_input ( struct Slirp *slirp, const uint8_t *pkt, + int pkt_len ) { + + slirp_input ( slirp, pkt, pkt_len ); +} + +/** + * Wrap slirp_pollfds_fill() + * + */ +void __asmcall +linux_slirp_pollfds_fill ( struct Slirp *slirp, uint32_t *timeout, + int ( __asmcall * add_poll ) ( int fd, int events, + void *opaque ), + void *opaque ) { + + slirp_pollfds_fill ( slirp, timeout, add_poll, opaque ); +} + +/** + * Wrap slirp_pollfds_poll() + * + */ +void __asmcall +linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error, + int ( __asmcall * get_revents ) ( int idx, + void *opaque ), + void *opaque ) { + + slirp_pollfds_poll ( slirp, select_error, get_revents, opaque ); +} + +#else /* HAVE_LIBSLIRP */ + +struct Slirp * __asmcall +linux_slirp_new ( const struct slirp_config *config, + const struct slirp_callbacks *callbacks, void *opaque ) { + return NULL; +} + +void __asmcall linux_slirp_cleanup ( struct Slirp *slirp ) { +} + +void __asmcall linux_slirp_input ( struct Slirp *slirp, const uint8_t *pkt, + int pkt_len ) { +} + +void __asmcall +linux_slirp_pollfds_fill ( struct Slirp *slirp, uint32_t *timeout, + int ( __asmcall * add_poll ) ( int fd, int events, + void *opaque ), + void *opaque ) { +} + +void __asmcall +linux_slirp_pollfds_poll ( struct Slirp *slirp, int select_error, + int ( __asmcall * get_revents ) ( int idx, + void *opaque ), + void *opaque ) { +} + +#endif /* HAVE_LIBSLIRP */ + +/****************************************************************************** + * * Symbol aliases * ****************************************************************************** @@ -371,3 +524,8 @@ PROVIDE_IPXE_SYM ( linux_socket ); PROVIDE_IPXE_SYM ( linux_bind ); PROVIDE_IPXE_SYM ( linux_sendto ); PROVIDE_IPXE_SYM ( linux_strerror ); +PROVIDE_IPXE_SYM ( linux_slirp_new ); +PROVIDE_IPXE_SYM ( linux_slirp_cleanup ); +PROVIDE_IPXE_SYM ( linux_slirp_input ); +PROVIDE_IPXE_SYM ( linux_slirp_pollfds_fill ); +PROVIDE_IPXE_SYM ( linux_slirp_pollfds_poll ); |