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