Subversion Repositories HelenOS-historic

Compare Revisions

Ignore whitespace Rev 1326 → Rev 1329

/kernel/trunk/generic/src/mm/as.c
364,18 → 364,18
return 0;
}
 
/** Send address space area to another task.
/** Steal address space area from another task.
*
* Address space area is sent to the specified task.
* If the destination task is willing to accept the
* area, a new area is created according to the
* source area. Moreover, any existing mapping
* Address space area is stolen from another task
* Moreover, any existing mapping
* is copied as well, providing thus a mechanism
* for sharing group of pages. The source address
* space area and any associated mapping is preserved.
*
* @param dst_id Task ID of the accepting task.
* @param src_task Pointer of source task
* @param src_base Base address of the source address space area.
* @param acc_size Expected size of the source area
* @param dst_base Target base address
*
* @return Zero on success or ENOENT if there is no such task or
* if there is no such address space area,
383,51 → 383,28
* ENOMEM if there was a problem in allocating destination
* address space area.
*/
int as_area_send(task_id_t dst_id, __address src_base)
int as_area_steal(task_t *src_task, __address src_base, size_t acc_size,
__address dst_base)
{
ipl_t ipl;
task_t *t;
count_t i;
as_t *dst_as;
__address dst_base;
as_t *src_as;
int src_flags;
size_t src_size;
as_area_t *src_area, *dst_area;
 
ipl = interrupts_disable();
spinlock_lock(&tasks_lock);
spinlock_lock(&src_task->lock);
src_as = src_task->as;
t = task_find_by_id(dst_id);
if (!NULL) {
spinlock_unlock(&tasks_lock);
interrupts_restore(ipl);
return ENOENT;
}
 
spinlock_lock(&t->lock);
spinlock_unlock(&tasks_lock);
 
dst_as = t->as;
dst_base = (__address) t->accept_arg.base;
if (dst_as == AS) {
/*
* The two tasks share the entire address space.
* Return error since there is no point in continuing.
*/
spinlock_unlock(&t->lock);
interrupts_restore(ipl);
return EPERM;
}
spinlock_lock(&AS->lock);
src_area = find_area_and_lock(AS, src_base);
spinlock_lock(&src_as->lock);
src_area = find_area_and_lock(src_as, src_base);
if (!src_area) {
/*
* Could not find the source address space area.
*/
spinlock_unlock(&t->lock);
spinlock_unlock(&AS->lock);
spinlock_unlock(&src_task->lock);
spinlock_unlock(&src_as->lock);
interrupts_restore(ipl);
return ENOENT;
}
434,18 → 411,14
src_size = src_area->pages * PAGE_SIZE;
src_flags = src_area->flags;
spinlock_unlock(&src_area->lock);
spinlock_unlock(&AS->lock);
spinlock_unlock(&src_as->lock);
 
if ((t->accept_arg.task_id != TASK->taskid) || (t->accept_arg.size != src_size) ||
(t->accept_arg.flags != src_flags)) {
/*
* Discrepancy in either task ID, size or flags.
*/
spinlock_unlock(&t->lock);
 
if (src_size != acc_size) {
spinlock_unlock(&src_task->lock);
interrupts_restore(ipl);
return EPERM;
}
/*
* Create copy of the source address space area.
* The destination area is created with AS_AREA_ATTR_PARTIAL
452,28 → 425,27
* attribute set which prevents race condition with
* preliminary as_page_fault() calls.
*/
dst_area = as_area_create(dst_as, src_flags, src_size, dst_base, AS_AREA_ATTR_PARTIAL);
dst_area = as_area_create(AS, src_flags, src_size, dst_base, AS_AREA_ATTR_PARTIAL);
if (!dst_area) {
/*
* Destination address space area could not be created.
*/
spinlock_unlock(&t->lock);
spinlock_unlock(&src_task->lock);
interrupts_restore(ipl);
return ENOMEM;
}
memsetb((__address) &t->accept_arg, sizeof(as_area_acptsnd_arg_t), 0);
spinlock_unlock(&t->lock);
spinlock_unlock(&src_task->lock);
/*
* Avoid deadlock by first locking the address space with lower address.
*/
if (dst_as < AS) {
spinlock_lock(&dst_as->lock);
if (AS < src_as) {
spinlock_lock(&AS->lock);
spinlock_lock(&src_as->lock);
} else {
spinlock_lock(&AS->lock);
spinlock_lock(&dst_as->lock);
spinlock_lock(&src_as->lock);
}
for (i = 0; i < SIZE2FRAMES(src_size); i++) {
480,22 → 452,22
pte_t *pte;
__address frame;
page_table_lock(AS, false);
pte = page_mapping_find(AS, src_base + i*PAGE_SIZE);
page_table_lock(src_as, false);
pte = page_mapping_find(src_as, src_base + i*PAGE_SIZE);
if (pte && PTE_VALID(pte)) {
ASSERT(PTE_PRESENT(pte));
frame = PTE_GET_FRAME(pte);
if (!(src_flags & AS_AREA_DEVICE))
frame_reference_add(ADDR2PFN(frame));
page_table_unlock(AS, false);
page_table_unlock(src_as, false);
} else {
page_table_unlock(AS, false);
page_table_unlock(src_as, false);
continue;
}
page_table_lock(dst_as, false);
page_mapping_insert(dst_as, dst_base + i*PAGE_SIZE, frame, area_flags_to_page_flags(src_flags));
page_table_unlock(dst_as, false);
page_table_lock(AS, false);
page_mapping_insert(AS, dst_base + i*PAGE_SIZE, frame, area_flags_to_page_flags(src_flags));
page_table_unlock(AS, false);
}
 
/*
508,7 → 480,7
spinlock_unlock(&dst_area->lock);
spinlock_unlock(&AS->lock);
spinlock_unlock(&dst_as->lock);
spinlock_unlock(&src_as->lock);
interrupts_restore(ipl);
return 0;
945,6 → 917,25
return true;
}
 
/** Return size of address space of current task pointed to by base */
size_t as_get_size(__address base)
{
ipl_t ipl;
as_area_t *src_area;
size_t size;
 
ipl = interrupts_disable();
src_area = find_area_and_lock(AS, base);
if (src_area){
size = src_area->pages * PAGE_SIZE;
spinlock_unlock(&src_area->lock);
} else {
size = 0;
}
interrupts_restore(ipl);
return size;
}
 
/*
* Address space related syscalls.
*/
970,56 → 961,3
return (__native) as_area_destroy(AS, address);
}
 
/** Prepare task for accepting address space area from another task.
*
* @param uspace_accept_arg Accept structure passed from userspace.
*
* @return EPERM if the task ID encapsulated in @uspace_accept_arg references
* TASK. Otherwise zero is returned.
*/
__native sys_as_area_accept(as_area_acptsnd_arg_t *uspace_accept_arg)
{
as_area_acptsnd_arg_t arg;
int rc;
rc = copy_from_uspace(&arg, uspace_accept_arg, sizeof(as_area_acptsnd_arg_t));
if (rc != 0)
return rc;
if (!arg.size)
return (__native) EPERM;
if (arg.task_id == TASK->taskid) {
/*
* Accepting from itself is not allowed.
*/
return (__native) EPERM;
}
memcpy(&TASK->accept_arg, &arg, sizeof(as_area_acptsnd_arg_t));
return 0;
}
 
/** Wrapper for as_area_send. */
__native sys_as_area_send(as_area_acptsnd_arg_t *uspace_send_arg)
{
as_area_acptsnd_arg_t arg;
int rc;
rc = copy_from_uspace(&arg, uspace_send_arg, sizeof(as_area_acptsnd_arg_t));
if (rc != 0)
return rc;
 
if (!arg.size)
return (__native) EPERM;
if (arg.task_id == TASK->taskid) {
/*
* Sending to itself is not allowed.
*/
return (__native) EPERM;
}
 
return (__native) as_area_send(arg.task_id, (__address) arg.base);
}
/kernel/trunk/generic/src/syscall/syscall.c
86,8 → 86,6
sys_as_area_create,
sys_as_area_resize,
sys_as_area_destroy,
sys_as_area_accept,
sys_as_area_send,
 
/* IPC related syscalls. */
sys_ipc_call_sync_fast,
/kernel/trunk/generic/src/ipc/sysipc.c
40,6 → 40,7
#include <print.h>
#include <syscall/copy.h>
#include <security/cap.h>
#include <mm/as.h>
 
#define GET_CHECK_PHONE(phone,phoneid,err) { \
if (phoneid > IPC_MAX_PHONES) { err; } \
82,11 → 83,13
return 1;
if (IPC_GET_METHOD(call->data) == IPC_M_CONNECT_ME_TO)
return 1;
if (IPC_GET_METHOD(call->data) == IPC_M_AS_SEND)
return 1;
return 0;
}
 
/** Interpret process answer as control information */
static inline void answer_preprocess(call_t *answer, ipc_data_t *olddata)
static inline int answer_preprocess(call_t *answer, ipc_data_t *olddata)
{
int phoneid;
 
105,7 → 108,7
}
 
if (!olddata)
return;
return 0;
 
if (IPC_GET_METHOD(*olddata) == IPC_M_CONNECT_TO_ME) {
phoneid = IPC_GET_ARG3(*olddata);
124,7 → 127,14
ipc_phone_connect((phone_t *)IPC_GET_ARG3(*olddata),
&TASK->answerbox);
}
} else if (IPC_GET_METHOD(*olddata) == IPC_M_AS_SEND) {
if (!IPC_GET_RETVAL(answer->data)) { /* Accepted, handle As_area receival */
return as_area_steal(answer->sender,
IPC_GET_ARG2(*olddata),IPC_GET_ARG3(*olddata),
IPC_GET_ARG1(answer->data));
}
}
return 0;
}
 
/** Called before the request is sent
134,6 → 144,7
static int request_preprocess(call_t *call)
{
int newphid;
size_t size;
 
switch (IPC_GET_METHOD(call->data)) {
case IPC_M_CONNECT_ME_TO:
145,6 → 156,13
call->flags |= IPC_CALL_CONN_ME_TO;
call->private = newphid;
break;
case IPC_M_AS_SEND:
size = as_get_size(IPC_GET_ARG2(call->data));
if (!size) {
return EPERM;
}
IPC_SET_ARG3(call->data, size);
break;
default:
break;
}
376,6 → 394,7
call_t *call;
ipc_data_t saved_data;
int saveddata = 0;
int rc;
 
call = get_call(callid);
if (!call)
389,10 → 408,10
IPC_SET_RETVAL(call->data, retval);
IPC_SET_ARG1(call->data, arg1);
IPC_SET_ARG2(call->data, arg2);
answer_preprocess(call, saveddata ? &saved_data : NULL);
rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
 
ipc_answer(&TASK->answerbox, call);
return 0;
return rc;
}
 
/** Send IPC answer */
416,11 → 435,11
if (rc != 0)
return rc;
 
answer_preprocess(call, saveddata ? &saved_data : NULL);
rc = answer_preprocess(call, saveddata ? &saved_data : NULL);
ipc_answer(&TASK->answerbox, call);
 
return 0;
return rc;
}
 
/** Hang up the phone