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. * Thread Control Block, kernel portion.
* *
* Copyright (C) 2007 Bahadir Balban * Copyright (C) 2007-2009 Bahadir Bilgehan Balban
*/ */
#ifndef __TCB_H__ #ifndef __TCB_H__
#define __TCB_H__ #define __TCB_H__
@@ -17,6 +17,10 @@
#include INC_GLUE(context.h) #include INC_GLUE(context.h)
#include INC_SUBARCH(mm.h) #include INC_SUBARCH(mm.h)
/*
* Bit mappings for the ktcb flags field
*/
/* /*
* These are a mixture of flags that indicate the task is * These are a mixture of flags that indicate the task is
* in a transitional state that could include one or more * 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) */ /* IPC resulted in a fault error (For ipcs that cannot page fault) */
#define IPC_EFAULT (1 << 3) #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 */ /* Task states */
enum task_state { enum task_state {
TASK_INACTIVE = 0, TASK_INACTIVE = 0,
@@ -106,7 +114,8 @@ struct ktcb {
struct waitqueue *wq; struct waitqueue *wq;
/* Extended ipc buffer, points to the space after ktcb */ /* 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. */ /* 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; 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 THREAD_IDS_MAX 1024
#define SPACE_IDS_MAX 1024 #define SPACE_IDS_MAX 1024
#define TGROUP_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; extern struct id_pool *space_id_pool;
struct ktcb *tcb_find(l4id_t tid); struct ktcb *tcb_find(l4id_t tid);
struct ktcb *tcb_find_by_space(l4id_t tid);
void tcb_add(struct ktcb *tcb); void tcb_add(struct ktcb *tcb);
void tcb_remove(struct ktcb *tcb); void tcb_remove(struct ktcb *tcb);

View File

@@ -10,6 +10,7 @@
#include <l4/api/kip.h> #include <l4/api/kip.h>
#include <l4/api/errno.h> #include <l4/api/errno.h>
#include <l4/lib/bit.h> #include <l4/lib/bit.h>
#include <l4/lib/math.h>
#include <l4/generic/kmalloc.h> #include <l4/generic/kmalloc.h>
#include INC_API(syscall.h) #include INC_API(syscall.h)
#include INC_GLUE(message.h) #include INC_GLUE(message.h)
@@ -25,7 +26,7 @@ enum IPC_TYPE {
IPC_SENDRECV = 3, 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_src = KTCB_REF_MR0(from);
unsigned int *mr0_dst = KTCB_REF_MR0(to); 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. */ /* 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 *from_utcb = (struct utcb *)from->utcb_address;
struct utcb *to_utcb = (struct utcb *)to->utcb_address; struct utcb *to_utcb = (struct utcb *)to->utcb_address;
int ret; int ret;
/* First do the short copy of primary mrs */ /* 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; return ret;
/* Check that utcb memory accesses won't fault us */ /* 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) 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) 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 * Extended copy is asymmetric in that the copying always occurs from
* the sender's kernel stack to receivers userspace buffers. * 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 size = min(from->extended_ipc_size,
unsigned long ipc_address; to->extended_ipc_size);
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);
/* /*
* Copy from sender's kernel stack buffer * Copy from sender's kernel stack buffer
* to receiver's paged-in userspace 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; 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 * L4_ANYTHREAD. This is done for security since the receiver cannot trust
* the sender info provided by the sender task. * 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; int ret = 0;
unsigned int *mr0_dst; 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 */ /* 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: case L4_IPC_FLAGS_SHORT:
ret = ipc_short_copy(to, from, flags); ret = ipc_short_copy(to, from);
break; break;
case L4_IPC_FLAGS_FULL: case L4_IPC_FLAGS_FULL:
ret = ipc_full_copy(to, from, flags); ret = ipc_full_copy(to, from);
break; break;
case L4_IPC_FLAGS_EXTENDED: case L4_IPC_FLAGS_EXTENDED:
ret = ipc_extended_copy(to, from, flags); ret = ipc_extended_copy(to, from);
break; break;
} }
@@ -385,12 +389,23 @@ int ipc_recv_extended(l4id_t sendertid, unsigned int flags)
/* Obtain extended ipc address */ /* Obtain extended ipc address */
ipc_address = (unsigned long)mr0_current[msg_index]; 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 */ /* Obtain extended ipc size */
size = extended_ipc_msg_size(flags); size = extended_ipc_msg_size(flags);
/* Check size is good */ /* Check size is good */
if (size > L4_IPC_EXTENDED_MAX_SIZE) if (size > L4_IPC_EXTENDED_MAX_SIZE)
return -EINVAL; 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 */ /* Page fault those pages on the current task if needed */
if ((err = check_access(ipc_address, size, 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 */ /* Check size is good */
if (size > L4_IPC_EXTENDED_MAX_SIZE) if (size > L4_IPC_EXTENDED_MAX_SIZE)
return -EINVAL; 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 */ /* Page fault those pages on the current task if needed */
if ((err = check_access(ipc_address, size, if ((err = check_access(ipc_address, size,
MAP_USR_RW_FLAGS, 1)) < 0) MAP_USR_RW_FLAGS, 1)) < 0)
return err; 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. * It is now safe to access user pages.
* Copy message from user buffer into kernel stack * Copy message from user buffer into kernel stack
@@ -516,7 +543,7 @@ int sys_ipc(syscall_context_t *regs)
int ret = 0; int ret = 0;
/* /*
if (flags) if ((flags & L4_IPC_FLAGS_TYPE_MASK) == L4_IPC_FLAGS_EXTENDED)
__asm__ __volatile__ ( __asm__ __volatile__ (
"1:\n" "1:\n"
"b 1b\n"); "b 1b\n");
@@ -549,6 +576,9 @@ int sys_ipc(syscall_context_t *regs)
goto error; goto error;
} }
/* Encode ipc type in task flags */
tcb_set_ipc_flags(current, flags);
if ((ret = __sys_ipc(to, from, ipc_type, flags)) < 0) if ((ret = __sys_ipc(to, from, ipc_type, flags)) < 0)
goto error; goto error;
return ret; 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 check_access(unsigned long vaddr, unsigned long size, unsigned int flags, int page_in)
{ {
int err; int err;
unsigned long start, end, mapsize;
/* Do not allow ridiculously big sizes */ /* Do not allow ridiculously big sizes */
if (size >= USER_AREA_SIZE) if (size >= USER_AREA_SIZE)
return -EINVAL; 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 */ /* 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? */ /* Is a page in requested? */
if (page_in) { if (page_in) {
/* Ask pager if paging in is possible */ /* 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; return err;
} else } else
return -EFAULT; return -EFAULT;

View File

@@ -182,6 +182,14 @@ int tcb_check_and_lazy_map_utcb(struct ktcb *task)
BUG_ON(!task->utcb_address); 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, if ((ret = check_access(task->utcb_address, UTCB_SIZE,
MAP_SVC_RW_FLAGS, 0)) < 0) { MAP_SVC_RW_FLAGS, 0)) < 0) {
/* Current task simply hasn't mapped its utcb */ /* Current task simply hasn't mapped its utcb */

View File

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

View File

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

View File

@@ -49,6 +49,9 @@ void main(void)
clonetest(); clonetest();
if (parent_of_all == getpid())
ipc_extended_test();
exectest(); exectest();
while (1) 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/arch/syslib.h>
#include <l4lib/ipcdefs.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. * Full ipc test. Sends/receives full utcb, done with the pager.
@@ -15,7 +25,8 @@ void ipc_full_test(void)
} }
/* Call the pager */ /* 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); printf("%s: Failed with %d\n", __FUNCTION__, ret);
BUG(); BUG();
} }
@@ -34,3 +45,100 @@ void ipc_full_test(void)
printf("FULL IPC TEST: -- FAILED --\n"); 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");
}