bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v2 1/2] libirqhelp: user interrupt handler helper library


From: Damien Zammit
Subject: [PATCH v2 1/2] libirqhelp: user interrupt handler helper library
Date: Thu, 22 Sep 2022 04:42:18 +0000

This provides a new helper lib for registering
interrupt handlers in userspace.

( -1, bus, dev, fun, ...): will look up gsi from ACPI
(gsi,  -1,  -1,  -1, ...): will use gsi and ignore pci commands

---
 Makefile             |   4 +
 libirqhelp/Makefile  |  28 ++++
 libirqhelp/irqhelp.c | 376 +++++++++++++++++++++++++++++++++++++++++++
 libirqhelp/irqhelp.h |  31 ++++
 4 files changed, 439 insertions(+)
 create mode 100644 libirqhelp/Makefile
 create mode 100644 libirqhelp/irqhelp.c
 create mode 100644 libirqhelp/irqhelp.h

diff --git a/Makefile b/Makefile
index 874349c0..2d754114 100644
--- a/Makefile
+++ b/Makefile
@@ -66,6 +66,10 @@ endif

 ifeq ($(HAVE_LIBACPICA),yes)
 prog-subdirs += acpi
+ ifeq ($(HAVE_LIBPCIACCESS),yes)
+ # Needs acpi translator and libpciaccess
+ lib-subdirs += libirqhelp
+ endif
 endif

 # Other directories
diff --git a/libirqhelp/Makefile b/libirqhelp/Makefile
new file mode 100644
index 00000000..fbc82072
--- /dev/null
+++ b/libirqhelp/Makefile
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 Free Software Foundation, Inc.
+#
+# 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, or (at
+# your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := libirqhelp
+makemode := library
+
+SRCS = irqhelp.c acpiUser.c
+
+OBJS = $(SRCS:.c=.o)
+HURDLIBS =
+LDLIBS += -lpthread $(libpciaccess_LIBS)
+libname = libirqhelp
+installhdrs = irqhelp.h
+
+include ../Makeconf
diff --git a/libirqhelp/irqhelp.c b/libirqhelp/irqhelp.c
new file mode 100644
index 00000000..92dfc04f
--- /dev/null
+++ b/libirqhelp/irqhelp.c
@@ -0,0 +1,376 @@
+/* Library providing helper functions for userspace irq handling.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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, or (at
+   your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#define _GNU_SOURCE 1
+
+#include "irqhelp.h"
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <hurd.h>
+#include <hurd/paths.h>
+#include <pciaccess.h>
+#include <device/notify.h>
+#include <device/device.h>
+#include "acpi_U.h"
+#include <mach.h>
+
+#define MAX_PCI_DEVS           128
+#define PCI_COMMAND            0x04
+#define PCI_COMMAND_INT_DISABLE        0x400
+#define IRQ_THREAD_PRIORITY    2
+
+
+struct irq {
+  int bus;
+  int dev;
+  int fun;
+  int gsi;
+  int cookie;
+  void (*handler)(void *);
+  void *context;
+  mach_port_t port;
+
+  LIST_ENTRY(irq) entries;
+};
+
+static LIST_HEAD(, irq) irqs = LIST_HEAD_INITIALIZER(&irqs);
+
+static mach_port_t master_host;
+static mach_port_t irqdev;
+static mach_port_t acpidev;
+static int refcnt;
+
+static struct pci_device *pci_devices[MAX_PCI_DEVS];
+static int numdevs;
+
+static error_t
+pci_init(void)
+{
+  int i = 0;
+  error_t err;
+  struct pci_device_iterator *dev_iter;
+  struct pci_device *pci_dev;
+
+  err = pci_system_init ();
+  if (err)
+    return err;
+
+  dev_iter = pci_slot_match_iterator_create (NULL);
+  while (((pci_dev = pci_device_next (dev_iter)) != NULL)
+      && (i < MAX_PCI_DEVS))
+    {
+      pci_device_probe(pci_dev);
+      pci_devices[i++] = pci_dev;
+    }
+  numdevs = i;
+  return 0;
+}
+
+static error_t
+pci_confread(int bus, int dev, int fun,
+             int reg, unsigned int *rv)
+{
+  int i;
+  *rv = 0xffffffff;
+
+  for (i = 0; i < numdevs; i++)
+    {
+      if ((pci_devices[i]->bus == bus)
+       && (pci_devices[i]->dev == dev)
+       && (pci_devices[i]->func == fun))
+       goto found;
+    }
+  return ENODEV;
+
+found:
+  pci_device_cfg_read_u32(pci_devices[i], rv, reg);
+  return 0;
+}
+
+static error_t
+pci_confwrite(int bus, int dev, int fun,
+              int reg, unsigned int v)
+{
+  int i;
+
+  for (i = 0; i < numdevs; i++)
+    {
+      if ((pci_devices[i]->bus == bus)
+       && (pci_devices[i]->dev == dev)
+       && (pci_devices[i]->func == fun))
+        goto found;
+    }
+  return ENODEV;
+
+found:
+  pci_device_cfg_write_u32(pci_devices[i], v, reg);
+  return 0;
+}
+
+static error_t
+get_acpi(void)
+{
+  error_t err = 0;
+  mach_port_t tryacpi, device_master;
+
+  acpidev = MACH_PORT_NULL;
+
+  tryacpi = file_name_lookup (_SERVERS_ACPI, O_RDONLY, 0);
+  if (tryacpi == MACH_PORT_NULL)
+    {
+      err = get_privileged_ports (0, &device_master);
+      if (err)
+        return err;
+
+      err = device_open (device_master, D_READ, "acpi", &tryacpi);
+      if (err)
+        {
+         mach_port_deallocate (mach_task_self (), device_master);
+          return err;
+       }
+
+      mach_port_deallocate (mach_task_self (), device_master);
+    }
+
+  acpidev = tryacpi;
+  return err;
+}
+
+static error_t
+get_irq(void)
+{
+  error_t err = 0;
+  mach_port_t tryirq, device_master;
+
+  irqdev = MACH_PORT_NULL;
+
+  err = get_privileged_ports (0, &device_master);
+  if (err)
+    return err;
+
+  err = device_open (device_master, D_READ|D_WRITE, "irq", &tryirq);
+  if (err)
+    {
+      mach_port_deallocate (mach_task_self (), device_master);
+      return err;
+    }
+
+  mach_port_deallocate (mach_task_self (), device_master);
+
+  irqdev = tryirq;
+  return err;
+}
+
+void *
+irqhelp_server_loop(void *arg)
+{
+  struct irq *irq = (struct irq *)arg;
+
+  if (!irq)
+    return NULL;
+
+  int interrupt_server (mach_msg_header_t *inp,
+                       mach_msg_header_t *outp)
+  {
+    char interrupt[4];
+
+    device_intr_notification_t *n = (device_intr_notification_t *) inp;
+
+    ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+    if (n->intr_header.msgh_id != DEVICE_INTR_NOTIFY)
+      {
+        /* not an interrupt */
+        return 0;
+      }
+
+    /* FIXME: id <-> gsi now has an indirection, assuming 1:1 */
+    if (n->id != irq->gsi)
+      {
+        /* interrupt not for us */
+        return 0;
+      }
+
+    /* enable pci interrupt if we know the device B/D/F and it is disabled */
+    if ((irq->bus >= 0) && (irq->dev >= 0) && (irq->fun >= 0))
+      {
+        unsigned int val;
+        pci_confread (irq->bus, irq->dev, irq->fun, PCI_COMMAND, &val);
+        if (val & PCI_COMMAND_INT_DISABLE)
+          {
+            val &= ~PCI_COMMAND_INT_DISABLE;
+            pci_confwrite (irq->bus, irq->dev, irq->fun, PCI_COMMAND, val);
+          }
+      }
+
+    /* call handler */
+    irq->handler(irq->context);
+
+    /* ACK interrupt */
+    device_intr_ack (irqdev, irq->port, MACH_MSG_TYPE_MAKE_SEND);
+
+    return 1;
+  }
+
+  /* Server loop */
+  mach_msg_server (interrupt_server, 0, irq->port);
+
+  return NULL;
+}
+
+static struct irq *
+interrupt_register(int gsi,
+                  int bus,
+                  int dev,
+                  int fun,
+                  void (*handler)(void *),
+                  void *context,
+                  int *cookie)
+{
+  mach_port_t delivery_port;
+  mach_port_t pset, psetcntl;
+  error_t err;
+  struct irq *irq = NULL;
+
+  irq = malloc(sizeof(struct irq));
+  if (!irq)
+    return NULL;
+
+  LIST_INSERT_HEAD(&irqs, irq, entries);
+
+  irq->handler = handler;
+  irq->context = context;
+  irq->bus = bus;
+  irq->dev = dev;
+  irq->fun = fun;
+  irq->gsi = gsi;
+  irq->cookie = ++refcnt;
+
+  err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE,
+                           &delivery_port);
+  if (err)
+    goto fail;
+
+  irq->port = delivery_port;
+
+  err = thread_get_assignment (mach_thread_self (), &pset);
+  if (err)
+    goto fail;
+
+  err = host_processor_set_priv (master_host, pset, &psetcntl);
+  if (err)
+    goto fail;
+
+  thread_max_priority (mach_thread_self (), psetcntl, 0);
+  err = thread_priority (mach_thread_self (), IRQ_THREAD_PRIORITY, 0);
+  if (err)
+    goto fail;
+
+  err = device_intr_register(irqdev, irq->gsi,
+                             0, irq->port,
+                             MACH_MSG_TYPE_MAKE_SEND);
+  if (err)
+    goto fail;
+
+  *cookie = irq->cookie;
+
+  return irq;
+
+fail:
+  free(irq);
+  --refcnt;
+  return NULL;
+}
+
+
+/* Accepts gsi or bus/dev/fun or both, but cant be all -1.
+   If gsi is -1, will lookup the gsi via ACPI.
+   If bus/dev/fun are all -1, will not use PCI
+   to clear possibly disabled pci interrupt.
+   Accepts a pointer to a cookie to be used to
+   deregister the handler later.  */
+void *
+irqhelp_install_interrupt_handler(int gsi,
+                                 int bus,
+                                 int dev,
+                                 int fun,
+                                 void (*handler)(void *),
+                                 void *context,
+                                 int *cookie)
+{
+  struct irq *irq;
+  error_t err;
+
+  if (!handler)
+    return NULL;
+
+  if (!cookie)
+    return NULL;
+
+  err = get_privileged_ports (&master_host, 0);
+  if (err)
+    return NULL;
+
+  err = get_irq();
+  if (err)
+    return NULL;
+
+  if (gsi < 0)
+    {
+      if ((bus < 0) && (dev < 0) && (fun < 0))
+        return NULL;
+
+      err = get_acpi();
+      if (err)
+        return NULL;
+
+      /* We need to call acpi translator to get gsi */
+      err = acpi_get_pci_irq (acpidev, bus, dev, fun, &gsi);
+      if (err)
+        return NULL;
+    }
+
+  if ((bus >= 0) && (dev >= 0) && (fun >= 0))
+    {
+      err = pci_init();
+      if (err)
+        return NULL;
+    }
+
+  irq = interrupt_register(gsi, bus, dev, fun, handler, context, cookie);
+
+  return (void *)irq;
+}
+
+error_t
+irqhelp_remove_interrupt_handler(int gsi,
+                                int bus,
+                                int dev,
+                                int fun,
+                                int cookie)
+{
+  /* Not implemented yet, but don't fail */
+  return 0;
+}
diff --git a/libirqhelp/irqhelp.h b/libirqhelp/irqhelp.h
new file mode 100644
index 00000000..f7947630
--- /dev/null
+++ b/libirqhelp/irqhelp.h
@@ -0,0 +1,31 @@
+/* Library providing helper functions for userspace irq handling.
+   Copyright (C) 2022 Free Software Foundation, Inc.
+
+   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, or (at
+   your option) 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _HURD_IRQHELP_
+#define _HURD_IRQHELP_
+
+#include <mach.h>
+#include <hurd/hurd_types.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+void * irqhelp_install_interrupt_handler(int gsi, int bus, int dev, int fun,
+                                        void (*handler)(void *), void 
*context, int *cookie);
+error_t irqhelp_remove_interrupt_handler(int gsi, int bus, int dev, int fun, 
int cookie);
+void * irqhelp_server_loop(void *arg);
+
+#endif
--
2.34.1





reply via email to

[Prev in Thread] Current Thread [Next in Thread]