From 70abb127cc32b8afb4cc592ce17dadcdaaad88eb Mon Sep 17 00:00:00 2001 From: David van Moolenbroek Date: Wed, 21 Mar 2012 23:51:18 +0100 Subject: [PATCH] Add sys_vumap() kernel call This new call is a vectored version of sys_umap(). It supports batch lookups, non-contiguous memory, faulting in memory, and basic access checks. --- commands/service/parse.c | 1 + include/minix/com.h | 11 +++ include/minix/const.h | 9 ++- include/minix/syslib.h | 3 + include/minix/type.h | 17 +++++ kernel/config.h | 1 + kernel/system.c | 1 + kernel/system.h | 5 ++ kernel/system/Makefile.inc | 1 + kernel/system/do_vumap.c | 135 +++++++++++++++++++++++++++++++++++++ lib/libsys/Makefile | 1 + lib/libsys/sys_vumap.c | 34 ++++++++++ 12 files changed, 216 insertions(+), 3 deletions(-) create mode 100644 kernel/system/do_vumap.c create mode 100644 lib/libsys/sys_vumap.c diff --git a/commands/service/parse.c b/commands/service/parse.c index 82488e723..048246d3b 100644 --- a/commands/service/parse.c +++ b/commands/service/parse.c @@ -807,6 +807,7 @@ struct { "VIRCOPY", SYS_VIRCOPY }, { "PHYSCOPY", SYS_PHYSCOPY }, { "UMAP_REMOTE", SYS_UMAP_REMOTE }, + { "VUMAP", SYS_VUMAP }, { "IRQCTL", SYS_IRQCTL }, { "INT86", SYS_INT86 }, { "DEVIO", SYS_DEVIO }, diff --git a/include/minix/com.h b/include/minix/com.h index f959d2c64..76dc34f23 100644 --- a/include/minix/com.h +++ b/include/minix/com.h @@ -313,6 +313,7 @@ # define SYS_VIRCOPY (KERNEL_CALL + 15) /* sys_vircopy() */ # define SYS_PHYSCOPY (KERNEL_CALL + 16) /* sys_physcopy() */ # define SYS_UMAP_REMOTE (KERNEL_CALL + 17) /* sys_umap_remote() */ +# define SYS_VUMAP (KERNEL_CALL + 18) /* sys_vumap() */ # define SYS_IRQCTL (KERNEL_CALL + 19) /* sys_irqctl() */ # define SYS_INT86 (KERNEL_CALL + 20) /* sys_int86() */ @@ -437,6 +438,16 @@ #define CP_DST_ADDR m5_l2 /* address where data go to */ #define CP_NR_BYTES m5_l3 /* number of bytes to copy */ +/* Field names for SYS_VUMAP. */ +#define VUMAP_ENDPT m10_i1 /* grant owner, or SELF for local addresses */ +#define VUMAP_VADDR m10_l1 /* address of virtual (input) vector */ +#define VUMAP_VCOUNT m10_i2 /* number of elements in virtual vector */ +#define VUMAP_OFFSET m10_l2 /* offset into first entry of input vector */ +#define VUMAP_ACCESS m10_i3 /* access requested for input (VUA_ flags) */ +#define VUMAP_PADDR m10_l3 /* address of physical (output) vector */ +#define VUMAP_PMAX m10_i4 /* max number of physical vector elements */ +#define VUMAP_PCOUNT m10_i1 /* upon return: number of elements filled */ + /* Field names for SYS_GETINFO. */ #define I_REQUEST m7_i3 /* what info to get */ # define GET_KINFO 0 /* get kernel information structure */ diff --git a/include/minix/const.h b/include/minix/const.h index 24eb4fa28..6adddfb98 100644 --- a/include/minix/const.h +++ b/include/minix/const.h @@ -46,9 +46,12 @@ #include /* NULL Pointer */ -#define SCPVEC_NR 64 /* max # of entries in a SYS_VSAFECOPY* request */ -#define NR_IOREQS 64 - /* maximum number of entries in an iorequest */ +#define SCPVEC_NR 64 /* max # of entries in a SYS_VSAFECOPY request */ +#define MAPVEC_NR 64 /* max # of entries in a SYS_VUMAP request */ +#define NR_IOREQS 64 /* maximum number of entries in an iorequest */ + +#define VUA_READ 0x01 /* for SYS_VUMAP: read access */ +#define VUA_WRITE 0x02 /* for SYS_VUMAP: write access */ /* Message passing constants. */ #define MESS_SIZE (sizeof(message)) /* might need usizeof from FS here */ diff --git a/include/minix/syslib.h b/include/minix/syslib.h index b085a2006..d670139ac 100644 --- a/include/minix/syslib.h +++ b/include/minix/syslib.h @@ -177,6 +177,9 @@ _PROTOTYPE(int sys_umap_data_fb, (endpoint_t proc_ep, vir_bytes vir_addr, vir_bytes bytes, phys_bytes *phys_addr)); _PROTOTYPE(int sys_umap_remote, (endpoint_t proc_ep, endpoint_t grantee, int seg, vir_bytes vir_addr, vir_bytes bytes, phys_bytes *phys_addr)); +_PROTOTYPE(int sys_vumap, (endpoint_t endpt, struct vumap_vir *vvec, + int vcount, size_t offset, int access, struct vumap_phys *pvec, + int *pcount)); /* Shorthands for sys_getinfo() system call. */ #define sys_getkmessages(dst) sys_getinfo(GET_KMESSAGES, dst, 0,0,0) diff --git a/include/minix/type.h b/include/minix/type.h index 2c8c74cdc..bb038f27c 100644 --- a/include/minix/type.h +++ b/include/minix/type.h @@ -59,6 +59,23 @@ struct vir_cp_req { phys_bytes count; }; +/* Structures for SYS_VUMAP. */ +struct vumap_vir { + union { + cp_grant_id_t u_grant; /* grant identifier, for non-SELF endpoint */ + vir_bytes u_addr; /* local virtual address, for SELF endpoint */ + } vv_u; + size_t vv_size; /* size in bytes */ +}; +#define vv_grant vv_u.u_grant +#define vv_addr vv_u.u_addr + +struct vumap_phys { + phys_bytes vp_addr; /* physical address */ + size_t vp_size; /* size in bytes */ +}; + +/* I/O vector structures used in protocols between services. */ typedef struct { vir_bytes iov_addr; /* address of an I/O buffer */ vir_bytes iov_size; /* sizeof an I/O buffer */ diff --git a/kernel/config.h b/kernel/config.h index d394697fe..a3dd15219 100644 --- a/kernel/config.h +++ b/kernel/config.h @@ -37,6 +37,7 @@ #define USE_PRIVCTL 1 /* system privileges control */ #define USE_UMAP 1 /* map virtual to physical address */ #define USE_UMAP_REMOTE 1 /* sys_umap on behalf of another process */ +#define USE_VUMAP 1 /* vectored virtual to physical mapping */ #define USE_VIRCOPY 1 /* copy using virtual addressing */ #define USE_PHYSCOPY 1 /* copy using physical addressing */ #define USE_MEMSET 1 /* write char to a given memory area */ diff --git a/kernel/system.c b/kernel/system.c index 77b0d2e32..ab18914c8 100644 --- a/kernel/system.c +++ b/kernel/system.c @@ -224,6 +224,7 @@ PUBLIC void system_init(void) /* Copying. */ map(SYS_UMAP, do_umap); /* map virtual to physical address */ map(SYS_UMAP_REMOTE, do_umap_remote); /* do_umap for non-caller process */ + map(SYS_VUMAP, do_vumap); /* vectored virtual to physical map */ map(SYS_VIRCOPY, do_vircopy); /* use pure virtual addressing */ map(SYS_PHYSCOPY, do_copy); /* use physical addressing */ map(SYS_SAFECOPYFROM, do_safecopy_from);/* copy with pre-granted permission */ diff --git a/kernel/system.h b/kernel/system.h index e69d47a67..85240e023 100644 --- a/kernel/system.h +++ b/kernel/system.h @@ -92,6 +92,11 @@ _PROTOTYPE( int do_umap_remote, (struct proc * caller, message *m_ptr) ); #define do_umap_remote NULL #endif +_PROTOTYPE( int do_vumap, (struct proc * caller, message *m_ptr) ); +#if ! USE_VUMAP +#define do_vumap NULL +#endif + _PROTOTYPE( int do_memset, (struct proc * caller, message *m_ptr) ); #if ! USE_MEMSET #define do_memset NULL diff --git a/kernel/system/Makefile.inc b/kernel/system/Makefile.inc index ff8a25613..4209c9901 100644 --- a/kernel/system/Makefile.inc +++ b/kernel/system/Makefile.inc @@ -21,6 +21,7 @@ SRCS+= \ do_copy.c \ do_umap.c \ do_umap_remote.c \ + do_vumap.c \ do_memset.c \ do_setgrant.c \ do_privctl.c \ diff --git a/kernel/system/do_vumap.c b/kernel/system/do_vumap.c new file mode 100644 index 000000000..5cdeaa6c3 --- /dev/null +++ b/kernel/system/do_vumap.c @@ -0,0 +1,135 @@ +/* The kernel call implemented in this file: + * m_type: SYS_VUMAP + * + * The parameters for this kernel call are: + * m10_i1: VUMAP_ENDPT (grant owner, or SELF for local addresses) + * m10_l1: VUMAP_VADDR (address of virtual (input) vector) + * m10_i2: VUMAP_VCOUNT (number of elements in virtual vector) + * m10_l2: VUMAP_OFFSET (offset into first entry of input vector) + * m10_i3: VUMAP_ACCESS (safecopy access requested for input) + * m10_l3: VUMAP_PADDR (address of physical (output) vector) + * m10_i4: VUMAP_PMAX (maximum number of physical vector elements) + * m10_i1: VUMAP_PCOUNT (upon return: number of elements filled) + */ + +#include "kernel/system.h" + +#include + +/*===========================================================================* + * do_vumap * + *===========================================================================*/ +PUBLIC int do_vumap(struct proc *caller, message *m_ptr) +{ +/* Map a vector of grants or local virtual addresses to physical addresses. + * Designed to be used by drivers to perform an efficient lookup of physical + * addresses for the purpose of direct DMA from/to a remote process. + */ + endpoint_t endpt, source, granter; + struct proc *procp; + struct vumap_vir vvec[MAPVEC_NR]; + struct vumap_phys pvec[MAPVEC_NR]; + vir_bytes vaddr, paddr, vir_addr, lin_addr; + phys_bytes phys_addr; + int i, r, proc_nr, vcount, pcount, pmax, access; + size_t size, chunk, offset; + + endpt = caller->p_endpoint; + + /* Retrieve and check input parameters. */ + source = m_ptr->VUMAP_ENDPT; + vaddr = (vir_bytes) m_ptr->VUMAP_VADDR; + vcount = m_ptr->VUMAP_VCOUNT; + offset = m_ptr->VUMAP_OFFSET; + access = m_ptr->VUMAP_ACCESS; + paddr = (vir_bytes) m_ptr->VUMAP_PADDR; + pmax = m_ptr->VUMAP_PMAX; + + if (vcount <= 0 || pmax <= 0) + return EINVAL; + + if (vcount > MAPVEC_NR) vcount = MAPVEC_NR; + if (pmax > MAPVEC_NR) pmax = MAPVEC_NR; + + /* Convert access to safecopy access flags. */ + switch (access) { + case VUA_READ: access = CPF_READ; break; + case VUA_WRITE: access = CPF_WRITE; break; + case VUA_READ|VUA_WRITE: access = CPF_READ|CPF_WRITE; break; + default: return EINVAL; + } + + /* Copy in the vector of virtual addresses. */ + size = vcount * sizeof(vvec[0]); + + if (data_copy(endpt, vaddr, KERNEL, (vir_bytes) vvec, size) != OK) + return EFAULT; + + pcount = 0; + + /* Go through the input entries, one at a time. Stop early in case the output + * vector has filled up. + */ + for (i = 0; i < vcount && pcount < pmax; i++) { + size = vvec[i].vv_size; + if (size <= offset) + return EINVAL; + size -= offset; + + if (source != SELF) { + r = verify_grant(source, endpt, vvec[i].vv_grant, size, access, + offset, &vir_addr, &granter); + if (r != OK) + return r; + } else { + vir_addr = vvec[i].vv_addr + offset; + granter = endpt; + } + + okendpt(granter, &proc_nr); + procp = proc_addr(proc_nr); + + lin_addr = umap_local(procp, D, vir_addr, size); + if (!lin_addr) + return EFAULT; + + /* Each virtual range is made up of one or more physical ranges. */ + while (size > 0 && pcount < pmax) { + chunk = vm_lookup_range(procp, lin_addr, &phys_addr, size); + + if (!chunk) { + /* Try to get the memory allocated, unless the memory + * is supposed to be there to be read from. + */ + if (access & CPF_READ) + return EFAULT; + + /* This call may suspend the current call, or return an + * error for a previous invocation. + */ + return vm_check_range(caller, procp, lin_addr, size); + } + + pvec[pcount].vp_addr = phys_addr; + pvec[pcount].vp_size = chunk; + pcount++; + + lin_addr += chunk; + size -= chunk; + } + + offset = 0; + } + + /* Copy out the resulting vector of physical addresses. */ + assert(pcount > 0); + + size = pcount * sizeof(pvec[0]); + + r = data_copy_vmcheck(caller, KERNEL, (vir_bytes) pvec, endpt, paddr, size); + + if (r == OK) + m_ptr->VUMAP_PCOUNT = pcount; + + return r; +} diff --git a/lib/libsys/Makefile b/lib/libsys/Makefile index e959818e9..d644b218b 100644 --- a/lib/libsys/Makefile +++ b/lib/libsys/Makefile @@ -109,6 +109,7 @@ SRCS= \ sys_voutw.c \ sys_vsafecopy.c \ sys_vtimer.c \ + sys_vumap.c \ taskcall.c \ tickdelay.c \ timers.c \ diff --git a/lib/libsys/sys_vumap.c b/lib/libsys/sys_vumap.c new file mode 100644 index 000000000..2ceaeb755 --- /dev/null +++ b/lib/libsys/sys_vumap.c @@ -0,0 +1,34 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_vumap * + *===========================================================================*/ +PUBLIC int sys_vumap( + endpoint_t endpt, /* source process endpoint, or SELF */ + struct vumap_vir *vvec, /* virtual (input) vector */ + int vcount, /* number of elements in vvec */ + size_t offset, /* offset into first vvec element */ + int access, /* requested safecopy access flags */ + struct vumap_phys *pvec, /* physical (output) vector */ + int *pcount /* (max, returned) nr of els in pvec */ +) +{ + message m; + int r; + + m.VUMAP_ENDPT = endpt; + m.VUMAP_VADDR = (vir_bytes) vvec; + m.VUMAP_VCOUNT = vcount; + m.VUMAP_OFFSET = offset; + m.VUMAP_ACCESS = access; + m.VUMAP_PADDR = (vir_bytes) pvec; + m.VUMAP_PMAX = *pcount; + + r = _kernel_call(SYS_VUMAP, &m); + + if (r != OK) + return r; + + *pcount = m.VUMAP_PCOUNT; + return OK; +} -- 2.44.0