diff options
author | Yang Zhang <yang.z.zhang@intel.com> | 2015-08-28 09:58:54 +0800 |
---|---|---|
committer | Yang Zhang <yang.z.zhang@intel.com> | 2015-09-01 12:44:00 +0800 |
commit | e44e3482bdb4d0ebde2d8b41830ac2cdb07948fb (patch) | |
tree | 66b09f592c55df2878107a468a91d21506104d3f /qemu/roms/ipxe/src/drivers/linux | |
parent | 9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00 (diff) |
Add qemu 2.4.0
Change-Id: Ic99cbad4b61f8b127b7dc74d04576c0bcbaaf4f5
Signed-off-by: Yang Zhang <yang.z.zhang@intel.com>
Diffstat (limited to 'qemu/roms/ipxe/src/drivers/linux')
-rw-r--r-- | qemu/roms/ipxe/src/drivers/linux/linux.c | 153 | ||||
-rw-r--r-- | qemu/roms/ipxe/src/drivers/linux/tap.c | 253 |
2 files changed, 406 insertions, 0 deletions
diff --git a/qemu/roms/ipxe/src/drivers/linux/linux.c b/qemu/roms/ipxe/src/drivers/linux/linux.c new file mode 100644 index 000000000..83546b27e --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/linux/linux.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2010 Piotr Jaroszyński <p.jaroszynski@gmail.com> + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +FILE_LICENCE(GPL2_OR_LATER); + +/** @file + * + * Linux root_device and root_driver. + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <ipxe/linux.h> +#include <ipxe/malloc.h> +#include <ipxe/settings.h> + +LIST_HEAD(linux_device_requests); +LIST_HEAD(linux_global_settings); + +/** Go over the device requests looking for a matching linux driver to handle them. */ +static int linux_probe(struct root_device *rootdev) +{ + struct linux_device_request *request; + struct linux_driver *driver; + struct linux_device *device = NULL; + int rc; + + /* Apply global settings */ + linux_apply_settings(&linux_global_settings, NULL); + + list_for_each_entry(request, &linux_device_requests, list) { + if (! device) + device = zalloc(sizeof(*device)); + + if (! device) + return -ENOMEM; + + rc = 1; + + for_each_table_entry(driver, LINUX_DRIVERS) { + if ((rc = strcmp(driver->name, request->driver)) == 0) + break; + } + + if (rc != 0) { + printf("Linux driver '%s' not found\n", request->driver); + continue; + } + + if (! driver->can_probe) { + printf("Driver '%s' cannot handle any more devices\n", driver->name); + continue; + } + + /* We found a matching driver so add the device to the hierarchy */ + list_add(&device->dev.siblings, &rootdev->dev.children); + device->dev.parent = &rootdev->dev; + INIT_LIST_HEAD(&device->dev.children); + + if (driver->probe(device, request) == 0) { + device->driver = driver; + device->dev.driver_name = driver->name; + /* Driver handled the device so release ownership */ + device = NULL; + } else { + /* Driver failed to handle the device so remove it from the hierarchy + * and reuse the object */ + list_del(&device->dev.siblings); + } + }; + + free(device); + + return 0; +} + +/** Remove all the linux devices registered in probe() */ +static void linux_remove(struct root_device *rootdev) +{ + struct linux_device *device; + struct linux_device *tmp; + + list_for_each_entry_safe(device, tmp, &rootdev->dev.children, dev.siblings) { + list_del(&device->dev.siblings); + device->driver->remove(device); + free(device); + } +} + +/** Linux root driver */ +static struct root_driver linux_root_driver = { + .probe = linux_probe, + .remove = linux_remove, +}; + +/** Linux root device */ +struct root_device linux_root_device __root_device = { + .dev = { .name = "linux" }, + .driver = &linux_root_driver, +}; + +struct linux_setting *linux_find_setting(char *name, struct list_head *settings) +{ + struct linux_setting *setting; + struct linux_setting *result = NULL; + + /* Find the last occurrence of a setting with the specified name */ + list_for_each_entry(setting, settings, list) { + if (strcmp(setting->name, name) == 0) { + result = setting; + } + } + + return result; +} + +void linux_apply_settings(struct list_head *new_settings, struct settings *settings_block) +{ + struct linux_setting *setting; + int rc; + + list_for_each_entry(setting, new_settings, list) { + /* Skip already applied settings */ + if (setting->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); + } + } +} diff --git a/qemu/roms/ipxe/src/drivers/linux/tap.c b/qemu/roms/ipxe/src/drivers/linux/tap.c new file mode 100644 index 000000000..979436654 --- /dev/null +++ b/qemu/roms/ipxe/src/drivers/linux/tap.c @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2010 Piotr Jaroszyński <p.jaroszynski@gmail.com> + * + * 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 St, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <linux_api.h> +#include <ipxe/list.h> +#include <ipxe/linux.h> +#include <ipxe/malloc.h> +#include <ipxe/device.h> +#include <ipxe/netdevice.h> +#include <ipxe/iobuf.h> +#include <ipxe/ethernet.h> +#include <ipxe/settings.h> +#include <ipxe/socket.h> + +/* This hack prevents pre-2.6.32 headers from redefining struct sockaddr */ +#define __GLIBC__ 2 +#include <linux/socket.h> +#undef __GLIBC__ +#include <linux/if.h> +#include <linux/if_ether.h> +#include <linux/if_tun.h> + +#define RX_BUF_SIZE 1536 + +/** @file + * + * The TAP driver. + * + * The TAP is a Virtual Ethernet network device. + */ + +struct tap_nic { + /** Tap interface name */ + char * interface; + /** File descriptor of the opened tap device */ + int fd; +}; + +/** Open the TAP device */ +static int tap_open(struct net_device * netdev) +{ + struct tap_nic * nic = netdev->priv; + struct ifreq ifr; + int ret; + + nic->fd = linux_open("/dev/net/tun", O_RDWR); + if (nic->fd < 0) { + DBGC(nic, "tap %p open('/dev/net/tun') = %d (%s)\n", nic, nic->fd, linux_strerror(linux_errno)); + return nic->fd; + } + + memset(&ifr, 0, sizeof(ifr)); + /* IFF_NO_PI for no extra packet information */ + ifr.ifr_flags = IFF_TAP | IFF_NO_PI; + strncpy(ifr.ifr_name, nic->interface, IFNAMSIZ); + DBGC(nic, "tap %p interface = '%s'\n", nic, nic->interface); + + ret = linux_ioctl(nic->fd, TUNSETIFF, &ifr); + + if (ret != 0) { + DBGC(nic, "tap %p ioctl(%d, ...) = %d (%s)\n", nic, nic->fd, ret, linux_strerror(linux_errno)); + linux_close(nic->fd); + return ret; + } + + /* Set nonblocking mode to make tap_poll easier */ + ret = linux_fcntl(nic->fd, F_SETFL, O_NONBLOCK); + + if (ret != 0) { + DBGC(nic, "tap %p fcntl(%d, ...) = %d (%s)\n", nic, nic->fd, ret, linux_strerror(linux_errno)); + linux_close(nic->fd); + return ret; + } + + return 0; +} + +/** Close the TAP device */ +static void tap_close(struct net_device *netdev) +{ + struct tap_nic * nic = netdev->priv; + linux_close(nic->fd); +} + +/** + * Transmit an ethernet packet. + * + * The packet can be written to the TAP device and marked as complete immediately. + */ +static int tap_transmit(struct net_device *netdev, struct io_buffer *iobuf) +{ + struct tap_nic * nic = netdev->priv; + int rc; + + /* Pad and align packet */ + iob_pad(iobuf, ETH_ZLEN); + + rc = linux_write(nic->fd, iobuf->data, iobuf->tail - iobuf->data); + DBGC2(nic, "tap %p wrote %d bytes\n", nic, rc); + netdev_tx_complete(netdev, iobuf); + + return 0; +} + +/** Poll for new packets */ +static void tap_poll(struct net_device *netdev) +{ + struct tap_nic * nic = netdev->priv; + struct pollfd pfd; + struct io_buffer * iobuf; + int r; + + pfd.fd = nic->fd; + pfd.events = POLLIN; + if (linux_poll(&pfd, 1, 0) == -1) { + DBGC(nic, "tap %p poll failed (%s)\n", nic, linux_strerror(linux_errno)); + return; + } + if ((pfd.revents & POLLIN) == 0) + return; + + /* At this point we know there is at least one new packet to be read */ + + iobuf = alloc_iob(RX_BUF_SIZE); + if (! iobuf) + goto allocfail; + + while ((r = linux_read(nic->fd, iobuf->data, RX_BUF_SIZE)) > 0) { + DBGC2(nic, "tap %p read %d bytes\n", nic, r); + + iob_put(iobuf, r); + netdev_rx(netdev, iobuf); + + iobuf = alloc_iob(RX_BUF_SIZE); + if (! iobuf) + goto allocfail; + } + + free_iob(iobuf); + return; + +allocfail: + DBGC(nic, "tap %p alloc_iob failed\n", nic); +} + +/** + * Set irq. + * + * Not used on linux, provide a dummy implementation. + */ +static void tap_irq(struct net_device *netdev, int enable) +{ + struct tap_nic *nic = netdev->priv; + + DBGC(nic, "tap %p irq enable = %d\n", nic, enable); +} + +/** Tap operations */ +static struct net_device_operations tap_operations = { + .open = tap_open, + .close = tap_close, + .transmit = tap_transmit, + .poll = tap_poll, + .irq = tap_irq, +}; + +/** Handle a device request for the tap driver */ +static int tap_probe(struct linux_device *device, struct linux_device_request *request) +{ + struct linux_setting *if_setting; + struct net_device *netdev; + struct tap_nic *nic; + int rc; + + netdev = alloc_etherdev(sizeof(*nic)); + if (! netdev) + return -ENOMEM; + + netdev_init(netdev, &tap_operations); + nic = netdev->priv; + linux_set_drvdata(device, netdev); + netdev->dev = &device->dev; + memset(nic, 0, sizeof(*nic)); + + /* Look for the mandatory if setting */ + if_setting = linux_find_setting("if", &request->settings); + + /* No if setting */ + if (! if_setting) { + printf("tap missing a mandatory if setting\n"); + rc = -EINVAL; + goto err_settings; + } + + nic->interface = if_setting->value; + snprintf ( device->dev.name, sizeof ( device->dev.name ), "%s", + nic->interface ); + device->dev.desc.bus_type = BUS_TYPE_TAP; + if_setting->applied = 1; + + /* Apply rest of the settings */ + linux_apply_settings(&request->settings, &netdev->settings.settings); + + /* Register network device */ + if ((rc = register_netdev(netdev)) != 0) + goto err_register; + + netdev_link_up(netdev); + + return 0; + +err_settings: + unregister_netdev(netdev); +err_register: + netdev_nullify(netdev); + netdev_put(netdev); + return rc; +} + +/** Remove the device */ +static void tap_remove(struct linux_device *device) +{ + struct net_device *netdev = linux_get_drvdata(device); + unregister_netdev(netdev); + netdev_nullify(netdev); + netdev_put(netdev); +} + +/** Tap linux_driver */ +struct linux_driver tap_driver __linux_driver = { + .name = "tap", + .probe = tap_probe, + .remove = tap_remove, + .can_probe = 1, +}; |