Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quellcode-Bibliothek vhci_sysfs.c

  Sprache: C
 

// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2003-2008 Takahiro Hirofuchi
 * Copyright (C) 2015-2016 Nobuo Iwata
 */


#include <linux/kthread.h>
#include <linux/file.h>
#include <linux/net.h>
#include <linux/platform_device.h>
#include <linux/slab.h>

/* Hardening for Spectre-v1 */
#include <linux/nospec.h>

#include "usbip_common.h"
#include "vhci.h"

/* TODO: refine locking ?*/

/*
 * output example:
 * hub port sta spd dev       sockfd local_busid
 * hs  0000 004 000 00000000  000003 1-2.3
 * ................................................
 * ss  0008 004 000 00000000  000004 2-3.4
 * ................................................
 *
 * Output includes socket fd instead of socket pointer address to avoid
 * leaking kernel memory address in:
 * /sys/devices/platform/vhci_hcd.0/status and in debug output.
 * The socket pointer address is not used at the moment and it was made
 * visible as a convenient way to find IP address from socket pointer
 * address by looking up /proc/net/{tcp,tcp6}. As this opens a security
 * hole, the change is made to use sockfd instead.
 *
 */

static void port_show_vhci(char **out, int hub, int port, struct vhci_device *vdev)
{
 if (hub == HUB_SPEED_HIGH)
  *out += sprintf(*out, "hs  %04u %03u ",
          port, vdev->ud.status);
 else /* hub == HUB_SPEED_SUPER */
  *out += sprintf(*out, "ss  %04u %03u ",
          port, vdev->ud.status);

 if (vdev->ud.status == VDEV_ST_USED) {
  *out += sprintf(*out, "%03u %08x ",
          vdev->speed, vdev->devid);
  *out += sprintf(*out, "%06u %s",
          vdev->ud.sockfd,
          dev_name(&vdev->udev->dev));

 } else {
  *out += sprintf(*out, "000 00000000 ");
  *out += sprintf(*out, "000000 0-0");
 }

 *out += sprintf(*out, "\n");
}

/* Sysfs entry to show port status */
static ssize_t status_show_vhci(int pdev_nr, char *out)
{
 struct platform_device *pdev = vhcis[pdev_nr].pdev;
 struct vhci *vhci;
 struct usb_hcd *hcd;
 struct vhci_hcd *vhci_hcd;
 char *s = out;
 int i;
 unsigned long flags;

 if (!pdev || !out) {
  usbip_dbg_vhci_sysfs("show status error\n");
  return 0;
 }

 hcd = platform_get_drvdata(pdev);
 vhci_hcd = hcd_to_vhci_hcd(hcd);
 vhci = vhci_hcd->vhci;

 spin_lock_irqsave(&vhci->lock, flags);

 for (i = 0; i < VHCI_HC_PORTS; i++) {
  struct vhci_device *vdev = &vhci->vhci_hcd_hs->vdev[i];

  spin_lock(&vdev->ud.lock);
  port_show_vhci(&out, HUB_SPEED_HIGH,
          pdev_nr * VHCI_PORTS + i, vdev);
  spin_unlock(&vdev->ud.lock);
 }

 for (i = 0; i < VHCI_HC_PORTS; i++) {
  struct vhci_device *vdev = &vhci->vhci_hcd_ss->vdev[i];

  spin_lock(&vdev->ud.lock);
  port_show_vhci(&out, HUB_SPEED_SUPER,
          pdev_nr * VHCI_PORTS + VHCI_HC_PORTS + i, vdev);
  spin_unlock(&vdev->ud.lock);
 }

 spin_unlock_irqrestore(&vhci->lock, flags);

 return out - s;
}

static ssize_t status_show_not_ready(int pdev_nr, char *out)
{
 char *s = out;
 int i = 0;

 for (i = 0; i < VHCI_HC_PORTS; i++) {
  out += sprintf(out, "hs  %04u %03u ",
        (pdev_nr * VHCI_PORTS) + i,
        VDEV_ST_NOTASSIGNED);
  out += sprintf(out, "000 00000000 0000000000000000 0-0");
  out += sprintf(out, "\n");
 }

 for (i = 0; i < VHCI_HC_PORTS; i++) {
  out += sprintf(out, "ss  %04u %03u ",
        (pdev_nr * VHCI_PORTS) + VHCI_HC_PORTS + i,
        VDEV_ST_NOTASSIGNED);
  out += sprintf(out, "000 00000000 0000000000000000 0-0");
  out += sprintf(out, "\n");
 }
 return out - s;
}

static int status_name_to_id(const char *name)
{
 char *c;
 long val;
 int ret;

 c = strchr(name, '.');
 if (c == NULL)
  return 0;

 ret = kstrtol(c+110, &val);
 if (ret < 0)
  return ret;

 return val;
}

static ssize_t status_show(struct device *dev,
      struct device_attribute *attr, char *out)
{
 char *s = out;
 int pdev_nr;

 out += sprintf(out,
         "hub port sta spd dev      sockfd local_busid\n");

 pdev_nr = status_name_to_id(attr->attr.name);
 if (pdev_nr < 0)
  out += status_show_not_ready(pdev_nr, out);
 else
  out += status_show_vhci(pdev_nr, out);

 return out - s;
}

static ssize_t nports_show(struct device *dev, struct device_attribute *attr,
      char *out)
{
 char *s = out;

 /*
 * Half the ports are for SPEED_HIGH and half for SPEED_SUPER,
 * thus the * 2.
 */

 out += sprintf(out, "%d\n", VHCI_PORTS * vhci_num_controllers);
 return out - s;
}
static DEVICE_ATTR_RO(nports);

/* Sysfs entry to shutdown a virtual connection */
static int vhci_port_disconnect(struct vhci_hcd *vhci_hcd, __u32 rhport)
{
 struct vhci_device *vdev = &vhci_hcd->vdev[rhport];
 struct vhci *vhci = vhci_hcd->vhci;
 unsigned long flags;

 usbip_dbg_vhci_sysfs("enter\n");

 mutex_lock(&vdev->ud.sysfs_lock);

 /* lock */
 spin_lock_irqsave(&vhci->lock, flags);
 spin_lock(&vdev->ud.lock);

 if (vdev->ud.status == VDEV_ST_NULL) {
  pr_err("not connected %d\n", vdev->ud.status);

  /* unlock */
  spin_unlock(&vdev->ud.lock);
  spin_unlock_irqrestore(&vhci->lock, flags);
  mutex_unlock(&vdev->ud.sysfs_lock);

  return -EINVAL;
 }

 /* unlock */
 spin_unlock(&vdev->ud.lock);
 spin_unlock_irqrestore(&vhci->lock, flags);

 usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN);

 mutex_unlock(&vdev->ud.sysfs_lock);

 return 0;
}

static int valid_port(__u32 *pdev_nr, __u32 *rhport)
{
 if (*pdev_nr >= vhci_num_controllers) {
  pr_err("pdev %u\n", *pdev_nr);
  return 0;
 }
 *pdev_nr = array_index_nospec(*pdev_nr, vhci_num_controllers);

 if (*rhport >= VHCI_HC_PORTS) {
  pr_err("rhport %u\n", *rhport);
  return 0;
 }
 *rhport = array_index_nospec(*rhport, VHCI_HC_PORTS);

 return 1;
}

static ssize_t detach_store(struct device *dev, struct device_attribute *attr,
       const char *buf, size_t count)
{
 __u32 port = 0, pdev_nr = 0, rhport = 0;
 struct usb_hcd *hcd;
 struct vhci_hcd *vhci_hcd;
 int ret;

 if (kstrtoint(buf, 10, &port) < 0)
  return -EINVAL;

 pdev_nr = port_to_pdev_nr(port);
 rhport = port_to_rhport(port);

 if (!valid_port(&pdev_nr, &rhport))
  return -EINVAL;

 hcd = platform_get_drvdata(vhcis[pdev_nr].pdev);
 if (hcd == NULL) {
  dev_err(dev, "port is not ready %u\n", port);
  return -EAGAIN;
 }

 usbip_dbg_vhci_sysfs("rhport %d\n", rhport);

 if ((port / VHCI_HC_PORTS) % 2)
  vhci_hcd = hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_ss;
 else
  vhci_hcd = hcd_to_vhci_hcd(hcd)->vhci->vhci_hcd_hs;

 ret = vhci_port_disconnect(vhci_hcd, rhport);
 if (ret < 0)
  return -EINVAL;

 usbip_dbg_vhci_sysfs("Leave\n");

 return count;
}
static DEVICE_ATTR_WO(detach);

static int valid_args(__u32 *pdev_nr, __u32 *rhport,
        enum usb_device_speed speed)
{
 if (!valid_port(pdev_nr, rhport)) {
  return 0;
 }

 switch (speed) {
 case USB_SPEED_LOW:
 case USB_SPEED_FULL:
 case USB_SPEED_HIGH:
 case USB_SPEED_WIRELESS:
 case USB_SPEED_SUPER:
 case USB_SPEED_SUPER_PLUS:
  break;
 default:
  pr_err("Failed attach request for unsupported USB speed: %s\n",
   usb_speed_string(speed));
  return 0;
 }

 return 1;
}

/* Sysfs entry to establish a virtual connection */
/*
 * To start a new USB/IP attachment, a userland program needs to setup a TCP
 * connection and then write its socket descriptor with remote device
 * information into this sysfs file.
 *
 * A remote device is virtually attached to the root-hub port of @rhport with
 * @speed. @devid is embedded into a request to specify the remote device in a
 * server host.
 *
 * write() returns 0 on success, else negative errno.
 */

static ssize_t attach_store(struct device *dev, struct device_attribute *attr,
       const char *buf, size_t count)
{
 struct socket *socket;
 int sockfd = 0;
 __u32 port = 0, pdev_nr = 0, rhport = 0, devid = 0, speed = 0;
 struct usb_hcd *hcd;
 struct vhci_hcd *vhci_hcd;
 struct vhci_device *vdev;
 struct vhci *vhci;
 int err;
 unsigned long flags;
 struct task_struct *tcp_rx = NULL;
 struct task_struct *tcp_tx = NULL;

 /*
 * @rhport: port number of vhci_hcd
 * @sockfd: socket descriptor of an established TCP connection
 * @devid: unique device identifier in a remote host
 * @speed: usb device speed in a remote host
 */

 if (sscanf(buf, "%u %u %u %u", &port, &sockfd, &devid, &speed) != 4)
  return -EINVAL;
 pdev_nr = port_to_pdev_nr(port);
 rhport = port_to_rhport(port);

 usbip_dbg_vhci_sysfs("port(%u) pdev(%d) rhport(%u)\n",
        port, pdev_nr, rhport);
 usbip_dbg_vhci_sysfs("sockfd(%u) devid(%u) speed(%u)\n",
        sockfd, devid, speed);

 /* check received parameters */
 if (!valid_args(&pdev_nr, &rhport, speed))
  return -EINVAL;

 hcd = platform_get_drvdata(vhcis[pdev_nr].pdev);
 if (hcd == NULL) {
  dev_err(dev, "port %d is not ready\n", port);
  return -EAGAIN;
 }

 vhci_hcd = hcd_to_vhci_hcd(hcd);
 vhci = vhci_hcd->vhci;

 if (speed >= USB_SPEED_SUPER)
  vdev = &vhci->vhci_hcd_ss->vdev[rhport];
 else
  vdev = &vhci->vhci_hcd_hs->vdev[rhport];

 mutex_lock(&vdev->ud.sysfs_lock);

 /* Extract socket from fd. */
 socket = sockfd_lookup(sockfd, &err);
 if (!socket) {
  dev_err(dev, "failed to lookup sock");
  err = -EINVAL;
  goto unlock_mutex;
 }
 if (socket->type != SOCK_STREAM) {
  dev_err(dev, "Expecting SOCK_STREAM - found %d",
   socket->type);
  sockfd_put(socket);
  err = -EINVAL;
  goto unlock_mutex;
 }

 /* create threads before locking */
 tcp_rx = kthread_create(vhci_rx_loop, &vdev->ud, "vhci_rx");
 if (IS_ERR(tcp_rx)) {
  sockfd_put(socket);
  err = -EINVAL;
  goto unlock_mutex;
 }
 tcp_tx = kthread_create(vhci_tx_loop, &vdev->ud, "vhci_tx");
 if (IS_ERR(tcp_tx)) {
  kthread_stop(tcp_rx);
  sockfd_put(socket);
  err = -EINVAL;
  goto unlock_mutex;
 }

 /* get task structs now */
 get_task_struct(tcp_rx);
 get_task_struct(tcp_tx);

 /* now begin lock until setting vdev status set */
 spin_lock_irqsave(&vhci->lock, flags);
 spin_lock(&vdev->ud.lock);

 if (vdev->ud.status != VDEV_ST_NULL) {
  /* end of the lock */
  spin_unlock(&vdev->ud.lock);
  spin_unlock_irqrestore(&vhci->lock, flags);

  sockfd_put(socket);
  kthread_stop_put(tcp_rx);
  kthread_stop_put(tcp_tx);

  dev_err(dev, "port %d already used\n", rhport);
  /*
 * Will be retried from userspace
 * if there's another free port.
 */

  err = -EBUSY;
  goto unlock_mutex;
 }

 dev_info(dev, "pdev(%u) rhport(%u) sockfd(%d)\n",
   pdev_nr, rhport, sockfd);
 dev_info(dev, "devid(%u) speed(%u) speed_str(%s)\n",
   devid, speed, usb_speed_string(speed));

 vdev->devid         = devid;
 vdev->speed         = speed;
 vdev->ud.sockfd     = sockfd;
 vdev->ud.tcp_socket = socket;
 vdev->ud.tcp_rx     = tcp_rx;
 vdev->ud.tcp_tx     = tcp_tx;
 vdev->ud.status     = VDEV_ST_NOTASSIGNED;
 usbip_kcov_handle_init(&vdev->ud);

 spin_unlock(&vdev->ud.lock);
 spin_unlock_irqrestore(&vhci->lock, flags);
 /* end the lock */

 wake_up_process(vdev->ud.tcp_rx);
 wake_up_process(vdev->ud.tcp_tx);

 rh_port_connect(vdev, speed);

 dev_info(dev, "Device attached\n");

 mutex_unlock(&vdev->ud.sysfs_lock);

 return count;

unlock_mutex:
 mutex_unlock(&vdev->ud.sysfs_lock);
 return err;
}
static DEVICE_ATTR_WO(attach);

#define MAX_STATUS_NAME 16

struct status_attr {
 struct device_attribute attr;
 char name[MAX_STATUS_NAME+1];
};

static struct status_attr *status_attrs;

static void set_status_attr(int id)
{
 struct status_attr *status;

 status = status_attrs + id;
 if (id == 0)
  strcpy(status->name, "status");
 else
  snprintf(status->name, MAX_STATUS_NAME+1"status.%d", id);
 status->attr.attr.name = status->name;
 status->attr.attr.mode = S_IRUGO;
 status->attr.show = status_show;
 sysfs_attr_init(&status->attr.attr);
}

static int init_status_attrs(void)
{
 int id;

 status_attrs = kcalloc(vhci_num_controllers, sizeof(struct status_attr),
          GFP_KERNEL);
 if (status_attrs == NULL)
  return -ENOMEM;

 for (id = 0; id < vhci_num_controllers; id++)
  set_status_attr(id);

 return 0;
}

static void finish_status_attrs(void)
{
 kfree(status_attrs);
}

struct attribute_group vhci_attr_group = {
 .attrs = NULL,
};

int vhci_init_attr_group(void)
{
 struct attribute **attrs;
 int ret, i;

 attrs = kcalloc((vhci_num_controllers + 5), sizeof(struct attribute *),
   GFP_KERNEL);
 if (attrs == NULL)
  return -ENOMEM;

 ret = init_status_attrs();
 if (ret) {
  kfree(attrs);
  return ret;
 }
 *attrs = &dev_attr_nports.attr;
 *(attrs + 1) = &dev_attr_detach.attr;
 *(attrs + 2) = &dev_attr_attach.attr;
 *(attrs + 3) = &dev_attr_usbip_debug.attr;
 for (i = 0; i < vhci_num_controllers; i++)
  *(attrs + i + 4) = &((status_attrs + i)->attr.attr);
 vhci_attr_group.attrs = attrs;
 return 0;
}

void vhci_finish_attr_group(void)
{
 finish_status_attrs();
 kfree(vhci_attr_group.attrs);
}

Messung V0.5 in Prozent
C=97 H=96 G=96

¤ Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.0.13Bemerkung:  (vorverarbeitet am  2026-06-08) ¤

*Bot Zugriff






Versionsinformation zu Columbo

Bemerkung:

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Statistik
#Sources=434850
#Domains=655579