summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael I. Bushnell <mib@gnu.org>1996-04-11 21:52:43 +0000
committerMichael I. Bushnell <mib@gnu.org>1996-04-11 21:52:43 +0000
commita67f3054c05106551ebb2e26806631b22cb241db (patch)
treeef5819b1b2b3a142797ddeff66eace7b3479231a
parent233372878bf8bdeed89d5839f09842e06c0ec21b (diff)
Initial revision
-rw-r--r--libpager/pager-memcpy.c101
1 files changed, 101 insertions, 0 deletions
diff --git a/libpager/pager-memcpy.c b/libpager/pager-memcpy.c
new file mode 100644
index 00000000..86f1e397
--- /dev/null
+++ b/libpager/pager-memcpy.c
@@ -0,0 +1,101 @@
+/* Fault-safe copy into or out of pager-backed memory.
+ Copyright (C) 1996 Free Software Foundation, Inc.
+ Written by Roland McGrath.
+
+ 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. */
+
+#include "pager.h"
+#include <hurd/sigpreempt.h>
+#include <assert.h>
+
+/* Try to copy *SIZE bytes between the region OTHER points to
+ and the region at OFFSET in the pager indicated by PAGER and MEMOBJ.
+ If PROT is VM_PROT_READ, copying is from the pager to OTHER;
+ if PROT contains VM_PROT_WRITE, copying is from OTHER into the pager.
+ *SIZE is always filled in the actual number of bytes successfully copied.
+ Returns an error code if the pager-backed memory faults;
+ if there is no fault, returns 0 and *SIZE will be unchanged. */
+
+error_t
+pager_memcpy (struct pager *pager, memory_object_t memobj,
+ vm_offset_t offset, void *other, size_t *size,
+ vm_prot_t prot)
+{
+ vm_address_t window = 0;
+ vm_size_t windowsize = 8 * vm_page_size;
+ size_t to_copy = *size;
+ error_t err;
+
+ error_t copy (struct hurd_signal_preempter *preempter)
+ {
+ while (to_copy > 0)
+ {
+ size_t pageoff = offset & (vm_page_size - 1);
+
+ if (window)
+ /* Deallocate the old window. */
+ vm_deallocate (mach_task_self (), window, windowsize);
+
+ /* Map in and copy a standard-sized window, unless that is
+ more than the total left to be copied. */
+
+ if (windowsize > pageoff + to_copy)
+ windowsize = pageoff + to_copy;
+
+ window = 0;
+ err = vm_map (mach_task_self (), &window, windowsize, 0, 1,
+ memobj, offset - pageoff, 0,
+ prot, prot, VM_INHERIT_NONE);
+ if (err)
+ return;
+
+ /* Realign the fault preempter for the new mapping window. */
+ preempter->first = window;
+ preempter->last = window + windowsize;
+
+ if (prot == VM_PROT_READ)
+ memcpy (other, (const void *) window + pageoff,
+ windowsize - pageoff);
+ else
+ memcpy ((void *) window + pageoff, other, windowsize - pageoff);
+
+ offset += windowsize - pageoff;
+ other += windowsize - pageoff;
+ to_copy -= windowsize - pageoff;
+ }
+ return 0;
+ }
+
+ jmp_buf buf;
+ void fault (int signo, long int sigcode, struct sigcontext *scp)
+ {
+ assert (scp->sc_error == EKERN_MEMORY_ERROR);
+ err = pager_get_error (pager, sigcode - window + offset);
+ to_copy -= sigcode - window;
+ longjmp (buf, 1);
+ }
+
+ if (setjmp (buf) == 0)
+ hurd_catch_signal (sigmask (SIGSEGV) | sigmask (SIGBUS),
+ window, window + windowsize,
+ &copy, (sighandler_t) &fault);
+
+ if (window)
+ vm_deallocate (mach_task_self (), window, windowsize);
+
+ *size -= to_copy;
+
+ return err;
+}