Added first part of extended ipc support.

- Extended ipc tests
- Need to copy from ktcb-to-ktcb instead of ktcb-to-user
- Need to check flags of both ipc parties before ipc copy type.
This commit is contained in:
Bahadir Balban
2009-05-27 14:07:17 +03:00
parent 19c71cc658
commit 3ff519439b
8 changed files with 222 additions and 41 deletions

View File

@@ -1,7 +1,7 @@
/*
* Thread Control Block, kernel portion.
*
* Copyright (C) 2007 Bahadir Balban
* Copyright (C) 2007-2009 Bahadir Bilgehan Balban
*/
#ifndef __TCB_H__
#define __TCB_H__
@@ -17,6 +17,10 @@
#include INC_GLUE(context.h)
#include INC_SUBARCH(mm.h)
/*
* Bit mappings for the ktcb flags field
*/
/*
* These are a mixture of flags that indicate the task is
* in a transitional state that could include one or more
@@ -29,6 +33,10 @@
/* IPC resulted in a fault error (For ipcs that cannot page fault) */
#define IPC_EFAULT (1 << 3)
/* IPC type is encoded in task flags in bits [7:4] */
#define TASK_FLAGS_IPC_TYPE_MASK 0xF0
#define TASK_FLAGS_IPC_TYPE_SHIFT 4
/* Task states */
enum task_state {
TASK_INACTIVE = 0,
@@ -106,7 +114,8 @@ struct ktcb {
struct waitqueue *wq;
/* Extended ipc buffer, points to the space after ktcb */
char extended_ipc_buffer[];
char *extended_ipc_buffer;
unsigned long extended_ipc_size;
};
/* Per thread kernel stack unified on a single page. */
@@ -129,6 +138,21 @@ static inline void set_task_ids(struct ktcb *task, struct task_ids *ids)
task->tgid = ids->tgid;
}
static inline void tcb_set_ipc_flags(struct ktcb *task,
unsigned int flags)
{
task->flags |= ((flags & L4_IPC_FLAGS_TYPE_MASK)
<< TASK_FLAGS_IPC_TYPE_SHIFT) &
TASK_FLAGS_IPC_TYPE_MASK;
}
static inline unsigned int tcb_get_ipc_flags(struct ktcb *task)
{
return ((task->flags & TASK_FLAGS_IPC_TYPE_MASK)
>> TASK_FLAGS_IPC_TYPE_SHIFT)
& L4_IPC_FLAGS_TYPE_MASK;
}
#define THREAD_IDS_MAX 1024
#define SPACE_IDS_MAX 1024
#define TGROUP_IDS_MAX 1024
@@ -138,7 +162,6 @@ extern struct id_pool *thread_id_pool;
extern struct id_pool *space_id_pool;
struct ktcb *tcb_find(l4id_t tid);
struct ktcb *tcb_find_by_space(l4id_t tid);
void tcb_add(struct ktcb *tcb);
void tcb_remove(struct ktcb *tcb);

View File

@@ -10,6 +10,7 @@
#include <l4/api/kip.h>
#include <l4/api/errno.h>
#include <l4/lib/bit.h>
#include <l4/lib/math.h>
#include <l4/generic/kmalloc.h>
#include INC_API(syscall.h)
#include INC_GLUE(message.h)
@@ -25,7 +26,7 @@ enum IPC_TYPE {
IPC_SENDRECV = 3,
};
int ipc_short_copy(struct ktcb *to, struct ktcb *from, unsigned int flags)
int ipc_short_copy(struct ktcb *to, struct ktcb *from)
{
unsigned int *mr0_src = KTCB_REF_MR0(from);
unsigned int *mr0_dst = KTCB_REF_MR0(to);
@@ -40,14 +41,14 @@ int ipc_short_copy(struct ktcb *to, struct ktcb *from, unsigned int flags)
/* Copy full utcb region from one task to another. */
int ipc_full_copy(struct ktcb *to, struct ktcb *from, unsigned int flags)
int ipc_full_copy(struct ktcb *to, struct ktcb *from)
{
struct utcb *from_utcb = (struct utcb *)from->utcb_address;
struct utcb *to_utcb = (struct utcb *)to->utcb_address;
int ret;
/* First do the short copy of primary mrs */
if ((ret = ipc_short_copy(to, from, flags)) < 0)
if ((ret = ipc_short_copy(to, from)) < 0)
return ret;
/* Check that utcb memory accesses won't fault us */
@@ -65,48 +66,31 @@ int ipc_full_copy(struct ktcb *to, struct ktcb *from, unsigned int flags)
static inline int extended_ipc_msg_index(unsigned int flags)
{
return (flags >> L4_IPC_FLAGS_MSG_INDEX_SHIFT) & L4_IPC_FLAGS_MSG_INDEX_MASK;
return (flags & L4_IPC_FLAGS_MSG_INDEX_MASK)
>> L4_IPC_FLAGS_MSG_INDEX_SHIFT;
}
static inline int extended_ipc_msg_size(unsigned int flags)
{
return (flags >> L4_IPC_FLAGS_SIZE_SHIFT) & L4_IPC_FLAGS_SIZE_MASK;
return (flags & L4_IPC_FLAGS_SIZE_MASK)
>> L4_IPC_FLAGS_SIZE_SHIFT;
}
/*
* Extended copy is asymmetric in that the copying always occurs from
* the sender's kernel stack to receivers userspace buffers.
*/
int ipc_extended_copy(struct ktcb *to, struct ktcb *from, unsigned int flags)
int ipc_extended_copy(struct ktcb *to, struct ktcb *from)
{
unsigned long msg_index;
unsigned long ipc_address;
unsigned int size;
unsigned int *mr0_receiver;
/*
* Obtain primary message register index
* containing extended ipc buffer address
*/
msg_index = extended_ipc_msg_index(flags);
/* Get the pointer to primary message registers */
mr0_receiver = KTCB_REF_MR0(to);
/* Obtain extended ipc address */
ipc_address = (unsigned long)mr0_receiver[msg_index];
/* Obtain extended ipc size */
size = extended_ipc_msg_size(flags);
/* This ought to be checked before coming here */
BUG_ON(size > L4_IPC_EXTENDED_MAX_SIZE);
unsigned long size = min(from->extended_ipc_size,
to->extended_ipc_size);
/*
* Copy from sender's kernel stack buffer
* to receiver's paged-in userspace buffer
*/
memcpy((void *)ipc_address, from->extended_ipc_buffer, size);
memcpy(to->extended_ipc_buffer,
from->extended_ipc_buffer, size);
return 0;
}
@@ -120,21 +104,41 @@ int ipc_extended_copy(struct ktcb *to, struct ktcb *from, unsigned int flags)
* L4_ANYTHREAD. This is done for security since the receiver cannot trust
* the sender info provided by the sender task.
*/
int ipc_msg_copy(struct ktcb *to, struct ktcb *from, unsigned int flags)
int ipc_msg_copy(struct ktcb *to, struct ktcb *from,
unsigned int current_flags)
{
int ret = 0;
unsigned int *mr0_dst;
#if 0
unsigned int recv_ipc_type = tcb_get_ipc_flags(to);
unsigned int send_ipc_type = tcb_get_ipc_flags(from);
if (recv_ipc_type == L4_IPC_FLAGS_FULL ||
send_ipc_type == L4_IPC_FLAGS_FULL) {
ret = ipc_full_copy(to, from);
}
if (recv_ipc_type == L4_IPC_FLAGS_SHORT)
/*
* Check ipc type flags of both parties and use the following rules:
*
* SHORT SHORT -> SHORT IPC
* FULL X -> FULL IPC
* EXTENDED EXTENDED-> EXTENDED IPC
* EXTENDED X -> X IPC
*/
#endif
/* Check type of utcb copying and do it */
switch (flags & L4_IPC_FLAGS_TYPE_MASK) {
switch (current_flags & L4_IPC_FLAGS_TYPE_MASK) {
case L4_IPC_FLAGS_SHORT:
ret = ipc_short_copy(to, from, flags);
ret = ipc_short_copy(to, from);
break;
case L4_IPC_FLAGS_FULL:
ret = ipc_full_copy(to, from, flags);
ret = ipc_full_copy(to, from);
break;
case L4_IPC_FLAGS_EXTENDED:
ret = ipc_extended_copy(to, from, flags);
ret = ipc_extended_copy(to, from);
break;
}
@@ -385,12 +389,23 @@ int ipc_recv_extended(l4id_t sendertid, unsigned int flags)
/* Obtain extended ipc address */
ipc_address = (unsigned long)mr0_current[msg_index];
/* Set extended ipc buffer as the user buffer address */
current->extended_ipc_buffer = (char *)ipc_address;
/* Obtain extended ipc size */
size = extended_ipc_msg_size(flags);
/* Check size is good */
if (size > L4_IPC_EXTENDED_MAX_SIZE)
return -EINVAL;
current->extended_ipc_size = size;
/*
* TODO: We may need to save primary mrs before engaging
* in page fault ipc. Currently after extended ipc address
* is obtained, primaries are not used during the ipc.
* In the future if needed, we should save them.
*/
/* Page fault those pages on the current task if needed */
if ((err = check_access(ipc_address, size,
@@ -436,12 +451,24 @@ int ipc_send_extended(l4id_t recv_tid, unsigned int flags)
/* Check size is good */
if (size > L4_IPC_EXTENDED_MAX_SIZE)
return -EINVAL;
current->extended_ipc_size = size;
/*
* TODO: We may need to save primary mrs before engaging
* in page fault ipc. Currently after extended ipc address
* is obtained, primaries are not used during the ipc.
* In the future if needed, we should save them.
*/
/* Page fault those pages on the current task if needed */
if ((err = check_access(ipc_address, size,
MAP_USR_RW_FLAGS, 1)) < 0)
return err;
/* Set extended ipc buffer as the end of ktcb */
current->extended_ipc_buffer =
(void *)current + sizeof(struct ktcb);
/*
* It is now safe to access user pages.
* Copy message from user buffer into kernel stack
@@ -516,7 +543,7 @@ int sys_ipc(syscall_context_t *regs)
int ret = 0;
/*
if (flags)
if ((flags & L4_IPC_FLAGS_TYPE_MASK) == L4_IPC_FLAGS_EXTENDED)
__asm__ __volatile__ (
"1:\n"
"b 1b\n");
@@ -549,6 +576,9 @@ int sys_ipc(syscall_context_t *regs)
goto error;
}
/* Encode ipc type in task flags */
tcb_set_ipc_flags(current, flags);
if ((ret = __sys_ipc(to, from, ipc_type, flags)) < 0)
goto error;
return ret;

View File

@@ -162,17 +162,24 @@ struct address_space *address_space_create(struct address_space *orig)
int check_access(unsigned long vaddr, unsigned long size, unsigned int flags, int page_in)
{
int err;
unsigned long start, end, mapsize;
/* Do not allow ridiculously big sizes */
if (size >= USER_AREA_SIZE)
return -EINVAL;
/* Get lower and upper page boundaries */
start = page_align(vaddr);
end = page_align_up(vaddr + size);
mapsize = end - start;
/* Check if the address is mapped with given flags */
if (!check_mapping(vaddr, size, flags)) {
if (!check_mapping(start, mapsize, flags)) {
/* Is a page in requested? */
if (page_in) {
/* Ask pager if paging in is possible */
if((err = pager_pagein_request(vaddr, size, flags)) < 0)
if((err = pager_pagein_request(start, mapsize,
flags)) < 0)
return err;
} else
return -EFAULT;

View File

@@ -182,6 +182,14 @@ int tcb_check_and_lazy_map_utcb(struct ktcb *task)
BUG_ON(!task->utcb_address);
/*
* FIXME:
*
* A task may have the utcb mapping of a destroyed thread
* at the given virtual address. This would silently be accepted
* as *mapped*. We need to ensure utcbs of destroyed tasks
* are cleared from all other task's page tables.
*/
if ((ret = check_access(task->utcb_address, UTCB_SIZE,
MAP_SVC_RW_FLAGS, 0)) < 0) {
/* Current task simply hasn't mapped its utcb */

View File

@@ -17,6 +17,7 @@
/*** IPC Tags used between server tasks ***/
/* For ping ponging */
#define L4_IPC_TAG_SYNC_EXTENDED 1
#define L4_IPC_TAG_SYNC_FULL 2
#define L4_IPC_TAG_SYNC 3

View File

@@ -14,6 +14,7 @@
extern pid_t parent_of_all;
void ipc_full_test(void);
void ipc_extended_test(void);
int shmtest(void);
int forktest(void);

View File

@@ -49,6 +49,9 @@ void main(void)
clonetest();
if (parent_of_all == getpid())
ipc_extended_test();
exectest();
while (1)

View File

@@ -1,5 +1,15 @@
/*
* Tests for more complex ipc such as full and extended
*
* Copyright (C) 2007-2009 Bahadir Bilgehan Balban
*/
#include <l4lib/arch/syslib.h>
#include <l4lib/ipcdefs.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
#include <tests.h>
/*
* Full ipc test. Sends/receives full utcb, done with the pager.
@@ -15,7 +25,8 @@ void ipc_full_test(void)
}
/* Call the pager */
if ((ret = l4_sendrecv_full(PAGER_TID, PAGER_TID, L4_IPC_TAG_SYNC_FULL)) < 0) {
if ((ret = l4_sendrecv_full(PAGER_TID, PAGER_TID,
L4_IPC_TAG_SYNC_FULL)) < 0) {
printf("%s: Failed with %d\n", __FUNCTION__, ret);
BUG();
}
@@ -34,3 +45,100 @@ void ipc_full_test(void)
printf("FULL IPC TEST: -- FAILED --\n");
}
/*
* This is an extended ipc test that is done between 2 tasks that fork.
*/
void ipc_extended_test(void)
{
pid_t child, parent;
void *base;
char *ipcbuf;
int err;
/* Get parent pid */
parent = getpid();
/* Fork the current task */
if ((child = fork()) < 0) {
printf("%s: Fork failed with %d\n", __FUNCTION__, errno);
goto out_err;
}
if (child)
printf("%d: Created child with pid %d\n", getpid(), child);
else
printf("Child %d running.\n", getpid());
/* This test makes this assumption */
BUG_ON(L4_IPC_EXTENDED_MAX_SIZE > PAGE_SIZE);
/*
* Both child and parent gets 2 pages
* of privately mapped anonymous memory
*/
if ((int)(base = mmap(0, PAGE_SIZE*2, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) < 0) {
printf("%s: mmap for extended ipc buffer failed: %d\n",
__FUNCTION__, (int)base);
goto out_err;
} else
printf("mmap: Anonymous private buffer at %p\n", base);
/*
* Both tasks read/write both pages
* across the page boundary for messages
*/
ipcbuf = base + PAGE_SIZE - L4_IPC_EXTENDED_MAX_SIZE / 2;
if (!child) {
/* Child creates a string of maximum extended ipc size */
for (int i = 0; i < L4_IPC_EXTENDED_MAX_SIZE; i++) {
ipcbuf[i] = 'A' + i % 20;
}
/* Finish string with newline and null pointer */
ipcbuf[L4_IPC_EXTENDED_MAX_SIZE - 2] = '\n';
ipcbuf[L4_IPC_EXTENDED_MAX_SIZE - 1] = '\0';
/* Send message to parent */
printf("Child sending message: %s\n", ipcbuf);
if ((err = l4_send_extended(parent, L4_IPC_TAG_SYNC_EXTENDED,
L4_IPC_EXTENDED_MAX_SIZE,
ipcbuf)) < 0) {
printf("Extended ipc error: %d\n", err);
goto out_err;
}
} else {
/*
* Parent receives on the buffer - we are sure that the
* buffer is not paged in because we did not touch it.
* This should prove that page faults are handled during
* the ipc.
*/
printf("Parent: extended receiving from child.\n");
if ((err = l4_receive_extended(child, L4_IPC_EXTENDED_MAX_SIZE,
ipcbuf)) < 0) {
printf("Extended ipc error: %d\n", err);
goto out_err;
}
/* We now print the message created by child. */
printf("(%d): Message received from child: %s\n",
getpid(), ipcbuf);
/* Check that child string is there */
for (int i = 0; i < L4_IPC_EXTENDED_MAX_SIZE; i++) {
if (ipcbuf[i] != ('A' + i % 20))
goto out_err;
}
printf("EXTENDED IPC TEST: -- PASSED --\n");
}
return;
out_err:
printf("EXTENDED IPC TEST: -- FAILED --\n");
}