summaryrefslogtreecommitdiffstats
path: root/src/drivers/linux
diff options
context:
space:
mode:
authorSimon Rettberg2021-03-08 15:55:33 +0100
committerSimon Rettberg2021-03-08 15:55:33 +0100
commit6707d9218c8e6e760e53068d5f41ceb31fac6ea0 (patch)
tree88e8fc84ede2a0ed2c1cec3a6109beb9fb53abf5 /src/drivers/linux
parentMerge branch 'master' into openslx (diff)
parent[linux] Do not assume that stat() works on sysfs files (diff)
downloadipxe-6707d9218c8e6e760e53068d5f41ceb31fac6ea0.tar.gz
ipxe-6707d9218c8e6e760e53068d5f41ceb31fac6ea0.tar.xz
ipxe-6707d9218c8e6e760e53068d5f41ceb31fac6ea0.zip
Merge branch 'master' into openslx
Diffstat (limited to 'src/drivers/linux')
-rw-r--r--src/drivers/linux/af_packet.c2
-rw-r--r--src/drivers/linux/linux.c50
-rw-r--r--src/drivers/linux/slirp.c552
-rw-r--r--src/drivers/linux/tap.c2
4 files changed, 591 insertions, 15 deletions
diff --git a/src/drivers/linux/af_packet.c b/src/drivers/linux/af_packet.c
index 65aafc5b1..9fa6ef2a5 100644
--- a/src/drivers/linux/af_packet.c
+++ b/src/drivers/linux/af_packet.c
@@ -19,7 +19,7 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
-#include <linux_api.h>
+#include <ipxe/linux_api.h>
#include <ipxe/list.h>
#include <ipxe/linux.h>
#include <ipxe/malloc.h>
diff --git a/src/drivers/linux/linux.c b/src/drivers/linux/linux.c
index 83546b27e..898f50024 100644
--- a/src/drivers/linux/linux.c
+++ b/src/drivers/linux/linux.c
@@ -130,24 +130,48 @@ struct linux_setting *linux_find_setting(char *name, struct list_head *settings)
return result;
}
-void linux_apply_settings(struct list_head *new_settings, struct settings *settings_block)
-{
- struct linux_setting *setting;
+/**
+ * Apply Linux command-line settings
+ *
+ * @v list List of command-line settings
+ * @v settings Settings block
+ */
+void linux_apply_settings ( struct list_head *list,
+ struct settings *settings ) {
+ struct linux_setting *lsetting;
+ struct settings *ignore;
+ struct setting setting;
int rc;
- list_for_each_entry(setting, new_settings, list) {
+ list_for_each_entry ( lsetting, list, list ) {
+
/* Skip already applied settings */
- if (setting->applied)
+ if ( lsetting->applied )
continue;
- struct setting *s = find_setting(setting->name);
- if (s) {
- rc = storef_setting(settings_block, find_setting(setting->name), setting->value);
- if (rc != 0)
- DBG("linux storing setting '%s' = '%s' failed\n", setting->name, setting->value);
- setting->applied = 1;
- } else {
- DBG("linux unknown setting '%s'\n", setting->name);
+ /* Parse setting name */
+ if ( ( rc = parse_setting_name ( lsetting->name,
+ find_child_settings, &ignore,
+ &setting ) ) != 0 ) {
+ DBGC ( settings, "Linux cannot parse %s: %s\n",
+ lsetting->name, strerror ( rc ) );
+ continue;
+ }
+
+ /* Apply default type if not specified */
+ if ( ! setting.type )
+ setting.type = &setting_type_string;
+
+ /* Store setting */
+ if ( ( rc = storef_setting ( settings, &setting,
+ lsetting->value ) ) != 0 ) {
+ DBGC ( settings, "Linux cannot set %s=\"%s\": %s\n",
+ lsetting->name, lsetting->value,
+ strerror ( rc ) );
+ continue;
}
+
+ /* Mark setting as applied */
+ lsetting->applied = 1;
}
}
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/drivers/linux/tap.c b/src/drivers/linux/tap.c
index db3b7955b..ff1e08bdb 100644
--- a/src/drivers/linux/tap.c
+++ b/src/drivers/linux/tap.c
@@ -19,7 +19,7 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
-#include <linux_api.h>
+#include <ipxe/linux_api.h>
#include <ipxe/list.h>
#include <ipxe/linux.h>
#include <ipxe/malloc.h>