On 9/7/20 9:45 am, Samuel Thibault wrote:
> Thanks! It looks good, just a couple last things:
> 
> - you don't need to introduce and call mach_cli(), splhigh already
>   disables interrupts, see the implementation in i386/i386/spl.S

ACK

> - Please keep intr.h's __INTR_H__ guard for consistency.

ACK

> - When moving code around, you have to copy over the licence statements,
>   so here the gpl2+ statement of irq.c
>   Ideally we'd dive back to check whether the mask_irq and unmask_irq
>   functions could be available under a BSD licence, but gpl2+ is
>   compatible with this BSD licence so it's fine to append the gpl2+
>   notice.

ACK

> - For new files, also add licensing terms. Since gnumach is mostly BSD
>   we can continue with BSD, see the terms I added to intr.[ch].
ACK

>> + *     We can specify physical memory range limits and alignment.
> 
> pmax is ambiguous, it could be understood as the last possible byte
> address, please document that it is the address of the byte just after
> the limit, e.g. 0x100000000 for limiting addresses to 32bits.

ACK
 
> For paddr, we really want to use phys_addr_t, otherwise we're stuck in
> the 4GiB area.

ACK

> I don't think you need to distinguish VM_PAGE_MAX_SEGS == 4 from == 3.
> Something like this should be fine:
> #ifdef VM_PAGE_SEL_DMA32
>               selector = VM_PAGE_SEL_DMA32;
>       if (pmax > VM_PAGE_DMA32_LIMIT)
> #endif

ACK

>> +    size = vm_page_round(size + palign);
> 
>>      alloc_size = (1 << (order + PAGE_SHIFT));
>>      npages = vm_page_atop(alloc_size);
> 
> I don't think you need to add palign, that could allocate twice as much
> as what you need when the size is equel to palign for instance. The
> alloc_size will be a power of two already, so you just need to bump
> order to vm_page_order(palign) if needed.

I don't think that is correct either.  The physical alignment of the memory
is unrelated to the size requested.  I thought I could hack it but it doesn't 
work
so I've left physical alignment as a TODO as well as pmin != 0, but included it
in the RPC itself so it can be committed even if it doesn't do anything yet.

Please see attached 2 new patches.

Thanks,
Damien
>From adfad36aab3bb1f9a626fa5ba22d12f286e39fd6 Mon Sep 17 00:00:00 2001
From: Damien Zammit <dam...@zamaudio.com>
Date: Sun, 5 Jul 2020 11:15:44 +1000
Subject: [PATCH 1/2] irq: Refactor interrupts into mach device

---
 device/dev_hdr.h                 |   9 ++
 device/ds_routines.c             |  66 +++++------
 device/ds_routines.h             |   3 -
 device/intr.c                    | 197 +++++++++++++++----------------
 device/intr.h                    |  67 +++++++----
 i386/Makefrag.am                 |   2 +
 i386/i386/irq.c                  |  66 +++++++++++
 i386/i386/irq.h                  |  27 +++++
 i386/i386/pic.c                  |  54 +++++++++
 i386/i386/pic.h                  |   2 +
 i386/i386at/conf.c               |   8 ++
 include/device/device.defs       |  22 ++--
 include/device/notify.defs       |   2 +-
 include/device/notify.h          |   2 +-
 linux/dev/arch/i386/kernel/irq.c |  88 +-------------
 15 files changed, 353 insertions(+), 262 deletions(-)
 create mode 100644 i386/i386/irq.c
 create mode 100644 i386/i386/irq.h

diff --git a/device/dev_hdr.h b/device/dev_hdr.h
index ad98e0bb..4bd12c1c 100644
--- a/device/dev_hdr.h
+++ b/device/dev_hdr.h
@@ -146,4 +146,13 @@ extern void dev_set_indirection(
     dev_ops_t   ops,
     int     	unit);
 
+/*
+ * compare device name
+ */
+extern boolean_t __attribute__ ((pure))
+name_equal(
+    const char  *src,
+    int         len,
+    const char  *target);
+
 #endif	/* _DEVICE_DEV_HDR_H_ */
diff --git a/device/ds_routines.c b/device/ds_routines.c
index 13c9a63e..69e963bd 100644
--- a/device/ds_routines.c
+++ b/device/ds_routines.c
@@ -321,44 +321,35 @@ ds_device_map (device_t dev, vm_prot_t prot, vm_offset_t offset,
 
 /* TODO: missing deregister support */
 io_return_t
-ds_device_intr_register (ipc_port_t master_port, int line,
-		       int id, int flags, ipc_port_t receive_port)
+ds_device_intr_register (device_t dev, int id,
+                         int flags, ipc_port_t receive_port)
 {
-#ifdef MACH_XEN
-  return D_INVALID_OPERATION;
-#else	/* MACH_XEN */
-  io_return_t ret;
+  kern_return_t err;
+  mach_device_t mdev = dev->emul_data;
 
-  /* Open must be called on the master device port.  */
-  if (master_port != master_device_port)
-    return D_INVALID_OPERATION;
+  /* Refuse if device is dead or not completely open.  */
+  if (dev == DEVICE_NULL)
+    return D_NO_SUCH_DEVICE;
 
-  /* XXX: move to arch-specific */
-  if (line < 0 || line >= 16)
+  /* Must be called on the irq device only */
+  if (! name_equal(mdev->dev_ops->d_name, 3, "irq"))
     return D_INVALID_OPERATION;
 
-  user_intr_t *user_intr = insert_intr_entry (line, receive_port);
-  if (!user_intr)
+  user_intr_t *e = insert_intr_entry (&irqtab, id, receive_port);
+  if (!e)
     return D_NO_MEMORY;
+
   // TODO The original port should be replaced
   // when the same device driver calls it again, 
   // in order to handle the case that the device driver crashes and restarts.
-  ret = install_user_intr_handler (line, flags, user_intr);
-
-  if (ret == 0)
-  {
-    /* If the port is installed successfully, increase its reference by 1.
-     * Thus, the port won't be destroyed after its task is terminated. */
-    ip_reference (receive_port);
-
-    /* For now netdde calls device_intr_enable once after registration. Assume
-     * it does so for now. When we move to IRQ acknowledgment convention we will
-     * change this. */
-    __disable_irq (line);
-  }
-
-  return ret;
-#endif	/* MACH_XEN */
+  err = install_user_intr_handler (&irqtab, id, flags, e);
+  if (err == D_SUCCESS)
+    {
+      /* If the port is installed successfully, increase its reference by 1.
+       * Thus, the port won't be destroyed after its task is terminated. */
+      ip_reference (receive_port);
+    }
+  return err;
 }
 
 boolean_t
@@ -1842,16 +1833,19 @@ device_writev_trap (mach_device_t device, dev_mode_t mode,
 }
 
 kern_return_t
-ds_device_intr_enable(ipc_port_t master_port, int line, char status)
+ds_device_intr_ack (device_t dev, ipc_port_t receive_port)
 {
-#ifdef MACH_XEN
-  return D_INVALID_OPERATION;
-#else	/* MACH_XEN */
-  if (master_port != master_device_port)
+  mach_device_t mdev = dev->emul_data;
+
+  /* Refuse if device is dead or not completely open.  */
+  if (dev == DEVICE_NULL)
+    return D_NO_SUCH_DEVICE;
+
+  /* Must be called on the irq device only */
+  if (! name_equal(mdev->dev_ops->d_name, 3, "irq"))
     return D_INVALID_OPERATION;
 
-  return user_intr_enable(line, status);
-#endif	/* MACH_XEN */
+  return irq_acknowledge(receive_port);
 }
 
 struct device_emulation_ops mach_device_emulation_ops =
diff --git a/device/ds_routines.h b/device/ds_routines.h
index e9f115fc..c0543cbc 100644
--- a/device/ds_routines.h
+++ b/device/ds_routines.h
@@ -83,7 +83,4 @@ io_return_t ds_device_writev_trap(
 	io_buf_vec_t 	*iovec,
 	vm_size_t 	count);
 
-/* XXX arch-specific */
-extern ipc_port_t intr_rcv_ports[16];
-
 #endif	/* DS_ROUTINES_H */
diff --git a/device/intr.c b/device/intr.c
index 1e9ab898..16b2c404 100644
--- a/device/intr.c
+++ b/device/intr.c
@@ -1,119 +1,110 @@
+/*
+ * Copyright (C) 2020 Free Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
 #include <device/intr.h>
-#include <device/ds_routines.h>
-#include <kern/queue.h>
+#include <device/device_types.h>
+#include <device/device_port.h>
+#include <device/notify.h>
 #include <kern/printf.h>
 #include <machine/spl.h>
+#include <machine/irq.h>
+#include <ipc/ipc_space.h>
 
 #ifndef MACH_XEN
 
-static boolean_t deliver_intr (int line, ipc_port_t dest_port);
-
-static queue_head_t intr_queue;
-/* The total number of unprocessed interrupts. */
-static int tot_num_intr;
-
-static struct intr_entry *
-search_intr (int line, ipc_port_t dest)
-{
-  struct intr_entry *e;
-  queue_iterate (&intr_queue, e, struct intr_entry *, chain)
-    {
-      if (e->dest == dest && e->line == line)
-	return e;
-    }
-  return NULL;
-}
+queue_head_t main_intr_queue;
+static boolean_t deliver_intr (int id, ipc_port_t dst_port);
 
-static struct intr_entry *
-search_intr_line (int line)
+static user_intr_t *
+search_intr (struct irqdev *dev, ipc_port_t dst_port)
 {
-  struct intr_entry *e;
-  queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+  user_intr_t *e;
+  queue_iterate (dev->intr_queue, e, user_intr_t *, chain)
     {
-      if (e->line == line &&
-	  (e->dest != MACH_PORT_NULL
-	   && e->dest->ip_references != 1
-	   && e->unacked_interrupts))
+      if (e->dst_port == dst_port)
 	return e;
     }
   return NULL;
 }
 
-kern_return_t user_intr_enable (int line, char status)
+kern_return_t
+irq_acknowledge (ipc_port_t receive_port)
 {
-  struct intr_entry *e;
-  kern_return_t ret = D_SUCCESS;
+  user_intr_t *e;
+  kern_return_t ret;
 
   spl_t s = splhigh ();
-  /* FIXME: Use search_intr instead once we get the delivery port from ds_device_intr_enable, and get rid of search_intr_line */
-  e = search_intr_line (line);
+  e = search_intr (&irqtab, receive_port);
 
   if (!e)
-    printf("didn't find user intr for interrupt %d!?\n", line);
-  else if (status)
-  {
-    if (!e->unacked_interrupts)
-      ret = D_INVALID_OPERATION;
-    else
-      e->unacked_interrupts--;
-  }
+    printf("didn't find user intr for interrupt !?\n");
   else
-  {
-    e->unacked_interrupts++;
-    if (!e->unacked_interrupts)
     {
-      ret = D_INVALID_OPERATION;
-      e->unacked_interrupts--;
+      if (!e->n_unacked)
+        ret = D_INVALID_OPERATION;
+      else
+        e->n_unacked--;
     }
-  }
   splx (s);
 
   if (ret)
     return ret;
 
-  if (status)
-    /* TODO: better name for generic-to-arch-specific call */
-    __enable_irq (line);
-  else
-    __disable_irq (line);
+  if (irqtab.irqdev_ack)
+    (*(irqtab.irqdev_ack)) (&irqtab, e->id);
+
+  __enable_irq (irqtab.irq[e->id]);
+
   return D_SUCCESS;
 }
 
 /* This function can only be used in the interrupt handler. */
 static void
-queue_intr (int line, user_intr_t *e)
+queue_intr (struct irqdev *dev, int id, user_intr_t *e)
 {
   /* Until userland has handled the IRQ in the driver, we have to keep it
    * disabled. Level-triggered interrupts would keep raising otherwise. */
-  __disable_irq (line);
+  __disable_irq (dev->irq[id]);
 
   spl_t s = splhigh ();
-  e->unacked_interrupts++;
+  e->n_unacked++;
   e->interrupts++;
-  tot_num_intr++;
+  dev->tot_num_intr++;
   splx (s);
 
   thread_wakeup ((event_t) &intr_thread);
 }
 
-int deliver_user_intr (int line, user_intr_t *intr)
+int
+deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e)
 {
   /* The reference of the port was increased
    * when the port was installed.
    * If the reference is 1, it means the port should
    * have been destroyed and I destroy it now. */
-  if (intr->dest
-      && intr->dest->ip_references == 1)
+  if (e->dst_port
+      && e->dst_port->ip_references == 1)
     {
-      printf ("irq handler %d: release a dead delivery port %p entry %p\n", line, intr->dest, intr);
-      ipc_port_release (intr->dest);
-      intr->dest = MACH_PORT_NULL;
+      printf ("irq handler [%d]: release a dead delivery port %p entry %p\n", id, e->dst_port, e);
+      ipc_port_release (e->dst_port);
+      e->dst_port = MACH_PORT_NULL;
       thread_wakeup ((event_t) &intr_thread);
       return 0;
     }
   else
     {
-      queue_intr (line, intr);
+      queue_intr (dev, id, e);
       return 1;
     }
 }
@@ -122,37 +113,32 @@ int deliver_user_intr (int line, user_intr_t *intr)
  * This entry exists in the queue until
  * the corresponding interrupt port is removed.*/
 user_intr_t *
-insert_intr_entry (int line, ipc_port_t dest)
+insert_intr_entry (struct irqdev *dev, int id, ipc_port_t dst_port)
 {
-  struct intr_entry *e, *new, *ret;
+  user_intr_t *e, *new, *ret;
   int free = 0;
 
-  new = (struct intr_entry *) kalloc (sizeof (*new));
+  new = (user_intr_t *) kalloc (sizeof (*new));
   if (new == NULL)
     return NULL;
 
   /* check whether the intr entry has been in the queue. */
   spl_t s = splhigh ();
-  e = search_intr (line, dest);
+  e = search_intr (dev, dst_port);
   if (e)
     {
-      printf ("the interrupt entry for line %d and port %p has already been inserted\n", line, dest);
+      printf ("the interrupt entry for irq[%d] and port %p has already been inserted\n", id, dst_port);
       free = 1;
       ret = NULL;
       goto out;
     }
-  printf("irq handler %d: new delivery port %p entry %p\n", line, dest, new);
+  printf("irq handler [%d]: new delivery port %p entry %p\n", id, dst_port, new);
   ret = new;
-  new->line = line;
-  new->dest = dest;
+  new->id = id;
+  new->dst_port = dst_port;
   new->interrupts = 0;
 
-  /* For now netdde calls device_intr_enable once after registration. Assume
-   * it does so for now. When we move to IRQ acknowledgment convention we will
-   * change this. */
-  new->unacked_interrupts = 1;
-
-  queue_enter (&intr_queue, new, struct intr_entry *, chain);
+  queue_enter (dev->intr_queue, new, user_intr_t *, chain);
 out:
   splx (s);
   if (free)
@@ -163,10 +149,10 @@ out:
 void
 intr_thread (void)
 {
-  struct intr_entry *e;
-  int line;
-  ipc_port_t dest;
-  queue_init (&intr_queue);
+  user_intr_t *e;
+  int id;
+  ipc_port_t dst_port;
+  queue_init (&main_intr_queue);
   
   for (;;)
     {
@@ -176,11 +162,11 @@ intr_thread (void)
       spl_t s = splhigh ();
 
       /* Check for aborted processes */
-      queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+      queue_iterate (&main_intr_queue, e, user_intr_t *, chain)
 	{
-	  if ((!e->dest || e->dest->ip_references == 1) && e->unacked_interrupts)
+	  if ((!e->dst_port || e->dst_port->ip_references == 1) && e->n_unacked)
 	    {
-	      printf ("irq handler %d: release dead delivery %d unacked irqs port %p entry %p\n", e->line, e->unacked_interrupts, e->dest, e);
+	      printf ("irq handler [%d]: release dead delivery %d unacked irqs port %p entry %p\n", e->id, e->n_unacked, e->dst_port, e);
 	      /* The reference of the port was increased
 	       * when the port was installed.
 	       * If the reference is 1, it means the port should
@@ -188,24 +174,24 @@ intr_thread (void)
 	       * handling can trigger, and we will cleanup later after the Linux
 	       * handler is cleared. */
 	      /* TODO: rather immediately remove from Linux handler */
-	      while (e->unacked_interrupts)
+	      while (e->n_unacked)
 	      {
-		__enable_irq(e->line);
-		e->unacked_interrupts--;
+		__enable_irq (irqtab.irq[e->id]);
+		e->n_unacked--;
 	      }
 	    }
 	}
 
       /* Now check for interrupts */
-      while (tot_num_intr)
+      while (irqtab.tot_num_intr)
 	{
 	  int del = 0;
 
-	  queue_iterate (&intr_queue, e, struct intr_entry *, chain)
+	  queue_iterate (&main_intr_queue, e, user_intr_t *, chain)
 	    {
 	      /* if an entry doesn't have dest port,
 	       * we should remove it. */
-	      if (e->dest == MACH_PORT_NULL)
+	      if (e->dst_port == MACH_PORT_NULL)
 		{
 		  clear_wait (current_thread (), 0, 0);
 		  del = 1;
@@ -215,13 +201,13 @@ intr_thread (void)
 	      if (e->interrupts)
 		{
 		  clear_wait (current_thread (), 0, 0);
-		  line = e->line;
-		  dest = e->dest;
+		  id = e->id;
+		  dst_port = e->dst_port;
 		  e->interrupts--;
-		  tot_num_intr--;
+		  irqtab.tot_num_intr--;
 
 		  splx (s);
-		  deliver_intr (line, dest);
+		  deliver_intr (id, dst_port);
 		  s = splhigh ();
 		}
 	    }
@@ -229,16 +215,16 @@ intr_thread (void)
 	  /* remove the entry without dest port from the queue and free it. */
 	  if (del)
 	    {
-	      assert (!queue_empty (&intr_queue));
-	      queue_remove (&intr_queue, e, struct intr_entry *, chain);
-	      if (e->unacked_interrupts)
-		printf("irq handler %d: still %d unacked irqs in entry %p\n", e->line, e->unacked_interrupts, e);
-	      while (e->unacked_interrupts)
+	      assert (!queue_empty (&main_intr_queue));
+	      queue_remove (&main_intr_queue, e, user_intr_t *, chain);
+	      if (e->n_unacked)
+		printf("irq handler [%d]: still %d unacked irqs in entry %p\n", e->id, e->n_unacked, e);
+	      while (e->n_unacked)
 	      {
-		__enable_irq(e->line);
-		e->unacked_interrupts--;
+		__enable_irq (irqtab.irq[e->id]);
+		e->n_unacked--;
 	      }
-	      printf("irq handler %d: removed entry %p\n", e->line, e);
+	      printf("irq handler [%d]: removed entry %p\n", e->id, e);
 	      splx (s);
 	      kfree ((vm_offset_t) e, sizeof (*e));
 	      s = splhigh ();
@@ -250,11 +236,11 @@ intr_thread (void)
 }
 
 static boolean_t
-deliver_intr (int line, ipc_port_t dest_port)
+deliver_intr (int id, ipc_port_t dst_port)
 {
   ipc_kmsg_t kmsg;
   device_intr_notification_t *n;
-  mach_port_t dest = (mach_port_t) dest_port;
+  mach_port_t dest = (mach_port_t) dst_port;
 
   if (dest == MACH_PORT_NULL)
     return FALSE;
@@ -285,11 +271,12 @@ deliver_intr (int line, ipc_port_t dest_port)
   t->msgt_unused = 0;
 
   n->intr_header.msgh_remote_port = dest;
-  n->line = line;
+  n->id = id;
 
-  ipc_port_copy_send (dest_port);
+  ipc_port_copy_send (dst_port);
   ipc_mqueue_send_always(kmsg);
 
   return TRUE;
 }
+
 #endif	/* MACH_XEN */
diff --git a/device/intr.h b/device/intr.h
index 48843cf0..5349ef03 100644
--- a/device/intr.h
+++ b/device/intr.h
@@ -1,37 +1,62 @@
-#ifndef __INTR_H__
+/*
+ * Copyright (C) 2020 Free Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
 
+#ifndef __INTR_H__
 #define __INTR_H__
 
-#include <device/device_types.h>
+#ifndef MACH_XEN
+
+#include <mach/kern_return.h>
+#include <mach/port.h>
 #include <kern/queue.h>
-#include <device/notify.h>
+#include <ipc/ipc_port.h>
+#include <device/conf.h>
+
+#define DEVICE_NOTIFY_MSGH_SEQNO 0
 
-typedef struct intr_entry
-{
+#include <sys/types.h>
+
+struct irqdev;
+#include <machine/irq.h>
+
+typedef struct {
   queue_chain_t chain;
-  ipc_port_t dest;
-  int line;
-  int interrupts;		/* The number of interrupts occur since last run of intr_thread. */
-  int unacked_interrupts;	/* Number of times irqs were disabled for this */
+  int interrupts; /* Number of interrupts occurred since last run of intr_thread */
+  int n_unacked;  /* Number of times irqs were disabled for this */
+  ipc_port_t dst_port; /* Notification port */
+  int id; /* Mapping to machine dependent irq_t array elem */
 } user_intr_t;
 
-#define DEVICE_NOTIFY_MSGH_SEQNO 0
-
-int install_user_intr_handler (unsigned int line,
-					unsigned long flags,
-					user_intr_t *user_intr);
+struct irqdev {
+  char *name;
+  void (*irqdev_ack)(struct irqdev *dev, int id);
 
-/* Returns 0 if action should be removed */
-int deliver_user_intr (int line, user_intr_t *intr);
+  queue_head_t *intr_queue;
+  int tot_num_intr; /* Total number of unprocessed interrupts */
 
-user_intr_t *insert_intr_entry (int line, ipc_port_t dest);
+  /* Machine dependent */
+  irq_t irq[NINTR];
+};
 
-/* TODO: should rather take delivery port */
-kern_return_t user_intr_enable (int line, char status);
+extern queue_head_t main_intr_queue;
+extern int install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags, user_intr_t *e);
+extern int deliver_user_intr (struct irqdev *dev, int id, user_intr_t *e);
+extern user_intr_t *insert_intr_entry (struct irqdev *dev, int id, ipc_port_t receive_port);
 
 void intr_thread (void);
+kern_return_t irq_acknowledge (ipc_port_t receive_port);
 
-void __disable_irq(unsigned int);
-void __enable_irq(unsigned int);
+#endif /* MACH_XEN */
 
 #endif
diff --git a/i386/Makefrag.am b/i386/Makefrag.am
index f38c0785..59571416 100644
--- a/i386/Makefrag.am
+++ b/i386/Makefrag.am
@@ -102,6 +102,8 @@ libkernel_a_SOURCES += \
 	i386/i386/io_perm.c \
 	i386/i386/io_perm.h \
 	i386/i386/ipl.h \
+	i386/i386/irq.c \
+	i386/i386/irq.h \
 	i386/i386/ktss.c \
 	i386/i386/ktss.h \
 	i386/i386/kttd_interface.c \
diff --git a/i386/i386/irq.c b/i386/i386/irq.c
new file mode 100644
index 00000000..c65d2ea2
--- /dev/null
+++ b/i386/i386/irq.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 1995 Shantanu Goel
+ * Copyright (C) 2020 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <i386/irq.h>
+#include <device/intr.h>
+#include <mach/kern_return.h>
+#include <kern/queue.h>
+#include <machine/machspl.h>
+
+extern queue_head_t main_intr_queue;
+
+static void
+irq_eoi (struct irqdev *dev, int id)
+{
+  /* TODO EOI(dev->irq[id]) */
+}
+
+static unsigned int ndisabled_irq[NINTR];
+
+void
+__disable_irq (irq_t irq_nr)
+{
+  assert (irq_nr < NINTR);
+
+  spl_t s = splhigh();
+  ndisabled_irq[irq_nr]++;
+  assert (ndisabled_irq[irq_nr] > 0);
+  if (ndisabled_irq[irq_nr] == 1)
+    mask_irq (irq_nr);
+  splx(s);
+}
+
+void
+__enable_irq (irq_t irq_nr)
+{
+  assert (irq_nr < NINTR);
+
+  spl_t s = splhigh();
+  assert (ndisabled_irq[irq_nr] > 0);
+  ndisabled_irq[irq_nr]--;
+  if (ndisabled_irq[irq_nr] == 0)
+    unmask_irq (irq_nr);
+  splx(s);
+}
+
+struct irqdev irqtab = {
+  "irq", irq_eoi, &main_intr_queue, 0,
+  {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
+};
+
diff --git a/i386/i386/irq.h b/i386/i386/irq.h
new file mode 100644
index 00000000..d48a8e92
--- /dev/null
+++ b/i386/i386/irq.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 Free Software Foundation, Inc.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+#ifndef _I386_IRQ_H
+#define _I386_IRQ_H
+
+#include <i386/pic.h>
+
+typedef unsigned int irq_t;
+
+void __enable_irq (irq_t irq);
+void __disable_irq (irq_t irq);
+
+extern struct irqdev irqtab;
+
+#endif
diff --git a/i386/i386/pic.c b/i386/i386/pic.c
index 0feebc6f..04b0471a 100644
--- a/i386/i386/pic.c
+++ b/i386/i386/pic.c
@@ -185,3 +185,57 @@ intnull(int unit_dev)
 	}
 
 }
+
+/*
+ * Mask a PIC IRQ.
+ */
+inline void
+mask_irq (unsigned int irq_nr)
+{
+	int new_pic_mask = curr_pic_mask | 1 << irq_nr;
+
+	if (curr_pic_mask != new_pic_mask)
+	{
+		curr_pic_mask = new_pic_mask;
+		if (irq_nr < 8)
+		{
+			outb (PIC_MASTER_OCW, curr_pic_mask & 0xff);
+		}
+		else
+		{
+			outb (PIC_SLAVE_OCW, curr_pic_mask >> 8);
+		}
+	}
+}
+
+/*
+ * Unmask a PIC IRQ.
+ */
+inline void
+unmask_irq (unsigned int irq_nr)
+{
+	int mask;
+	int new_pic_mask;
+
+	mask = 1 << irq_nr;
+	if (irq_nr >= 8)
+	{
+		mask |= 1 << 2;
+	}
+
+	new_pic_mask = curr_pic_mask & ~mask;
+
+	if (curr_pic_mask != new_pic_mask)
+	{
+		curr_pic_mask = new_pic_mask;
+		if (irq_nr < 8)
+		{
+			outb (PIC_MASTER_OCW, curr_pic_mask & 0xff);
+		}
+		else
+		{
+			outb (PIC_SLAVE_OCW, curr_pic_mask >> 8);
+		}
+	}
+}
+
diff --git a/i386/i386/pic.h b/i386/i386/pic.h
index 553c4bcc..01fc5ccc 100644
--- a/i386/i386/pic.h
+++ b/i386/i386/pic.h
@@ -180,6 +180,8 @@ WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 extern void picinit (void);
 extern int curr_pic_mask;
 extern void intnull(int unit);
+extern inline void mask_irq (unsigned int irq_nr);
+extern inline void unmask_irq (unsigned int irq_nr);
 #endif /* __ASSEMBLER__ */
 
 #endif	/* _I386_PIC_H_ */
diff --git a/i386/i386at/conf.c b/i386/i386at/conf.c
index fe7c7c09..ca5d0dfb 100644
--- a/i386/i386at/conf.c
+++ b/i386/i386at/conf.c
@@ -68,6 +68,9 @@
 #define hypcnname		"hyp"
 #endif	/* MACH_HYP */
 
+#include <device/intr.h>
+#define irqname			"irq"
+
 /*
  * List of devices - console must be at slot 0
  */
@@ -149,6 +152,11 @@ struct dev_ops	dev_name_list[] =
 	  nodev },
 #endif	/* MACH_HYP */
 
+        { irqname,      nulldev_open,   nulldev_close,    nulldev_read,
+          nulldev_write,nulldev_getstat,nulldev_setstat,  nomap,
+          nodev,        nulldev,        nulldev_portdeath,0,
+          nodev },
+
 };
 int	dev_name_count = sizeof(dev_name_list)/sizeof(dev_name_list[0]);
 
diff --git a/include/device/device.defs b/include/device/device.defs
index dca1be4e..ec4b5bf8 100644
--- a/include/device/device.defs
+++ b/include/device/device.defs
@@ -143,22 +143,20 @@ routine device_set_filter(
 	);
 
 routine device_intr_register(
-		master_port	: mach_port_t;
-	in	line		: int;
+		device		: device_t;
 	in	id		: int;
 	in	flags		: int;
 	in	receive_port	: mach_port_send_t
 	);
 
 /*
- *	enable/disable the specified line.
+ *	Acknowledge the specified interrupt notification.
  */
-/* XXX: Naming a function taht can disable something "xxx_enable" is confusing. */
-/* Is the disable part actually used at all? AIUI, the kernel IRQ handler
-should always disable the line; and the userspace driver only has to
-reenable it, after acknowledging and handling the interrupt...
-*/
-routine device_intr_enable(
-		master_port	: mach_port_t;
-		line		: int;
-		status		: char);
+/*
+ * When an IRQ happens and an intr notification is thus sent, the IRQ line
+ * is kept disabled until the notification is acknowledged with this RPC
+ */
+routine device_intr_ack(
+		device		: device_t;
+	in	receive_port	: mach_port_send_t);
+
diff --git a/include/device/notify.defs b/include/device/notify.defs
index ea374d26..7919b339 100644
--- a/include/device/notify.defs
+++ b/include/device/notify.defs
@@ -33,4 +33,4 @@ serverdemux device_intr_notify_server;
 
 simpleroutine device_intr_notify(
 		notify	: notify_port_t;
-		name	: int);
+		id	: int);
diff --git a/include/device/notify.h b/include/device/notify.h
index b6907b03..addf9114 100644
--- a/include/device/notify.h
+++ b/include/device/notify.h
@@ -26,7 +26,7 @@ typedef struct
 {
   mach_msg_header_t intr_header;
   mach_msg_type_t   intr_type;
-  int		    line;
+  int		    id;
 } device_intr_notification_t;
 
 #define DEVICE_INTR_NOTIFY 100
diff --git a/linux/dev/arch/i386/kernel/irq.c b/linux/dev/arch/i386/kernel/irq.c
index bc752013..d5070dd5 100644
--- a/linux/dev/arch/i386/kernel/irq.c
+++ b/linux/dev/arch/i386/kernel/irq.c
@@ -89,49 +89,6 @@ static struct linux_action *irq_action[16] =
   NULL, NULL, NULL, NULL
 };
 
-/*
- * Mask an IRQ.
- */
-static inline void
-mask_irq (unsigned int irq_nr)
-{
-  int new_pic_mask = curr_pic_mask | 1 << irq_nr;
-
-  if (curr_pic_mask != new_pic_mask)
-    {
-      curr_pic_mask = new_pic_mask;
-      if (irq_nr < 8)
-       outb (curr_pic_mask & 0xff, PIC_MASTER_OCW);
-      else
-       outb (curr_pic_mask >> 8, PIC_SLAVE_OCW);
-    }
-}
-
-/*
- * Unmask an IRQ.
- */
-static inline void
-unmask_irq (unsigned int irq_nr)
-{
-  int mask;
-  int new_pic_mask;
-
-  mask = 1 << irq_nr;
-  if (irq_nr >= 8)
-    mask |= 1 << 2;
-
-  new_pic_mask = curr_pic_mask & ~mask;
-
-  if (curr_pic_mask != new_pic_mask)
-    {
-      curr_pic_mask = new_pic_mask;
-      if (irq_nr < 8)
-	outb (curr_pic_mask & 0xff, PIC_MASTER_OCW);
-      else
-	outb (curr_pic_mask >> 8, PIC_SLAVE_OCW);
-    }
-}
-
 /*
  * Generic interrupt handler for Linux devices.
  * Set up a fake `struct pt_regs' then call the real handler.
@@ -157,7 +114,7 @@ linux_intr (int irq)
       // the current device. But I don't do it for now.
       if (action->user_intr)
 	{
-	  if (!deliver_user_intr(irq, action->user_intr))
+	  if (!deliver_user_intr(&irqtab, irq, action->user_intr))
 	  {
 	    *prev = action->next;
 	    linux_kfree(action);
@@ -184,43 +141,6 @@ linux_intr (int irq)
   intr_count--;
 }
 
-/* Count how many subsystems requested to disable each IRQ */
-static unsigned ndisabled_irq[NR_IRQS];
-
-/* These disable/enable IRQs for real after counting how many subsystems
- * requested that */
-void
-__disable_irq (unsigned int irq_nr)
-{
-  unsigned long flags;
-
-  assert (irq_nr < NR_IRQS);
-
-  save_flags (flags);
-  cli ();
-  ndisabled_irq[irq_nr]++;
-  assert (ndisabled_irq[irq_nr] > 0);
-  if (ndisabled_irq[irq_nr] == 1)
-    mask_irq (irq_nr);
-  restore_flags (flags);
-}
-
-void
-__enable_irq (unsigned int irq_nr)
-{
-  unsigned long flags;
-
-  assert (irq_nr < NR_IRQS);
-
-  save_flags (flags);
-  cli ();
-  assert (ndisabled_irq[irq_nr] > 0);
-  ndisabled_irq[irq_nr]--;
-  if (ndisabled_irq[irq_nr] == 0)
-    unmask_irq (irq_nr);
-  restore_flags (flags);
-}
-
 /* IRQ mask according to Linux drivers */
 static unsigned linux_pic_mask;
 
@@ -300,13 +220,15 @@ setup_x86_irq (int irq, struct linux_action *new)
 }
 
 int
-install_user_intr_handler (unsigned int irq, unsigned long flags,
+install_user_intr_handler (struct irqdev *dev, int id, unsigned long flags,
 			  user_intr_t *user_intr)
 {
   struct linux_action *action;
   struct linux_action *old;
   int retval;
 
+  unsigned int irq = dev->irq[id];
+
   assert (irq < 16);
 
   /* Test whether the irq handler has been set */
@@ -314,7 +236,7 @@ install_user_intr_handler (unsigned int irq, unsigned long flags,
   old = irq_action[irq];
   while (old)
     {
-      if (old->user_intr && old->user_intr->dest == user_intr->dest)
+      if (old->user_intr && old->user_intr->dst_port == user_intr->dst_port)
 	{
 	  printk ("The interrupt handler has already been installed on line %d", irq);
 	  return linux_to_mach_error (-EAGAIN);
-- 
2.25.1

>From ae39029b82490d89c1012e0c963f28ca55313427 Mon Sep 17 00:00:00 2001
From: Damien Zammit <dam...@zamaudio.com>
Date: Wed, 8 Jul 2020 21:26:12 +1000
Subject: [PATCH 2/2] mach: vm_allocate_contiguous RPC now allows physical
 alignment/limits

---
 i386/include/mach/i386/machine_types.defs |  9 ++++++
 include/mach/mach.defs                    | 35 ++++++++------------
 vm/vm_user.c                              | 39 +++++++++++++++++++----
 3 files changed, 54 insertions(+), 29 deletions(-)

diff --git a/i386/include/mach/i386/machine_types.defs b/i386/include/mach/i386/machine_types.defs
index 6ff93dbd..f3fbc314 100755
--- a/i386/include/mach/i386/machine_types.defs
+++ b/i386/include/mach/i386/machine_types.defs
@@ -58,4 +58,13 @@ type natural_t = uint32_t;
  */
 type integer_t = int32_t;
 
+/*
+ * Physical address size
+ */
+#ifdef	PAE
+type phys_addr_t = uint64_t;
+#else
+type phys_addr_t = uint32_t;
+#endif
+
 #endif	/* _MACHINE_MACHINE_TYPES_DEFS_ */
diff --git a/include/mach/mach.defs b/include/mach/mach.defs
index 77cc7d49..a024cf75 100644
--- a/include/mach/mach.defs
+++ b/include/mach/mach.defs
@@ -723,34 +723,25 @@ skip;	/* old host_fpa_counters_reset */
  *	This routine is created for allocating DMA buffers.
  *	We are going to get a contiguous physical memory
  *	and its physical address in addition to the virtual address.
+ *	We can specify physical memory range limits and alignment.
+ *	NB:
+ *	  pmax is defined as the byte after the maximum address,
+ *	  eg 0x100000000 for 4GiB limit.
  */
-
- /* XXX
- This RPC lacks a few additional constraints like boundaries, alignment
-and maybe phase. We may not use them now, but they're important for
-portability (e.g. if GNU Mach supports PAE, drivers that can't use
-physical memory beyond the 4 GiB limit must be able to express it).
-
-> What do you mean by "phase"?
-
-Offset from the alignment. But I don't think it's useful at all in this
-case. Minimum and maximum addresses and alignment should do. Maybe
-boundary crossing but usually, specifying the right alignment and size
-is enough.
-
-For upstream
-inclusion, we need to do it properly: the RPC should return a special
-memory object (similar to device_map() ), which can then be mapped into
-the process address space with vm_map() like any other memory object.
-
-phys_address_t?
+/* XXX
+ * Future work: the RPC should return a special
+ * memory object (similar to device_map() ), which can then be mapped into
+ * the process address space with vm_map() like any other memory object.
  */
 routine vm_allocate_contiguous(
 		host_priv	: host_priv_t;
 		target_task	: vm_task_t;
 	out	vaddr		: vm_address_t;
-	out	paddr		: vm_address_t;
-		size		: vm_size_t);
+	out	paddr		: phys_addr_t;
+		size		: vm_size_t;
+		pmin		: phys_addr_t;
+		pmax		: phys_addr_t;
+		palign		: phys_addr_t);
 
 /*
  *	There is no more room in this interface for additional calls.
diff --git a/vm/vm_user.c b/vm/vm_user.c
index 1789dbfa..09a35ab2 100644
--- a/vm/vm_user.c
+++ b/vm/vm_user.c
@@ -532,17 +532,24 @@ kern_return_t vm_msync(
 	return vm_map_msync(map, (vm_offset_t) address, size, sync_flags);
 }
 
-kern_return_t vm_allocate_contiguous(host_priv, map, result_vaddr, result_paddr, size)
-	host_t			host_priv;
-	vm_map_t		map;
-	vm_address_t		*result_vaddr;
-	vm_address_t		*result_paddr;
-	vm_size_t		size;
+/* TODO: respect physical alignment (palign)
+ *       and minimum physical address (pmin)
+ */
+kern_return_t vm_allocate_contiguous(
+	host_t			host_priv,
+	vm_map_t		map,
+	vm_address_t		*result_vaddr,
+	phys_addr_t		*result_paddr,
+	vm_size_t		size,
+	phys_addr_t		pmin,
+	phys_addr_t		pmax,
+	phys_addr_t		palign)
 {
 	vm_size_t		alloc_size;
 	unsigned int		npages;
 	unsigned int		i;
 	unsigned int		order;
+	unsigned int		selector;
 	vm_page_t		pages;
 	vm_object_t		object;
 	kern_return_t		kr;
@@ -554,6 +561,24 @@ kern_return_t vm_allocate_contiguous(host_priv, map, result_vaddr, result_paddr,
 	if (map == VM_MAP_NULL)
 		return KERN_INVALID_TASK;
 
+	/* FIXME */
+	if (pmin != 0)
+		return KERN_INVALID_ARGUMENT;
+
+	/* FIXME */
+	if (palign == 0)
+		palign = PAGE_SIZE;
+
+	selector = VM_PAGE_SEL_DMA;
+	if (pmax > VM_PAGE_DMA_LIMIT)
+#ifdef VM_PAGE_DMA32_LIMIT
+		selector = VM_PAGE_SEL_DMA32;
+	if (pmax > VM_PAGE_DMA32_LIMIT)
+#endif
+		selector = VM_PAGE_SEL_DIRECTMAP;
+	if (pmax > VM_PAGE_DIRECTMAP_LIMIT)
+		selector = VM_PAGE_SEL_HIGHMEM;
+
 	size = vm_page_round(size);
 
 	if (size == 0)
@@ -573,7 +598,7 @@ kern_return_t vm_allocate_contiguous(host_priv, map, result_vaddr, result_paddr,
 	alloc_size = (1 << (order + PAGE_SHIFT));
 	npages = vm_page_atop(alloc_size);
 
-	pages = vm_page_grab_contig(alloc_size, VM_PAGE_SEL_DIRECTMAP);
+	pages = vm_page_grab_contig(alloc_size, selector);
 
 	if (pages == NULL) {
 		vm_object_deallocate(object);
-- 
2.25.1

Reply via email to