minix/kernel/system/do_safemap.c
Tomas Hruby cca24d06d8 This patch removes the global variables who_p and who_e from the
kernel (sys task).  The main reason is that these would have to become
cpu local variables on SMP.  Once the system task is not a task but a
genuine part of the kernel there is even less reason to have these
extra variables as proc_ptr will already contain all neccessary
information. In addition converting who_e to the process pointer and
back again all the time will be avoided.

Although proc_ptr will contain all important information, accessing it
as a cpu local variable will be fairly expensive, hence the value
would be assigned to some on stack local variable. Therefore it is
better to add the 'caller' argument to the syscall handlers to pass
the value on stack anyway. It also clearly denotes on who's behalf is
the syscall being executed.

This patch also ANSIfies the syscall function headers.

Last but not least, it also fixes a potential bug in virtual_copy_f()
in case the check is disabled. So far the function in case of a
failure could possible reuse an old who_p in case this function had
not been called from the system task.

virtual_copy_f() takes the caller as a parameter too. In case the
checking is disabled, the caller must be NULL and non NULL if it is
enabled as we must be able to suspend the caller.
2010-02-03 09:04:48 +00:00

281 lines
8.2 KiB
C

/* The kernel call implemented in this file:
* m_type: SYS_SAFEMAP or SYS_SAFEREVMAP or SYS_SAFEUNMAP
*
* The parameters for this kernel call are:
* SMAP_EP endpoint of the grantor
* SMAP_GID grant id
* SMAP_OFFSET offset of the grant space
* SMAP_SEG segment
* SMAP_ADDRESS address
* SMAP_BYTES bytes to be copied
* SMAP_FLAG access, writable map or not?
*/
#include <minix/type.h>
#include <minix/safecopies.h>
#include "../system.h"
struct map_info_s {
int flag;
/* Grantor. */
endpoint_t grantor;
int gid;
vir_bytes offset;
vir_bytes address_Dseg; /* seg always is D */
/* Grantee. */
endpoint_t grantee;
int seg;
vir_bytes address;
/* Length. */
vir_bytes bytes;
};
#define MAX_MAP_INFO 20
static struct map_info_s map_info[MAX_MAP_INFO];
/*===========================================================================*
* add_info *
*===========================================================================*/
static int add_info(endpoint_t grantor, endpoint_t grantee, int gid,
vir_bytes offset, vir_bytes address_Dseg,
int seg, vir_bytes address, vir_bytes bytes)
{
int i;
for(i = 0; i < MAX_MAP_INFO; i++) {
if(map_info[i].flag == 0)
break;
}
if(i == MAX_MAP_INFO)
return EBUSY;
map_info[i].flag = 1;
map_info[i].grantor = grantor;
map_info[i].grantee = grantee;
map_info[i].gid = gid;
map_info[i].address_Dseg = address_Dseg;
map_info[i].offset = offset;
map_info[i].seg = seg;
map_info[i].address = address;
map_info[i].bytes = bytes;
return OK;
}
/*===========================================================================*
* get_revoke_info *
*===========================================================================*/
static struct map_info_s *get_revoke_info(endpoint_t grantor, int flag, int arg)
{
int i;
for(i = 0; i < MAX_MAP_INFO; i++) {
if(map_info[i].flag == 1
&& map_info[i].grantor == grantor
&& (flag ? (map_info[i].gid == arg)
: (map_info[i].address_Dseg == arg)))
return &map_info[i];
}
return NULL;
}
/*===========================================================================*
* get_unmap_info *
*===========================================================================*/
static struct map_info_s *get_unmap_info(endpoint_t grantee, int seg,
vir_bytes address)
{
int i;
for(i = 0; i < MAX_MAP_INFO; i++) {
if(map_info[i].flag == 1
&& map_info[i].grantee == grantee
&& map_info[i].seg == seg
&& map_info[i].address == address)
return &map_info[i];
}
return NULL;
}
/*===========================================================================*
* clear_info *
*===========================================================================*/
static int clear_info(struct map_info_s *p)
{
p->flag = 0;
return 0;
}
/*===========================================================================*
* map_invoke_vm *
*===========================================================================*/
PUBLIC int map_invoke_vm(struct proc * caller,
int req_type, /* VMPTYPE_... COWMAP, SMAP, SUNMAP */
endpoint_t end_d, int seg_d, vir_bytes off_d,
endpoint_t end_s, int seg_s, vir_bytes off_s,
size_t size, int flag)
{
struct proc *src, *dst;
phys_bytes lin_src, lin_dst;
src = endpoint_lookup(end_s);
dst = endpoint_lookup(end_d);
lin_src = umap_local(src, seg_s, off_s, size);
lin_dst = umap_local(dst, seg_d, off_d, size);
if(lin_src == 0 || lin_dst == 0) {
kprintf("map_invoke_vm: error in umap_local.\n");
return EINVAL;
}
/* Make sure the linear addresses are both page aligned. */
if(lin_src % CLICK_SIZE != 0
|| lin_dst % CLICK_SIZE != 0) {
kprintf("map_invoke_vm: linear addresses not page aligned.\n");
return EINVAL;
}
vmassert(!RTS_ISSET(caller, RTS_VMREQUEST));
vmassert(!RTS_ISSET(caller, RTS_VMREQTARGET));
vmassert(!RTS_ISSET(dst, RTS_VMREQUEST));
vmassert(!RTS_ISSET(dst, RTS_VMREQTARGET));
RTS_LOCK_SET(caller, RTS_VMREQUEST);
RTS_LOCK_SET(dst, RTS_VMREQTARGET);
/* Map to the destination. */
caller->p_vmrequest.req_type = req_type;
caller->p_vmrequest.target = end_d; /* destination proc */
caller->p_vmrequest.params.map.vir_d = lin_dst; /* destination addr */
caller->p_vmrequest.params.map.ep_s = end_s; /* source process */
caller->p_vmrequest.params.map.vir_s = lin_src; /* source address */
caller->p_vmrequest.params.map.length = (vir_bytes) size;
caller->p_vmrequest.params.map.writeflag = flag;
caller->p_vmrequest.type = VMSTYPE_MAP;
/* Connect caller on vmrequest wait queue. */
if(!(caller->p_vmrequest.nextrequestor = vmrequest))
lock_notify(SYSTEM, VM_PROC_NR);
vmrequest = caller;
return OK;
}
/*===========================================================================*
* do_safemap *
*===========================================================================*/
PUBLIC int do_safemap(struct proc * caller, message * m_ptr)
{
endpoint_t grantor = m_ptr->SMAP_EP;
cp_grant_id_t gid = m_ptr->SMAP_GID;
vir_bytes offset = (vir_bytes) m_ptr->SMAP_OFFSET;
int seg = (int) m_ptr->SMAP_SEG;
vir_bytes address = (vir_bytes) m_ptr->SMAP_ADDRESS;
vir_bytes bytes = (vir_bytes) m_ptr->SMAP_BYTES;
int flag = m_ptr->SMAP_FLAG;
vir_bytes offset_result;
endpoint_t new_grantor;
int r;
int access = CPF_MAP | CPF_READ;
/* Check the grant. We currently support safemap with both direct and
* indirect grants, as verify_grant() stores the original grantor
* transparently in new_grantor below. However, we maintain the original
* semantics associated to indirect grants only here at safemap time.
* After the mapping has been set up, if a process part of the chain
* of trust crashes or exits without revoking the mapping, the mapping
* can no longer be manually or automatically revoked for any of the
* processes lower in the chain. This solution reduces complexity but
* could be improved if we make the assumption that only one process in
* the chain of trust can effectively map the original memory region.
*/
if(flag != 0)
access |= CPF_WRITE;
r = verify_grant(grantor, caller->p_endpoint, gid, bytes, access,
offset, &offset_result, &new_grantor);
if(r != OK) {
kprintf("verify_grant for gid %d from %d to %d failed: %d\n",
gid, grantor, caller->p_endpoint, r);
return r;
}
/* Add map info. */
r = add_info(new_grantor, caller->p_endpoint, gid, offset,
offset_result, seg, address, bytes);
if(r != OK)
return r;
/* Invoke VM. */
return map_invoke_vm(caller, VMPTYPE_SMAP,
caller->p_endpoint, seg, address, new_grantor, D, offset_result, bytes,flag);
}
/*===========================================================================*
* safeunmap *
*===========================================================================*/
PRIVATE int safeunmap(struct proc * caller, struct map_info_s *p)
{
vir_bytes offset_result;
endpoint_t new_grantor;
int r;
r = verify_grant(p->grantor, p->grantee, p->gid, p->bytes,
CPF_MAP, p->offset, &offset_result, &new_grantor);
if(r != OK) {
kprintf("safeunmap: error in verify_grant.\n");
return r;
}
r = map_invoke_vm(caller, VMPTYPE_SUNMAP,
p->grantee, p->seg, p->address,
new_grantor, D, offset_result,
p->bytes, 0);
clear_info(p);
if(r != OK) {
kprintf("safeunmap: error in map_invoke_vm.\n");
return r;
}
return OK;
}
/*===========================================================================*
* do_saferevmap *
*===========================================================================*/
PUBLIC int do_saferevmap(struct proc * caller, message * m_ptr)
{
struct map_info_s *p;
int flag = m_ptr->SMAP_FLAG;
int arg = m_ptr->SMAP_GID; /* gid or address_Dseg */
int r;
while((p = get_revoke_info(caller->p_endpoint, flag, arg)) != NULL) {
if((r = safeunmap(caller, p)) != OK)
return r;
}
return OK;
}
/*===========================================================================*
* do_safeunmap *
*===========================================================================*/
PUBLIC int do_safeunmap(struct proc * caller, message * m_ptr)
{
vir_bytes address = m_ptr->SMAP_ADDRESS;
int seg = (int)m_ptr->SMAP_SEG;
struct map_info_s *p;
int r;
while((p = get_unmap_info(caller->p_endpoint, seg, address)) != NULL) {
if((r = safeunmap(caller, p)) != OK)
return r;
}
return OK;
}