summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libdiskfs/=exc.c147
1 files changed, 93 insertions, 54 deletions
diff --git a/libdiskfs/=exc.c b/libdiskfs/=exc.c
index 76cb1bcc..d272a08d 100644
--- a/libdiskfs/=exc.c
+++ b/libdiskfs/=exc.c
@@ -1,5 +1,5 @@
/*
- Copyright (C) 1994 Free Software Foundation
+ Copyright (C) 1994, 1995 Free Software Foundation
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
@@ -17,46 +17,93 @@
#include "priv.h"
-#define EXC_TABLE_SIZE 1024 /* Ack!XXX -- should be dynamically sized! */
-/* This is the table of addresses for which we should report faults
- back to the faulting thread. */
-static struct
+/* List of preempters that are around. */
+struct preempt_record
{
+ struct preempt_record *next;
+ struct hurd_signal_preempt preempter1, preempter2;
struct pager *p;
- vm_offset_t pager_offset;
- void *offset;
- long length;
-} memory_fault_table[EXC_TABLE_SIZE];
+ vm_address_t off;
+ void *addr;
+ long len;
+};
+
+static struct preempt_record *preempt_list;
+static spin_lock_t preempt_list_lock = SPIN_LOCK_INITIALIZER;
+
+/* This special signal handler is run anytime we have taken a fault on
+ a page that we map (registered through
+ diskfs_register_memory_fault_area). What we do is just longjmp to the
+ context saved on the stack by diskfs_catch_exception. */
+static void
+special_segv_handler (int code, int subcode, int error)
+{
+ struct preempt_record *rec;
+ struct thread_stuff *stack_record;
+
+ /* SUBCODE is the address and ERROR is the error returned by the
+ pager through the kernel. But because there is an annoying
+ kernel bug (or rather unimplemented feature) the errors are not
+ actually passed back. So we have to scan the list of preempt
+ records and find the one that got us here, and then query the
+ pager to find out what error it gave the kernel. */
+ spin_lock (&preempt_list_lock);
+ for (rec = preempt_list; rec; rec = rec->next)
+ if ((void *)subcode >= rec->addr
+ && (void *)subcode < rec->addr + rec->len)
+ break;
+ assert (rec);
+ spin_unlock (&preempt_list_lock);
+ error = pager_get_error (rec->p, rec->off + (void *)subcode - rec->addr);
+
+ /* Now look up the stack record left by diskfs_catch_exception, consuming it
+ on the way */
+ stack_record = (struct thread_stuff *) cthread_data (cthread_self ());
+ assert (stack_record);
+ cthread_set_data (cthread_self (), (any_t)stack_record->link);
+
+ /* And return to it... */
+ longjmp (&stack_record->buf, error);
-static spin_lock_t memory_fault_lock;
+ abort ();
+}
+
+/* Return a signal handler for a thread which has faulted inside a
+ region registered as expecting such faults. This routine runs
+ inside the library's signal thread, and accordingly must be
+ careful. */
+static sighandler_t
+segv_preempter (thread_t thread, int signo,
+ long int sigcode, int sigerror)
+{
+ /* Just assume that everything is cool (how could it not be?)
+ and return our special handler above. */
+ return special_segv_handler;
+}
/* Mark the memory at ADDR continuing for LEN bytes as mapped from pager P
- at offset OFF. Call when vm_map-ing part of the disk.
- CAVEAT: addr must not be zero. */
+ at offset OFF. Call when vm_map-ing part of the disk. */
void
diskfs_register_memory_fault_area (struct pager *p,
vm_address_t off,
void *addr,
long len)
{
- int i;
-
- assert (addr);
-
- spin_lock (&memory_fault_lock);
-
- for (i = 0; i < EXC_TABLE_SIZE; i++)
- if (!memory_fault_table[i].offset)
- {
- memory_fault_table[i].p = p;
- memory_fault_table[i].pager_offset = off;
- memory_fault_table[i].offset = addr;
- memory_fault_table[i].length = len;
- spin_unlock (&memory_fault_lock);
- return;
- }
-
- assert (0);
+ struct preempt_record *rec = malloc (sizeof (struct preempt_record));
+
+ hurd_preempt_signals (&rec->preempter1, SIGSEGV, addr, addr + len,
+ segv_preempter);
+ hurd_preempt_signals (&rec->preempter1, SIGBUS, addr, addr + len,
+ segv_preempter);
+ rec->p = p;
+ rec->off = off;
+ rec->addr = addr;
+ rec->len = len;
+
+ spin_lock (&preempt_list_lock);
+ rec->next = preempt_list;
+ preempt_list = rec;
+ spin_unlock (&preempt_list_lock);
}
/* Mark the memory at ADDR continuing for LEN bytes as no longer
@@ -65,19 +112,25 @@ void
diskfs_unregister_memory_fault_area (void *addr,
long len)
{
- int i;
-
- assert (addr);
-
- spin_lock (&memory_fault_lock);
- for (i = 0; i < EXC_TABLE_SIZE; i++)
- if (memory_fault_table[i].offset == addr
- && memory_fault_table[i].length == len)
+ struct preempt_record *rec, **prevp;
+
+ spin_lock (&preempt_list_lock);
+ for (rec = preempt_list, prevp = &preempt_list;
+ rec;
+ rec = rec->next, prevp = &rec->next)
+ if (rec->addr == addr && rec->len == len)
{
- memory_fault_table[i].offset = 0;
- spin_unlock (&memory_fault_lock);
+ /* This is it, make it go away. */
+ *prevp = rec->next;
+ spin_unlock (&preempt_list_lock);
+ hurd_unpreempt_signals (&rec->preempter1, SIGSEGV);
+ hurd_unpreempt_signals (&rec->preempter2, SIGBUS);
+ free (rec);
return;
}
+ spin_unlock (&preempt_list_lock);
+
+ /* Not found */
assert (0);
}
@@ -85,18 +138,4 @@ diskfs_unregister_memory_fault_area (void *addr,
void
init_exceptions ()
{
- int i;
-
- for (i = 0; i < EXC_TABLE_SIZE; i++)
- memory_fault_table[i].offset = 0;
- spin_lock_init (&memory_fault_lock);
-
-#if notdebugging
- mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &excport);
- mach_port_insert_right (mach_task_self (), excport, excport,
- MACH_MSG_TYPE_MAKE_SEND);
- task_get_special_port (mach_task_self (), TASK_EXCEPTION_PORT, &oldexcport);
- task_set_special_port (mach_task_self (), TASK_EXCEPTION_PORT, excport);
- mach_port_deallocate (mach_task_self (), excport);
-#endif
}