419 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			419 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* The kernel call implemented in this file:
 | 
						|
 *   m_type:	SYS_SAFECOPYFROM or SYS_SAFECOPYTO or SYS_VSAFECOPY
 | 
						|
 *
 | 
						|
 * The parameters for this kernel call are:
 | 
						|
 *    	SCP_FROM_TO	other endpoint
 | 
						|
 *    	SCP_SEG		segment in own address space
 | 
						|
 *    	SCP_GID		grant id
 | 
						|
 *    	SCP_OFFSET	offset within granted space
 | 
						|
 *	SCP_ADDRESS	address in own address space
 | 
						|
 *    	SCP_BYTES	bytes to be copied
 | 
						|
 *
 | 
						|
 * For the vectored variant (do_vsafecopy): 
 | 
						|
 *      VSCP_VEC_ADDR   address of vector
 | 
						|
 *      VSCP_VEC_SIZE   number of significant elements in vector
 | 
						|
 */
 | 
						|
 | 
						|
#include <minix/type.h>
 | 
						|
#include <minix/safecopies.h>
 | 
						|
 | 
						|
#include "kernel/system.h"
 | 
						|
 | 
						|
#define MAX_INDIRECT_DEPTH 5	/* up to how many indirect grants to follow? */
 | 
						|
 | 
						|
#define MEM_TOP 0xFFFFFFFFUL
 | 
						|
 | 
						|
static int safecopy(struct proc *, endpoint_t, endpoint_t,
 | 
						|
	cp_grant_id_t, int, int, size_t, vir_bytes, vir_bytes, int);
 | 
						|
 | 
						|
#define HASGRANTTABLE(gr) \
 | 
						|
	(priv(gr) && priv(gr)->s_grant_table)
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				verify_grant				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int verify_grant(granter, grantee, grant, bytes, access,
 | 
						|
	offset_in, offset_result, e_granter)
 | 
						|
endpoint_t granter, grantee;	/* copyee, copyer */
 | 
						|
cp_grant_id_t grant;		/* grant id */
 | 
						|
vir_bytes bytes;		/* copy size */
 | 
						|
int access;			/* direction (read/write) */
 | 
						|
vir_bytes offset_in;		/* copy offset within grant */
 | 
						|
vir_bytes *offset_result;	/* copy offset within virtual address space */
 | 
						|
endpoint_t *e_granter;		/* new granter (magic grants) */
 | 
						|
{
 | 
						|
	static cp_grant_t g;
 | 
						|
	static int proc_nr;
 | 
						|
	static const struct proc *granter_proc;
 | 
						|
	int depth = 0;
 | 
						|
 | 
						|
	do {
 | 
						|
		/* Get granter process slot (if valid), and check range of
 | 
						|
		 * grant id.
 | 
						|
		 */
 | 
						|
		if(!isokendpt(granter, &proc_nr) ) {
 | 
						|
			printf(
 | 
						|
			"grant verify failed: invalid granter %d\n", (int) granter);
 | 
						|
			return(EINVAL);
 | 
						|
		}
 | 
						|
		if(!GRANT_VALID(grant)) {
 | 
						|
			printf(
 | 
						|
			"grant verify failed: invalid grant %d\n", (int) grant);
 | 
						|
			return(EINVAL);
 | 
						|
		}
 | 
						|
		granter_proc = proc_addr(proc_nr);
 | 
						|
 | 
						|
		/* If there is no priv. structure, or no grant table in the
 | 
						|
		 * priv. structure, or the grant table in the priv. structure
 | 
						|
		 * is too small for the grant, return EPERM.
 | 
						|
		 */
 | 
						|
		if(!HASGRANTTABLE(granter_proc)) {
 | 
						|
			printf(
 | 
						|
			"grant verify failed: granter %d has no grant table\n",
 | 
						|
			granter);
 | 
						|
			return(EPERM);
 | 
						|
		}
 | 
						|
 | 
						|
		if(priv(granter_proc)->s_grant_entries <= grant) {
 | 
						|
				printf(
 | 
						|
				"verify_grant: grant verify failed in ep %d "
 | 
						|
				"proc %d: grant %d out of range "
 | 
						|
				"for table size %d\n",
 | 
						|
					granter, proc_nr, grant,
 | 
						|
					priv(granter_proc)->s_grant_entries);
 | 
						|
			return(EPERM);
 | 
						|
		}
 | 
						|
 | 
						|
		/* Copy the grant entry corresponding to this id to see what it
 | 
						|
		 * looks like. If it fails, hide the fact that granter has
 | 
						|
		 * (presumably) set an invalid grant table entry by returning
 | 
						|
		 * EPERM, just like with an invalid grant id.
 | 
						|
		 */
 | 
						|
		if(data_copy(granter,
 | 
						|
			priv(granter_proc)->s_grant_table + sizeof(g)*grant,
 | 
						|
			KERNEL, (vir_bytes) &g, sizeof(g)) != OK) {
 | 
						|
			printf(
 | 
						|
			"verify_grant: grant verify: data_copy failed\n");
 | 
						|
			return EPERM;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Check validity. */
 | 
						|
		if((g.cp_flags & (CPF_USED | CPF_VALID)) !=
 | 
						|
			(CPF_USED | CPF_VALID)) {
 | 
						|
			printf(
 | 
						|
			"verify_grant: grant failed: invalid (%d flags 0x%lx)\n",
 | 
						|
				grant, g.cp_flags);
 | 
						|
			return EPERM;
 | 
						|
		}
 | 
						|
 | 
						|
		/* The given grant may be an indirect grant, that is, a grant
 | 
						|
		 * that provides permission to use a grant given to the
 | 
						|
		 * granter (i.e., for which it is the grantee). This can lead
 | 
						|
		 * to a chain of indirect grants which must be followed back.
 | 
						|
		 */
 | 
						|
		if((g.cp_flags & CPF_INDIRECT)) {
 | 
						|
			/* Stop after a few iterations. There may be a loop. */
 | 
						|
			if (depth == MAX_INDIRECT_DEPTH) {
 | 
						|
				printf(
 | 
						|
					"verify grant: indirect grant verify "
 | 
						|
					"failed: exceeded maximum depth\n");
 | 
						|
				return ELOOP;
 | 
						|
			}
 | 
						|
			depth++;
 | 
						|
 | 
						|
			/* Verify actual grantee. */
 | 
						|
			if(g.cp_u.cp_indirect.cp_who_to != grantee &&
 | 
						|
				grantee != ANY &&
 | 
						|
				g.cp_u.cp_indirect.cp_who_to != ANY) {
 | 
						|
				printf(
 | 
						|
					"verify_grant: indirect grant verify "
 | 
						|
					"failed: bad grantee\n");
 | 
						|
				return EPERM;
 | 
						|
			}
 | 
						|
 | 
						|
			/* Start over with new granter, grant, and grantee. */
 | 
						|
			grantee = granter;
 | 
						|
			granter = g.cp_u.cp_indirect.cp_who_from;
 | 
						|
			grant = g.cp_u.cp_indirect.cp_grant;
 | 
						|
		}
 | 
						|
	} while(g.cp_flags & CPF_INDIRECT);
 | 
						|
 | 
						|
	/* Check access of grant. */
 | 
						|
	if(((g.cp_flags & access) != access)) {
 | 
						|
		printf(
 | 
						|
	"verify_grant: grant verify failed: access invalid; want 0x%x, have 0x%x\n",
 | 
						|
			access, g.cp_flags);
 | 
						|
		return EPERM;
 | 
						|
	}
 | 
						|
 | 
						|
	if((g.cp_flags & CPF_DIRECT)) {
 | 
						|
		/* Don't fiddle around with grants that wrap, arithmetic
 | 
						|
		 * below may be confused.
 | 
						|
		 */
 | 
						|
		if(MEM_TOP - g.cp_u.cp_direct.cp_len + 1 <
 | 
						|
			g.cp_u.cp_direct.cp_start) {
 | 
						|
			printf(
 | 
						|
		"verify_grant: direct grant verify failed: len too long\n");
 | 
						|
			return EPERM;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Verify actual grantee. */
 | 
						|
		if(g.cp_u.cp_direct.cp_who_to != grantee && grantee != ANY
 | 
						|
			&& g.cp_u.cp_direct.cp_who_to != ANY) {
 | 
						|
			printf(
 | 
						|
		"verify_grant: direct grant verify failed: bad grantee\n");
 | 
						|
			return EPERM;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Verify actual copy range. */
 | 
						|
		if((offset_in+bytes < offset_in) ||
 | 
						|
		    offset_in+bytes > g.cp_u.cp_direct.cp_len) {
 | 
						|
			printf(
 | 
						|
		"verify_grant: direct grant verify failed: bad size or range. "
 | 
						|
		"granted %d bytes @ 0x%lx; wanted %d bytes @ 0x%lx\n",
 | 
						|
				g.cp_u.cp_direct.cp_len,
 | 
						|
				g.cp_u.cp_direct.cp_start,
 | 
						|
				bytes, offset_in);
 | 
						|
			return EPERM;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Verify successful - tell caller what address it is. */
 | 
						|
		*offset_result = g.cp_u.cp_direct.cp_start + offset_in;
 | 
						|
		*e_granter = granter;
 | 
						|
	} else if(g.cp_flags & CPF_MAGIC) {
 | 
						|
		/* Currently, it is hardcoded that only FS may do
 | 
						|
		 * magic grants.
 | 
						|
		 */
 | 
						|
		if(granter != VFS_PROC_NR) {
 | 
						|
			printf(
 | 
						|
		"verify_grant: magic grant verify failed: granter (%d) "
 | 
						|
		"is not FS (%d)\n", granter, VFS_PROC_NR);
 | 
						|
			return EPERM;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Verify actual grantee. */
 | 
						|
		if(g.cp_u.cp_magic.cp_who_to != grantee && grantee != ANY
 | 
						|
			&& g.cp_u.cp_direct.cp_who_to != ANY) {
 | 
						|
			printf(
 | 
						|
		"verify_grant: magic grant verify failed: bad grantee\n");
 | 
						|
			return EPERM;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Verify actual copy range. */
 | 
						|
		if((offset_in+bytes < offset_in) ||
 | 
						|
		    offset_in+bytes > g.cp_u.cp_magic.cp_len) {
 | 
						|
			printf(
 | 
						|
		"verify_grant: magic grant verify failed: bad size or range. "
 | 
						|
		"granted %d bytes @ 0x%lx; wanted %d bytes @ 0x%lx\n",
 | 
						|
				g.cp_u.cp_magic.cp_len,
 | 
						|
				g.cp_u.cp_magic.cp_start,
 | 
						|
				bytes, offset_in);
 | 
						|
			return EPERM;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Verify successful - tell caller what address it is. */
 | 
						|
		*offset_result = g.cp_u.cp_magic.cp_start + offset_in;
 | 
						|
		*e_granter = g.cp_u.cp_magic.cp_who_from;
 | 
						|
	} else {
 | 
						|
		printf(
 | 
						|
		"verify_grant: grant verify failed: unknown grant type\n");
 | 
						|
		return EPERM;
 | 
						|
	}
 | 
						|
 | 
						|
	return OK;
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				safecopy				     *
 | 
						|
 *===========================================================================*/
 | 
						|
static int safecopy(caller, granter, grantee, grantid, src_seg, dst_seg, bytes,
 | 
						|
	g_offset, addr, access)
 | 
						|
struct proc * caller;
 | 
						|
endpoint_t granter, grantee;
 | 
						|
cp_grant_id_t grantid;
 | 
						|
int src_seg, dst_seg;
 | 
						|
size_t bytes;
 | 
						|
vir_bytes g_offset, addr;
 | 
						|
int access;			/* CPF_READ for a copy from granter to grantee, CPF_WRITE
 | 
						|
				 * for a copy from grantee to granter.
 | 
						|
				 */
 | 
						|
{
 | 
						|
	static struct vir_addr v_src, v_dst;
 | 
						|
	static vir_bytes v_offset;
 | 
						|
	endpoint_t new_granter, *src, *dst;
 | 
						|
	struct proc *granter_p;
 | 
						|
	int r;
 | 
						|
#if PERF_USE_COW_SAFECOPY
 | 
						|
	vir_bytes size;
 | 
						|
#endif
 | 
						|
 | 
						|
	/* See if there is a reasonable grant table. */
 | 
						|
	if(!(granter_p = endpoint_lookup(granter))) return EINVAL;
 | 
						|
	if(!HASGRANTTABLE(granter_p)) {
 | 
						|
		printf(
 | 
						|
		"safecopy failed: granter %d has no grant table\n", granter);
 | 
						|
		return(EPERM);
 | 
						|
	}
 | 
						|
 | 
						|
	/* Decide who is src and who is dst. */
 | 
						|
	if(access & CPF_READ) {
 | 
						|
		src = &granter;
 | 
						|
		dst = &grantee;
 | 
						|
	} else {
 | 
						|
		src = &grantee;
 | 
						|
		dst = &granter;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Verify permission exists. */
 | 
						|
	if((r=verify_grant(granter, grantee, grantid, bytes, access,
 | 
						|
	    g_offset, &v_offset, &new_granter)) != OK) {
 | 
						|
			printf(
 | 
						|
		"grant %d verify to copy %d->%d by %d failed: err %d\n",
 | 
						|
				grantid, *src, *dst, grantee, r);
 | 
						|
		return r;
 | 
						|
	}
 | 
						|
 | 
						|
	/* verify_grant() can redirect the grantee to someone else,
 | 
						|
	 * meaning the source or destination changes.
 | 
						|
	 */
 | 
						|
	granter = new_granter;
 | 
						|
 | 
						|
	/* Now it's a regular copy. */
 | 
						|
	v_src.segment = src_seg;
 | 
						|
	v_dst.segment = dst_seg;
 | 
						|
	v_src.proc_nr_e = *src;
 | 
						|
	v_dst.proc_nr_e = *dst;
 | 
						|
 | 
						|
	/* Now the offset in virtual addressing is known in 'offset'.
 | 
						|
	 * Depending on the access, this is the source or destination
 | 
						|
	 * address.
 | 
						|
	 */
 | 
						|
	if(access & CPF_READ) {
 | 
						|
		v_src.offset = v_offset;
 | 
						|
		v_dst.offset = (vir_bytes) addr;
 | 
						|
	} else {
 | 
						|
		v_src.offset = (vir_bytes) addr;
 | 
						|
		v_dst.offset = v_offset;
 | 
						|
	}
 | 
						|
 | 
						|
	/* Do the regular copy. */
 | 
						|
#if PERF_USE_COW_SAFECOPY
 | 
						|
	if(v_offset % CLICK_SIZE != addr % CLICK_SIZE || bytes < CLICK_SIZE) {
 | 
						|
		/* Give up on COW immediately when offsets are not aligned
 | 
						|
		 * or we are copying less than a page.
 | 
						|
		 */
 | 
						|
		return virtual_copy_vmcheck(caller, &v_src, &v_dst, bytes);
 | 
						|
	}
 | 
						|
 | 
						|
	if((size = v_offset % CLICK_SIZE) != 0) {
 | 
						|
		/* Normal copy for everything before the first page boundary. */
 | 
						|
		size = CLICK_SIZE - size;
 | 
						|
		r = virtual_copy_vmcheck(caller, &v_src, &v_dst, size);
 | 
						|
		if(r != OK)
 | 
						|
			return r;
 | 
						|
		v_src.offset += size;
 | 
						|
		v_dst.offset += size;
 | 
						|
		bytes -= size;
 | 
						|
	}
 | 
						|
	if((size = bytes / CLICK_SIZE) != 0) {
 | 
						|
		/* Use COW optimization when copying entire pages. */
 | 
						|
		size *= CLICK_SIZE;
 | 
						|
		r = map_invoke_vm(VMPTYPE_COWMAP,
 | 
						|
			v_dst.proc_nr_e, v_dst.segment, v_dst.offset,
 | 
						|
			v_src.proc_nr_e, v_src.segment, v_src.offset,
 | 
						|
			size, 0);
 | 
						|
		if(r != OK)
 | 
						|
			return r;
 | 
						|
		v_src.offset += size;
 | 
						|
		v_dst.offset += size;
 | 
						|
		bytes -= size;
 | 
						|
	}
 | 
						|
	if(bytes != 0) {
 | 
						|
		/* Normal copy for everything after the last page boundary. */
 | 
						|
		r = virtual_copy_vmcheck(caller, &v_src, &v_dst, bytes);
 | 
						|
		if(r != OK)
 | 
						|
			return r;
 | 
						|
	}
 | 
						|
 | 
						|
	return OK;
 | 
						|
#else
 | 
						|
	return virtual_copy_vmcheck(caller, &v_src, &v_dst, bytes);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				do_safecopy_to				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int do_safecopy_to(struct proc * caller, message * m_ptr)
 | 
						|
{
 | 
						|
	return safecopy(caller, m_ptr->SCP_FROM_TO, caller->p_endpoint,
 | 
						|
		(cp_grant_id_t) m_ptr->SCP_GID, m_ptr->SCP_SEG, D,
 | 
						|
		m_ptr->SCP_BYTES, m_ptr->SCP_OFFSET,
 | 
						|
		(vir_bytes) m_ptr->SCP_ADDRESS, CPF_WRITE);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				do_safecopy_from			     *
 | 
						|
 *===========================================================================*/
 | 
						|
int do_safecopy_from(struct proc * caller, message * m_ptr)
 | 
						|
{
 | 
						|
	return safecopy(caller, m_ptr->SCP_FROM_TO, caller->p_endpoint,
 | 
						|
		(cp_grant_id_t) m_ptr->SCP_GID, D, m_ptr->SCP_SEG,
 | 
						|
		m_ptr->SCP_BYTES, m_ptr->SCP_OFFSET,
 | 
						|
		(vir_bytes) m_ptr->SCP_ADDRESS, CPF_READ);
 | 
						|
}
 | 
						|
 | 
						|
/*===========================================================================*
 | 
						|
 *				do_vsafecopy				     *
 | 
						|
 *===========================================================================*/
 | 
						|
int do_vsafecopy(struct proc * caller, message * m_ptr)
 | 
						|
{
 | 
						|
	static struct vscp_vec vec[SCPVEC_NR];
 | 
						|
	static struct vir_addr src, dst;
 | 
						|
	int r, i, els;
 | 
						|
	size_t bytes;
 | 
						|
 | 
						|
	/* Set vector copy parameters. */
 | 
						|
	src.proc_nr_e = caller->p_endpoint;
 | 
						|
	src.offset = (vir_bytes) m_ptr->VSCP_VEC_ADDR;
 | 
						|
	src.segment = dst.segment = D;
 | 
						|
	dst.proc_nr_e = KERNEL;
 | 
						|
	dst.offset = (vir_bytes) vec;
 | 
						|
 | 
						|
	/* No. of vector elements. */
 | 
						|
	els = m_ptr->VSCP_VEC_SIZE;
 | 
						|
	bytes = els * sizeof(struct vscp_vec);
 | 
						|
 | 
						|
	/* Obtain vector of copies. */
 | 
						|
	if((r=virtual_copy_vmcheck(caller, &src, &dst, bytes)) != OK)
 | 
						|
		return r;
 | 
						|
 | 
						|
	/* Perform safecopies. */
 | 
						|
	for(i = 0; i < els; i++) {
 | 
						|
		int access;
 | 
						|
		endpoint_t granter;
 | 
						|
		if(vec[i].v_from == SELF) {
 | 
						|
			access = CPF_WRITE;
 | 
						|
			granter = vec[i].v_to;
 | 
						|
		} else if(vec[i].v_to == SELF) {
 | 
						|
			access = CPF_READ;
 | 
						|
			granter = vec[i].v_from;
 | 
						|
		} else {
 | 
						|
			printf("vsafecopy: %d: element %d/%d: no SELF found\n",
 | 
						|
				caller->p_endpoint, i, els);
 | 
						|
			return EINVAL;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Do safecopy for this element. */
 | 
						|
		if((r=safecopy(caller, granter, caller->p_endpoint,
 | 
						|
			vec[i].v_gid, D, D,
 | 
						|
			vec[i].v_bytes, vec[i].v_offset,
 | 
						|
			vec[i].v_addr, access)) != OK) {
 | 
						|
			return r;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return OK;
 | 
						|
}
 | 
						|
 |