|
@@ -58,9 +58,10 @@ static pte_t *maybe_map(unsigned long virt, int is_write)
|
|
static int do_op_one_page(unsigned long addr, int len, int is_write,
|
|
static int do_op_one_page(unsigned long addr, int len, int is_write,
|
|
int (*op)(unsigned long addr, int len, void *arg), void *arg)
|
|
int (*op)(unsigned long addr, int len, void *arg), void *arg)
|
|
{
|
|
{
|
|
|
|
+ jmp_buf buf;
|
|
struct page *page;
|
|
struct page *page;
|
|
pte_t *pte;
|
|
pte_t *pte;
|
|
- int n;
|
|
|
|
|
|
+ int n, faulted;
|
|
|
|
|
|
pte = maybe_map(addr, is_write);
|
|
pte = maybe_map(addr, is_write);
|
|
if (pte == NULL)
|
|
if (pte == NULL)
|
|
@@ -70,82 +71,62 @@ static int do_op_one_page(unsigned long addr, int len, int is_write,
|
|
addr = (unsigned long) kmap_atomic(page, KM_UML_USERCOPY) +
|
|
addr = (unsigned long) kmap_atomic(page, KM_UML_USERCOPY) +
|
|
(addr & ~PAGE_MASK);
|
|
(addr & ~PAGE_MASK);
|
|
|
|
|
|
- n = (*op)(addr, len, arg);
|
|
|
|
|
|
+ current->thread.fault_catcher = &buf;
|
|
|
|
+
|
|
|
|
+ faulted = UML_SETJMP(&buf);
|
|
|
|
+ if (faulted == 0)
|
|
|
|
+ n = (*op)(addr, len, arg);
|
|
|
|
+ else
|
|
|
|
+ n = -1;
|
|
|
|
+
|
|
|
|
+ current->thread.fault_catcher = NULL;
|
|
|
|
|
|
kunmap_atomic(page, KM_UML_USERCOPY);
|
|
kunmap_atomic(page, KM_UML_USERCOPY);
|
|
|
|
|
|
return n;
|
|
return n;
|
|
}
|
|
}
|
|
|
|
|
|
-static void do_buffer_op(void *jmpbuf, void *arg_ptr)
|
|
|
|
|
|
+static int buffer_op(unsigned long addr, int len, int is_write,
|
|
|
|
+ int (*op)(unsigned long, int, void *), void *arg)
|
|
{
|
|
{
|
|
- va_list args;
|
|
|
|
- unsigned long addr;
|
|
|
|
- int len, is_write, size, remain, n;
|
|
|
|
- int (*op)(unsigned long, int, void *);
|
|
|
|
- void *arg;
|
|
|
|
- int *res;
|
|
|
|
-
|
|
|
|
- va_copy(args, *(va_list *)arg_ptr);
|
|
|
|
- addr = va_arg(args, unsigned long);
|
|
|
|
- len = va_arg(args, int);
|
|
|
|
- is_write = va_arg(args, int);
|
|
|
|
- op = va_arg(args, void *);
|
|
|
|
- arg = va_arg(args, void *);
|
|
|
|
- res = va_arg(args, int *);
|
|
|
|
- va_end(args);
|
|
|
|
|
|
+ int size, remain, n;
|
|
|
|
+
|
|
size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
|
|
size = min(PAGE_ALIGN(addr) - addr, (unsigned long) len);
|
|
remain = len;
|
|
remain = len;
|
|
|
|
|
|
- current->thread.fault_catcher = jmpbuf;
|
|
|
|
n = do_op_one_page(addr, size, is_write, op, arg);
|
|
n = do_op_one_page(addr, size, is_write, op, arg);
|
|
if (n != 0) {
|
|
if (n != 0) {
|
|
- *res = (n < 0 ? remain : 0);
|
|
|
|
|
|
+ remain = (n < 0 ? remain : 0);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
addr += size;
|
|
addr += size;
|
|
remain -= size;
|
|
remain -= size;
|
|
- if (remain == 0) {
|
|
|
|
- *res = 0;
|
|
|
|
|
|
+ if (remain == 0)
|
|
goto out;
|
|
goto out;
|
|
- }
|
|
|
|
|
|
|
|
- while(addr < ((addr + remain) & PAGE_MASK)) {
|
|
|
|
|
|
+ while (addr < ((addr + remain) & PAGE_MASK)) {
|
|
n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
|
|
n = do_op_one_page(addr, PAGE_SIZE, is_write, op, arg);
|
|
if (n != 0) {
|
|
if (n != 0) {
|
|
- *res = (n < 0 ? remain : 0);
|
|
|
|
|
|
+ remain = (n < 0 ? remain : 0);
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
addr += PAGE_SIZE;
|
|
addr += PAGE_SIZE;
|
|
remain -= PAGE_SIZE;
|
|
remain -= PAGE_SIZE;
|
|
}
|
|
}
|
|
- if (remain == 0) {
|
|
|
|
- *res = 0;
|
|
|
|
|
|
+ if (remain == 0)
|
|
goto out;
|
|
goto out;
|
|
- }
|
|
|
|
|
|
|
|
n = do_op_one_page(addr, remain, is_write, op, arg);
|
|
n = do_op_one_page(addr, remain, is_write, op, arg);
|
|
- if (n != 0)
|
|
|
|
- *res = (n < 0 ? remain : 0);
|
|
|
|
- else *res = 0;
|
|
|
|
- out:
|
|
|
|
- current->thread.fault_catcher = NULL;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int buffer_op(unsigned long addr, int len, int is_write,
|
|
|
|
- int (*op)(unsigned long addr, int len, void *arg),
|
|
|
|
- void *arg)
|
|
|
|
-{
|
|
|
|
- int faulted, res;
|
|
|
|
-
|
|
|
|
- faulted = setjmp_wrapper(do_buffer_op, addr, len, is_write, op, arg,
|
|
|
|
- &res);
|
|
|
|
- if (!faulted)
|
|
|
|
- return res;
|
|
|
|
|
|
+ if (n != 0) {
|
|
|
|
+ remain = (n < 0 ? remain : 0);
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
|
|
- return addr + len - (unsigned long) current->thread.fault_addr;
|
|
|
|
|
|
+ return 0;
|
|
|
|
+ out:
|
|
|
|
+ return remain;
|
|
}
|
|
}
|
|
|
|
|
|
static int copy_chunk_from_user(unsigned long from, int len, void *arg)
|
|
static int copy_chunk_from_user(unsigned long from, int len, void *arg)
|